def __init__(self, view, parent=None): super(TreeMenuHandler, self).__init__(view, parent=parent) self.__expandOnLoadAction = Action(selectionBased=False, text="Keep All Expanded", checkable=True, runFn=self.__setExpandOnLoad) self._staticActions = [ Action(selectionBased=True, text="Toggle Expanded", runFn=self.view._toggleExpanded), Action(selectionBased=True, text="Expand", shortcut=QtGui.QKeySequence(QtCore.Qt.Key_Plus), isValidFn=self.__canExpand, runFn=self.expandSelected), Action(selectionBased=True, text="Collapse", shortcut=QtGui.QKeySequence(QtCore.Qt.Key_Minus), isValidFn=self.__canCollapse, runFn=self.collapseSelected), Action(selectionBased=True, text="Expand all", runFn=self.view.expandAll), Action(selectionBased=True, text="Collapse all", runFn=self.view.collapseAll), self.__expandOnLoadAction ]
def startImageCycling(self, index): """ Start frame cycling on a cell at <index> :parameters: index : QtCore.QModelIndex the model index to start frame cycling on """ if self.__imageCyclingEnabled and index.isValid( ) and index != self.__cycleIndex: # stop cycling on old index self.stopImageCycling() newRect = self._thumbnailRect(index) if newRect.isValid(): filename = str(index.data()) if not common.imageFormatIsAnimatable( filename) or not os.path.isfile(filename): return scaledImage = index.data(role=self.ROLE_SCALED_IMAGE) # On OSX 10.9 PyQt 4.10 replaces null QImages with QPyNullVariants when retreiving them from a model if scaledImage is None or not isinstance( scaledImage, QtGui.QImage) or scaledImage.isNull(): return # make the index widget ( QLabel displaying a QMovie ) movieLabel = QtGui.QLabel() movie = QtGui.QMovie(filename, parent=movieLabel) movie.setCacheMode(QtGui.QMovie.CacheAll) # QMovie bug?, jumpToNextFrame() will only return False for static images after it has been called more than once movie.jumpToNextFrame() # if there is no frame 1, then it is a static image, so abort. # this must be done after it has been set and started playing # or the QMovie has no frame attributes if movie.jumpToNextFrame() is False: self.stopImageCycling() return # movieLabel.setFrameStyle( QtGui.QFrame.Box ) movieLabel.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) newSize = scaledImage.size() movie.setScaledSize(newSize) movieLabel.setFixedSize(newSize) movieLabel.setMovie(movie) # start playing movie.start() self.__cycleIndex = QtCore.QPersistentModelIndex(index) # set the new index widget self._view.setIndexWidget(index, movieLabel) # move to center of cell movieLabel.move( movieLabel.pos().x() + ((newRect.width() / 2) - (newSize.width() / 2)), movieLabel.pos().y())
def __init__(self, text=None, icon=None, tip=None, shortcut=None, shortcutContext=QtCore.Qt.WidgetShortcut, menu=None, checkable=False, separator=False, selectionBased=False, signal="triggered()", enabled=True, isValidFn=None, runFn=None, parent=None): super(Action, self).__init__(parent) if text: self.setText(text) if icon: self.setIcon(icon) if tip: self.setToolTip(tip) self.setStatusTip(tip) if shortcut: if isinstance(shortcut, list): shortcuts = [QtGui.QKeySequence(key) for key in shortcut] self.setShortcuts(shortcuts) else: self.setShortcut(QtGui.QKeySequence(shortcut)) self.setShortcutContext(shortcutContext) if menu: self.setMenu(menu) self.setCheckable(checkable) self.setEnabled(enabled) self.setSeparator(separator) self.__selectionBased = selectionBased self.__isValidFn = isValidFn self.__runFn = runFn self.connect(self, QtCore.SIGNAL(signal), self.run) self.__subActions = []
def sectionSizeFromContents(self, logicalIndex): """ :parameters: :return: :rtype: """ size = QtGui.QHeaderView.sectionSizeFromContents(self, logicalIndex) if self.model(): if self.isSortIndicatorShown() and not self.model().headerData( logicalIndex, self.orientation(), common.ROLE_IS_SORTABLE): # remove the sort indicator margin for columns that aren't sortable opt = QtGui.QStyleOptionHeader() self.initStyleOption(opt) margin = self.style().pixelMetric(QtGui.QStyle.PM_HeaderMargin, opt, self) if self.orientation() == QtCore.Qt.Horizontal: size.setWidth(size.width() - size.height() - margin) else: size.setHeight(size.height() - size.width() - margin) # compensate for icon if self.model().headerData(logicalIndex, QtCore.Qt.Horizontal, role=QtCore.Qt.DecorationRole): size.setWidth(size.width() + 12) return size
def __populate(self): """ Populate this menu with actions """ for program in self._getProgramSuggestionList(): self.__addOpenAction(program) # custom open actions self.addAction(Action(separator=True, parent=self)) # browse action browseAction = QtGui.QAction('Browse...', self) browseAction.triggered.connect(self.__browse) self.addAction(browseAction) # custom command action cmdAction = QtGui.QAction('Custom command...', self) cmdAction.triggered.connect(self.__addCustomCmd) self.addAction(cmdAction)
def createAction(parentWidget, text=None, slot=None, shortcut=None, shortcutContext=QtCore.Qt.WidgetShortcut, icon=None, menu=None, tip=None, checkable=False, signal="triggered()"): action = QtGui.QAction(parentWidget) if text is not None: action.setText(text) if icon is not None: action.setIcon(icon) if menu is not None: action.setMenu(menu) if shortcut: if isinstance(shortcut, list): action.setShortcuts(shortcut) else: action.setShortcut(shortcut) action.setShortcutContext(shortcutContext) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: QtCore.QObject.connect(action, QtCore.SIGNAL(signal), slot) action.setCheckable(checkable) return action
def __imageLoaded(self, filePath, targetSize, image, extraArgs): """ Repaint the rect containing the image :parameters: filePath : str path to image file targetSize : QtCore.QSize intended size of the image image : QtGui.QImage the image that has been loaded extraArgs : list Index of model item containing the image [ QtCore.QPersistandModelIndex ] """ index = QtCore.QModelIndex(extraArgs[0]) model = index.model() if model is None: return # On OSX 10.9 PyQt 4.10 replaces null QImages with QPyNullVariants when retreiving them from a model if not isinstance(image, QtGui.QImage): image = QtGui.QImage() model.setItemData(index, { common.ROLE_IMAGE: image, self.ROLE_SCALED_IMAGE: image }) if image.isNull(): return if index.isValid(): rect = self._view.visualRect(index) if rect.isValid(): self._view.viewport().repaint(rect)
def drawRow(self, painter, option, index): """ """ # draw the partially selected rows a lighter colour selectionState = index.model().itemFromIndex(index).data( role=common.ROLE_SELECTION_STATE) if selectionState == 1: palette = self.palette() selectionColor = palette.color(palette.Highlight) selectionColor.setAlpha(127) painter.save() painter.fillRect(option.rect, selectionColor) painter.restore() QtGui.QTreeView.drawRow(self, painter, option, index) # draw the grid line if self.__showGrid: painter.save() gridHint = self.style().styleHint( QtGui.QStyle.SH_Table_GridLineColor, self.viewOptions(), self, None) # must ensure that the value is positive before constructing a QColor from it # http://www.riverbankcomputing.com/pipermail/pyqt/2010-February/025893.html gridColor = QtGui.QColor.fromRgb(gridHint & 0xffffffff) painter.setPen(QtGui.QPen(gridColor, 0, QtCore.Qt.SolidLine)) # paint the horizontal line painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom()) painter.restore()
def _propagateSelectionUp(self, selection, command): """ """ parentSelection = QtGui.QItemSelection() # filter out duplicates by unique id because pyside QModelIndexes are not hashable, and cannot be added to a set parentIndexes = map(QtCore.QModelIndex.parent, selection.indexes()) parentIndexes = dict( zip(map(self.model().uniqueIdFromIndex, parentIndexes), parentIndexes)).values() for index in parentIndexes: while index.isValid(): if not (selection.contains(index) or parentSelection.contains(index)): if command & QtGui.QItemSelectionModel.Deselect: # children are being deselected, deselect parents too parentSelection.select(index, index) elif command & QtGui.QItemSelectionModel.Select: # children are being selected, select parent if all children are now selected numChildren = self.model().rowCount(index) if numChildren: numSelected = 0 for row in xrange(numChildren): childIndex = self.model().index(row, 0, index) if selection.contains(childIndex) or \ parentSelection.contains(childIndex) or \ (not (command & QtGui.QItemSelectionModel.Clear) and self.isSelected( childIndex)): numSelected += 1 else: break if numSelected == numChildren: # all children are selected, select parent too parentSelection.select(index, index) index = index.parent() return parentSelection
def paint(self, painter, option, index): """ """ origRect = QtCore.QRect(option.rect) if self._view.showGrid(): # remove 1 pixel from right and bottom edge of rect, to # make room for drawing grid option.rect.setRight(option.rect.right() - 1) option.rect.setBottom(option.rect.bottom() - 1) # paint the item Delegate.paint(self, painter, option, index) # draw the vertical grid lines (horizontal lines are painted # in TreeView.drawRow()) if self._view.showGrid(): painter.save() gridHint = QtGui.QApplication.style().styleHint( QtGui.QStyle.SH_Table_GridLineColor, option, self._view, None) # must ensure that the value is positive before # constructing a QColor from it # http://www.riverbankcomputing.com/pipermail/pyqt/2010-February/025893.html gridColor = QtGui.QColor.fromRgb(gridHint & 0xffffffff) painter.setPen(QtGui.QPen(gridColor, 0, QtCore.Qt.SolidLine)) # paint the vertical line painter.drawLine(origRect.right(), origRect.top(), origRect.right(), origRect.bottom()) painter.restore()
def infoDialog(parent, text, title="", detailedText=None, standardButtons=QtGui.QMessageBox.Ok, defaultButton=QtGui.QMessageBox.NoButton): return __newMessageBox(parent, title, text, detailedText, standardButtons, defaultButton, QtGui.QPixmap(icons.ICON_INFO_LRG))
def warningDialog(parent, text, title="Warning", detailedText=None, standardButtons=QtGui.QMessageBox.Ok, defaultButton=QtGui.QMessageBox.NoButton): return __newMessageBox(parent, title, text, detailedText, standardButtons, defaultButton, QtGui.QPixmap(icons.ICON_WARNING_LRG))
def paint(self, painter, option, index): """ Paint the cell :parameters: painter : QtGui.QPainter painter to draw the cell with option : QtGui.QStyleOption style information index : QtCore.QModelIndex index if the cell to draw """ option4 = QtGui.QStyleOptionViewItemV4(option) i_model = index.model() # TODO: cache the data type of the model, if seen before dataType = i_model.dataType(index) self.initStyleOption(option4, index, model=i_model, dataType=dataType) style = self._view.style() if dataType == common.TYPE_IMAGE: # draw background style.drawPrimitive(QtGui.QStyle.PE_PanelItemViewItem, option4, painter, self._view) # paint image self._paintImage(painter, option, index) elif dataType == common.TYPE_BOOLEAN: # draw background style.drawPrimitive(QtGui.QStyle.PE_PanelItemViewItem, option4, painter, self._view) # paint checkbox self._paintBoolean(painter, option4, index) else: # paint everything normally style.drawControl(QtGui.QStyle.CE_ItemViewItem, option4, painter, self._view) # paint border if option4.borderColor: oldPen = painter.pen() painter.setPen(option4.borderColor) if option4.viewItemPosition == option4.OnlyOne: painter.drawRect(option4.rect.adjusted(0, 0, -1, -1)) else: topRight = option4.rect.topRight() topLeft = option4.rect.topLeft() bottomRight = option4.rect.bottomRight() bottomLeft = option4.rect.bottomLeft() drawLine = painter.drawLine # draw rect edges drawLine(topLeft, topRight) drawLine(bottomLeft, bottomRight) if option4.viewItemPosition == option4.Beginning: drawLine(topLeft, bottomLeft) elif option4.viewItemPosition == option4.End: drawLine(topRight, bottomRight) painter.setPen(oldPen)
def questionDialog(parent, text, title="", detailedText=None, standardButtons=QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, defaultButton=QtGui.QMessageBox.No): return __newMessageBox(parent, title, text, detailedText, standardButtons, defaultButton, QtGui.QPixmap(icons.ICON_QUESTION_LRG))
def getColor(colorKey, foreground=True): # lookup color by colorkey try: colorKeyMap = __getColorKeyMap() colorTuple = colorKeyMap[colorKey][0 if foreground else 1] except KeyError: colorTuple = None if colorTuple: return QtGui.QColor(*colorTuple) return None
def __init__(self, dataTypeHandler, parent=None): super(FilterValEdit, self).__init__(parent) self._dataTypeHandler = dataTypeHandler # create layout layout = QtGui.QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) # add widgets to layout self._inputWidgets = self._createWidgets(layout) self.resetVals() self.setLayout(layout)
def __propagateSelectionDown(self, selection): """ """ childSelection = QtGui.QItemSelection() indexQueue = selection.indexes() while indexQueue: index = indexQueue.pop(0) if index.isValid(): numChildren = self.model().rowCount(index) childIndexes = [ self.model().index(row, 0, index) for row in xrange(numChildren) ] if childIndexes: # add child indexes to the selection childSelection.append( QtGui.QItemSelectionRange(childIndexes[0], childIndexes[-1])) indexQueue.extend(childIndexes) return childSelection
def _createWidgets(self, layout): """ """ widgetStart = self._dataTypeHandler.getInputWidget(self) widgetEnd = self._dataTypeHandler.getInputWidget(self) toLabel = QtGui.QLabel("to", self) toLabel.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) layout.addWidget(widgetStart) layout.addWidget(toLabel) layout.addWidget(widgetEnd) return [widgetStart, widgetEnd]
def __init__(self, parent=None): super(ResolutionHandler.ResolutionInputWidget, self).__init__(parent) intValidator = QtGui.QIntValidator(self) intValidator.setBottom(0) self.width = QtGui.QLineEdit(self) self.width.setFrame(False) self.width.setValidator(intValidator) self.height = QtGui.QLineEdit(self) self.height.setValidator(intValidator) self.height.setFrame(False) layout = QtGui.QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self.width) layout.addWidget(QtGui.QLabel("x", self)) layout.addWidget(self.height) self.setLayout(layout)
def _createWidgets(self, layout): """ """ from type_handler import DateTimeHandler widgetLength = IntHandler.getInputWidget(self) widgetUnits = QtGui.QComboBox(self) widgetUnits.addItems( common.DATETIME_DURATIONS if self._dataTypeHandler is DateTimeHandler else common.DATE_DURATIONS) widgetUnits.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) layout.addWidget(widgetLength) layout.addWidget(widgetUnits) return [widgetLength, widgetUnits]
def _getCheckboxRect(self, boundingRect): """ Get the bounding box of a checkbox that has been centered in a cell :rtype: QtCore.QRect """ checkboxRect = QtGui.QApplication.style().subElementRect( QtGui.QStyle.SE_CheckBoxIndicator, QtGui.QStyleOptionButton(), None) checkboxRect.moveCenter(boundingRect.center()) return checkboxRect
def _getIndexDialog(self, item, data, columnDescriptor): d = QtGui.QDialog() field = columnDescriptor.fields.get( item.dataObject.dataInterface().name()) dataType = field.getProperty("display_type") if field == 'multi_line_text': dataType = wizqt.TYPE_STRING_MULTILINE else: dataType = columnDescriptor.wizqtDataType typeHandler = wizqt.core.type_handler.getTypeHandler(dataType) displayWidget = typeHandler.getInputWidget(d) assert typeHandler.setInputValue(displayWidget, data), \ "Failed to set data for object {0}".format(item.uniqueId) layout = QtGui.QVBoxLayout(d) scrollArea = QtGui.QScrollArea() layout.addWidget(scrollArea) scrollArea.setWidget(displayWidget) scrollArea.setWidgetResizable(True) d.setWindowTitle("{0} for {1} {2}".format(columnDescriptor.name, item.dataType, item.uniqueId)) d.resize(d.height(), 640) return d
def exceptionDialog(parent, exc_info, text="", title="Uncaught Exception", standardButtons=QtGui.QMessageBox.Ok, defaultButton=QtGui.QMessageBox.NoButton): if not text: text = "An error occurred. %s: %s" % (exc_info[0].__name__, exc_info[1]) msgBox = __newMessageBox(parent, title, text, None, standardButtons, defaultButton, QtGui.QPixmap(icons.ICON_ERROR_LRG)) # put exception stack trace as detailed text msgBox.setDetailedText("".join(traceback.format_exception(*exc_info))) return msgBox
def selectAll(self): """ """ if not isinstance(self.selectionModel(), TreeSelectionModel): return QtGui.QTreeView.selectAll(self) # store parameters selectionModel = self.selectionModel() model = self.model() getIndex = model.index columnCount = model.columnCount(QtCore.QModelIndex()) selection = QtGui.QItemSelection() propagate = selectionModel.propagateSelection() oldSelection = selectionModel.selection() def selectChildRange(parentIndex): rowCount = model.rowCount(parentIndex) firstIndex = getIndex(0, 0, parentIndex) lastIndex = getIndex(rowCount - 1, columnCount - 1, parentIndex) selection.select(firstIndex, lastIndex) def recursiveSelect(parentIndex): selectChildRange(parentIndex) if propagate is True: # if we skip this check it will always select child rows. for row in range(model.rowCount(parentIndex)): index = getIndex(row, 0, parentIndex) if index.isValid(): recursiveSelect(index) # prepare block = selectionModel.blockSignals(True) self.setUpdatesEnabled(False) if propagate is True: selectionModel.setPropagateSelection(False) # do selection if self.selectionMode() == QtGui.QAbstractItemView.SingleSelection: selection.select( model.index(0, 0, QtCore.QModelIndex()), model.index(0, columnCount - 1, QtCore.QModelIndex())) else: recursiveSelect(QtCore.QModelIndex()) selectionModel.select(selection, QtGui.QItemSelectionModel.Select) # restore previous settings self.setUpdatesEnabled(True) selectionModel.setPropagateSelection(propagate) selectionModel.blockSignals(block) # refresh view QtGui.QApplication.processEvents() selectionModel.selectionChanged.emit(selection, oldSelection)
def sizeHint(self, option, index): """ Get the sizehint for a cell :parameters: option : QtGui.QStyleOption style information index : QtCore.QModelIndex index if the cell to draw :return: The preferred size :rtype: QtCore.QSize """ # TODO: speed this up further by caching more? i_dataType = None i_model = index.model() if i_model: i_dataType = i_model.dataType(index) size = index.data(QtCore.Qt.SizeHintRole) if not size: option4 = QtGui.QStyleOptionViewItemV4(option) self.initStyleOption(option4, index, model=i_model, dataType=i_dataType) style = self._view.style() size = style.sizeFromContents(QtGui.QStyle.CT_ItemViewItem, option4, QtCore.QSize(), self._view) # if it is an image column and has data... if i_model and i_dataType == common.TYPE_IMAGE and index.data( QtCore.Qt.DisplayRole): imageHeight = int( self._view.topHeader().sectionSize(index.column()) / self.__imageCellAspectRatio ) # give the cell a 16/9 aspect ratio if imageHeight > size.height(): size.setHeight(imageHeight) return size if isinstance(size, QtCore.QSize) else size.toSize()
def drawBranches(self, painter, rect, index): """ """ QtGui.QTreeView.drawBranches(self, painter, rect, index) # draw the grid line if self.__showGrid: painter.save() gridHint = QtGui.QApplication.style().styleHint( QtGui.QStyle.SH_Table_GridLineColor, self.viewOptions(), self, None) # must ensure that the value is positive before # constructing a QColor from it # http://www.riverbankcomputing.com/pipermail/pyqt/2010-February/025893.html gridColor = QtGui.QColor.fromRgb(gridHint & 0xffffffff) painter.setPen(QtGui.QPen(gridColor, 0, QtCore.Qt.SolidLine)) # paint the horizontal line painter.drawLine(rect.left(), rect.bottom(), rect.right(), rect.bottom()) painter.restore()
def __newMessageBox(parent, title, text, detailedText, standardButtons, defaultButton, iconPixmap=None): msgBox = QtGui.QMessageBox(QtGui.QMessageBox.NoIcon, title, text, standardButtons, parent) if iconPixmap: msgBox.setIconPixmap(iconPixmap) if detailedText: msgBox.setDetailedText(detailedText) defaultBtn = msgBox.button(defaultButton) if defaultBtn: msgBox.setDefaultButton(defaultBtn) return msgBox
def __init__(self, text, parent, icon=None, **menuKwargs): """ Constructor :parameters: text : str action text parent : QtCore.QObject parent of this action :keywords: icon : QtGui.QIcon action icon menuKwargs : kwargs :See: OpenWithMenu.__init__ """ super(OpenWithAction, self).__init__(icon or QtGui.QIcon(), text, parent) self.__menu = self._makeMenu(**menuKwargs) self.setMenu(self.__menu)
def _paintBoolean(self, painter, option, index): """ Draw a check box :parameters: painter : QtGui.QPainter painter to draw the image with option : QtGui.QStyleOption style information index : QtCore.QModelIndex index if the cell to draw """ buttonStyleOption = QtGui.QStyleOptionButton() # set the check state based on the boolean value for this model index buttonStyleOption.state = QtGui.QStyle.State_On if index.data( ) else QtGui.QStyle.State_Off # center the checkbox in the cell buttonStyleOption.rect = self._getCheckboxRect(option.rect) # paint the checkbox painter.save() QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_CheckBox, buttonStyleOption, painter) painter.restore()
def select(self, selection, command): """ Select items :parameters: selection : QtGui.QItemSelection selected model items command : QtGui.QItemSelectionModel.SelectionFlags NoUpdate: No selection will be made. Clear: The complete selection will be cleared. Select: All specified indexes will be selected. Deselect: All specified indexes will be deselected. Toggle: All specified indexes will be selected or deselected depending on their current state. Current: The current selection will be updated. Rows: All indexes will be expanded to span rows. Columns: All indexes will be expanded to span columns. SelectCurrent:A combination of Select and Current, provided for convenience. ToggleCurrent: A combination of Toggle and Current, provided for convenience. ClearAndSelect: A combination of Clear and Select, provided for convenience. """ if self.__propagateSelection: # propagate selection to children/parents of selected indexes if isinstance(selection, QtCore.QModelIndex): selection = QtGui.QItemSelection(selection, selection) # propagate selection down to children childSelection = self.__propagateSelectionDown(selection) # propagate selection up to parents parentSelection = self._propagateSelectionUp(selection, command) selection.merge(childSelection, QtGui.QItemSelectionModel.SelectCurrent) selection.merge(parentSelection, QtGui.QItemSelectionModel.SelectCurrent) # NOTE: the 'command' parameter is really the 'selectionFlags' self.__lastselectionflags = command if (command & QtGui.QItemSelectionModel.Columns): # NOTE: I'm not sure anyone ever has this set but just in # case for compatibility. In future we should apptrack # this and if no one uses column seleciton then this # option should be removed. previousSelection = self.selectedIndexes() else: # This saves on many many duplicates in the selection previousSelection = self.selectedRows() QtGui.QItemSelectionModel.select(self, selection, command) # NOTE: the 'command' parameter is really 'selectionFlags' if (command & QtGui.QItemSelectionModel.Columns): # NOTE: I'm not sure anyone ever has this set but just in # case for compatibility. In future we should apptrack # this and if no one uses column seleciton then this # option should be removed. selected_now = self.selectedIndexes() else: # This saves on many many duplicates in the selection selected_now = self.selectedRows() self.setSelectionStates(selected_now, previous=previousSelection) self.requestRefresh()