def browseColor(self): """ Show the color dialog. :rtype: None """ color = self.currentColor() if color: color = studioqt.Color.fromString(color) d = QtWidgets.QColorDialog(self) d.setCurrentColor(color) standardColors = self.browserColors() if standardColors: index = -1 for standardColor in standardColors: index += 1 try: # Support for new qt5 signature standardColor = QtGui.QColor(standardColor) d.setStandardColor(index, standardColor) except: # Support for new qt4 signature standardColor = QtGui.QColor(standardColor).rgba() d.setStandardColor(index, standardColor) d.currentColorChanged.connect(self._colorChanged) if d.exec_(): self._colorChanged(d.selectedColor()) else: self._colorChanged(color)
def __init__(self, *args): QtWidgets.QWidget.__init__(self, *args) self._dpi = 1 self._padding = self.DEFAULT_PADDING w, h = self.DEFAULT_ZOOM_AMOUNT, self.DEFAULT_ZOOM_AMOUNT self._iconSize = QtCore.QSize(w, h) self._itemSizeHint = QtCore.QSize(w, h) self._zoomAmount = self.DEFAULT_ZOOM_AMOUNT self._labelDisplayOption = self.LABEL_DISPLAY_OPTION self._dataset = None self._treeWidget = TreeWidget(self) self._listView = ListView(self) self._listView.setTreeWidget(self._treeWidget) self._listView.installEventFilter(self) self._delegate = ItemDelegate() self._delegate.setItemsWidget(self) self._listView.setItemDelegate(self._delegate) self._treeWidget.setItemDelegate(self._delegate) self._treeWidget.installEventFilter(self) self._toastWidget = ToastWidget(self) self._toastWidget.hide() self._toastEnabled = True self._textColor = QtGui.QColor(255, 255, 255, 200) self._textSelectedColor = QtGui.QColor(255, 255, 255, 200) self._itemBackgroundColor = QtGui.QColor(255, 255, 255, 30) self._backgroundHoverColor = QtGui.QColor(255, 255, 255, 35) self._backgroundSelectedColor = QtGui.QColor(30, 150, 255) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._treeWidget) layout.addWidget(self._listView) header = self.treeWidget().header() header.sortIndicatorChanged.connect(self._sortIndicatorChanged) self.setLayout(layout) self.listView().itemClicked.connect(self._itemClicked) self.listView().itemDoubleClicked.connect(self._itemDoubleClicked) self.treeWidget().itemClicked.connect(self._itemClicked) self.treeWidget().itemDoubleClicked.connect(self._itemDoubleClicked) self.itemMoved = self._listView.itemMoved self.itemDropped = self._listView.itemDropped self.itemSelectionChanged = self._treeWidget.itemSelectionChanged
def backgroundSelectedColor(self): """ Return the background color when the item is selected. :rtype: QtWidgets.QtColor """ return QtGui.QColor(0, 0, 0, 0)
def __init__(self, *args): QtWidgets.QListView.__init__(self, *args) ItemViewMixin.__init__(self) self._treeWidget = None self._rubberBand = None self._rubberBandStartPos = None self._rubberBandColor = QtGui.QColor(QtCore.Qt.white) self._customSortOrder = [] self._drag = None self._dragStartPos = None self._dragStartIndex = None self._dropEnabled = True self.setSpacing(5) self.setMouseTracking(True) self.setSelectionRectVisible(True) self.setViewMode(QtWidgets.QListView.IconMode) self.setResizeMode(QtWidgets.QListView.Adjust) self.setSelectionMode(QtWidgets.QListWidget.ExtendedSelection) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setAcceptDrops(True) self.setDragEnabled(True) self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) self.clicked.connect(self._indexClicked) self.doubleClicked.connect(self._indexDoubleClicked)
def paintBackground(self, painter, option, index): """ Draw the background for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :type index: QtCore.QModelIndex :rtype: None """ super(GroupItem, self).paintBackground(painter, option, index) painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) visualRect = self.visualRect(option) text = self.name() metrics = QtGui.QFontMetricsF(self._font) textWidth = metrics.width(text) padding = (25 * self.dpi()) visualRect.setX(textWidth + padding) visualRect.setY(visualRect.y() + (visualRect.height() / 2)) visualRect.setHeight(2 * self.dpi()) visualRect.setWidth(visualRect.width() - padding) color = QtGui.QColor( self.textColor().red(), self.textColor().green(), self.textColor().blue(), 10 ) painter.setBrush(QtGui.QBrush(color)) painter.drawRect(visualRect)
def setBadge(self, x, y, w, h, color=None): """ Set a for the icon. :type x: int :type y: int :type w: int :type h: int :type color: QtGui.QColor or None """ color = color or QtGui.QColor(240, 100, 100) size = self.actualSize(QtCore.QSize(256, 256)) pixmap = self.pixmap(size) painter = QtGui.QPainter(pixmap) pen = QtGui.QPen(color) pen.setWidth(0) painter.setPen(pen) painter.setBrush(color) painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.drawEllipse(x, y, w, h) painter.end() icon = QtGui.QIcon(pixmap) self.swap(icon)
def backgroundHoverColor(self): """ Return the background color when the mouse is over the item. :rtype: QtWidgets.QtColor """ return QtGui.QColor(0, 0, 0, 0)
def backgroundColor(self): """ Return the background color for the item. :rtype: QtWidgets.QtColor """ return QtGui.QColor(0, 0, 0, 0)
def example(): """ Example: import studiolibrary.widgets.colorpicker reload(studiolibrary.widgets.colorpicker) studiolibrary.widgets.colorpicker.example() """ def _colorChanged(color): print("colorChanged:", color) style = """ #colorButton { margin: 5px; min-width: 100px; min-height: 100px; } #browseColorButton { margin: 5px; font-size: 45px; min-width: 100px; min-height: 100px; } """ colors = [ "rgb(230, 60, 60, 255)", "rgb(255, 90, 40)", "rgb(255, 125, 100, 255)", "rgb(250, 200, 0, 255)", "rgb(80, 200, 140, 255)", "rgb(50, 180, 240, 255)", "rgb(110, 110, 240, 255)", ] browserColors = [] browserColors_ = [ # Top row, Bottom row (230, 60, 60), (250, 80, 130), (255, 90, 40), (240, 100, 170), (255, 125, 100), (240, 200, 150), (250, 200, 0), (225, 200, 40), (80, 200, 140), (80, 225, 120), (50, 180, 240), (100, 200, 245), (130, 110, 240), (180, 160, 255), (180, 110, 240), (210, 110, 255) ] for colorR, colorG, colorB in browserColors_: for i in range(0, 3): color = QtGui.QColor(colorR, colorG, colorB) browserColors.append(color) picker = ColorPickerWidget() picker.setColors(colors) picker.setStyleSheet(style) picker.setBrowserColors(browserColors) picker.colorChanged.connect(_colorChanged) picker.show()
def setBackgroundSelectedColor(self, color): """ Set the background color when an item is selected. :type color: QtWidgets.QtColor """ self._backgroundSelectedColor = color self._listView.setRubberBandColor(QtGui.QColor(200, 200, 200, 255))
def createColorDialog( self, parent, standardColors=None, currentColor=None, ): """ Create a new instance of the color dialog. :type parent: QtWidgets.QWidget :type standardColors: list[int] :rtype: QtWidgets.QColorDialog """ dialog = QtWidgets.QColorDialog(parent) if standardColors: index = -1 for colorR, colorG, colorB in standardColors: index += 1 color = QtGui.QColor(colorR, colorG, colorB).rgba() try: # Support for new qt5 signature color = QtGui.QColor(color) dialog.setStandardColor(index, color) except: # Support for new qt4 signature color = QtGui.QColor(color).rgba() dialog.setStandardColor(index, color) # PySide2 doesn't support d.open(), so we need to pass a blank slot. dialog.open(self, QtCore.SLOT("blankSlot()")) if currentColor: dialog.setCurrentColor(currentColor) return dialog
def drawIconBorder(self, painter, pixmapRect): """ Draw a border around the icon. :type painter: QtWidgets.QPainter :type pixmapRect: QtWidgets.QRect :rtype: None """ pixmapRect = QtCore.QRect(pixmapRect) pixmapRect.setX(pixmapRect.x() - 5) pixmapRect.setY(pixmapRect.y() - 5) pixmapRect.setWidth(pixmapRect.width() + 5) pixmapRect.setHeight(pixmapRect.height() + 5) color = QtGui.QColor(255, 255, 255, 10) painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) painter.setBrush(QtGui.QBrush(color)) painter.drawRect(pixmapRect)
def setIconColor(): menuBarWidget.setIconColor(QtGui.QColor(255, 255, 0))
class Theme(QtCore.QObject): updated = QtCore.Signal() DEFAULT_DARK_COLOR = QtGui.QColor(60, 60, 60) DEFAULT_LIGHT_COLOR = QtGui.QColor(220, 220, 220) DEFAULT_ACCENT_COLOR = QtGui.QColor(0, 175, 255) DEFAULT_BACKGROUND_COLOR = QtGui.QColor(60, 60, 80) def __init__(self): QtCore.QObject.__init__(self) self._dpi = 1 self._name = "Default" self._accentColor = None self._backgroundColor = None self.setAccentColor(self.DEFAULT_ACCENT_COLOR) self.setBackgroundColor(self.DEFAULT_BACKGROUND_COLOR) def settings(self): """ Return a dictionary of settings for the current Theme. :rtype: dict """ settings = {} settings["name"] = self.name() accentColor = self.accentColor() settings["accentColor"] = accentColor.toString() backgroundColor = self.backgroundColor() settings["backgroundColor"] = backgroundColor.toString() return settings def setSettings(self, settings): """ Set a dictionary of settings for the current Theme. :type settings: dict :rtype: None """ name = settings.get("name") self.setName(name) color = settings.get("accentColor") if color: color = studioqt.Color.fromString(color) self.setAccentColor(color) color = settings.get("backgroundColor") if color: color = studioqt.Color.fromString(color) self.setBackgroundColor(color) def dpi(self): """ Return the dpi for the Theme :rtype: float """ return self._dpi def setDpi(self, dpi): """ Set the dpi for the Theme :type dpi: float :rtype: None """ self._dpi = dpi def name(self): """ Return the name for the Theme :rtype: str """ return self._name def setName(self, name): """ Set the name for the Theme :type name: str :rtype: None """ self._name = name def isDark(self): """ Return True if the current theme is dark. rtype: bool """ # The luminance for digital formats are (0.299, 0.587, 0.114) red = self.backgroundColor().redF() * 0.299 green = self.backgroundColor().greenF() * 0.587 blue = self.backgroundColor().blueF() * 0.114 darkness = red + green + blue if darkness < 0.6: return True return False def setDark(self): """ Set the current theme to the default dark color. :rtype: None """ self.setBackgroundColor(self.DEFAULT_DARK_COLOR) def setLight(self): """ Set the current theme to the default light color. :rtype: None """ self.setBackgroundColor(self.DEFAULT_LIGHT_COLOR) def iconColor(self): """ Return the icon color for the theme. :rtype: studioqt.Color """ return self.forgroundColor() def accentForgroundColor(self): """ Return the foreground color for the accent color. :rtype: studioqt.Color """ return studioqt.Color(255, 255, 255, 255) def forgroundColor(self): """ Return the foreground color for the theme. :rtype: studioqt.Color """ if self.isDark(): return studioqt.Color(250, 250, 250, 225) else: return studioqt.Color(0, 40, 80, 180) def itemBackgroundColor(self): """ Return the item background color. :rtype: studioqt.Color """ if self.isDark(): return studioqt.Color(255, 255, 255, 20) else: return studioqt.Color(255, 255, 255, 120) def itemBackgroundHoverColor(self): """ Return the item background color when the mouse hovers over the item. :rtype: studioqt.Color """ return studioqt.Color(255, 255, 255, 60) def accentColor(self): """ Return the accent color for the theme. :rtype: studioqt.Color or None """ return self._accentColor def backgroundColor(self): """ Return the background color for the theme. :rtype: studioqt.Color or None """ return self._backgroundColor def setAccentColor(self, color): """ Set the accent color for the theme. :type color: studioqt.Color | QtGui.QColor """ if isinstance(color, basestring): color = studioqt.Color.fromString(color) if isinstance(color, QtGui.QColor): color = studioqt.Color.fromColor(color) self._accentColor = color self.updated.emit() def setBackgroundColor(self, color): """ Set the background color for the theme. :type color: studioqt.Color | QtGui.QColor """ if isinstance(color, basestring): color = studioqt.Color.fromString(color) if isinstance(color, QtGui.QColor): color = studioqt.Color.fromColor(color) self._backgroundColor = color self.updated.emit() def createColorDialog( self, parent, standardColors=None, currentColor=None, ): """ Create a new instance of the color dialog. :type parent: QtWidgets.QWidget :type standardColors: list[int] :rtype: QtWidgets.QColorDialog """ dialog = QtWidgets.QColorDialog(parent) if standardColors: index = -1 for colorR, colorG, colorB in standardColors: index += 1 color = QtGui.QColor(colorR, colorG, colorB).rgba() try: # Support for new qt5 signature color = QtGui.QColor(color) dialog.setStandardColor(index, color) except: # Support for new qt4 signature color = QtGui.QColor(color).rgba() dialog.setStandardColor(index, color) # PySide2 doesn't support d.open(), so we need to pass a blank slot. dialog.open(self, QtCore.SLOT("blankSlot()")) if currentColor: dialog.setCurrentColor(currentColor) return dialog def browseAccentColor(self, parent=None): """ Show the color dialog for changing the accent color. :type parent: QtWidgets.QWidget :rtype: None """ standardColors = [(230, 60, 60), (210, 40, 40), (190, 20, 20), (250, 80, 130), (230, 60, 110), (210, 40, 90), (255, 90, 40), (235, 70, 20), (215, 50, 0), (240, 100, 170), (220, 80, 150), (200, 60, 130), (255, 125, 100), (235, 105, 80), (215, 85, 60), (240, 200, 150), (220, 180, 130), (200, 160, 110), (250, 200, 0), (230, 180, 0), (210, 160, 0), (225, 200, 40), (205, 180, 20), (185, 160, 0), (80, 200, 140), (60, 180, 120), (40, 160, 100), (80, 225, 120), (60, 205, 100), (40, 185, 80), (50, 180, 240), (30, 160, 220), (10, 140, 200), (100, 200, 245), (80, 180, 225), (60, 160, 205), (130, 110, 240), (110, 90, 220), (90, 70, 200), (180, 160, 255), (160, 140, 235), (140, 120, 215), (180, 110, 240), (160, 90, 220), (140, 70, 200), (210, 110, 255), (190, 90, 235), (170, 70, 215)] currentColor = self.accentColor() dialog = self.createColorDialog(parent, standardColors, currentColor) dialog.currentColorChanged.connect(self.setAccentColor) if dialog.exec_(): self.setAccentColor(dialog.selectedColor()) else: self.setAccentColor(currentColor) def browseBackgroundColor(self, parent=None): """ Show the color dialog for changing the background color. :type parent: QtWidgets.QWidget :rtype: None """ standardColors = [(0, 0, 0), (20, 20, 20), (40, 40, 40), (60, 60, 60), (80, 80, 80), (100, 100, 100), (20, 20, 30), (40, 40, 50), (60, 60, 70), (80, 80, 90), (100, 100, 110), (120, 120, 130), (0, 30, 60), (20, 50, 80), (40, 70, 100), (60, 90, 120), (80, 110, 140), (100, 130, 160), (0, 60, 60), (20, 80, 80), (40, 100, 100), (60, 120, 120), (80, 140, 140), (100, 160, 160), (0, 60, 30), (20, 80, 50), (40, 100, 70), (60, 120, 90), (80, 140, 110), (100, 160, 130), (60, 0, 10), (80, 20, 30), (100, 40, 50), (120, 60, 70), (140, 80, 90), (160, 100, 110), (60, 0, 40), (80, 20, 60), (100, 40, 80), (120, 60, 100), (140, 80, 120), (160, 100, 140), (40, 15, 5), (60, 35, 25), (80, 55, 45), (100, 75, 65), (120, 95, 85), (140, 115, 105)] currentColor = self.backgroundColor() dialog = self.createColorDialog(parent, standardColors, currentColor) dialog.currentColorChanged.connect(self.setBackgroundColor) if dialog.exec_(): self.setBackgroundColor(dialog.selectedColor()) else: self.setBackgroundColor(currentColor) def options(self): """ Return the variables used to customise the style sheet. :rtype: dict """ accentColor = self.accentColor() accentForegroundColor = self.accentForgroundColor() foregroundColor = self.forgroundColor() backgroundColor = self.backgroundColor() itemBackgroundColor = self.itemBackgroundColor() itemBackgroundHoverColor = self.itemBackgroundHoverColor() if self.isDark(): darkness = "white" else: darkness = "black" resourceDirname = studiolibrary.resource.RESOURCE_DIRNAME resourceDirname = resourceDirname.replace("\\", "/") options = { "DARKNESS": darkness, "RESOURCE_DIRNAME": resourceDirname, "ACCENT_COLOR": accentColor.toString(), "ACCENT_COLOR_R": str(accentColor.red()), "ACCENT_COLOR_G": str(accentColor.green()), "ACCENT_COLOR_B": str(accentColor.blue()), "ACCENT_FOREGROUND_COLOR": accentForegroundColor.toString(), "FOREGROUND_COLOR": foregroundColor.toString(), "FOREGROUND_COLOR_R": str(foregroundColor.red()), "FOREGROUND_COLOR_G": str(foregroundColor.green()), "FOREGROUND_COLOR_B": str(foregroundColor.blue()), "BACKGROUND_COLOR": backgroundColor.toString(), "BACKGROUND_COLOR_R": str(backgroundColor.red()), "BACKGROUND_COLOR_G": str(backgroundColor.green()), "BACKGROUND_COLOR_B": str(backgroundColor.blue()), "ITEM_TEXT_COLOR": foregroundColor.toString(), "ITEM_TEXT_SELECTED_COLOR": accentForegroundColor.toString(), "ITEM_BACKGROUND_COLOR": itemBackgroundColor.toString(), "ITEM_BACKGROUND_HOVER_COLOR": itemBackgroundHoverColor.toString(), "ITEM_BACKGROUND_SELECTED_COLOR": accentColor.toString(), } return options def styleSheet(self): """ Return the style sheet for this theme. :rtype: str """ options = self.options() path = studiolibrary.resource.get("css", "default.css") styleSheet = studioqt.StyleSheet.fromPath(path, options=options, dpi=self.dpi()) return styleSheet.data()
class Item(QtWidgets.QTreeWidgetItem): """The Item is used to hold rows of information for an item view.""" ICON_PATH = None TYPE_ICON_PATH = None ThreadPool = QtCore.QThreadPool() THUMBNAIL_PATH = "" MAX_ICON_SIZE = 256 DEFAULT_FONT_SIZE = 12 DEFAULT_PLAYHEAD_COLOR = QtGui.QColor(255, 255, 255, 220) THUMBNAIL_COLUMN = 0 ENABLE_THUMBNAIL_THREAD = True PAINT_SLIDER = False _globalSignals = GlobalSignals() sliderChanged = _globalSignals.sliderChanged def __init__(self, *args): QtWidgets.QTreeWidgetItem.__init__(self, *args) self._url = None self._path = None self._size = None self._rect = None self._textColumnOrder = [] self._data = {} self._itemData = {} self._icon = {} self._fonts = {} self._thread = None self._pixmap = {} self._pixmapRect = None self._pixmapScaled = None self._iconPath = None self._typePixmap = None self._thumbnailIcon = None self._underMouse = False self._searchText = None self._infoWidget = None self._groupItem = None self._groupColumn = 0 self._mimeText = None self._itemsWidget = None self._stretchToWidget = None self._dragEnabled = True self._imageSequence = None self._imageSequencePath = "" self._sliderDown = False self._sliderValue = 0.0 self._sliderPreviousValue = 0.0 self._sliderPosition = None self._sliderEnabled = False self._worker = ImageWorker() self._worker.setAutoDelete(False) self._worker.signals.triggered.connect(self._thumbnailFromImage) self._workerStarted = False def __eq__(self, other): return id(other) == id(self) def __ne__(self, other): return id(other) != id(self) def __del__(self): """ Make sure the sequence is stopped when deleted. :rtype: None """ self.stop() def columnFromLabel(self, label): if self.treeWidget(): return self.treeWidget().columnFromLabel(label) else: return None def labelFromColumn(self, column): if self.treeWidget(): return self.treeWidget().labelFromColumn(column) else: return None def mimeText(self): """ Return the mime text for drag and drop. :rtype: str """ return self._mimeText or self.text(0) def setMimeText(self, text): """ Set the mime text for drag and drop. :type text: str :rtype: None """ self._mimeText = text def setHidden(self, value): """ Set the item hidden. :type value: bool :rtype: None """ QtWidgets.QTreeWidgetItem.setHidden(self, value) row = self.treeWidget().indexFromItem(self).row() self.itemsWidget().listView().setRowHidden(row, value) def setDragEnabled(self, value): """ Set True if the item can be dragged. :type value: bool :rtype: None """ self._dragEnabled = value def dragEnabled(self): """ Return True if the item can be dragged. :rtype: bool """ return self._dragEnabled def setIcon(self, column, icon, color=None): """ Set the icon to be displayed in the given column. :type column: int or str :type icon: QtGui.QIcon :type color: QtGui.QColor or None :rtype: None """ # Safe guard for when the class is being used without the gui. isAppRunning = bool(QtWidgets.QApplication.instance()) if not isAppRunning: return if isinstance(icon, basestring): if not os.path.exists(icon): color = color or studioqt.Color(255, 255, 255, 20) icon = studiolibrary.resource.icon("image", color=color) else: icon = QtGui.QIcon(icon) if isinstance(column, basestring): self._icon[column] = icon else: self._pixmap[column] = None QtWidgets.QTreeWidgetItem.setIcon(self, column, icon) self.updateIcon() def setItemData(self, data): """ Set the given dictionary as the data for the item. :type data: dict :rtype: None """ self._itemData = data def itemData(self): """ Return the item data for this item. :rtype: dict """ return self._itemData def setName(self, text): """ Set the name that is shown under the icon and in the Name column. :type text: str :rtype: None """ itemData = self.itemData() itemData['icon'] = text itemData['name'] = text def name(self): """ Return text for the Name column. :rtype: str """ return self.itemData().get("name") def displayText(self, label): """ Return the sort data for the given column. :type label: str :rtype: str """ return unicode(self.itemData().get(label, '')) def sortText(self, label): """ Return the sort data for the given column. :type label: str :rtype: str """ return unicode(self.itemData().get(label, '')) def update(self): """ Refresh the visual state of the icon. :rtype: None """ self.updateIcon() self.updateFrame() def updateIcon(self): """ Clear the pixmap cache for the item. :rtype: None """ self.clearCache() def clearCache(self): """Clear the thumbnail cache.""" self._pixmap = {} self._pixmapRect = None self._pixmapScaled = None self._thumbnailIcon = None def dpi(self): """ Used for high resolution devices. :rtype: int """ if self.itemsWidget(): return self.itemsWidget().dpi() else: return 1 def clicked(self): """ Triggered when an item is clicked. :rtype: None """ pass def takeFromTree(self): """ Takes this item from the tree. """ tree = self.treeWidget() parent = self.parent() if parent: parent.takeChild(parent.indexOfChild(self)) else: tree.takeTopLevelItem(tree.indexOfTopLevelItem(self)) def selectionChanged(self): """ Triggered when an item has been either selected or deselected. :rtype: None """ self.resetSlider() def doubleClicked(self): """ Triggered when an item is double clicked. :rtype: None """ pass def setGroupItem(self, groupItem): """ Set the group item that this item is a child to. :type groupItem: groupitem.GroupItem """ self._groupItem = groupItem def groupItem(self): """ Get the group item that this item is a child to. :rtype: groupitem.GroupItem """ return self._groupItem def itemsWidget(self): """ Returns the items widget that contains the items. :rtype: ItemsWidget """ itemsWidget = None if self.treeWidget(): itemsWidget = self.treeWidget().parent() return itemsWidget def url(self): """ Return the url object for the given item. :rtype: QtCore.QUrl or None """ if not self._url: self._url = QtCore.QUrl(self.text(0)) return self._url def setUrl(self, url): """ Set the url object for the item. :type: QtCore.QUrl or None :rtype: None """ self._url = url def searchText(self): """ Return the search string used for finding the item. :rtype: str """ if not self._searchText: self._searchText = unicode(self._data) return self._searchText def setStretchToWidget(self, widget): """ Set the width of the item to the width of the given widget. :type widget: QtWidgets.QWidget :rtype: None """ self._stretchToWidget = widget def stretchToWidget(self): """ Return the sretchToWidget. :rtype: QtWidgets.QWidget """ return self._stretchToWidget def setSize(self, size): """ Set the size for the item. :type size: QtCore.QSize :rtype: None """ self._size = size def sizeHint(self, column=0): """ Return the current size of the item. :type column: int :rtype: QtCore.QSize """ if self.stretchToWidget(): if self._size: size = self._size else: size = self.itemsWidget().iconSize() w = self.stretchToWidget().width() h = size.height() return QtCore.QSize(w - 20, h) if self._size: return self._size else: iconSize = self.itemsWidget().iconSize() if self.isLabelUnderItem(): w = iconSize.width() h = iconSize.width() + self.textHeight() iconSize = QtCore.QSize(w, h) return iconSize def setPixmap(self, column, pixmap): """ Set the pixmap to be displayed in the given column. :type column: int :type pixmap: QtWidgets.QPixmap :rtype: None """ self._pixmap[column] = pixmap def thumbnailPath(self): """ Return the thumbnail path on disk. :rtype: None or str """ return "" def _thumbnailFromImage(self, image): """ Called after the given image object has finished loading. :type image: QtGui.QImage :rtype: None """ self.clearCache() pixmap = QtGui.QPixmap() pixmap.convertFromImage(image) icon = QtGui.QIcon(pixmap) self._thumbnailIcon = icon if self.itemsWidget(): self.itemsWidget().update() def defaultThumbnailPath(self): """ Get the default thumbnail path. :rtype: str """ return self.THUMBNAIL_PATH def defaultThumbnailIcon(self): """ Get the default thumbnail icon. :rtype: QtGui.QIcon """ return QtGui.QIcon(self.defaultThumbnailPath()) def thumbnailIcon(self): """ Return the thumbnail icon. :rtype: QtGui.QIcon """ thumbnailPath = self.thumbnailPath() if not self._thumbnailIcon: if self.ENABLE_THUMBNAIL_THREAD and not self._workerStarted: self._workerStarted = True self._worker.setPath(thumbnailPath) self.ThreadPool.start(self._worker) self._thumbnailIcon = self.defaultThumbnailIcon() else: self._thumbnailIcon = QtGui.QIcon(thumbnailPath) return self._thumbnailIcon def icon(self, column): """ Overriding the icon method to add support for the thumbnail icon. :type column: int :rtype: QtGui.QIcon """ icon = QtWidgets.QTreeWidgetItem.icon(self, column) if not icon and column == self.THUMBNAIL_COLUMN: icon = self.thumbnailIcon() return icon def pixmap(self, column): """ Return the pixmap for the given column. :type column: int :rtype: QtWidgets.QPixmap """ if not self._pixmap.get(column): icon = self.icon(column) if icon: size = QtCore.QSize(self.MAX_ICON_SIZE, self.MAX_ICON_SIZE) iconSize = icon.actualSize(size) self._pixmap[column] = icon.pixmap(iconSize) return self._pixmap.get(column) def padding(self): """ Return the padding/border size for the item. :rtype: int """ return self.itemsWidget().padding() def textHeight(self): """ Return the height of the text for the item. :rtype: int """ return self.itemsWidget().itemTextHeight() def isTextVisible(self): """ Check if the label should be displayed. :rtype: bool """ return self.labelDisplayOption() != LabelDisplayOption.Hide def isLabelOverItem(self): """ Check if the label should be displayed over the item. :rtype: bool """ return self.labelDisplayOption() == LabelDisplayOption.Over def isLabelUnderItem(self): """ Check if the label should be displayed under the item. :rtype: bool """ return self.labelDisplayOption() == LabelDisplayOption.Under def labelDisplayOption(self): """ Return True if the text is visible. :rtype: bool """ return self.itemsWidget().labelDisplayOption() def textAlignment(self, column): """ Return the text alignment for the label in the given column. :type column: int :rtype: QtCore.Qt.AlignmentFlag """ if self.itemsWidget().isIconView(): return QtCore.Qt.AlignCenter else: return QtWidgets.QTreeWidgetItem.textAlignment(self, column) # ----------------------------------------------------------------------- # Support for mouse and key events # ----------------------------------------------------------------------- def underMouse(self): """Return True if the item is under the mouse cursor.""" return self._underMouse def contextMenu(self, menu): """ Return the context menu for the item. Reimplement in a subclass to return a custom context menu for the item. :rtype: QtWidgets.QMenu """ pass def dropEvent(self, event): """ Reimplement in a subclass to receive drop events for the item. :type event: QtWidgets.QDropEvent :rtype: None """ def mouseLeaveEvent(self, event): """ Reimplement in a subclass to receive mouse leave events for the item. :type event: QtWidgets.QMouseEvent :rtype: None """ self._underMouse = False self.stop() def mouseEnterEvent(self, event): """ Reimplement in a subclass to receive mouse enter events for the item. :type event: QtWidgets.QMouseEvent :rtype: None """ self._underMouse = True self.play() def mouseMoveEvent(self, event): """ Reimplement in a subclass to receive mouse move events for the item. :type event: QtWidgets.QMouseEvent :rtype: None """ self.sliderEvent(event) self.imageSequenceEvent(event) def mousePressEvent(self, event): """ Reimplement in a subclass to receive mouse press events for the item. :type event: QtWidgets.QMouseEvent :rtype: None """ if event.button() == QtCore.Qt.MidButton: if self.isSliderEnabled(): self.setSliderDown(True) self._sliderPosition = event.pos() def mouseReleaseEvent(self, event): """ Reimplement in a subclass to receive mouse release events for the item. :type event: QtWidgets.QMouseEvent :rtype: None """ if self.isSliderDown(): self._sliderPosition = None self._sliderPreviousValue = self.sliderValue() def keyPressEvent(self, event): """ Reimplement in a subclass to receive key press events for the item. :type event: QtWidgets.QKeyEvent :rtype: None """ pass def keyReleaseEvent(self, event): """ Reimplement in a subclass to receive key release events for the item. :type event: QtWidgets.QKeyEvent :rtype: None """ pass # ----------------------------------------------------------------------- # Support for custom painting # ----------------------------------------------------------------------- def textColor(self): """ Return the text color for the item. :rtype: QtWidgets.QtColor """ # This will be changed to use the palette soon. # Note: There were problems with older versions of Qt's palette (Maya 2014). # Eg: # return self.itemsWidget().palette().color(self.itemsWidget().foregroundRole()) return self.itemsWidget().textColor() def textSelectedColor(self): """ Return the selected text color for the item. :rtype: QtWidgets.QtColor """ return self.itemsWidget().textSelectedColor() def backgroundColor(self): """ Return the background color for the item. :rtype: QtWidgets.QtColor """ return self.itemsWidget().itemBackgroundColor() def backgroundHoverColor(self): """ Return the background color when the mouse is over the item. :rtype: QtWidgets.QtColor """ return self.itemsWidget().backgroundHoverColor() def backgroundSelectedColor(self): """ Return the background color when the item is selected. :rtype: QtWidgets.QtColor """ return self.itemsWidget().backgroundSelectedColor() def rect(self): """ Return the rect for the current paint frame. :rtype: QtCore.QRect """ return self._rect def setRect(self, rect): """ Set the rect for the current paint frame. :type rect: QtCore.QRect :rtype: None """ self._rect = rect def visualRect(self, option): """ Return the visual rect for the item. :type option: QtWidgets.QStyleOptionViewItem :rtype: QtCore.QRect """ return QtCore.QRect(option.rect) def paintRow(self, painter, option, index): """ Paint performs low-level painting for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :type index: QtCore.QModelIndex :rtype: None """ QtWidgets.QTreeWidget.drawRow(self.treeWidget(), painter, option, index) def paint(self, painter, option, index): """ Paint performs low-level painting for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :type index: QtCore.QModelIndex :rtype: None """ self.setRect(QtCore.QRect(option.rect)) painter.save() try: self.paintBackground(painter, option, index) self.paintIcon(painter, option, index) if index.column() == 0 and self.sliderValue() != 0: self.paintSlider(painter, option, index) if self.isTextVisible(): self.paintText(painter, option, index) if index.column() == 0: self.paintTypeIcon(painter, option) if index.column() == 0 and self.imageSequence(): self.paintPlayhead(painter, option) finally: painter.restore() def paintBackground(self, painter, option, index): """ Draw the background for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :type index: QtCore.QModelIndex """ isSelected = option.state & QtWidgets.QStyle.State_Selected isMouseOver = option.state & QtWidgets.QStyle.State_MouseOver painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) visualRect = self.visualRect(option) if isSelected: color = self.backgroundSelectedColor() painter.setBrush(QtGui.QBrush(color)) elif isMouseOver: color = self.backgroundHoverColor() painter.setBrush(QtGui.QBrush(color)) else: color = self.backgroundColor() painter.setBrush(QtGui.QBrush(color)) if not self.itemsWidget().isIconView(): spacing = 1 * self.dpi() height = visualRect.height() - spacing visualRect.setHeight(height) painter.drawRect(visualRect) def paintSlider(self, painter, option, index): """ Draw the virtual slider for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :type index: QtCore.QModelIndex """ if not self.PAINT_SLIDER: return if not self.itemsWidget().isIconView(): return # Draw slider background painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) rect = self.visualRect(option) color = self.itemsWidget().backgroundColor().toRgb() color.setAlpha(75) painter.setBrush(QtGui.QBrush(color)) height = rect.height() ratio = self.sliderValue() if ratio < 0: width = 0 elif ratio > 100: width = rect.width() else: width = rect.width() * (float(ratio) / 100) rect.setWidth(width) rect.setHeight(height) painter.drawRect(rect) # Draw slider value rect = self.visualRect(option) rect.setY(rect.y() + (4 * self.dpi())) color = self.itemsWidget().textColor().toRgb() color.setAlpha(220) pen = QtGui.QPen(color) align = QtCore.Qt.AlignTop | QtCore.Qt.AlignHCenter painter.setPen(pen) painter.drawText(rect, align, str(self.sliderValue()) + "%") def iconRect(self, option): """ Return the icon rect for the item. :type option: QtWidgets.QStyleOptionViewItem :rtype: QtCore.QRect """ padding = self.padding() rect = self.visualRect(option) width = rect.width() height = rect.height() if self.isLabelUnderItem(): height -= self.textHeight() width -= padding height -= padding rect.setWidth(width) rect.setHeight(height) x = 0 x += float(padding) / 2 x += float((width - rect.width())) / 2 y = float((height - rect.height())) / 2 y += float(padding) / 2 rect.translate(x, y) return rect def scalePixmap(self, pixmap, rect): """ Scale the given pixmap to the give rect size. This method will cache the scaled pixmap if called with the same size. :type pixmap: QtGui.QPixmap :type rect: QtCore.QRect :rtype: QtGui.QPixmap """ rectChanged = True if self._pixmapRect: widthChanged = self._pixmapRect.width() != rect.width() heightChanged = self._pixmapRect.height() != rect.height() rectChanged = widthChanged or heightChanged if not self._pixmapScaled or rectChanged: self._pixmapScaled = pixmap.scaled( rect.width(), rect.height(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation, ) self._pixmapRect = rect return self._pixmapScaled def paintIcon(self, painter, option, index, align=None): """ Draw the icon for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :rtype: None """ column = index.column() pixmap = self.pixmap(column) if not pixmap: return rect = self.iconRect(option) pixmap = self.scalePixmap(pixmap, rect) pixmapRect = QtCore.QRect(rect) pixmapRect.setWidth(pixmap.width()) pixmapRect.setHeight(pixmap.height()) align = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter x, y = 0, 0 isAlignBottom = align == QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft \ or align == QtCore.Qt.AlignBottom | QtCore.Qt.AlignHCenter \ or align == QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight isAlignHCenter = align == QtCore.Qt.AlignHCenter \ or align == QtCore.Qt.AlignCenter \ or align == QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom \ or align == QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop isAlignVCenter = align == QtCore.Qt.AlignVCenter \ or align == QtCore.Qt.AlignCenter \ or align == QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft \ or align == QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight if isAlignHCenter: x += float(rect.width() - pixmap.width()) / 2 if isAlignVCenter: y += float(rect.height() - pixmap.height()) / 2 elif isAlignBottom: y += float(rect.height() - pixmap.height()) pixmapRect.translate(x, y) painter.drawPixmap(pixmapRect, pixmap) def drawIconBorder(self, painter, pixmapRect): """ Draw a border around the icon. :type painter: QtWidgets.QPainter :type pixmapRect: QtWidgets.QRect :rtype: None """ pixmapRect = QtCore.QRect(pixmapRect) pixmapRect.setX(pixmapRect.x() - 5) pixmapRect.setY(pixmapRect.y() - 5) pixmapRect.setWidth(pixmapRect.width() + 5) pixmapRect.setHeight(pixmapRect.height() + 5) color = QtGui.QColor(255, 255, 255, 10) painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) painter.setBrush(QtGui.QBrush(color)) painter.drawRect(pixmapRect) def fontSize(self): """ Return the font size for the item. :rtype: int """ return self.DEFAULT_FONT_SIZE def font(self, column): """ Return the font for the given column. :type column: int :rtype: QtWidgets.QFont """ default = QtWidgets.QTreeWidgetItem.font(self, column) font = self._fonts.get(column, default) font.setPixelSize(self.fontSize() * self.dpi()) return font def setFont(self, column, font): """ Set the font for the given column. :type column: int :type font: QtWidgets.QFont :rtype: Noen """ self._fonts[column] = font def paintText(self, painter, option, index): """ Draw the text for the item. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :rtype: None """ column = index.column() if column == 0 and self.itemsWidget().isTableView(): return self._paintText(painter, option, column) def textWidth(self, column): text = self.text(column) font = self.font(column) metrics = QtGui.QFontMetricsF(font) textWidth = metrics.width(text) return textWidth def _paintText(self, painter, option, column): if self.itemsWidget().isIconView(): text = self.name() else: label = self.labelFromColumn(column) text = self.displayText(label) isSelected = option.state & QtWidgets.QStyle.State_Selected if isSelected: color = self.textSelectedColor() else: color = self.textColor() visualRect = self.visualRect(option) width = visualRect.width() height = visualRect.height() padding = self.padding() x = padding / 2 y = padding / 2 visualRect.translate(x, y) visualRect.setWidth(width - padding) visualRect.setHeight(height - padding) font = self.font(column) align = self.textAlignment(column) metrics = QtGui.QFontMetricsF(font) if text: textWidth = metrics.width(text) else: textWidth = 1 # # Check if the current text fits within the rect. if textWidth > visualRect.width() - padding: visualWidth = visualRect.width() text = metrics.elidedText(text, QtCore.Qt.ElideRight, visualWidth) align = QtCore.Qt.AlignLeft align = align | QtCore.Qt.AlignVCenter rect = QtCore.QRect(visualRect) if self.itemsWidget().isIconView(): if self.isLabelOverItem() or self.isLabelUnderItem(): padding = 8 if padding < 8 else padding height = metrics.height() + (padding / 2) y = (rect.y() + rect.height()) - height rect.setY(y) rect.setHeight(height) if self.isLabelOverItem(): color2 = self.itemsWidget().backgroundColor().toRgb() color2.setAlpha(200) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtGui.QBrush(color2)) painter.drawRect(rect) pen = QtGui.QPen(color) painter.setPen(pen) painter.setFont(font) painter.drawText(rect, align, text) # ------------------------------------------------------------------------ # Support for middle mouse slider # ------------------------------------------------------------------------ def setSliderEnabled(self, enabled): """ Set if middle mouse slider is enabled. :type enabled: bool """ self._sliderEnabled = enabled def isSliderEnabled(self): """ Return true if middle mouse slider is enabled. :rtype: bool """ return self._sliderEnabled def sliderEvent(self, event): """ Called when the mouse moves while the middle mouse button is held down. :param event: QtGui.QMouseEvent """ if self.isSliderDown(): value = (event.pos().x() - self.sliderPosition().x()) / 1.5 value = math.ceil(value) + self.sliderPreviousValue() try: self.setSliderValue(value) except Exception: self.setSliderDown(False) def resetSlider(self): """Reset the slider value to zero.""" self._sliderValue = 0.0 self._sliderPreviousValue = 0.0 def setSliderDown(self, down): """Called when the middle mouse button is released.""" self._sliderDown = down if not down: self._sliderPosition = None self._sliderPreviousValue = self.sliderValue() def isSliderDown(self): """ Return True if blending. :rtype: bool """ return self._sliderDown def setSliderValue(self, value): """ Set the blend value. :type value: float :rtype: bool """ if self.isSliderEnabled(): self._sliderValue = value if self.PAINT_SLIDER: self.update() self.sliderChanged.emit(value) if self.PAINT_SLIDER: self.update() logger.debug("Blending:" + str(value)) def sliderValue(self): """ Return the blend value. :rtype: float """ return self._sliderValue def sliderPreviousValue(self): """ :rtype: float """ return self._sliderPreviousValue def sliderPosition(self): """ :rtype: QtGui.QPoint """ return self._sliderPosition # ------------------------------------------------------------------------ # Support animated image sequence # ------------------------------------------------------------------------ def imageSequenceEvent(self, event): """ :type event: QtCore.QEvent :rtype: None """ if self.imageSequence(): if studioqt.isControlModifier(): if self.rect(): x = event.pos().x() - self.rect().x() width = self.rect().width() percent = 1.0 - (float(width - x) / float(width)) frame = int(self.imageSequence().frameCount() * percent) self.imageSequence().jumpToFrame(frame) self.updateFrame() def resetImageSequence(self): self._imageSequence = None def imageSequence(self): """ :rtype: studioqt.ImageSequence """ return self._imageSequence def setImageSequence(self, value): """ :type value: studioqt.ImageSequence """ self._imageSequence = value def setImageSequencePath(self, path): """ :type path: str """ self._imageSequencePath = path def imageSequencePath(self): """ :rtype: str """ return self._imageSequencePath def stop(self): """Stop playing the image sequence movie.""" if self.imageSequence(): self.imageSequence().stop() def play(self): """Start playing the image sequence movie.""" self.resetImageSequence() path = self.imageSequencePath() or self.thumbnailPath() movie = None if os.path.isfile(path) and path.lower().endswith(".gif"): movie = QtGui.QMovie(path) movie.setCacheMode(QtGui.QMovie.CacheAll) movie.frameChanged.connect(self._frameChanged) elif os.path.isdir(path): if not self.imageSequence(): movie = studioqt.ImageSequence(path) movie.frameChanged.connect(self._frameChanged) if movie: self.setImageSequence(movie) self.imageSequence().start() def _frameChanged(self, frame): """Triggered when the movie object updates to the given frame.""" if not studioqt.isControlModifier(): self.updateFrame() def updateFrame(self): """Triggered when the movie object updates the current frame.""" if self.imageSequence(): pixmap = self.imageSequence().currentPixmap() self.setIcon(0, pixmap) def playheadColor(self): """ Return the playhead color. :rtype: QtGui.Color """ return self.DEFAULT_PLAYHEAD_COLOR def paintPlayhead(self, painter, option): """ Paint the playhead if the item has an image sequence. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :rtype: None """ imageSequence = self.imageSequence() if imageSequence and self.underMouse(): count = imageSequence.frameCount() current = imageSequence.currentFrameNumber() if count > 0: percent = float((count + current) + 1) / count - 1 else: percent = 0 r = self.iconRect(option) c = self.playheadColor() painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtGui.QBrush(c)) if percent <= 0: width = 0 elif percent >= 1: width = r.width() else: width = (percent * r.width()) - 1 height = 3 * self.dpi() y = r.y() + r.height() - (height - 1) painter.drawRect(r.x(), y, width, height) def typeIconPath(self): """ Return the type icon path on disc. :rtype: path or None """ if self.TYPE_ICON_PATH is None: return self.ICON_PATH return self.TYPE_ICON_PATH def typePixmap(self): """ Return the type pixmap for the plugin. :rtype: QtWidgets.QPixmap """ if not self._typePixmap: iconPath = self.typeIconPath() if iconPath and os.path.exists(iconPath): self._typePixmap = QtGui.QPixmap(iconPath) return self._typePixmap def typeIconRect(self, option): """ Return the type icon rect. :rtype: QtGui.QRect """ padding = 2 * self.dpi() r = self.iconRect(option) x = r.x() + padding y = r.y() + padding rect = QtCore.QRect(x, y, 13 * self.dpi(), 13 * self.dpi()) return rect def paintTypeIcon(self, painter, option): """ Draw the item type icon at the top left. :type painter: QtWidgets.QPainter :type option: QtWidgets.QStyleOptionViewItem :rtype: None """ rect = self.typeIconRect(option) typePixmap = self.typePixmap() if typePixmap: painter.setOpacity(0.5) painter.drawPixmap(rect, typePixmap) painter.setOpacity(1)
def fa(cls, path, **kwargs): """ Create a new icon with the given path, options and state. Example: icon = studioqt.Icon.fa( path, color="rgb(255,255,255)" color_disabled="rgb(0,200,200,20)", ) :type path: str :type kwargs: dict :rtype: studioqt.Icon """ color = kwargs.get('color', QtGui.QColor(0, 0, 0)) pixmap = studioqt.Pixmap(path) pixmap.setColor(color) valid_options = [ 'active', 'selected', 'disabled', 'on', 'off', 'on_active', 'on_selected', 'on_disabled', 'off_active', 'off_selected', 'off_disabled', 'color', 'color_on', 'color_off', 'color_active', 'color_selected', 'color_disabled', 'color_on_selected', 'color_on_active', 'color_on_disabled', 'color_off_selected', 'color_off_active', 'color_off_disabled', ] default = { "on_active": kwargs.get("active", studioqt.Pixmap(path)), "off_active": kwargs.get("active", studioqt.Pixmap(path)), "on_disabled": kwargs.get("disabled", studioqt.Pixmap(path)), "off_disabled": kwargs.get("disabled", studioqt.Pixmap(path)), "on_selected": kwargs.get("selected", studioqt.Pixmap(path)), "off_selected": kwargs.get("selected", studioqt.Pixmap(path)), "color_on_active": kwargs.get("color_active", color), "color_off_active": kwargs.get("color_active", color), "color_on_disabled": kwargs.get("color_disabled", color), "color_off_disabled": kwargs.get("color_disabled", color), "color_on_selected": kwargs.get("color_selected", color), "color_off_selected": kwargs.get("color_selected", color), } default.update(kwargs) kwargs = copy.copy(default) for option in valid_options: if 'color' in option: kwargs[option] = kwargs.get(option, color) else: svg_path = kwargs.get(option, path) kwargs[option] = studioqt.Pixmap(svg_path) options = { QtGui.QIcon.On: { QtGui.QIcon.Normal: (kwargs['color_on'], kwargs['on']), QtGui.QIcon.Active: (kwargs['color_on_active'], kwargs['on_active']), QtGui.QIcon.Disabled: (kwargs['color_on_disabled'], kwargs['on_disabled']), QtGui.QIcon.Selected: (kwargs['color_on_selected'], kwargs['on_selected']) }, QtGui.QIcon.Off: { QtGui.QIcon.Normal: (kwargs['color_off'], kwargs['off']), QtGui.QIcon.Active: (kwargs['color_off_active'], kwargs['off_active']), QtGui.QIcon.Disabled: (kwargs['color_off_disabled'], kwargs['off_disabled']), QtGui.QIcon.Selected: (kwargs['color_off_selected'], kwargs['off_selected']) } } icon = cls(pixmap) for state in options: for mode in options[state]: color, pixmap = options[state][mode] pixmap = studioqt.Pixmap(pixmap) pixmap.setColor(color) icon.addPixmap(pixmap, mode, state) return icon
class ImageSequenceWidget(QtWidgets.QToolButton): DEFAULT_PLAYHEAD_COLOR = QtGui.QColor(255, 255, 255, 220) def __init__(self, *args): QtWidgets.QToolButton.__init__(self, *args) self._imageSequence = ImageSequence("") self._imageSequence.frameChanged.connect(self._frameChanged) self._toolBar = QtWidgets.QToolBar(self) self._toolBar.setStyleSheet(STYLE) studioqt.fadeOut(self._toolBar, duration=0) spacer = QtWidgets.QWidget() spacer.setMaximumWidth(4) spacer.setSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred ) self._toolBar.addWidget(spacer) spacer = QtWidgets.QWidget() spacer.setMaximumWidth(4) spacer.setSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred ) self._firstSpacer = self._toolBar.addWidget(spacer) self.setSize(150, 150) self.setMouseTracking(True) def hasFrames(self): """ Check if the images sequence has any frames. :rtype: bool """ return bool(self.firstFrame()) def firstFrame(self): """ Get the first frame in the image sequence. :rtype: str """ return self._imageSequence.firstFrame() def isSequence(self): """ Check if the image sequence has more than one frame. :rtype: bool """ return bool(self._imageSequence.frameCount() > 1) def dirname(self): """ Get the directory to the image sequence on disk. :rtype: str """ return self._imageSequence.dirname() def addAction(self, path, text, tip, callback): """ Add an action to the tool bar. :type path: str :type text: str :type tip: str :type callback: func :rtype: QtWidgets.QAction """ icon = studioqt.Icon.fa( path, color="rgb(250,250,250,160)", color_active="rgb(250,250,250,250)", color_disabled="rgb(0,0,0,20)" ) action = QtWidgets.QAction(icon, text, self._toolBar) action.setToolTip(tip) # action.setStatusTip(tip) # action.setWhatsThis(tip) self._toolBar.insertAction(self._firstSpacer, action) action.triggered.connect(callback) return action def actions(self): """ Get all the actions that are a child of the ToolBar. :rtype: list[QtWidgets.QAction] """ actions = [] for child in self._toolBar.children(): if isinstance(child, QtWidgets.QAction): actions.append(child) return actions def resizeEvent(self, event): """ Called when the widget is resized. :type event: QtWidgets.QResizeEvent """ self.updateToolBar() def updateToolBar(self): """Update the tool bar size depending on the number of actions.""" self._toolBar.setIconSize(QtCore.QSize(16, 16)) count = (len(self.actions())) - 3 width = (26 * count) self._toolBar.setGeometry(0, 0, width, 25) x = self.rect().center().x() - (self._toolBar.width()/2) y = self.height() - self._toolBar.height() - 12 width = self._toolBar.width() self._toolBar.setGeometry(x, y, width, self._toolBar.height()) def isControlModifier(self): """ Check if the the control modifier is active. :rtype: bool """ modifiers = QtWidgets.QApplication.keyboardModifiers() return modifiers == QtCore.Qt.ControlModifier def setSize(self, w, h): """ Reimplemented so that the icon size is set at the same time. :type w: int :type h: int :rtype: None """ self._size = QtCore.QSize(w, h) self.setIconSize(self._size) self.setFixedSize(self._size) def setPath(self, path): """ Set a single frame image sequence. :type path: str """ self._imageSequence.setPath(path) self.updateIcon() def setDirname(self, dirname): """ This method has been deprecated. Please use setPath instead. :type dirname: str """ self._imageSequence.setPath(dirname) self.updateIcon() def updateIcon(self): """Update the icon for the current frame.""" if self._imageSequence.frames(): icon = self._imageSequence.currentIcon() self.setIcon(icon) def enterEvent(self, event): """ Start playing the image sequence when the mouse enters the widget. :type event: QtCore.QEvent :rtype: None """ self._imageSequence.start() studioqt.fadeIn(self._toolBar, duration=300) def leaveEvent(self, event): """ Stop playing the image sequence when the mouse leaves the widget. :type event: QtCore.QEvent :rtype: None """ self._imageSequence.pause() studioqt.fadeOut(self._toolBar, duration=300) def mouseMoveEvent(self, event): """ Reimplemented to add support for scrubbing. :type event: QtCore.QEvent :rtype: None """ if self.isControlModifier() and self._imageSequence.frameCount() > 1: percent = 1.0 - (float(self.width() - event.pos().x()) / float(self.width())) frame = int(self._imageSequence.frameCount() * percent) self._imageSequence.jumpToFrame(frame) icon = self._imageSequence.currentIcon() self.setIcon(icon) def _frameChanged(self, frame=None): """ Triggered when the image sequence changes frame. :type frame: int or None :rtype: None """ if not self.isControlModifier(): icon = self._imageSequence.currentIcon() self.setIcon(icon) def currentFilename(self): """ Return the current image location. :rtype: str """ return self._imageSequence.currentFilename() def playheadHeight(self): """ Return the height of the playhead. :rtype: int """ return 4 def paintEvent(self, event): """ Triggered on frame changed. :type event: QtCore.QEvent :rtype: None """ QtWidgets.QToolButton.paintEvent(self, event) painter = QtGui.QPainter() painter.begin(self) if self.currentFilename() and self._imageSequence.frameCount() > 1: r = event.rect() playheadHeight = self.playheadHeight() playheadPosition = self._imageSequence.percent() * r.width()-1 x = r.x() y = self.height() - playheadHeight painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtGui.QBrush(self.DEFAULT_PLAYHEAD_COLOR)) painter.drawRect(x, y, playheadPosition, playheadHeight) painter.end()