def _create_UI(self): QtWidgets.QVBoxLayout(self) o = common.MARGIN() self.layout().setContentsMargins(o, o * 0.5, o, o) self.layout().setSpacing(0) row = common_ui.add_row(None, padding=None, parent=self) self.hide_button = common_ui.ClickableIconButton( u'close', (common.REMOVE, common.REMOVE), common.ROW_HEIGHT() * 0.6) label = common_ui.PaintedLabel(u'Preferences', size=common.LARGE_FONT_SIZE()) row.layout().addWidget(label, 0) row.layout().addStretch(1) row.layout().addWidget(self.hide_button, 0) self.hide_button.clicked.connect( lambda: self.done(QtWidgets.QDialog.Rejected)) splitter = QtWidgets.QSplitter(parent=self) self.layout().addWidget(splitter) self.sections_list_widget = SectionSwitcherWidget(parent=self) splitter.addWidget(self.sections_list_widget) scroll_area = QtWidgets.QScrollArea(parent=self) scroll_area.setWidgetResizable(True) splitter.addWidget(scroll_area) self.sections_stack_widget = SectionsStackWidget(parent=self) scroll_area.setWidget(self.sections_stack_widget)
def _create_UI(self): QtWidgets.QVBoxLayout(self) o = common.MARGIN() self.layout().setContentsMargins(o, o, o, o) self.layout().setSpacing(common.INDICATOR_WIDTH()) height = common.ROW_HEIGHT() * 0.7 row = common_ui.add_row(None, height=height, padding=None, parent=self) self.channel_button = common_ui.ClickableIconButton( u'slack', (common.TEXT, common.TEXT), height, ) label = common_ui.PaintedLabel(u'Send Message', size=common.LARGE_FONT_SIZE(), parent=row) label.setFixedHeight(height) self.hide_button = common_ui.ClickableIconButton( u'close', (common.REMOVE, common.REMOVE), height, parent=row) row.layout().addWidget(label, 0) row.layout().addStretch(1) row.layout().addWidget(self.channel_button, 0) row.layout().addWidget(self.hide_button, 0) self.message_widget = MessageWidget(self.token, parent=self) self.layout().addSpacing(o * 0.5) self.layout().addWidget(self.message_widget) self.send_button = common_ui.PaintedButton(u'Send', parent=self) self.layout().addSpacing(o) self.layout().addWidget(self.send_button) self.layout().addSpacing(o * 0.5)
def add_section(label, description, data): """Utility method for creating the layout needed to edit default paths.""" height = common.ROW_HEIGHT() * 0.8 o = common.MARGIN() grp = common_ui.get_group(parent=self) grp.layout().setContentsMargins(o, o, o, o) grp.layout().setSpacing(0) label = common_ui.PaintedLabel(label, size=common.LARGE_FONT_SIZE(), parent=self) grp.layout().addWidget(label) grp.layout().addSpacing(o) if description: common_ui.add_description(description, label=None, parent=grp) grp.layout().addSpacing(o) scroll_area = QtWidgets.QScrollArea(parent=self) scroll_area.setWidgetResizable(True) scroll_area.setMaximumHeight(common.HEIGHT() * 0.66) scroll_area.setAttribute(QtCore.Qt.WA_NoBackground) scroll_area.setAttribute(QtCore.Qt.WA_TranslucentBackground) grp.layout().addWidget(scroll_area) _row = common_ui.add_row(None, vertical=True, padding=None, height=None, parent=grp) _row.layout().setContentsMargins(0, 0, 0, 0) _row.layout().setSpacing(0) scroll_area.setWidget(_row) for k, v in sorted(data.items()): label = u'<span style="color:rgba({ADD});">{k}</span> - {v}:'.format( ADD=common.rgb(common.ADD), k=k.upper(), v=v[u'description']) row = common_ui.add_row(None, padding=None, height=height, parent=_row) common_ui.add_description(label, label=u'', parent=row) row = common_ui.add_row(None, padding=None, height=height, parent=_row) line_edit = common_ui.add_line_edit(v[u'default'], parent=row) line_edit.setAlignment(QtCore.Qt.AlignLeft) line_edit.setText(v[u'value']) line_edit.textChanged.connect( functools.partial(text_changed, data, k))
def _create_UI(self): QtWidgets.QVBoxLayout(self) o = common.MARGIN() * 2 self.layout().setContentsMargins(o, o, o, o) self.layout().setSpacing(0) self.layout().setAlignment(QtCore.Qt.AlignCenter) row = add_row(None, parent=self, padding=0, height=common.ROW_HEIGHT()) icon = ClickableIconButton( u'filter', (common.REMOVE, common.REMOVE), common.ROW_HEIGHT() ) label = u'Search filter' label = PaintedLabel(label, parent=self) self.editor_widget = LineEdit(parent=self) row.layout().addWidget(icon, 0) row.layout().addWidget(label, 0) row.layout().addWidget(self.editor_widget, 1) self.layout().addStretch(1)
def _add_sections(self): """Adds the sections defined in the ``SECTIONS`` variable.""" for s in get_sections(): item = QtWidgets.QListWidgetItem() item.setData(QtCore.Qt.DisplayRole, u' {}'.format(s[u'name'].title())) item.setData(common.DescriptionRole, s[u'description']) item.setData(QtCore.Qt.StatusTipRole, s[u'description']) item.setData(QtCore.Qt.ToolTipRole, s[u'description']) item.setData(QtCore.Qt.SizeHintRole, QtCore.QSize(0, common.ROW_HEIGHT() * 0.66)) self.sections_list_widget.addItem(item) self.sections_stack_widget.addWidget(s[u'cls'](parent=self))
def __init__(self, args): super(StandaloneApp, self).__init__(args) import bookmarks self.setApplicationVersion(bookmarks.__version__) self.setApplicationName(common.PRODUCT) self.set_model_id() common.font_db = common.FontDatabase() pixmap = images.ImageCache.get_rsc_pixmap(u'icon', None, common.ROW_HEIGHT() * 7.0) icon = QtGui.QIcon(pixmap) self.setWindowIcon(icon)
def add_label(text, parent=None): """Utility method for creating a label. Returns: QLabel: label widget. """ label = QtWidgets.QLabel(text, parent=parent) label.setFixedHeight(common.ROW_HEIGHT()) label.setSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding ) label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft) parent.layout().addWidget(label, 0)
def add_name_template(): height = common.ROW_HEIGHT() * 0.8 o = common.MARGIN() grp = common_ui.get_group(parent=self) grp.layout().setContentsMargins(o, o, o, o) grp.layout().setSpacing(0) label = common_ui.PaintedLabel(u'Name template', size=common.LARGE_FONT_SIZE(), parent=grp) grp.layout().addWidget(label) grp.layout().addSpacing(o) label = u'<span style="color:rgba({ADD});">File name pattern</span> - {v}:'.format( ADD=common.rgb(common.ADD), v=u'The template used to generate new file names') row = common_ui.add_row(None, padding=None, height=height, parent=grp) common_ui.add_description(label, label=u'', parent=row) row = common_ui.add_row(None, padding=None, height=height, parent=grp) line_edit = common_ui.add_line_edit(defaultpaths.FILE_NAME_PATTERN, parent=row) line_edit.textChanged.connect( functools.partial(text_changed, defaultpaths.FILE_NAME_PATTERN, u'defaultpaths/filenamepattern')) line_edit.setAlignment(QtCore.Qt.AlignLeft) line_edit.setText(defaultpaths.FILE_NAME_PATTERN) s = \ u'Available tokens<br><br>\ <span style="color:rgba({ADD});">{{folder}}</span> - The destination folder<br>\ <span style="color:rgba({ADD});">{{prefix}}</span> - Prefix defined by the bookmark<br>\ <span style="color:rgba({ADD});">{{asset}}</span> - Asset name<br>\ <span style="color:rgba({ADD});">{{mode}}</span> - Selected mode (see below)<br>\ <span style="color:rgba({ADD});">{{user}}</span> - Name of the current user<br>\ <span style="color:rgba({ADD});">{{version}}</span> - Version number<br>\ <span style="color:rgba({ADD});">{{ext}}</span> - File extension' .format( ADD=common.rgb(common.ADD), v=u'The template used to generate new file names' ) grp.layout().addSpacing(o) common_ui.add_description(s, label='', parent=grp)
def _create_UI(self): o = 0 top_widget = QtWidgets.QWidget(parent=self) QtWidgets.QVBoxLayout(top_widget) top_widget.layout().setContentsMargins(o, o, o, o) top_widget.layout().setSpacing(0) self.addWidget(top_widget) self.message_widget = QtWidgets.QTextEdit(parent=self) _o = common.MARGIN() self.message_widget.document().setDocumentMargin(_o) self.message_widget.setPlaceholderText(u'Enter a message to send...') self.message_widget.setAcceptRichText(False) self.message_widget.moveCursor(QtGui.QTextCursor.End) top_widget.layout().addWidget(self.message_widget, 0) bottom_widget = QtWidgets.QWidget(parent=self) o = common.MARGIN() * 0.5 height = common.ROW_HEIGHT() QtWidgets.QVBoxLayout(bottom_widget) bottom_widget.layout().setContentsMargins(o, 0, o, 0) bottom_widget.layout().setSpacing(0) row = common_ui.add_row(u'', height=height, parent=bottom_widget) row.layout().setAlignment(QtCore.Qt.AlignBottom) label = common_ui.PaintedLabel(u'Channels & Direct Messages', parent=self) row.layout().addWidget(label, 0) row = common_ui.add_row(u'', height=height, parent=bottom_widget) row.layout().setAlignment(QtCore.Qt.AlignBottom) self.user_filter = common_ui.LineEdit(parent=self) self.user_filter.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight) self.user_filter.setPlaceholderText(u'Search...') row.layout().addWidget(self.user_filter, 1) self.users_widget = UsersWidget(self.token, parent=self) bottom_widget.layout().addWidget(self.users_widget) self.addWidget(bottom_widget) self.addWidget(top_widget) self.setSizes([common.WIDTH() * 0.08, common.WIDTH() * 0.2])
def paintEvent(self, event): painter = QtGui.QPainter() painter.begin(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) pen = QtGui.QPen(common.SEPARATOR) pen.setWidthF(common.ROW_SEPARATOR()) painter.setPen(pen) o = common.MARGIN() rect = self.rect().marginsRemoved(QtCore.QMargins(o, o, o, o)) rect.setHeight(common.ROW_HEIGHT() + (common.MARGIN() * 2)) painter.setBrush(common.SECONDARY_BACKGROUND) painter.setOpacity(0.9) painter.drawRoundedRect( rect, common.INDICATOR_WIDTH(), common.INDICATOR_WIDTH()) painter.end()
def add_row(label, parent=None, padding=common.MARGIN(), height=common.ROW_HEIGHT(), cls=None, vertical=False): """Utility method for creating a row widget. Returns: QWidget: row widget. """ if cls: w = cls(parent=parent) else: w = QtWidgets.QWidget(parent=parent) if vertical: QtWidgets.QVBoxLayout(w) else: QtWidgets.QHBoxLayout(w) w.layout().setContentsMargins(0, 0, 0, 0) w.layout().setSpacing(common.INDICATOR_WIDTH()) w.layout().setAlignment(QtCore.Qt.AlignCenter) w.setSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding, ) if height: w.setFixedHeight(height) w.setAttribute(QtCore.Qt.WA_NoBackground) w.setAttribute(QtCore.Qt.WA_TranslucentBackground) if label: l = PaintedLabel(label, size=common.SMALL_FONT_SIZE(), color=common.SECONDARY_TEXT, parent=parent) l.setFixedWidth(common.MARGIN() * 6.6667) l.setDisabled(True) if padding: w.layout().addSpacing(padding) w.layout().addWidget(l, 0) if parent: parent.layout().addWidget(w, 1) return w
def _create_UI(self): QtWidgets.QVBoxLayout(self) o = common.MARGIN() self.layout().setContentsMargins(o, o, o, o) self.layout().setSpacing(0) height = common.ROW_HEIGHT() * 0.8 # ******************************************** row = common_ui.add_row(None, padding=None, parent=self) self.save_button = common_ui.ClickableIconButton( u'check', (common.ADD, common.ADD), height) bookmark = u'{}/{}/{}'.format(self.server, self.job, self.root) source = images.get_thumbnail_path(self.server, self.job, self.root, bookmark) pixmap = images.ImageCache.get_pixmap(source, row.height()) if not pixmap: source = images.get_placeholder_path( bookmark, fallback=u'thumb_bookmark_gray') pixmap = images.ImageCache.get_pixmap(source, row.height()) if pixmap: thumbnail = QtWidgets.QLabel(parent=self) thumbnail.setPixmap(pixmap) row.layout().addWidget(thumbnail, 0) row.layout().addSpacing(o * 0.5) text = u'{} | {}'.format(self.job.upper(), self.root.upper()) label = common_ui.PaintedLabel(text, size=common.LARGE_FONT_SIZE()) row.layout().addWidget(label) row.layout().addStretch(1) row.layout().addWidget(self.save_button, 0) self.scrollarea = ScrollArea(parent=self) self.layout().addSpacing(o * 0.5) self.layout().addWidget(self.scrollarea) self.save_button.clicked.connect( lambda: self.done(QtWidgets.QDialog.Accepted))
def data(self, index, role): # pylint: disable=W0613 """Name data.""" if not index.isValid(): return None node = index.internalPointer() if role == QtCore.Qt.DisplayRole: if u'ABC.childBnds' in node.name: return self._name + u'.childBnds' return node.name if role == QtCore.Qt.DecorationRole: if u'.childBnds' in node.name: return images.ImageCache.get_rsc_pixmap(u'abc', None, common.MARGIN()) if u'.geom' in node.name: return images.ImageCache.get_rsc_pixmap(u'mesh', None, common.MARGIN()) if u'.xform' in node.name: return images.ImageCache.get_rsc_pixmap(u'loc', None, common.MARGIN()) if role == QtCore.Qt.SizeHintRole: return QtCore.QSize(0, common.ROW_HEIGHT()) return None
class TaskFolderModel(BaseModel): """This model holds all the necessary data needed to display items to select for selecting the asset subfolders and/or bookmarks and assets. The model keeps track of the selections internally and is updated via the signals and slots.""" ROW_SIZE = QtCore.QSize(1, common.ROW_HEIGHT() * 0.8) queue_type = threads.TaskFolderInfoQueue def __init__(self, parent=None): self._parent = parent super(TaskFolderModel, self).__init__(parent=parent) self.modelDataResetRequested.connect(self.__resetdata__) def initialise_threads(self): """Starts and connects the threads.""" @QtCore.Slot(QtCore.QThread) def thread_started(thread): """Signals the model an item has been updated.""" cnx = QtCore.Qt.QueuedConnection thread.updateRow.connect(self.updateRow, QtCore.Qt.DirectConnection) self.startCheckQueue.connect(lambda: log.debug( 'startCheckQueue -> worker.startCheckQueue', self)) self.startCheckQueue.connect(thread.startCheckQueue, cnx) self.stopCheckQueue.connect(lambda: log.debug( 'stopCheckQueue -> worker.stopCheckQueue', self)) self.stopCheckQueue.connect(thread.stopCheckQueue, cnx) self.startCheckQueue.emit() info_worker = threads.TaskFolderWorker(threads.TaskFolderInfoQueue) info_thread = threads.BaseThread(info_worker) self.threads[common.InfoThread].append(info_thread) info_thread.started.connect(partial(thread_started, info_thread)) info_thread.start() @property def parent_path(self): """We will use the currently active asset as the parent.""" if self.view.parent(): view = self.view.parent().parent().parent().fileswidget return view.model().sourceModel().parent_path return None @parent_path.setter def parent_path(self, val): pass def data_type(self): return common.FileItem def sort_data(self): """This model is always alphabetical.""" pass @initdata def __initdata__(self): """Bookmarks and assets are static. But files will be any number of """ task_folder = self.task_folder() self.INTERNAL_MODEL_DATA[task_folder] = common.DataDict({ common.FileItem: common.DataDict(), common.SequenceItem: common.DataDict() }) flags = (QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEditable) data = self.model_data() if not self.parent_path: return # Thumbnail image default_thumbnail = images.ImageCache.get_rsc_pixmap( u'folder_sm', common.SECONDARY_TEXT, self.ROW_SIZE.height()) default_thumbnail = default_thumbnail.toImage() parent_path = u'/'.join(self.parent_path) entries = sorted(([f for f in _scandir.scandir(parent_path)]), key=lambda x: x.name) for entry in entries: if entry.name.startswith(u'.'): continue if not entry.is_dir(): continue idx = len(data) data[idx] = common.DataDict({ QtCore.Qt.DisplayRole: entry.name, QtCore.Qt.EditRole: entry.name, QtCore.Qt.StatusTipRole: entry.path.replace(u'\\', u'/'), QtCore.Qt.ToolTipRole: u'', QtCore.Qt.ToolTipRole: defaultpaths.get_description(entry.name), QtCore.Qt.SizeHintRole: self.ROW_SIZE, # common.FlagsRole: flags, common.ParentPathRole: self.parent_path, # common.FileInfoLoaded: False, common.ThumbnailLoaded: True, common.TodoCountRole: 0, # common.IdRole: idx, }) thread = self.threads[common.InfoThread][0] thread.add_to_queue(weakref.ref(data[idx]))
def _create_UI(self): def get_row(vertical=False, parent=None): row = QtWidgets.QWidget(parent=parent) if vertical: QtWidgets.QVBoxLayout(row) else: QtWidgets.QHBoxLayout(row) row.layout().setContentsMargins(0, 0, 0, 0) row.layout().setSpacing(0) row.setSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding, ) parent.layout().addWidget(row) return row QtWidgets.QHBoxLayout(self) o = 0 self.layout().setContentsMargins(o, o, o, o) self.layout().setSpacing(o) main_row = get_row(parent=self) main_row.layout().setContentsMargins(0, 0, 0, 0) main_row.layout().setSpacing(0) columns = get_row(vertical=True, parent=main_row) columns.layout().setContentsMargins(0, 0, 0, 0) columns.layout().setSpacing(0) short_text_row = get_row(parent=columns) columns.layout().addWidget(short_text_row, 1) long_text_row = get_row(parent=columns) pixmap = images.ImageCache.get_rsc_pixmap( self.icon, self.secondary_color.lighter(150), common.ROW_HEIGHT()) label = QtWidgets.QLabel(parent=self) label.setPixmap(pixmap) label.setSizePolicy( QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding, ) label.setStyleSheet( u'padding: {}px; background-color: rgba({});'.format( common.MEDIUM_FONT_SIZE(), common.rgb(self.primary_color) ) ) main_row.layout().insertWidget(0, label) short_text_row.layout().addWidget(self.short_text_label) self.short_text_label.setStyleSheet( u'padding:{m}px {s}px {m}px {s}px; background-color: rgba({c}); font-size: {s}px;'.format( m=common.MARGIN(), c=common.rgb(self.secondary_color.lighter(125)), s=common.MEDIUM_FONT_SIZE() )) self.short_text_label.setAlignment(QtCore.Qt.AlignLeft) long_text_row.layout().addWidget(self.long_text_label) self.long_text_label.setStyleSheet( u'padding:{m}px;background-color: rgba({c}); font-size:{s}px;'.format( m=common.MARGIN(), c=common.rgb(self.secondary_color), s=common.SMALL_FONT_SIZE() )) self.long_text_label.setAlignment(QtCore.Qt.AlignLeft) buttons_row = get_row(parent=columns) buttons_row.setStyleSheet( u'background-color: rgba({});'.format(common.rgb(self.secondary_color))) self.ok_button = QtWidgets.QPushButton(u'Ok', parent=self) buttons_row.layout().addWidget(self.ok_button) self.ok_button.setStyleSheet( """ QPushButton {{ font-size: {px}px; color: rgba(255,255,255,150); border-radius: {i}px; border: {s}px solid {c}; margin: {i}px; padding: {i}px; background-color: rgba({p}); }} QPushButton:hover {{ color: white; background-color: rgba({pl}); }} QPushButton:pressed {{ color: rgba(255,255,255,150); background-color: rgba({pd}); }} """.format( px=common.SMALL_FONT_SIZE(), i=common.INDICATOR_WIDTH(), s=common.ROW_SEPARATOR(), c=common.rgb(self.secondary_color.lighter(150)), p=common.rgb(self.primary_color), pl=common.rgb(self.primary_color.lighter(120)), pd=common.rgb(self.primary_color.darker(120)) ) )
def _create_UI(self): """Creates the ui layout.""" QtWidgets.QVBoxLayout(self) o = common.MARGIN() self.layout().setSpacing(common.INDICATOR_WIDTH()) self.layout().setContentsMargins(o, o, o, o) # Top row height = common.ROW_HEIGHT() * 0.6666 row = common_ui.add_row(None, height=height, parent=self) row.layout().addSpacing(height * 0.33) def paintEvent(event): painter = QtGui.QPainter() painter.begin(row) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtGui.QColor(0, 0, 0, 255)) rect = row.rect() rect.setTop(rect.bottom()) painter.drawRect(rect) painter.end() # row.paintEvent = paintEvent # Thumbnail self.add_button = common_ui.ClickableIconButton( u'add', (common.ADD, common.ADD), height, description=u'Click to add a new Todo item...', parent=self) # Name label text = u'Notes and Tasks' label = common_ui.PaintedLabel(text, color=common.SEPARATOR, size=common.LARGE_FONT_SIZE(), parent=self) row.layout().addWidget(label, 1) row.layout().addStretch(1) self.refresh_button = common_ui.ClickableIconButton( u'refresh', (QtGui.QColor(0, 0, 0, 255), QtGui.QColor(0, 0, 0, 255)), height, description=u'Refresh...', parent=self) self.refresh_button.clicked.connect(self.refresh) row.layout().addWidget(self.refresh_button, 0) self.remove_button = common_ui.ClickableIconButton( u'close', (QtGui.QColor(0, 0, 0, 255), QtGui.QColor(0, 0, 0, 255)), height, description=u'Refresh...', parent=self) self.remove_button.clicked.connect(self.close) row.layout().addWidget(self.remove_button, 0) row = common_ui.add_row(None, height=height, parent=self) text = u'Add new note' label = common_ui.PaintedLabel(text, color=common.SECONDARY_TEXT, size=common.SMALL_FONT_SIZE(), parent=self) row.layout().addWidget(self.add_button, 0) row.layout().addWidget(label, 0) row.layout().addStretch(1) self.add_button.clicked.connect(lambda: self.add_item(idx=0)) self.todoeditors_widget = TodoEditors(parent=self) self.setMinimumHeight(common.ROW_HEIGHT() * 3.0) self.scrollarea = QtWidgets.QScrollArea(parent=self) self.scrollarea.setWidgetResizable(True) self.scrollarea.setWidget(self.todoeditors_widget) self.scrollarea.setAttribute(QtCore.Qt.WA_NoSystemBackground) self.scrollarea.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.layout().addWidget(self.scrollarea)
def __init__(self, text, width=None, parent=None): super(PaintedButton, self).__init__(text, parent=parent) self.setFixedHeight(common.ROW_HEIGHT() * 0.7) if width: self.setFixedWidth(width)
class FilesModel(baselist.BaseModel): """The model used store individual and file sequences found in `parent_path`. File data is saved ``self.INTERNAL_MODEL_DATA`` using the **task folder**, and **data_type** keys. .. code-block:: python data = self.model_data() # the currently exposed dataset print data == self.INTERNAL_MODEL_DATA[self.task_folder()][self.data_type()] # True """ DEFAULT_ROW_SIZE = QtCore.QSize(1, common.ROW_HEIGHT()) val = settings.local_settings.value(u'widget/FilesModel/rowheight') val = val if val else DEFAULT_ROW_SIZE.height() val = DEFAULT_ROW_SIZE.height() if val < DEFAULT_ROW_SIZE.height() else val ROW_SIZE = QtCore.QSize(1, val) queue_type = threads.FileInfoQueue thumbnail_queue_type = threads.FileThumbnailQueue def _entry_iterator(self, path): for entry in _scandir.scandir(path): if entry.is_dir(): for _entry in self._entry_iterator(entry.path): yield _entry else: yield entry @baselist.initdata def __initdata__(self): """The method is responsible for getting the bare-bones file and sequence definitions by running a file-iterator stemming from ``self.parent_path``. Getting all additional information, like description, item flags, thumbnails are costly and therefore are populated by thread-workers. The method will iterate through all files in every subfolder and will automatically save individual ``FileItems`` and collapsed ``SequenceItems``. Switching between the two datasets is done via emitting the ``dataTypeChanged`` signal. Note: Experiencing serious performance issues with the built-in QDirIterator on Mac OS X samba shares and the performance isn't great on windows either. Querrying the filesystem using the method is magnitudes slower than using the same methods on windows. A workaround I found was to use Python 3+'s ``scandir`` module. Both on Windows and Mac OS X the performance seems to be good. Internally, the actual files are returned by `self._entry_iterator()`, this is where scandir is evoked. """ def dflags(): """The default flags to apply to the item.""" return (QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) task_folder = self.task_folder().lower() SEQUENCE_DATA = common.DataDict() MODEL_DATA = common.DataDict({ common.FileItem: common.DataDict(), common.SequenceItem: common.DataDict(), }) favourites = settings.local_settings.favourites() sfavourites = set(favourites) activefile = settings.local_settings.value(u'activepath/file') server, job, root, asset = self.parent_path task_folder_extensions = defaultpaths.get_task_folder_extensions( task_folder) parent_path = u'/'.join(self.parent_path).lower() + \ u'/' + task_folder nth = 987 c = 0 if not QtCore.QFileInfo(parent_path).exists(): return for entry in self._entry_iterator(parent_path): if self._interrupt_requested: break # skipping directories if entry.is_dir(): continue filename = entry.name.lower() if filename[0] == u'.': continue if u'thumbs.db' in filename: continue filepath = entry.path.lower().replace(u'\\', u'/') ext = filename.split(u'.')[-1] if task_folder_extensions and ext not in task_folder_extensions: continue # Progress bar c += 1 if not c % nth: self.progressMessage.emit(u'Loading files (found ' + unicode(c) + u' items)...') QtWidgets.QApplication.instance().processEvents() # Getting the fileroot fileroot = filepath.replace(parent_path, u'') fileroot = u'/'.join(fileroot.split(u'/')[:-1]).strip(u'/') seq = common.get_sequence(filepath) flags = dflags() if seq: seqpath = seq.group(1) + common.SEQPROXY + \ seq.group(3) + u'.' + seq.group(4) seqpath = seqpath.lower() if seqpath in sfavourites: flags = flags | common.MarkedAsFavourite else: if filepath in sfavourites: flags = flags | common.MarkedAsFavourite if activefile: if activefile in filepath: flags = flags | common.MarkedAsActive parent_path_role = (server, job, root, asset, task_folder, fileroot) # Let's limit the maximum number of items we load idx = len(MODEL_DATA[common.FileItem]) if idx >= common.MAXITEMS: break MODEL_DATA[common.FileItem][idx] = common.DataDict({ QtCore.Qt.DisplayRole: filename, QtCore.Qt.EditRole: filename, QtCore.Qt.StatusTipRole: filepath, QtCore.Qt.SizeHintRole: self.ROW_SIZE, # common.EntryRole: [ entry, ], common.FlagsRole: flags, common.ParentPathRole: parent_path_role, common.DescriptionRole: u'', common.TodoCountRole: 0, common.FileDetailsRole: u'', common.SequenceRole: seq, common.FramesRole: [], common.FileInfoLoaded: False, common.StartpathRole: None, common.EndpathRole: None, # common.ThumbnailLoaded: False, # common.TypeRole: common.FileItem, # common.SortByNameRole: common.namekey(filepath), common.SortByLastModifiedRole: 0, common.SortBySizeRole: 0, # common.IdRole: idx # non-mutable }) # If the file in question is a sequence, we will also save a reference # to it in `self._model_data[location][True]` dictionary. if seq: # If the sequence has not yet been added to our dictionary # of seqeunces we add it here if seqpath not in SEQUENCE_DATA: # ... and create it if it doesn't exist seqname = seqpath.split(u'/')[-1] flags = dflags() if seqpath in sfavourites: flags = flags | common.MarkedAsFavourite SEQUENCE_DATA[seqpath] = common.DataDict({ QtCore.Qt.DisplayRole: seqname, QtCore.Qt.EditRole: seqname, QtCore.Qt.StatusTipRole: seqpath, QtCore.Qt.SizeHintRole: self.ROW_SIZE, common.EntryRole: [], common.FlagsRole: flags, common.ParentPathRole: parent_path_role, common.DescriptionRole: u'', common.TodoCountRole: 0, common.FileDetailsRole: u'', common.SequenceRole: seq, common.FramesRole: [], common.FileInfoLoaded: False, common.StartpathRole: None, common.EndpathRole: None, # common.ThumbnailLoaded: False, # common.TypeRole: common.SequenceItem, common.SortByNameRole: common.namekey(seqpath), common.SortByLastModifiedRole: 0, common.SortBySizeRole: 0, # Initializing with null-size # common.IdRole: 0 }) SEQUENCE_DATA[seqpath][common.FramesRole].append(seq.group(2)) SEQUENCE_DATA[seqpath][common.EntryRole].append(entry) else: SEQUENCE_DATA[filepath] = MODEL_DATA[common.FileItem][idx] # Casting the sequence data back onto the model for v in SEQUENCE_DATA.itervalues(): idx = len(MODEL_DATA[common.SequenceItem]) if len(v[common.FramesRole]) == 1: # A sequence with only one element is not a sequence _seq = v[common.SequenceRole] filepath = _seq.group(1) + v[common.FramesRole][ 0] + _seq.group(3) + u'.' + _seq.group(4) filename = filepath.split(u'/')[-1] v[QtCore.Qt.DisplayRole] = filename v[QtCore.Qt.EditRole] = filename v[QtCore.Qt.StatusTipRole] = filepath v[common.TypeRole] = common.FileItem v[common.SortByNameRole] = common.namekey(filepath) v[common.SortByLastModifiedRole] = 0 flags = dflags() if filepath.lower() in sfavourites: flags = flags | common.MarkedAsFavourite if activefile: if activefile in filepath: flags = flags | common.MarkedAsActive v[common.FlagsRole] = flags elif len(v[common.FramesRole]) == 0: v[common.TypeRole] = common.FileItem else: if activefile: _seq = v[common.SequenceRole] _firsframe = _seq.group(1) + min( v[common.FramesRole]) + _seq.group( 3) + u'.' + _seq.group(4) if activefile in _firsframe: v[common.FlagsRole] = v[ common.FlagsRole] | common.MarkedAsActive MODEL_DATA[common.SequenceItem][idx] = v MODEL_DATA[common.SequenceItem][idx][common.IdRole] = idx self.INTERNAL_MODEL_DATA[task_folder] = MODEL_DATA def task_folder(self): """Current key to the data dictionary.""" if not self._task_folder: val = None key = u'activepath/task_folder' savedval = settings.local_settings.value(key) return savedval.lower() if savedval else val return self._task_folder @QtCore.Slot(unicode) def set_task_folder(self, val): """Slot used to save task folder to the model instance and the local settings. Each subfolder inside the root folder, defined by``parent_path``, corresponds to a `key`. We use these keys to save model data associated with these folders. It's important to make sure the key we're about to set corresponds to an existing folder. We will use a reasonable default if the folder does not exist. """ log.debug('set_task_folder({})'.format(val), self) try: k = u'activepath/task_folder' stored_value = settings.local_settings.value(k) stored_value = stored_value.lower( ) if stored_value else stored_value self._task_folder = self._task_folder.lower( ) if self._task_folder else self._task_folder val = val.lower() if val else val # Nothing to do for us when the parent is not set if not self.parent_path: return if self._task_folder is None and stored_value: self._task_folder = stored_value.lower() # We are in sync with a valid value set already if stored_value is not None and self._task_folder == val == stored_value: val = None return # We only have to update the local settings, the model is # already set if self._task_folder == val and val != stored_value: settings.local_settings.setValue(k, val) return if val is not None and val == self._task_folder: val = None return # Let's check the asset folder before setting # the key to make sure we're pointing at a valid folder path = u'/'.join(self.parent_path) entries = [f.name.lower() for f in _scandir.scandir(path)] if not entries: val = None self._task_folder = val return # The key is valid if val in entries: self._task_folder = val settings.local_settings.setValue(k, val) return # The new proposed task_folder does not exist but the old one is # valid. We'll just stick with the old value instead... if val not in entries and self._task_folder in entries: val = self._task_folder.lower() settings.local_settings.setValue(k, self._task_folder) return # And finally, let's try to revert to a fall-back... if val not in entries and u'scenes' in entries: val = u'scenes' self._task_folder = val settings.local_settings.setValue(k, val) return # All else... let's select the first folder val = entries[0].lower() self._task_folder = val settings.local_settings.setValue(k, val) except: log.error(u'Could not set task folder') finally: if not self.model_data(): self.__initdata__() else: self.sort_data() def data_type(self): """Current key to the data dictionary.""" task_folder = self.task_folder() if task_folder not in self._datatype: cls = self.__class__.__name__ key = u'widget/{}/{}/datatype'.format(cls, task_folder) val = settings.local_settings.value(key) val = val if val else common.SequenceItem self._datatype[task_folder] = val return self._datatype[task_folder] def mimeData(self, indexes): """The data necessary for supporting drag and drop operations are constructed here. There is ambiguity in the absence of any good documentation I could find regarding what mime types have to be defined exactly for fully supporting drag and drop on all platforms. Note: On windows, ``application/x-qt-windows-mime;value="FileName"`` and ``application/x-qt-windows-mime;value="FileNameW"`` types seems to be necessary, but on MacOS a simple uri list seem to suffice. """ def add_path_to_mime(mime, path): """Adds the given path to the mime data.""" if not isinstance(path, unicode): s = u'Expected <type \'unicode\'>, got {}'.format(type(str)) log.error(s) raise TypeError(s) path = QtCore.QFileInfo(path).absoluteFilePath() mime.setUrls(mime.urls() + [ QtCore.QUrl.fromLocalFile(path), ]) path = QtCore.QDir.toNativeSeparators(path).encode('utf-8') _bytes = QtCore.QByteArray(path) mime.setData(u'application/x-qt-windows-mime;value="FileName"', _bytes) mime.setData(u'application/x-qt-windows-mime;value="FileNameW"', _bytes) return mime mime = QtCore.QMimeData() modifiers = QtWidgets.QApplication.instance().keyboardModifiers() no_modifier = modifiers == QtCore.Qt.NoModifier alt_modifier = modifiers & QtCore.Qt.AltModifier shift_modifier = modifiers & QtCore.Qt.ShiftModifier for index in indexes: if not index.isValid(): continue path = index.data(QtCore.Qt.StatusTipRole) if no_modifier: path = common.get_sequence_endpath(path) add_path_to_mime(mime, path) elif alt_modifier and shift_modifier: path = QtCore.QFileInfo(path).dir().path() add_path_to_mime(mime, path) elif alt_modifier: path = common.get_sequence_startpath(path) add_path_to_mime(mime, path) elif shift_modifier: paths = common.get_sequence_paths(index) for path in paths: add_path_to_mime(mime, path) return mime
def add_extensions(): height = common.ROW_HEIGHT() * 0.8 o = common.MARGIN() grp = common_ui.get_group(parent=self) grp.layout().setContentsMargins(o, o, o, o) grp.layout().setSpacing(0) description = \ u'Edit the list of valid extensions. Use \ <span style="color:rgba({ADD});">*</span> to allow all files.' .format( p=common.PRODUCT, ADD=common.rgb(common.ADD)) label = common_ui.PaintedLabel(u'Default extension filters', size=common.LARGE_FONT_SIZE(), parent=self) grp.layout().addWidget(label) grp.layout().addSpacing(o) if description: common_ui.add_description(description, label=None, parent=grp) grp.layout().addSpacing(o) scroll_area = QtWidgets.QScrollArea(parent=self) scroll_area.setWidgetResizable(True) scroll_area.setMaximumHeight(common.HEIGHT() * 0.66) scroll_area.setAttribute(QtCore.Qt.WA_NoBackground) scroll_area.setAttribute(QtCore.Qt.WA_TranslucentBackground) grp.layout().addWidget(scroll_area) _row = common_ui.add_row(None, vertical=True, padding=None, height=None, parent=grp) _row.layout().setContentsMargins(0, 0, 0, 0) _row.layout().setSpacing(0) scroll_area.setWidget(_row) for k, v in sorted(defaultpaths.FORMAT_FILTERS.items(), key=lambda x: x[0]): label = u'<span style="color:rgba({ADD});">{k}</span> - {v}:'.format( ADD=common.rgb(common.ADD), k=v[u'name'], v=v[u'description']) row = common_ui.add_row(None, padding=None, height=height, parent=_row) common_ui.add_description(label, label=u'', parent=row) row = common_ui.add_row(None, padding=None, height=height, parent=_row) line_edit = common_ui.add_line_edit(v[u'default'], parent=row) line_edit.textChanged.connect( functools.partial(text_changed, defaultpaths.FORMAT_FILTERS, k)) line_edit.setAlignment(QtCore.Qt.AlignLeft) line_edit.setText(v[u'value'])
def __init__(self, parent=None): """Init method. Adding the `HeaderWidget` here - this is the widget responsible for moving the widget around and providing the close and hide buttons. Also, the properties necessary to resize the frameless window are also defines here. These properties work in conjunction with the mouse events """ global _instance if _instance is not None: raise RuntimeError( '{} cannot be initialised more than once.'.format( self.__class__.__name__)) _instance = self super(StandaloneMainWidget, self).__init__(parent=None) k = u'preferences/frameless_window' self._frameless = settings.local_settings.value(k) if self._frameless is True: self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint) self.setAttribute(QtCore.Qt.WA_NoSystemBackground) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) common.set_custom_stylesheet(self) self.resize_initial_pos = QtCore.QPoint(-1, -1) self.resize_initial_rect = None self.resize_area = None self.resize_overlay = None self.resize_distance = QtWidgets.QApplication.instance( ).startDragDistance() * 2 self.resize_override_icons = { 1: QtCore.Qt.SizeFDiagCursor, 2: QtCore.Qt.SizeBDiagCursor, 3: QtCore.Qt.SizeBDiagCursor, 4: QtCore.Qt.SizeFDiagCursor, 5: QtCore.Qt.SizeVerCursor, 6: QtCore.Qt.SizeHorCursor, 7: QtCore.Qt.SizeVerCursor, 8: QtCore.Qt.SizeHorCursor, } self.tray = QtWidgets.QSystemTrayIcon(parent=self) pixmap = images.ImageCache.get_rsc_pixmap(u'icon_bw', None, common.ROW_HEIGHT() * 7.0) icon = QtGui.QIcon(pixmap) self.tray.setIcon(icon) self.tray.setContextMenu(TrayMenu(parent=self)) self.tray.setToolTip(common.PRODUCT) self.tray.show() self.tray.activated.connect(self.trayActivated) self.installEventFilter(self) self.setMouseTracking(True) self.initialized.connect(self.connect_extra_signals) self.initialized.connect(self.showNormal) self.initialized.connect(self.activateWindow) def say_hello(): import bookmarks.common_ui as common_ui if self.stackedwidget.widget( 0).model().sourceModel().rowCount() == 0: common_ui.MessageBox( u'Bookmarks is not set up (yet!).', u'To add a server, create new jobs and bookmark folders, right-click on the main window and select "Manage Bookmarks".', parent=self.stackedwidget.widget(0)).open() self.initialized.connect(say_hello) self.shutdown.connect(self.hide) shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(u'Ctrl+Q'), self) shortcut.setAutoRepeat(False) shortcut.setContext(QtCore.Qt.ApplicationShortcut) shortcut.activated.connect(self.shutdown, type=QtCore.Qt.QueuedConnection) self.adjustSize()
def _create_UI(self): import bookmarks label = common_ui.PaintedLabel(u'General Preferences', size=common.LARGE_FONT_SIZE(), parent=self) self.layout().addWidget(label) grp = common_ui.get_group(parent=self) row = common_ui.add_row(u'Frameless window', parent=grp) self.frameless_window = QtWidgets.QCheckBox(u'Use frameless window', parent=self) row.layout().addStretch(1) row.layout().addWidget(self.frameless_window) label = common_ui.PaintedLabel(u'(Restart required)', size=common.SMALL_FONT_SIZE(), color=common.TEXT_DISABLED) row.layout().addWidget(label, 0) if common.STANDALONE: row = common_ui.add_row(u'Scale interface', parent=grp) self.ui_scale = QtWidgets.QComboBox(parent=self) self.ui_scale.setFixedHeight(common.ROW_HEIGHT() * 0.66) for s in (u'100%', u'125%', u'150%', u'175%', u'200%'): self.ui_scale.addItem(s) idx = self.ui_scale.count() - 1 data = int(s.strip(u'%')) * 0.01 self.ui_scale.setItemData(idx, data, role=QtCore.Qt.UserRole) data = QtCore.QSize(1, common.ROW_HEIGHT() * 0.66) self.ui_scale.setItemData(idx, data, role=QtCore.Qt.SizeHintRole) row.layout().addWidget(self.ui_scale, 1) label = common_ui.PaintedLabel(u'(Restart required)', size=common.SMALL_FONT_SIZE(), color=common.TEXT_DISABLED) row.layout().addWidget(label, 0) ############################## row = common_ui.add_row(u'Update', parent=grp) self.check_updates = common_ui.PaintedButton(u'Check for Updates', parent=row) self.show_help = common_ui.PaintedButton(u'Help', parent=row) row.layout().addWidget(self.check_updates) row.layout().addWidget(self.show_help) row.layout().addStretch(1.0) ####################################################### row = common_ui.add_row(None, parent=self) label = common_ui.PaintedLabel(u'Shotgun RV', size=common.LARGE_FONT_SIZE(), parent=row) row.layout().addWidget(label) row.layout().addStretch(1) grp = common_ui.get_group(parent=self) row = common_ui.add_row(u'Path to RV', parent=grp) self.rv_path = common_ui.add_line_edit(u'eg. c:/rv/bin/rv.exe', parent=row) row.layout().addWidget(self.rv_path, 1) button = common_ui.PaintedButton(u'Pick') button.clicked.connect(self.pick_rv) row.layout().addWidget(button) button = common_ui.PaintedButton(u'Reveal') button.clicked.connect(lambda: common.reveal(self.rv_path.text())) row.layout().addWidget(button) text = \ u'You can use {} to push footage to Shotgun RV \ (<span style="color:rgba({});">CTRL+P)</span>. Select the RV executable for this to work.' .format( common.PRODUCT, common.rgb(common.ADD)) common_ui.add_description(text, label=u'Hint', parent=grp) ####################################################### label = common_ui.PaintedLabel(u'Shortcuts', size=common.LARGE_FONT_SIZE(), parent=self) self.layout().addWidget(label) grp = common_ui.get_group(parent=self) label = QtWidgets.QLabel(parent=self) s = u'<table width="100%">' def r(): return unicode('<tr>\ <td align="center" style="background-color:rgba(0,0,0,80);padding:{pad}px;">\ <span style="color:rgba({ADD});">{shortcut}</span>\ </td>\ <td align="left" style="background-color:rgba(0,0,0,30);padding:{pad}px;">\ <span style="color:rgba({TEXT});">{description}</span>\ </td>\ </tr>') for shortcut, description in ( (u'Ctrl+N', u'Open new {} instance'.format(common.PRODUCT)), (u'Enter', u'Activate item'), (u'Space', u'Preview thumbnail'), (u'Arrow Up/Down', u'Navigate list'), (u'Ctrl+R', u'Reload'), (u'Ctrl+F', u'Edit filter'), (u'Ctrl+O', u'Reveal in file manager'), (u'Ctrl+C', u'Copy path'), (u'Ctrl+Shift+C', u'Copy path (alt)'), (u'Ctrl+S', u'Save/remove favourite'), (u'Ctrl+A', u'Archive/enable'), (u'Ctrl+T', u'Show Notes & Todos'), (u'Ctrl+H', u'Hide buttons'), (u'Ctrl+M', u'Toggle thumbnail loading'), (u'Ctrl+Shift+A', u'Show/Hide archived items'), (u'Ctrl+Shift+F', u'Show favourites only/Show all'), (u'Tab', u'Edit item description'), (u'Shift+Tab', u'Edit item description'), (u'Alt+Left', u'Show previous tab'), (u'Alt+Right', u'Show next tab'), (u'Ctrl+1', u'Show bookmarks'), (u'Ctrl+2', u'Show assets'), (u'Ctrl+3', u'Show files'), (u'Ctrl+4', u'Show favourites'), (u'Ctrl+Plus', u'Increase row height'), (u'Ctrl+Minus', u'Decrease row height'), (u'Ctrl+0', u'Reset row height'), ): s += r().format( shortcut=shortcut, description=description, pad=int(common.INDICATOR_WIDTH() * 1.5), ADD=common.rgb(common.ADD), TEXT=common.rgb(common.SECONDARY_TEXT), ) s += u'</table>' label.setText(s) label.setWordWrap(True) grp.layout().addWidget(label) label = common_ui.PaintedLabel(u'About {}'.format(common.PRODUCT), size=common.LARGE_FONT_SIZE(), parent=grp) self.layout().addWidget(label) grp = common_ui.get_group(parent=self) o = common.MARGIN() grp.layout().setContentsMargins(o, o, o, o) # row = common_ui.add_row(u'Version', parent=grp, height=None) s = u'\n'.join(bookmarks.get_info()) common_ui.add_description(s, label=None, parent=grp) self.layout().addStretch(1)
def adjust_height(self): height = self.document().size().height() if height > (common.ROW_HEIGHT() * 2) and not self.isEnabled(): self.setFixedHeight(common.ROW_HEIGHT() * 2) return self.setFixedHeight(height)
def _create_UI(self): o = common.INDICATOR_WIDTH() self.setMinimumWidth(common.WIDTH() * 0.5) height = common.ROW_HEIGHT() * 0.8 widget = QtWidgets.QWidget(parent=self) QtWidgets.QVBoxLayout(widget) widget.layout().setAlignment(QtCore.Qt.AlignCenter) widget.layout().setContentsMargins(0, 0, 0, 0) widget.layout().setSpacing(o * 2) self.setWidget(widget) # Main group grpA = common_ui.get_group(parent=widget) # GROUP grp = common_ui.get_group(parent=grpA) numvalidator = QtGui.QRegExpValidator(parent=self) numvalidator.setRegExp(QtCore.QRegExp(ur'[0-9]+[\.]?[0-9]*')) textvalidator = QtGui.QRegExpValidator(parent=self) textvalidator.setRegExp(QtCore.QRegExp(ur'[a-zA-Z0-9]+')) # ROW1 self.rectangles_widget = RectanglesWidget(parent=self) grp.layout().addWidget(self.rectangles_widget, 1) # ROW row = common_ui.add_row(u'Resolution', parent=grp, height=height) self.width_editor = common_ui.LineEdit(parent=self) self.width_editor.setPlaceholderText(u'Width...') self.width_editor.setValidator(numvalidator) row.layout().addWidget(self.width_editor, 0) self.height_editor = common_ui.LineEdit(parent=self) self.height_editor.setPlaceholderText(u'Height...') self.height_editor.setValidator(numvalidator) row.layout().addWidget(self.height_editor, 0) # ROW row = common_ui.add_row(u'Frame rate', parent=grp, height=height) self.framerate_editor = common_ui.LineEdit(parent=self) self.framerate_editor.setPlaceholderText(u'Frame rate...') self.framerate_editor.setValidator(numvalidator) row.layout().addWidget(self.framerate_editor, 0) # ******************************************** grp = common_ui.get_group(parent=grpA) row = common_ui.add_row(u'Bookmark Prefix', parent=grp, height=height) self.prefix_editor = common_ui.LineEdit(parent=self) self.prefix_editor.setPlaceholderText(u'Prefix (eg. \'MYJOB\')...') self.prefix_editor.setValidator(textvalidator) self.suggest_prefix_button = common_ui.PaintedButton(u'Suggest') self.suggest_prefix_button.setFixedHeight(height * 0.7) row.layout().addWidget(self.prefix_editor, 0) row.layout().addWidget(self.suggest_prefix_button, 0) # ******************************************** grp = common_ui.get_group(parent=grpA) row = common_ui.add_row(u'Start Frame', parent=grp, height=height) self.startframe_editor = common_ui.LineEdit(parent=self) self.startframe_editor.setPlaceholderText(u'Start Frame...') self.startframe_editor.setValidator(numvalidator) row.layout().addWidget(self.startframe_editor, 0) row = common_ui.add_row(u'Duration', parent=grp, height=height) self.duration_editor = common_ui.LineEdit(parent=self) self.duration_editor.setPlaceholderText(u'Duration...') self.duration_editor.setValidator(numvalidator) row.layout().addWidget(self.duration_editor, 0) # ******************************************** grp = common_ui.get_group(parent=widget) row = common_ui.add_row(u'Asset Identifier', parent=grp) self.identifier_editor = common_ui.LineEdit(parent=row) self.identifier_editor.setPlaceholderText( u'Asset identifier, eg. \'workspace.mel\'') row.layout().addWidget(self.identifier_editor, 0) text = u'Only folders containing this file will be treated as assets.<br>\ Using the default Maya Workspace the identifier normally is \ <span style="text-decoration: underline;">workspace.mel</span>, but \ any other file can be used as long it is present in the root of \ the asset.<br>If not set, all folders inside the Bookmark \ will be read as assets.'.format(common.PRODUCT) common_ui.add_description(text, label='Hint', parent=grp) # ******************************************** grpA = common_ui.get_group(parent=widget) # Slack API token label = common_ui.PaintedLabel(u'Slack Settings', size=common.MEDIUM_FONT_SIZE() * 1.2) grpA.layout().addWidget(label, 0) grpA.layout().addSpacing(o * 2) grp = common_ui.get_group(parent=grpA) row = common_ui.add_row(u'Slack API Token', parent=grp, height=height) self.slacktoken_editor = common_ui.LineEdit(parent=self) self.slacktoken_editor.setPlaceholderText( u'xoxb-01234567890-0123456...') button = common_ui.PaintedButton(u'Test Token') button.setFixedHeight(height * 0.7) button.clicked.connect(self.test_slack_token) row.layout().addWidget(self.slacktoken_editor, 0) row.layout().addWidget(button) text = u'{p} can send messages to team-members using Slack.<br><br>\ To get started, create a new app and install it for your workspace. \ Paste the generated <span style="{h}">OAuth</span> token, usually starting with "xoxb-0123..." above.<br> \ See <a href="http://api.slack.com/apps">http://api.slack.com/apps</a> for more information.<br><br> \ The Slack app needs to have the <span style="{h}">users:read</span> and \ <span style="{h}">chat:write</span> scopes enabled. To send messages to channels \ the bot is not part of, add <span style="{h}">chat:write.public</span>. \ Scopes <span style="{h}">channels:read</span> and <span style="{h}">groups:read</span> are needed to list available \ Slack Channels.<br><br>'.format(p=common.PRODUCT, h='color: rgba({});'.format( common.rgb(common.ADD))) common_ui.add_description(text, label=u'Slack API Hint', parent=grp) # ******************************************** self.widget().layout().addStretch(1)
class UsersModel(QtCore.QAbstractItemModel): """Model used to store the available profiles. """ modelDataResetRequested = QtCore.Signal() row_size = QtCore.QSize(1, common.ROW_HEIGHT() * 0.8) def __init__(self, token, parent=None): super(UsersModel, self).__init__(parent=parent) self.client = Client(token) self.INTERNAL_USER_DATA = common.DataDict() self.modelDataResetRequested.connect(self.__initdata__) def __initdata__(self): self.beginResetModel() self.INTERNAL_USER_DATA = common.DataDict() channels = self.client.get_channels() profiles = self.client.get_user_profiles() # Channels try: for channel in sorted(channels, key=lambda x: x['name']): idx = len(self.INTERNAL_USER_DATA) self.INTERNAL_USER_DATA[idx] = common.DataDict({ QtCore.Qt.DisplayRole: u'Channel: ' + channel['name'], QtCore.Qt.DecorationRole: QtGui.QIcon(), QtCore.Qt.SizeHintRole: self.row_size, QtCore.Qt.FontRole: common.font_db.primary_font( font_size=common.SMALL_FONT_SIZE())[0], IdRole: channel[u'id'], ThumbnailHashRole: None, ThumbnailUrlRole: None, }) except Exception as e: log.error(u'Could not get channels.') try: for profile in sorted(profiles, key=self.get_pretty_name): idx = len(self.INTERNAL_USER_DATA) self.INTERNAL_USER_DATA[idx] = common.DataDict({ QtCore.Qt.DisplayRole: self.get_pretty_name(profile), QtCore.Qt.DecorationRole: QtGui.QIcon(), QtCore.Qt.SizeHintRole: self.row_size, QtCore.Qt.FontRole: common.font_db.primary_font( font_size=common.SMALL_FONT_SIZE())[0], IdRole: profile[u'id'], ThumbnailHashRole: profile[u'profile']['avatar_hash'], ThumbnailUrlRole: profile[u'profile']['image_32'], }) index = self.index(idx, 0) self.get_icon(index) except Exception as e: log.error('Could not get profiles') self.endResetModel() def columnCount(self, index, parent=QtCore.QModelIndex()): return 2 def rowCount(self, parent=QtCore.QModelIndex()): return len(self.INTERNAL_USER_DATA) def setData(self, index, data, role=QtCore.Qt.DisplayRole): if not index.isValid(): return if index.row() not in self.INTERNAL_USER_DATA: return self.INTERNAL_USER_DATA[index.row()][role] = data self.dataChanged.emit(index, index) def data(self, index, role=QtCore.Qt.DisplayRole): if index.row() not in self.INTERNAL_USER_DATA: return None if role not in self.INTERNAL_USER_DATA[index.row()]: return None return self.INTERNAL_USER_DATA[index.row()][role] def index(self, row, column, parent=QtCore.QModelIndex()): """Bog-standard index creator.""" return self.createIndex(row, 0, parent=parent) def parent(self, index): return QtCore.QModelIndex() def get_pretty_name(self, member): """Returns a pretty name for the given member. """ p = member['profile'] d = u'display_name' f = u'first_name' l = u'last_name' r = u'real_name' if all((d in p, f in p, l in p)): if all((p[d], p[f], p[l])): name = u'{} ({} {})'.format(p[d], p[f], p[l]) elif p[d]: name = p[d] elif all((p[f], p[l])): name = u'{} {}'.format(p[f], p[l]) else: if d in p: name = p[d] elif f in p and not l in p: name = p[f] elif f in p and l in p: name = u'{} {}'.format(p[f], p[l]) if not name and r in p: name = p[r] return name @QtCore.Slot(QtCore.QModelIndex) def get_icon(self, index): """Downloads and sets the icon for the given index.""" if not index.isValid(): return try: url = index.data(ThumbnailUrlRole) response = urllib2.urlopen(url) except Exception as e: log.error('Could not save thumbnail') return # Cache directory cache_dir_path = QtCore.QStandardPaths.writableLocation( QtCore.QStandardPaths.GenericDataLocation) cache_dir_path = u'{}/{}/slack'.format(cache_dir_path, common.PRODUCT) cache_file_path = u'{}/{}.png'.format(cache_dir_path, index.data(ThumbnailHashRole)) # Let's check if the thumbnail has already been cached and if not, download it. if not QtCore.QFileInfo(cache_file_path).exists(): QtCore.QDir(cache_dir_path).mkpath('.') with open(cache_file_path, 'wb') as f: f.write(response.read()) image = images.oiio_get_qimage(cache_file_path) if not image: return if image.isNull(): return if image.isNull(): return icon = QtGui.QIcon() pixmap = QtGui.QPixmap.fromImage(image) icon.addPixmap(pixmap, QtGui.QIcon.Normal) icon.addPixmap(pixmap, QtGui.QIcon.Active) self.setData(index, icon, role=QtCore.Qt.DecorationRole)