def browseColor(self): """ Show the color dialog. :rtype: None """ color = self.currentColor() 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 browseBackgroundColor(self): """ :rtype: None """ color = self.backgroundColor() d = QtWidgets.QColorDialog(self) d.setCurrentColor(color) colors = [(0, 0, 0), (20, 20, 30), (0, 30, 60), (0, 60, 60), (0, 60, 30), (60, 0, 10), (60, 0, 40), (40, 15, 5)] index = -1 for colorR, colorG, colorB in colors: for i in range(0, 6): index += 1 try: standardColor = QtGui.QColor(colorR, colorG, colorB) d.setStandardColor(index, standardColor) except: standardColor = QtGui.QColor(colorR, colorG, colorB).rgba() d.setStandardColor(index, standardColor) colorR += 20 colorB += 20 colorG += 20 d.currentColorChanged.connect(self.setBackgroundColor) d.open(self, QtCore.SLOT('blankSlot()')) if d.exec_(): self.setBackgroundColor(d.selectedColor()) else: self.setBackgroundColor(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._isItemTextVisible = True self._dataset = None self._treeWidget = TreeWidget(self) self._support_drag = self.DEFAULT_SUPPORT_DRAG self._listView = ListView(self._support_drag, self) self._listView.setTreeWidget(self._treeWidget) self._delegate = ItemDelegate() self._delegate.setItemsWidget(self) self._listView.setItemDelegate(self._delegate) self._treeWidget.setItemDelegate(self._delegate) 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._backgroundColor = 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 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 createWidget(self, parent): """ :type parent: QtWidgets.QMenu """ height = 25 spacing = 1 options = self.library().theme().options() styleSheet = studioqt.StyleSheet.fromText(LibraryAction.STYLE_SHEET, options=options) actionWidget = QtWidgets.QFrame(parent) actionWidget.setObjectName('actionWidget') actionWidget.setStyleSheet(styleSheet.data()) actionLabel = QtWidgets.QLabel(self.library().name(), actionWidget) actionLabel.setObjectName('actionLabel') actionLabel.setFixedHeight(height) iconColor = QtGui.QColor(255, 255, 255, 220) icon = studiolibrary.resource().icon('delete', color=iconColor) actionOption = QtWidgets.QPushButton('', actionWidget) actionOption.setObjectName('actionOption') actionOption.setIcon(icon) actionOption.setFixedHeight(height + spacing) actionOption.setFixedWidth(height) actionOption.clicked.connect(self.deleteLibrary) actionIcon = QtWidgets.QLabel('', actionWidget) actionIcon.setObjectName('actionIcon') actionIcon.setFixedWidth(10) actionIcon.setFixedHeight(height) actionLayout = QtWidgets.QHBoxLayout(actionWidget) actionLayout.setSpacing(0) actionLayout.setContentsMargins(0, 0, 0, 0) actionLayout.addWidget(actionIcon, stretch=1) actionLayout.addWidget(actionLabel, stretch=1) actionLayout.addWidget(actionOption, stretch=1) return actionWidget
def createBackgroundColorBar(self): """ Create and setup the background color bar. :rtype: None """ browserColors_ = [ (0, 0, 0), (20, 20, 30), (0, 30, 60), (0, 60, 60), (0, 60, 30), (60, 0, 10), (60, 0, 40), (40, 15, 5), ] browserColors = [] for colorR, colorG, colorB in browserColors_: for i in range(0, 6): color = QtGui.QColor(colorR, colorG, colorB).rgba() browserColors.append(color) colorR += 20 colorB += 20 colorG += 20 hColorBar = studioqt.HColorBar() hColorBar.setColors(self.DEFAULT_BACKGROUND_COLORS) hColorBar.setCurrentColor(self.DEFAULT_BACKGROUND_COLOR) hColorBar.setBrowserColors(browserColors) hColorBar.colorChanged.connect(self._backgroundColorClicked) self.ui.backgroundColorBarFrame.layout().addWidget(hColorBar)
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 __init__(self, supportDrag=True, *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(supportDrag) self.setDragEnabled(supportDrag) self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) self.clicked.connect(self._indexClicked) self.doubleClicked.connect(self._indexDoubleClicked)
def backgroundColor(self): """ Return the background color for the item. :rtype: QtWidgets.QtColor """ return QtGui.QColor(0, 0, 0, 0)
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 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 """ studioqt.CombinedWidgetItem.paintBackground(self, painter, option, index) painter.setPen(QtGui.QPen(QtCore.Qt.NoPen)) visualRect = self.visualRect(option) textWidth = self.textWidth(0) padding = (20 * 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(250, 250, 250, 20) painter.setBrush(QtGui.QBrush(color)) painter.drawRect(visualRect)
def backgroundSelectedColor(self): """ Return the background color when the item is selected. :rtype: QtWidgets.QtColor """ return QtGui.QColor(0, 0, 0, 0)
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 browseColor(self): """ :rtype: None """ color = self.color() d = QtWidgets.QColorDialog(self) d.setCurrentColor(color) colors = [(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)] index = -1 for colorR, colorG, colorB in colors: for i in range(0, 3): index += 1 if colorR < 0: colorR = 0 if colorG < 0: colorG = 0 if colorB < 0: colorB = 0 try: standardColor = QtGui.QColor(colorR, colorG, colorB) d.setStandardColor(index, standardColor) except: standardColor = QtGui.QColor(colorR, colorG, colorB).rgba() d.setStandardColor(index, standardColor) colorR -= 20 colorB -= 20 colorG -= 20 d.currentColorChanged.connect(self.setColor) d.open(self, QtCore.SLOT('blankSlot()')) if d.exec_(): self.setColor(d.selectedColor()) else: self.setColor(color)
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 browseColor(self): """ Show the color dialog. :rtype: None """ color = self.currentColor() 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) # PySide2 doesn't support d.open(), so we need to pass a blank slot. d.open(self, QtCore.SLOT("blankSlot()")) if d.exec_(): self._colorChanged(d.selectedColor()) else: self._colorChanged(color)
def createAccentColorBar(self): """ Create and setup the accent color bar. :rtype: None """ 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), ] browserColors = [] for colorR, colorG, colorB in browserColors_: for i in range(0, 3): colorR = colorR if colorR > 0 else 0 colorG = colorG if colorG > 0 else 0 colorB = colorB if colorB > 0 else 0 color = QtGui.QColor(colorR, colorG, colorB).rgba() browserColors.append(color) colorR -= 20 colorB -= 20 colorG -= 20 hColorBar = studioqt.HColorBar() hColorBar.setColors(self.DEFAULT_ACCENT_COLORS) hColorBar.setCurrentColor(self.DEFAULT_ACCENT_COLOR) hColorBar.setBrowserColors(browserColors) hColorBar.colorChanged.connect(self._accentColorChanged) self.ui.accentColorBarFrame.layout().addWidget(hColorBar)
def example(): """ Example to show/test the HColorBar. :rtype: None """ def _colorChanged(color): print "colorChanged:", color theme = studioqt.Theme() colors = [ studioqt.Color(230, 60, 60, 255), studioqt.Color(255, 90, 40), studioqt.Color(255, 125, 100, 255), studioqt.Color(250, 200, 0, 255), studioqt.Color(80, 200, 140, 255), studioqt.Color(50, 180, 240, 255), studioqt.Color(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) colorBar = HColorBar() colorBar.setColors(colors) colorBar.setBrowserColors(browserColors) colorBar.colorChanged.connect(_colorChanged) colorBar.show()
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)
class LibraryItem(studioqt.CombinedWidgetItem): DEFAULT_PLAYHEAD_COLOR = QtGui.QColor(255, 255, 255, 220) itemSignal = ListWidgetItemSignal() def __init__(self): studioqt.CombinedWidgetItem.__init__(self) self._typePixmap = None self._typeIconPath = None self._imageSequence = None self._imageSequencePath = None self._blendValue = 0.0 self._blendPreviousValue = 0.0 self._blendPosition = None self._blendingEnabled = False self.blendChanged = self.itemSignal.blendChanged def __del__(self): self.stop() def setTypeIconPath(self, path): self._typeIconPath = path def typeIconPath(self): return self._typeIconPath def setBlendingEnabled(self, enabled): self._blendingEnabled = enabled def isBlendingEnabled(self): return self._blendingEnabled def blendingEvent(self, event): if self.isBlending(): value = (event.pos().x() - self.blendPosition().x()) / 1.5 value = math.ceil(value) + self.blendPreviousValue() try: self.setBlendValue(value) except Exception as msg: self.stopBlending() def startBlendingEvent(self, event): if self.isBlendingEnabled(): if event.button() == QtCore.Qt.MidButton: self._blendPosition = event.pos() def stopBlending(self): self._blendPosition = None self._blendPreviousValue = self.blendValue() def resetBlending(self): """ :rtype: None """ self._blendValue = 0.0 self._blendPreviousValue = 0.0 def isBlending(self): """ :rtype: bool | None """ return self.blendPosition() is not None def setBlendValue(self, value): """ :type value: float :rtype: bool """ if self.isBlendingEnabled(): self._blendValue = value self.blendChanged.emit(value) logger.debug('BLENDING:' + str(value)) def blendValue(self): """ :rtype: float """ return self._blendValue def blendPreviousValue(self): """ :rtype: float """ return self._blendPreviousValue def blendPosition(self): """ :rtype: QtGui.QPoint """ return self._blendPosition def selectionChanged(self): """ :rtype: QtGui.QPoint """ self.resetBlending() def mouseEnterEvent(self, event): """ :type event: QtGui.QEvent """ studioqt.CombinedWidgetItem.mouseEnterEvent(self, event) self._imageSequence = None self.play() def mouseLeaveEvent(self, event): """ :type event: QtGui.QEvent """ studioqt.CombinedWidgetItem.mouseLeaveEvent(self, event) self.stop() def mousePressEvent(self, event): """ :type event: QtCore.QEvent """ studioqt.CombinedWidgetItem.mousePressEvent(self, event) if event.button() == QtCore.Qt.MidButton: self._blendPosition = event.pos() def mouseReleaseEvent(self, event): """ :type event: QtCore.QEvent """ if self.isBlending(): self._blendPosition = None self._blendPreviousValue = self.blendValue() studioqt.CombinedWidgetItem.mouseReleaseEvent(self, event) def mouseMoveEvent(self, event): """ :type event: QtCore.QEvent """ studioqt.CombinedWidgetItem.mouseMoveEvent(self, event) self.blendingEvent(event) 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().duration() * percent) self.imageSequence().setCurrentFrame(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 :rtype: None """ self._imageSequencePath = path def imageSequencePath(self): """ :rtype: str """ return self._imageSequencePath def stop(self): """ :rtype: None """ if self.imageSequence(): self.imageSequence().stop() def play(self): """ :rtype: None """ if self.imageSequencePath(): if not self.imageSequence(): imageSequence = studioqt.ImageSequence() imageSequence.setDirname(self.imageSequencePath()) imageSequence.frameChanged.connect(self._frameChanged) self.setImageSequence(imageSequence) self.imageSequence().start() def _frameChanged(self): """ :rtype: None """ if not studioqt.isControlModifier(): self.updateFrame() def updateFrame(self): """ :rtype: None """ filename = self.imageSequence().currentFilename() icon = QtGui.QIcon(filename) self.setIcon(0, icon) def playheadColor(self): """ :rtype: str """ return self.DEFAULT_PLAYHEAD_COLOR def paintPlayhead(self, painter, option): """ :type painter: QtGui.QPainter :param option: """ if self.imageSequence().currentFilename(): r = self.iconRect(option) c = self.playheadColor() imageSequence = self.imageSequence() painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtGui.QBrush(c)) if imageSequence.percent() <= 0: width = 0 elif imageSequence.percent() >= 1: width = r.width() else: width = imageSequence.percent() * r.width() - 1 height = 3 * self.dpi() y = r.y() + r.height() - (height - 1) painter.drawRect(r.x(), y, width, height) def typeIconRect(self, option): """ :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): """ :type painter: QtGui.QPainter :type option: """ rect = self.typeIconRect(option) if not self._typePixmap: path = self.typeIconPath() if path: self._typePixmap = QtGui.QPixmap(path) if self._typePixmap: painter.setOpacity(0.5) painter.drawPixmap(rect, self._typePixmap) painter.setOpacity(1) def paint(self, painter, option, index): """ :type painter: QtGui.QPainter :type option: """ studioqt.CombinedWidgetItem.paint(self, painter, option, index) painter.save() try: if index.column() == 0: self.paintTypeIcon(painter, option) if self.imageSequence(): self.paintPlayhead(painter, option) finally: painter.restore()
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().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.""" SortRole = "SortRole" DataRole = "DataRole" ThreadPool = QtCore.QThreadPool() DefaultThumbnailPath = "" MAX_ICON_SIZE = 256 DEFAULT_FONT_SIZE = 13 DEFAULT_PLAYHEAD_COLOR = QtGui.QColor(255, 255, 255, 220) THUMBNAIL_COLUMN = 0 ENABLE_THUMBNAIL_THREAD = True _globalSignals = GlobalSignals() blendChanged = _globalSignals.blendChanged 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 = "" 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 = None self._blendValue = 0.0 self._blendPreviousValue = 0.0 self._blendPosition = None self._blendingEnabled = 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.resetBlending() def doubleClicked(self): """ Triggered when an item is double clicked. :rtype: None """ pass def setGroupItem(self, groupItem): self._groupItem = groupItem def groupItem(self): 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.isTextVisible(): 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.DefaultThumbnailPath 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): """ Return True if the text is visible. :rtype: bool """ return self.itemsWidget().isItemTextVisible() 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.blendingEvent(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: self._blendPosition = 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.isBlending(): self._blendPosition = None self._blendPreviousValue = self.blendValue() 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().backgroundColor() 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 """ rect = QtCore.QRect(option.rect) return 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) if self.isTextVisible(): self.paintText(painter, option, index) self.paintIcon(painter, option, index) if index.column() == 0: if 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 :rtype: None """ 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)) painter.drawRect(visualRect) 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.isTextVisible() and self.itemsWidget().isIconView(): 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 if self.itemsWidget().isIconView(): align = align | QtCore.Qt.AlignBottom else: align = align | QtCore.Qt.AlignVCenter pen = QtGui.QPen(color) painter.setPen(pen) painter.setFont(font) painter.drawText(visualRect, align, text) # ------------------------------------------------------------------------ # Support for middle mouse blending (slider) # ------------------------------------------------------------------------ def setBlendingEnabled(self, enabled): """ Set if middle mouse slider is enabled. :type enabled: bool :rtype: None """ self._blendingEnabled = enabled def isBlendingEnabled(self): """ Return true if middle mouse slider is enabled. :rtype: None """ return self._blendingEnabled def blendingEvent(self, event): """ Called when the mouse moves while the middle mouse button is held down. :param event: QtGui.QMouseEvent :rtype: None """ if self.isBlending(): value = (event.pos().x() - self.blendPosition().x()) / 1.5 value = math.ceil(value) + self.blendPreviousValue() try: self.setBlendValue(value) except Exception: self.stopBlending() def startBlendingEvent(self, event): """ Called when the middle mouse button is pressed. :param event: QtGui.QMouseEvent :rtype: None """ if self.isBlendingEnabled(): if event.button() == QtCore.Qt.MidButton: self._blendPosition = event.pos() def stopBlending(self): """ Called when the middle mouse button is released. :param event: QtGui.QMouseEvent :rtype: None """ self._blendPosition = None self._blendPreviousValue = self.blendValue() def resetBlending(self): """ Reset the blending value to zero. :rtype: None """ self._blendValue = 0.0 self._blendPreviousValue = 0.0 def isBlending(self): """ Return True if blending. :rtype: bool """ return self.blendPosition() is not None def setBlendValue(self, value): """ Set the blend value. :type value: float :rtype: bool """ if self.isBlendingEnabled(): self._blendValue = value self.blendChanged.emit(value) logger.debug("BLENDING:" + str(value)) def blendValue(self): """ Return the blend value. :rtype: float """ return self._blendValue def blendPreviousValue(self): """ :rtype: float """ return self._blendPreviousValue def blendPosition(self): """ :rtype: QtGui.QPoint """ return self._blendPosition # ------------------------------------------------------------------------ # 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 :rtype: None """ self._imageSequencePath = path def imageSequencePath(self): """ :rtype: str """ return self._imageSequencePath def stop(self): """ :rtype: None """ if self.imageSequence(): self.imageSequence().stop() def play(self): """ :rtype: None """ 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)
class CombinedWidgetItem(QtWidgets.QTreeWidgetItem): """ Combined Widget items are used to hold rows of information for a combined widget. """ MAX_ICON_SIZE = 256 DEFAULT_FONT_SIZE = 13 DEFAULT_PLAYHEAD_COLOR = QtGui.QColor(255, 255, 255, 220) _globalSignals = GlobalSignals() blendChanged = _globalSignals.blendChanged def __init__(self, *args): QtWidgets.QTreeWidgetItem.__init__(self, *args) self._url = None self._path = None self._size = None self._rect = None self.textColumnOrder = [] self._text = {} self._sortText = {} self._displayText = {} self._icon = {} self._fonts = {} self._pixmap = {} self._pixmapRect = None self._iconPath = "" self._underMouse = False self._searchText = None self._infoWidget = None self._groupColumn = 0 self._mimeText = None self._combinedWidget = None self._stretchToWidget = None self._dragEnabled = True self._imageSequence = None self._imageSequencePath = None self._blendValue = 0.0 self._blendPreviousValue = 0.0 self._blendPosition = None self._blendingEnabled = 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 toJson(self): """ Return a dict of the current item text. :rtype: dict[] """ return self._text 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.combinedWidget().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 setData(self, column, role, value): """ Reimplemented to set the search text to dirty. Set the value for the item's column and role to the given value. :type column: int or str :type role: int :type value: QtCore.QVariant :rtype: None """ self._searchText = None QtWidgets.QTreeWidgetItem.setData(self, column, role, value) 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 :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 = studioqt.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) def data(self, column, role, **kwargs): """ Reimplemented to add support for setting the sort data. :type column: int :type role: int :type kwargs: dict :rtype: object """ if role == QtCore.Qt.DisplayRole: text = self.sortText(column) if not text: text = QtWidgets.QTreeWidgetItem.data(self, column, role) else: text = QtWidgets.QTreeWidgetItem.data(self, column, role) return text def setText(self, column, value, alignment=None): """ Set the text to be displayed for the given column. :type column: int or str :type value: str :rtype: None """ self.textColumnOrder.append(column) if isinstance(column, basestring): self._text[column] = value else: QtWidgets.QTreeWidgetItem.setText(self, column, unicode(value)) def text(self, column): """ Return the text for the given column. :type column: int or str :rtype: str """ # if isinstance(column, int): # column = self.treeWidget().labelFromColumn(column) if isinstance(column, basestring): text = self._sortText.get(column) if not text: text = self._text.get(column, "") else: text = QtWidgets.QTreeWidgetItem.text(self, column) return text def setSortText(self, column, value): """ Set the sort data for the given column. :type column: int :int value: str :rtype: None """ self._sortText[column] = value def sortText(self, column): """ Return the sort data for the given column. :type column: int :rtype: str """ if isinstance(column, int): column = self.treeWidget().labelFromColumn(column) text = self._sortText.get(column, None) if not text: text = self._text.get(column, "") return text def displayText(self, column): """ Return the data to be displayed for the given column. :type column: int :rtype: str """ if isinstance(column, basestring): text = self._text.get(column, "") else: # Check the text before the display role data label = self.treeWidget().labelFromColumn(column) text = self._text.get(label, "") if not text: text = QtWidgets.QTreeWidgetItem.data(self, column, QtCore.Qt.DisplayRole) return text def updateData(self): """ Update the text data to the corresponding column. :rtype: None """ treeWidget = self.treeWidget() for label in self._text: column = treeWidget.columnFromLabel(label) if column < 0: treeWidget.addHeaderLabel(label) text = self._text[label] self.setText(column, text) for label in self._icon: column = treeWidget.columnFromLabel(label) self.setIcon(column, self._icon[label]) def dpi(self): """ Used for high resolution devices. :rtype: int """ if self.combinedWidget(): return self.combinedWidget().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 """ pass def doubleClicked(self): """ Triggered when an item is double clicked. :rtype: None """ pass def combinedWidget(self): """ Returns the combined widget that contains the item. :rtype: CombinedWidget """ combinedWidget = None if self.treeWidget(): combinedWidget = self.treeWidget().parent() return combinedWidget 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: searchText = [] for column in range(self.columnCount()): text = self.data(column, QtCore.Qt.DisplayRole) if text: searchText.append(text) self._searchText = " ".join(searchText) 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.combinedWidget().iconSize() w = self.stretchToWidget().width() h = size.height() return QtCore.QSize(w - 20, h) if self._size: return self._size else: iconSize = self.combinedWidget().iconSize() if self.isTextVisible(): 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 "" def icon(self, column): icon = QtWidgets.QTreeWidgetItem.icon(self, column) if not icon and column == 0: iconPath = self.thumbnailPath() if iconPath: icon = QtGui.QIcon(iconPath) return icon def pixmap(self, column): """ Return the pixmap for the given column. :type column: :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.combinedWidget().padding() def textHeight(self): """ Return the height of the text for the item. :rtype: int """ return self.combinedWidget().itemTextHeight() def isTextVisible(self): """ Return True if the text is visible. :rtype: bool """ return self.combinedWidget().isItemTextVisible() def textAlignment(self, column): """ Return the text alignment for the label in the given column. :type column: int :rtype: QtCore.Qt.AlignmentFlag """ if self.combinedWidget().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 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.blendingEvent(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: self._blendPosition = 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.isBlending(): self._blendPosition = None self._blendPreviousValue = self.blendValue() 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.combinedWidget().palette().color(self.combinedWidget().foregroundRole()) return self.combinedWidget().textColor() def textSelectedColor(self): """ Return the selected text color for the item. :rtype: QtWidgets.QtColor """ return self.combinedWidget().textSelectedColor() def backgroundColor(self): """ Return the background color for the item. :rtype: QtWidgets.QtColor """ return self.combinedWidget().backgroundColor() def backgroundHoverColor(self): """ Return the background color when the mouse is over the item. :rtype: QtWidgets.QtColor """ return self.combinedWidget().backgroundHoverColor() def backgroundSelectedColor(self): """ Return the background color when the item is selected. :rtype: QtWidgets.QtColor """ return self.combinedWidget().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 """ rect = QtCore.QRect(option.rect) return rect def repaint(self): self.update(self.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) if self.isTextVisible(): self.paintText(painter, option, index) self.paintIcon(painter, option, index) if index.column() == 0: if 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 :rtype: None """ 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)) painter.drawRect(visualRect) 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.isTextVisible() and self.combinedWidget().isIconView(): 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 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 = pixmap.scaled( rect.width(), rect.height(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation, ) 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 option: QtWidgets.QStyleOptionViewItem :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 """ font = self._fonts.get(column, QtWidgets.QTreeWidgetItem.font(self, column)) 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.combinedWidget().isTableView(): return self._paintText(painter, option, column) def textWidth(self, column): text = self.displayText(column) font = self.font(column) metrics = QtGui.QFontMetricsF(font) textWidth = metrics.width(text) return textWidth def _paintText(self, painter, option, column): text = self.displayText(column) 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: text = metrics.elidedText(text, QtCore.Qt.ElideRight, visualRect.width()) align = QtCore.Qt.AlignLeft if self.combinedWidget().isIconView(): align = align | QtCore.Qt.AlignBottom else: align = align | QtCore.Qt.AlignVCenter pen = QtGui.QPen(color) painter.setPen(pen) painter.setFont(font) painter.drawText(visualRect, align, text) # ------------------------------------------------------------------------ # Support for middle mouse blending (slider) # ------------------------------------------------------------------------ def setBlendingEnabled(self, enabled): """ Set if middle mouse slider is enabled. :type enabled: bool :rtype: None """ self._blendingEnabled = enabled def isBlendingEnabled(self): """ Return true if middle mouse slider is enabled. :rtype: None """ return self._blendingEnabled def blendingEvent(self, event): """ Called when the mouse moves while the middle mouse button is held down. :param event: QtGui.QMouseEvent :rtype: None """ if self.isBlending(): value = (event.pos().x() - self.blendPosition().x()) / 1.5 value = math.ceil(value) + self.blendPreviousValue() try: self.setBlendValue(value) except Exception, msg: self.stopBlending()
class MenuBarWidget(QtWidgets.QFrame): ICON_COLOR = QtGui.QColor(255, 255, 255) SPACING = 4 DEFAULT_EXPANDED_HEIGHT = 36 DEFAULT_COLLAPSED_HEIGHT = 10 def __init__(self, parent=None): QtWidgets.QFrame.__init__(self, parent) self._dpi = 1 self._expanded = True self._expandedHeight = self.DEFAULT_EXPANDED_HEIGHT self._collapsedHeight = self.DEFAULT_COLLAPSED_HEIGHT layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(self.SPACING) self.setLayout(layout) self._leftToolBar = QtWidgets.QToolBar(self) self._rightToolBar = QtWidgets.QToolBar(self) self._leftToolBar.layout().setSpacing(self.SPACING) self._rightToolBar.layout().setSpacing(self.SPACING) self.layout().addWidget(self._leftToolBar) self.layout().addWidget(self._rightToolBar) def addAction(self, name, icon=None, tip=None, callback=None, side="Right"): """ Add a button/action to menu bar widget. :type name: str :type icon: QtWidget.QIcon :param tip: str :param side: str :param callback: func :rtype: QtWidget.QAction """ # The method below is needed to fix an issue with PySide2. def _callback(): callback() if side == "Left": action = self.addLeftAction(name) else: action = self.addRightAction(name) if icon: action.setIcon(icon) if tip: action.setToolTip(tip) action.setStatusTip(tip) if callback: action.triggered.connect(_callback) return action def dpi(self): return self._dpi def setDpi(self, dpi): self._dpi = dpi self.update() def update(self): self.refreshSize() self.updateIconColor() def refreshSize(self): self.setChildrenHeight(self.height()) if self.isExpanded(): self.expand() else: self.collapse() def updateIconColor(self): color = self.palette().color(self.foregroundRole()) color = studioqt.Color.fromColor(color) self.setIconColor(color) def addLeftAction(self, text): action = QtWidgets.QAction(text, self._leftToolBar) self._leftToolBar.addAction(action) return action def addRightAction(self, text): action = QtWidgets.QAction(text, self._rightToolBar) self._rightToolBar.addAction(action) return action def widgets(self): widgets = [] for i in range(0, self.layout().count()): w = self.layout().itemAt(i).widget() if isinstance(w, QtWidgets.QWidget): widgets.append(w) return widgets def findAction(self, text): action1 = self._findAction(self._leftToolBar, text) action2 = self._findAction(self._rightToolBar, text) return action1 or action2 def _findAction(self, toolBar, text): for child in toolBar.children(): if isinstance(child, QtWidgets.QAction): if child.text() == text: return child def findToolButton(self, text): button1 = self._findToolButton(self._leftToolBar, text) button2 = self._findToolButton(self._rightToolBar, text) button = button1 or button2 return button def _findToolButton(self, toolBar, text): for child in toolBar.children(): if isinstance(child, QtWidgets.QAction): if child.text() == text: return toolBar.widgetForAction(child) def actions(self): actions = [] children = self._leftToolBar.children() children.extend(self._rightToolBar.children()) for child in children: if isinstance(child, QtWidgets.QAction): actions.append(child) return actions def isExpanded(self): return self._expanded def setExpandedHeight(self, height): self._expandedHeight = height self.setChildrenHeight(height) def expandedHeight(self): return int(self._expandedHeight * self.dpi()) def expand(self): self._expanded = True height = self.expandedHeight() self.setFixedHeight(height) self.setChildrenHeight(height) self.updateIconColor() def collapse(self): self._expanded = False height = self.collapsedHeight() self.setFixedHeight(height) self.setChildrenHeight(0) self.updateIconColor() def collapsedHeight(self): return int(self._collapsedHeight * self.dpi()) def setIconColor(self, color): for action in self.actions(): icon = action.icon() icon = studioqt.Icon(icon) icon.setColor(color) action.setIcon(icon) def setChildrenHidden(self, value): for w in self.widgets(): w.setHidden(value) def setChildrenHeight(self, height): for w in self.widgets(): w.setFixedHeight(height) padding = self.SPACING * self.dpi() width = height + (padding * 2) height = height - padding self._leftToolBar.setFixedHeight(height) self._leftToolBar.setIconSize(QtCore.QSize(width, height)) self._rightToolBar.setFixedHeight(height) self._rightToolBar.setIconSize(QtCore.QSize(width, height)) def resizeEvent(self, *args, **kwargs): self.refreshSize() def mousePressEvent(self, *args, **kwargs): self.expand()
class ImageSequenceWidget(QtWidgets.QToolButton): DEFAULT_PLAYHEAD_COLOR = QtGui.QColor(255, 255, 255, 220) def __init__(self, *args): QtWidgets.QToolButton.__init__(self, *args) self.setStyleSheet('border: 0px solid rgb(0, 0, 0, 20);') self._filename = None self._imageSequence = ImageSequence(self) self._imageSequence.frameChanged.connect(self._frameChanged) self.setSize(150, 150) self.setMouseTracking(True) def isControlModifier(self): """ :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 currentIcon(self): """ Return a icon object from the current icon path. :rtype: QtGui.QIcon """ return QtGui.QIcon(self._imageSequence.currentFilename()) def setDirname(self, dirname): """ Set the location to the image sequence. :type dirname: str :rtype: None """ self._imageSequence.setDirname(dirname) if self._imageSequence.frames(): icon = self.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() def leaveEvent(self, event): """ Stop playing the image sequence when the mouse leaves the widget. :type event: QtCore.QEvent :rtype: None """ self._imageSequence.pause() def mouseMoveEvent(self, event): """ Reimplemented to add support for scrubbing. :type event: QtCore.QEvent :rtype: None """ if self.isControlModifier(): percent = 1.0 - float(self.width() - event.pos().x()) / float( self.width()) frame = int(self._imageSequence.duration() * percent) self._imageSequence.setCurrentFrame(frame) icon = self.currentIcon() self.setIcon(icon) def _frameChanged(self, filename=None): """ Triggered when the image sequence changes frame. :type filename: str or None :rtype: None """ if not self.isControlModifier(): self._filename = filename icon = self.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(): 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()
def setIconColor(): menuBarWidget.setIconColor(QtGui.QColor(255, 255, 0))
class ToastWidget(QtWidgets.QLabel): DEFAULT_DURATION = 500 # 0.5 seconds FOREGROUND_COLOR = QtGui.QColor(255, 255, 255) BACKGROUND_COLOR = QtGui.QColor(0, 0, 0) def __init__(self, *args): QtWidgets.QLabel.__init__(self, *args) self.setMouseTracking(True) self.setAlignment(QtCore.Qt.AlignCenter) self.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents) self._alpha = 255 self._messageDisplayTimer = QtCore.QTimer(self) self._messageDisplayTimer.timeout.connect(self.fadeOut) self._messageFadeOutTimer = QtCore.QTimer(self) self._messageFadeOutTimer.timeout.connect(self._fadeOut) def alignTo(self, widget, padding=30): width = self.textWidth() + padding height = self.textHeight() + padding x = widget.width() / 2 - width / 2 y = (widget.height() - height) / 1.2 self.setGeometry(x, y, width, height) def textRect(self): text = self.text() font = self.font() metrics = QtGui.QFontMetricsF(font) return metrics.boundingRect(text) def textWidth(self): textWidth = self.textRect().width() return max(0, textWidth) def textHeight(self): textHeight = self.textRect().height() return max(0, textHeight) def fadeOut(self): """ :rtype: None """ self._messageFadeOutTimer.start(5) def _fadeOut(self): """ :rtype: None """ alpha = self.alpha() if alpha > 0: alpha -= 2 self.setAlpha(alpha) else: self.hide() self._messageFadeOutTimer.stop() self._messageDisplayTimer.stop() def setText(self, text, duration=None): """ :type text: str :rtype: None """ QtWidgets.QLabel.setText(self, text) duration = duration or self.DEFAULT_DURATION self.setAlpha(255) self._messageDisplayTimer.stop() self._messageDisplayTimer.start(duration) self.show() self.update() self.repaint() def alpha(self): """ :rtype: float """ return float(self._alpha) def setAlpha(self, value): """ :type value: float :rtype: None """ if value < 0: value = 0 textAlpha = value backgroundAlpha = value / 1.4 color = ToastWidget.FOREGROUND_COLOR backgroundColor = ToastWidget.BACKGROUND_COLOR # color = self.palette().color(self.foregroundRole()) color = studioqt.Color.fromColor(color) color.setAlpha(textAlpha) # backgroundColor = self.palette().color(self.backgroundRole()) backgroundColor = studioqt.Color.fromColor(backgroundColor) backgroundColor.setAlpha(backgroundAlpha) styleSheet = "color: {0}; background-color: {1};" styleSheet = styleSheet.format(color.toString(), backgroundColor.toString()) self.setStyleSheet(styleSheet) self._alpha = value
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
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()
class SettingsDialog(QtWidgets.QDialog): DEFAULT_ACCENT_COLOR = QtGui.QColor(20, 175, 250) DEFAULT_BACKGROUND_COLOR = QtGui.QColor(70, 70, 80) DEFAULT_ACCENT_COLORS = [ QtGui.QColor(230, 75, 75), QtGui.QColor(235, 100, 70), QtGui.QColor(240, 125, 100), QtGui.QColor(240, 190, 40), QtGui.QColor(80, 200, 140), QtGui.QColor(20, 175, 250), QtGui.QColor(110, 110, 240), ] DEFAULT_BACKGROUND_COLORS = [ QtGui.QColor(70, 70, 80), QtGui.QColor(65, 65, 75), QtGui.QColor(55, 55, 65), QtGui.QColor(50, 50, 57), QtGui.QColor(40, 40, 47), ] accentColorChanged = QtCore.Signal(object) backgroundColorChanged = QtCore.Signal(object) def __init__(self, parent=None): """ :type parent: QtWidgets.QWidget :type library: studiolibrary.Library """ QtWidgets.QDialog.__init__(self, parent) studioqt.loadUi(self) self._validator = None self._accentColor = self.DEFAULT_ACCENT_COLOR self._backgroundColor = self.DEFAULT_BACKGROUND_COLOR resource = studiolibrary.resource() self.setWindowIcon(resource.icon("icon_black")) windowTitle = "Studio Library - {version}" windowTitle = windowTitle.format(version=studiolibrary.version()) self.setWindowTitle(windowTitle) self.createAccentColorBar() self.createBackgroundColorBar() self.ui.acceptButton.clicked.connect(self.accept) self.ui.rejectButton.clicked.connect(self.close) self.ui.browsePathButton.clicked.connect(self.browsePath) self.updateStyleSheet() self.center() def resizeEvent(self, event): """ Reimplemented to support the logo scaling on DPI screens. :type event: QtGui.QEvent :rtype: None """ scaleFactor = 1.4 height = self.ui.headerFrame.height() self.ui.logo.setFixedWidth(height / scaleFactor) self.ui.logo.setFixedHeight(height / scaleFactor) def _accentColorChanged(self, color): """ Triggered when the user clicks/changes the accent color. :type color: studioqt.Color :rtype: None """ self.setAccentColor(color) def _backgroundColorClicked(self, color): """ Triggered when the user clicks/changes the background color. :type color: studioqt.Color :rtype: None """ self.setBackgroundColor(color) def createAccentColorBar(self): """ Create and setup the accent color bar. :rtype: None """ 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), ] browserColors = [] for colorR, colorG, colorB in browserColors_: for i in range(0, 3): colorR = colorR if colorR > 0 else 0 colorG = colorG if colorG > 0 else 0 colorB = colorB if colorB > 0 else 0 color = QtGui.QColor(colorR, colorG, colorB).rgba() browserColors.append(color) colorR -= 20 colorB -= 20 colorG -= 20 hColorBar = studioqt.HColorBar() hColorBar.setColors(self.DEFAULT_ACCENT_COLORS) hColorBar.setCurrentColor(self.DEFAULT_ACCENT_COLOR) hColorBar.setBrowserColors(browserColors) hColorBar.colorChanged.connect(self._accentColorChanged) self.ui.accentColorBarFrame.layout().addWidget(hColorBar) def createBackgroundColorBar(self): """ Create and setup the background color bar. :rtype: None """ browserColors_ = [ (0, 0, 0), (20, 20, 30), (0, 30, 60), (0, 60, 60), (0, 60, 30), (60, 0, 10), (60, 0, 40), (40, 15, 5), ] browserColors = [] for colorR, colorG, colorB in browserColors_: for i in range(0, 6): color = QtGui.QColor(colorR, colorG, colorB).rgba() browserColors.append(color) colorR += 20 colorB += 20 colorG += 20 hColorBar = studioqt.HColorBar() hColorBar.setColors(self.DEFAULT_BACKGROUND_COLORS) hColorBar.setCurrentColor(self.DEFAULT_BACKGROUND_COLOR) hColorBar.setBrowserColors(browserColors) hColorBar.colorChanged.connect(self._backgroundColorClicked) self.ui.backgroundColorBarFrame.layout().addWidget(hColorBar) def accept(self): """ Hides the modal dialog and sets the result code to Accepted. :rtype: None """ self.validate() QtWidgets.QDialog.accept(self) def setValidator(self, validator): """ Set the validator for the dialog. :type validator: func :rtype: None """ self._validator = validator def validator(self): """ Return the validator for the dialog. :rtype: func """ return self._validator def validate(self): """ Run the validate. :rtype: None """ try: validator = self.validator() if validator: validator() except Exception, e: QtWidgets.QMessageBox.critical(self, "Validate Error", str(e)) raise