class XDockToolbar(QWidget): Position = enum('North', 'South', 'East', 'West') actionTriggered = Signal(object) actionMiddleTriggered = Signal(object) actionMenuRequested = Signal(object, QPoint) currentActionChanged = Signal(object) actionHovered = Signal(object) def __init__(self, parent=None): super(XDockToolbar, self).__init__(parent) # defines the position for this widget self._currentAction = -1 self._selectedAction = None self._padding = 8 self._position = XDockToolbar.Position.South self._minimumPixmapSize = QSize(16, 16) self._maximumPixmapSize = QSize(48, 48) self._hoverTimer = QTimer(self) self._hoverTimer.setSingleShot(True) self._hoverTimer.setInterval(1000) self._actionHeld = False self._easingCurve = QEasingCurve(QEasingCurve.InOutQuad) self._duration = 200 self._animating = False # install an event filter to update the location for this toolbar layout = QBoxLayout(QBoxLayout.LeftToRight) layout.setContentsMargins(2, 2, 2, 2) layout.setSpacing(0) layout.addStretch(1) layout.addStretch(1) self.setLayout(layout) self.setContentsMargins(2, 2, 2, 2) self.setMouseTracking(True) parent.window().installEventFilter(self) parent.window().statusBar().installEventFilter(self) self._hoverTimer.timeout.connect(self.emitActionHovered) def __markAnimatingFinished(self): self._animating = False def actionAt(self, pos): """ Returns the action at the given position. :param pos | <QPoint> :return <QAction> || None """ child = self.childAt(pos) if child: return child.action() return None def actionHeld(self): """ Returns whether or not the action will be held instead of closed on leaving. :return <bool> """ return self._actionHeld def actionLabels(self): """ Returns the labels for this widget. :return <XDockActionLabel> """ l = self.layout() return [l.itemAt(i).widget() for i in range(1, l.count() - 1)] def addAction(self, action): """ Adds the inputed action to this toolbar. :param action | <QAction> """ super(XDockToolbar, self).addAction(action) label = XDockActionLabel(action, self.minimumPixmapSize(), self) label.setPosition(self.position()) layout = self.layout() layout.insertWidget(layout.count() - 1, label) def clear(self): """ Clears out all the actions and items from this toolbar. """ # clear the actions from this widget for act in self.actions(): act.setParent(None) act.deleteLater() # clear the labels from this widget for lbl in self.actionLabels(): lbl.close() lbl.deleteLater() def currentAction(self): """ Returns the currently hovered/active action. :return <QAction> || None """ return self._currentAction def duration(self): """ Returns the duration value for the animation of the icons. :return <int> """ return self._duration def easingCurve(self): """ Returns the easing curve that will be used for the animation of animated icons for this dock bar. :return <QEasingCurve> """ return self._easingCurve def emitActionHovered(self): """ Emits a signal when an action is hovered. """ if not self.signalsBlocked(): self.actionHovered.emit(self.currentAction()) def eventFilter(self, object, event): """ Filters the parent objects events to rebuild this toolbar when the widget resizes. :param object | <QObject> event | <QEvent> """ if event.type() in (event.Move, event.Resize): if self.isVisible(): self.rebuild() elif object.isVisible(): self.setVisible(True) return False def holdAction(self): """ Returns whether or not the action should be held instead of clearing on leave. :return <bool> """ self._actionHeld = True def labelForAction(self, action): """ Returns the label that contains the inputed action. :return <XDockActionLabel> || None """ for label in self.actionLabels(): if label.action() == action: return label return None def leaveEvent(self, event): """ Clears the current action for this widget. :param event | <QEvent> """ super(XDockToolbar, self).leaveEvent(event) if not self.actionHeld(): self.setCurrentAction(None) def maximumPixmapSize(self): """ Returns the maximum pixmap size for this toolbar. :return <int> """ return self._maximumPixmapSize def minimumPixmapSize(self): """ Returns the minimum pixmap size that will be displayed to the user for the dock widget. :return <int> """ return self._minimumPixmapSize def mouseMoveEvent(self, event): """ Updates the labels for this dock toolbar. :param event | <XDockToolbar> """ # update the current label self.setCurrentAction(self.actionAt(event.pos())) def padding(self): """ Returns the padding value for this toolbar. :return <int> """ return self._padding def paintEvent(self, event): """ Paints the background for the dock toolbar. :param event | <QPaintEvent> """ x = 1 y = 1 w = self.width() h = self.height() clr_a = QColor(220, 220, 220) clr_b = QColor(190, 190, 190) grad = QLinearGradient() grad.setColorAt(0.0, clr_a) grad.setColorAt(0.6, clr_a) grad.setColorAt(1.0, clr_b) # adjust the coloring for the horizontal toolbar if self.position() & (self.Position.North | self.Position.South): h = self.minimumPixmapSize().height() + 6 if self.position() == self.Position.South: y = self.height() - h grad.setStart(0, y) grad.setFinalStop(0, self.height()) else: grad.setStart(0, 0) grad.setFinalStart(0, h) # adjust the coloring for the vertical toolbar if self.position() & (self.Position.East | self.Position.West): w = self.minimumPixmapSize().width() + 6 if self.position() == self.Position.West: x = self.width() - w grad.setStart(x, 0) grad.setFinalStop(self.width(), 0) else: grad.setStart(0, 0) grad.setFinalStop(w, 0) with XPainter(self) as painter: painter.fillRect(x, y, w, h, grad) # show the active action action = self.selectedAction() if action is not None and \ not self.currentAction() and \ not self._animating: for lbl in self.actionLabels(): if lbl.action() != action: continue geom = lbl.geometry() size = lbl.pixmapSize() if self.position() == self.Position.North: x = geom.left() y = 0 w = geom.width() h = size.height() + geom.top() + 2 elif self.position() == self.Position.East: x = 0 y = geom.top() w = size.width() + geom.left() + 2 h = geom.height() painter.setPen(QColor(140, 140, 40)) painter.setBrush(QColor(160, 160, 160)) painter.drawRect(x, y, w, h) break def position(self): """ Returns the position for this docktoolbar. :return <XDockToolbar.Position> """ return self._position def rebuild(self): """ Rebuilds the widget based on the position and current size/location of its parent. """ if not self.isVisible(): return self.raise_() max_size = self.maximumPixmapSize() min_size = self.minimumPixmapSize() widget = self.window() rect = widget.rect() rect.setBottom(rect.bottom() - widget.statusBar().height()) rect.setTop(widget.menuBar().height()) offset = self.padding() # align this widget to the north if self.position() == XDockToolbar.Position.North: self.move(rect.left(), rect.top()) self.resize(rect.width(), min_size.height() + offset) # align this widget to the east elif self.position() == XDockToolbar.Position.East: self.move(rect.left(), rect.top()) self.resize(min_size.width() + offset, rect.height()) # align this widget to the south elif self.position() == XDockToolbar.Position.South: self.move(rect.left(), rect.top() - min_size.height() - offset) self.resize(rect.width(), min_size.height() + offset) # align this widget to the west else: self.move(rect.right() - min_size.width() - offset, rect.top()) self.resize(min_size.width() + offset, rect.height()) def resizeToMinimum(self): """ Resizes the dock toolbar to the minimum sizes. """ offset = self.padding() min_size = self.minimumPixmapSize() if self.position() in (XDockToolbar.Position.East, XDockToolbar.Position.West): self.resize(min_size.width() + offset, self.height()) elif self.position() in (XDockToolbar.Position.North, XDockToolbar.Position.South): self.resize(self.width(), min_size.height() + offset) def selectedAction(self): """ Returns the action that was last selected. :return <QAction> """ return self._selectedAction def setActionHeld(self, state): """ Sets whether or not this action should be held before clearing on leaving. :param state | <bool> """ self._actionHeld = state def setCurrentAction(self, action): """ Sets the current action for this widget that highlights the size for this toolbar. :param action | <QAction> """ if action == self._currentAction: return self._currentAction = action self.currentActionChanged.emit(action) labels = self.actionLabels() anim_grp = QParallelAnimationGroup(self) max_size = self.maximumPixmapSize() min_size = self.minimumPixmapSize() if action: label = self.labelForAction(action) index = labels.index(label) # create the highlight effect palette = self.palette() effect = QGraphicsDropShadowEffect(label) effect.setXOffset(0) effect.setYOffset(0) effect.setBlurRadius(20) effect.setColor(QColor(40, 40, 40)) label.setGraphicsEffect(effect) offset = self.padding() if self.position() in (XDockToolbar.Position.East, XDockToolbar.Position.West): self.resize(max_size.width() + offset, self.height()) elif self.position() in (XDockToolbar.Position.North, XDockToolbar.Position.South): self.resize(self.width(), max_size.height() + offset) w = max_size.width() h = max_size.height() dw = (max_size.width() - min_size.width()) / 3 dh = (max_size.height() - min_size.height()) / 3 for i in range(4): before = index - i after = index + i if 0 <= before and before < len(labels): anim = XObjectAnimation(labels[before], 'setPixmapSize', anim_grp) anim.setEasingCurve(self.easingCurve()) anim.setStartValue(labels[before].pixmapSize()) anim.setEndValue(QSize(w, h)) anim.setDuration(self.duration()) anim_grp.addAnimation(anim) if i: labels[before].setGraphicsEffect(None) if after != before and 0 <= after and after < len(labels): anim = XObjectAnimation(labels[after], 'setPixmapSize', anim_grp) anim.setEasingCurve(self.easingCurve()) anim.setStartValue(labels[after].pixmapSize()) anim.setEndValue(QSize(w, h)) anim.setDuration(self.duration()) anim_grp.addAnimation(anim) if i: labels[after].setGraphicsEffect(None) w -= dw h -= dh else: offset = self.padding() for label in self.actionLabels(): # clear the graphics effect label.setGraphicsEffect(None) # create the animation anim = XObjectAnimation(label, 'setPixmapSize', self) anim.setEasingCurve(self.easingCurve()) anim.setStartValue(label.pixmapSize()) anim.setEndValue(min_size) anim.setDuration(self.duration()) anim_grp.addAnimation(anim) anim_grp.finished.connect(self.resizeToMinimum) anim_grp.start() self._animating = True anim_grp.finished.connect(anim_grp.deleteLater) anim_grp.finished.connect(self.__markAnimatingFinished) if self._currentAction: self._hoverTimer.start() else: self._hoverTimer.stop() def setDuration(self, duration): """ Sets the duration value for the animation of the icon. :param duration | <int> """ self._duration = duration def setEasingCurve(self, curve): """ Sets the easing curve for this toolbar to the inputed curve. :param curve | <QEasingCurve> """ self._easingCurve = QEasingCurve(curve) def setMaximumPixmapSize(self, size): """ Sets the maximum pixmap size for this toolbar. :param size | <int> """ self._maximumPixmapSize = size position = self.position() self._position = None self.setPosition(position) def setMinimumPixmapSize(self, size): """ Sets the minimum pixmap size that will be displayed to the user for the dock widget. :param size | <int> """ self._minimumPixmapSize = size position = self.position() self._position = None self.setPosition(position) def setPadding(self, padding): """ Sets the padding amount for this toolbar. :param padding | <int> """ self._padding = padding def setPosition(self, position): """ Sets the position for this widget and its parent. :param position | <XDockToolbar.Position> """ if position == self._position: return self._position = position widget = self.window() layout = self.layout() offset = self.padding() min_size = self.minimumPixmapSize() # set the layout to north if position == XDockToolbar.Position.North: self.move(0, 0) widget.setContentsMargins(0, min_size.height() + offset, 0, 0) layout.setDirection(QBoxLayout.LeftToRight) # set the layout to east elif position == XDockToolbar.Position.East: self.move(0, 0) widget.setContentsMargins(min_size.width() + offset, 0, 0, 0) layout.setDirection(QBoxLayout.TopToBottom) # set the layout to the south elif position == XDockToolbar.Position.South: widget.setContentsMargins(0, 0, 0, min_size.height() + offset) layout.setDirection(QBoxLayout.LeftToRight) # set the layout to the west else: widget.setContentsMargins(0, 0, min_size.width() + offset, 0) layout.setDirection(QBoxLayout.TopToBottom) # update the label alignments for label in self.actionLabels(): label.setPosition(position) # rebuilds the widget self.rebuild() self.update() def setSelectedAction(self, action): """ Sets the selected action instance for this toolbar. :param action | <QAction> """ self._hoverTimer.stop() self._selectedAction = action def setVisible(self, state): """ Sets whether or not this toolbar is visible. If shown, it will rebuild. :param state | <bool> """ super(XDockToolbar, self).setVisible(state) if state: self.rebuild() self.setCurrentAction(None) def unholdAction(self): """ Unholds the action from being blocked on the leave event. """ self._actionHeld = False point = self.mapFromGlobal(QCursor.pos()) self.setCurrentAction(self.actionAt(point)) def visualRect(self, action): """ Returns the visual rect for the inputed action, or a blank QRect if no matching action was found. :param action | <QAction> :return <QRect> """ for widget in self.actionLabels(): if widget.action() == action: return widget.geometry() return QRect()
class XMenu(QMenu): def __init__(self, parent=None): super(XMenu, self).__init__(parent) # define custom parameters self._acceptedAction = None self._showTitle = True self._advancedMap = {} self._customData = {} self._titleHeight = 24 self._toolTipAction = None self._toolTipTimer = QTimer(self) self._toolTipTimer.setInterval(1000) self._toolTipTimer.setSingleShot(True) # set default parameters self.setContentsMargins(0, self._titleHeight, 0, 0) self.setShowTitle(False) # create connections self.hovered.connect(self.startActionToolTip) self.aboutToShow.connect(self.clearAcceptedAction) self._toolTipTimer.timeout.connect(self.showActionToolTip) def acceptAdvanced(self): self._acceptedAction = self.sender().defaultAction() self.close() def acceptedAction(self): return self._acceptedAction def addMenu(self, submenu): """ Adds a new submenu to this menu. Overloads the base QMenu addMenu \ method so that it will return an XMenu instance vs. a QMenu when \ creating a submenu by passing in a string. :param submenu | <str> || <QMenu> :return <QMenu> """ # create a new submenu based on a string input if not isinstance(submenu, QMenu): title = nativestring(submenu) submenu = XMenu(self) submenu.setTitle(title) submenu.setShowTitle(self.showTitle()) super(XMenu, self).addMenu(submenu) else: super(XMenu, self).addMenu(submenu) submenu.menuAction().setData(wrapVariant('menu')) return submenu def addSearchAction(self): """ Adds a search action that will allow the user to search through the actions and sub-actions within in this menu. :return <XSearchAction> """ action = XSearchAction(self) self.addAction(action) return action def addSection(self, section): """ Adds a section to this menu. A section will create a label for the menu to separate sections of the menu out. :param section | <str> """ label = QLabel(section, self) label.setMinimumHeight(self.titleHeight()) # setup font font = label.font() font.setBold(True) # setup palette palette = label.palette() palette.setColor(palette.WindowText, palette.color(palette.Mid)) # setup label label.setFont(font) label.setAutoFillBackground(True) label.setPalette(palette) # create the widget action action = QWidgetAction(self) action.setDefaultWidget(label) self.addAction(action) return action def adjustMinimumWidth( self ): """ Updates the minimum width for this menu based on the font metrics \ for its title (if its shown). This method is called automatically \ when the menu is shown. """ if not self.showTitle(): return metrics = QFontMetrics(self.font()) width = metrics.width(self.title()) + 20 if self.minimumWidth() < width: self.setMinimumWidth(width) def clearAdvancedActions( self ): """ Clears out the advanced action map. """ self._advancedMap.clear() margins = list(self.getContentsMargins()) margins[2] = 0 self.setContentsMargins(*margins) def clearAcceptedAction(self): self._acceptedAction = None def customData( self, key, default = None ): """ Returns data that has been stored on this menu. :param key | <str> default | <variant> :return <variant> """ key = nativestring(key) menu = self while (not key in menu._customData and \ isinstance(menu.parent(), XMenu)): menu = menu.parent() return menu._customData.get(nativestring(key), default) def paintEvent( self, event ): """ Overloads the paint event for this menu to draw its title based on its \ show title property. :param event | <QPaintEvent> """ super(XMenu, self).paintEvent(event) if self.showTitle(): with XPainter(self) as painter: palette = self.palette() painter.setBrush(palette.color(palette.Button)) painter.setPen(Qt.NoPen) painter.drawRect(1, 1, self.width() - 2, 22) painter.setBrush(Qt.NoBrush) painter.setPen(palette.color(palette.ButtonText)) painter.drawText(1, 1, self.width() - 2, 22, Qt.AlignCenter, self.title()) def rebuildButtons(self): """ Rebuilds the buttons for the advanced actions. """ for btn in self.findChildren(XAdvancedButton): btn.close() btn.setParent(None) btn.deleteLater() for standard, advanced in self._advancedMap.items(): rect = self.actionGeometry(standard) btn = XAdvancedButton(self) btn.setFixedWidth(22) btn.setFixedHeight(rect.height()) btn.setDefaultAction(advanced) btn.setAutoRaise(True) btn.move(rect.right() + 1, rect.top()) btn.show() if btn.icon().isNull(): btn.setIcon(QIcon(resources.find('img/advanced.png'))) btn.clicked.connect(self.acceptAdvanced) def setAdvancedAction(self, standardAction, advancedAction): """ Links an advanced action with the inputed standard action. This will \ create a tool button alongside the inputed standard action when the \ menu is displayed. If the user selects the advanced action, then the \ advancedAction.triggered signal will be emitted. :param standardAction | <QAction> advancedAction | <QAction> || None """ if advancedAction: self._advancedMap[standardAction] = advancedAction margins = list(self.getContentsMargins()) margins[2] = 22 self.setContentsMargins(*margins) elif standardAction in self._advancedMap: self._advancedMap.pop(standardAction) if not self._advancedMap: margins = list(self.getContentsMargins()) margins[2] = 22 self.setContentsMargins(*margins) def setCustomData( self, key, value ): """ Sets custom data for the developer on this menu instance. :param key | <str> value | <variant> """ self._customData[nativestring(key)] = value def setShowTitle( self, state ): """ Sets whether or not the title for this menu should be displayed in the \ popup. :param state | <bool> """ self._showTitle = state margins = list(self.getContentsMargins()) if state: margins[1] = self.titleHeight() else: margins[1] = 0 self.setContentsMargins(*margins) def showEvent(self, event): """ Overloads the set visible method to update the advanced action buttons \ to match their corresponding standard action location. :param state | <bool> """ super(XMenu, self).showEvent(event) self.adjustSize() self.adjustMinimumWidth() self.rebuildButtons() def setTitleHeight(self, height): """ Sets the height for the title of this menu bar and sections. :param height | <int> """ self._titleHeight = height def showActionToolTip(self): """ Shows the tool tip of the action that is currently being hovered over. :param action | <QAction> """ if ( not self.isVisible() ): return geom = self.actionGeometry(self._toolTipAction) pos = self.mapToGlobal(QPoint(geom.left(), geom.top())) pos.setY(pos.y() + geom.height()) tip = nativestring(self._toolTipAction.toolTip()).strip().strip('.') text = nativestring(self._toolTipAction.text()).strip().strip('.') # don't waste time showing the user what they already see if ( tip == text ): return QToolTip.showText(pos, self._toolTipAction.toolTip()) def showTitle( self ): """ Returns whether or not this menu should show the title in the popup. :return <bool> """ return self._showTitle def startActionToolTip( self, action ): """ Starts the timer to hover over an action for the current tool tip. :param action | <QAction> """ self._toolTipTimer.stop() QToolTip.hideText() if not action.toolTip(): return self._toolTipAction = action self._toolTipTimer.start() def titleHeight(self): """ Returns the height for the title of this menu bar and sections. :return <int> """ return self._titleHeight def updateCustomData( self, data ): """ Updates the custom data dictionary with the inputed data. :param data | <dict> """ if ( not data ): return self._customData.update(data) @staticmethod def fromString( parent, xmlstring, actions = None ): """ Loads the xml string as xml data and then calls the fromXml method. :param parent | <QWidget> xmlstring | <str> actions | {<str> name: <QAction>, .. } || None :return <XMenu> || None """ try: xdata = ElementTree.fromstring(xmlstring) except ExpatError, e: logger.exception(e) return None return XMenu.fromXml(parent, xdata, actions)
class XMenu(QMenu): def __init__(self, parent=None): super(XMenu, self).__init__(parent) # define custom parameters self._acceptedAction = None self._showTitle = True self._advancedMap = {} self._customData = {} self._titleHeight = 24 self._toolTipAction = None self._toolTipTimer = QTimer(self) self._toolTipTimer.setInterval(1000) self._toolTipTimer.setSingleShot(True) # set default parameters self.setContentsMargins(0, self._titleHeight, 0, 0) self.setShowTitle(False) # create connections self.hovered.connect(self.startActionToolTip) self.aboutToShow.connect(self.clearAcceptedAction) self._toolTipTimer.timeout.connect(self.showActionToolTip) def acceptAdvanced(self): self._acceptedAction = self.sender().defaultAction() self.close() def acceptedAction(self): return self._acceptedAction def addMenu(self, submenu): """ Adds a new submenu to this menu. Overloads the base QMenu addMenu \ method so that it will return an XMenu instance vs. a QMenu when \ creating a submenu by passing in a string. :param submenu | <str> || <QMenu> :return <QMenu> """ # create a new submenu based on a string input if not isinstance(submenu, QMenu): title = nativestring(submenu) submenu = XMenu(self) submenu.setTitle(title) submenu.setShowTitle(self.showTitle()) super(XMenu, self).addMenu(submenu) else: super(XMenu, self).addMenu(submenu) submenu.menuAction().setData(wrapVariant('menu')) return submenu def addSearchAction(self): """ Adds a search action that will allow the user to search through the actions and sub-actions within in this menu. :return <XSearchAction> """ action = XSearchAction(self) self.addAction(action) return action def addSection(self, section): """ Adds a section to this menu. A section will create a label for the menu to separate sections of the menu out. :param section | <str> """ label = QLabel(section, self) label.setMinimumHeight(self.titleHeight()) # setup font font = label.font() font.setBold(True) # setup palette palette = label.palette() palette.setColor(palette.WindowText, palette.color(palette.Mid)) # setup label label.setFont(font) label.setAutoFillBackground(True) label.setPalette(palette) # create the widget action action = QWidgetAction(self) action.setDefaultWidget(label) self.addAction(action) return action def adjustMinimumWidth(self): """ Updates the minimum width for this menu based on the font metrics \ for its title (if its shown). This method is called automatically \ when the menu is shown. """ if not self.showTitle(): return metrics = QFontMetrics(self.font()) width = metrics.width(self.title()) + 20 if self.minimumWidth() < width: self.setMinimumWidth(width) def clearAdvancedActions(self): """ Clears out the advanced action map. """ self._advancedMap.clear() margins = list(self.getContentsMargins()) margins[2] = 0 self.setContentsMargins(*margins) def clearAcceptedAction(self): self._acceptedAction = None def customData(self, key, default=None): """ Returns data that has been stored on this menu. :param key | <str> default | <variant> :return <variant> """ key = nativestring(key) menu = self while (not key in menu._customData and \ isinstance(menu.parent(), XMenu)): menu = menu.parent() return menu._customData.get(nativestring(key), default) def paintEvent(self, event): """ Overloads the paint event for this menu to draw its title based on its \ show title property. :param event | <QPaintEvent> """ super(XMenu, self).paintEvent(event) if self.showTitle(): with XPainter(self) as painter: palette = self.palette() painter.setBrush(palette.color(palette.Button)) painter.setPen(Qt.NoPen) painter.drawRect(1, 1, self.width() - 2, 22) painter.setBrush(Qt.NoBrush) painter.setPen(palette.color(palette.ButtonText)) painter.drawText(1, 1, self.width() - 2, 22, Qt.AlignCenter, self.title()) def rebuildButtons(self): """ Rebuilds the buttons for the advanced actions. """ for btn in self.findChildren(XAdvancedButton): btn.close() btn.setParent(None) btn.deleteLater() for standard, advanced in self._advancedMap.items(): rect = self.actionGeometry(standard) btn = XAdvancedButton(self) btn.setFixedWidth(22) btn.setFixedHeight(rect.height()) btn.setDefaultAction(advanced) btn.setAutoRaise(True) btn.move(rect.right() + 1, rect.top()) btn.show() if btn.icon().isNull(): btn.setIcon(QIcon(resources.find('img/advanced.png'))) btn.clicked.connect(self.acceptAdvanced) def setAdvancedAction(self, standardAction, advancedAction): """ Links an advanced action with the inputed standard action. This will \ create a tool button alongside the inputed standard action when the \ menu is displayed. If the user selects the advanced action, then the \ advancedAction.triggered signal will be emitted. :param standardAction | <QAction> advancedAction | <QAction> || None """ if advancedAction: self._advancedMap[standardAction] = advancedAction margins = list(self.getContentsMargins()) margins[2] = 22 self.setContentsMargins(*margins) elif standardAction in self._advancedMap: self._advancedMap.pop(standardAction) if not self._advancedMap: margins = list(self.getContentsMargins()) margins[2] = 22 self.setContentsMargins(*margins) def setCustomData(self, key, value): """ Sets custom data for the developer on this menu instance. :param key | <str> value | <variant> """ self._customData[nativestring(key)] = value def setShowTitle(self, state): """ Sets whether or not the title for this menu should be displayed in the \ popup. :param state | <bool> """ self._showTitle = state margins = list(self.getContentsMargins()) if state: margins[1] = self.titleHeight() else: margins[1] = 0 self.setContentsMargins(*margins) def showEvent(self, event): """ Overloads the set visible method to update the advanced action buttons \ to match their corresponding standard action location. :param state | <bool> """ super(XMenu, self).showEvent(event) self.adjustSize() self.adjustMinimumWidth() self.rebuildButtons() def setTitleHeight(self, height): """ Sets the height for the title of this menu bar and sections. :param height | <int> """ self._titleHeight = height def showActionToolTip(self): """ Shows the tool tip of the action that is currently being hovered over. :param action | <QAction> """ if (not self.isVisible()): return geom = self.actionGeometry(self._toolTipAction) pos = self.mapToGlobal(QPoint(geom.left(), geom.top())) pos.setY(pos.y() + geom.height()) tip = nativestring(self._toolTipAction.toolTip()).strip().strip('.') text = nativestring(self._toolTipAction.text()).strip().strip('.') # don't waste time showing the user what they already see if (tip == text): return QToolTip.showText(pos, self._toolTipAction.toolTip()) def showTitle(self): """ Returns whether or not this menu should show the title in the popup. :return <bool> """ return self._showTitle def startActionToolTip(self, action): """ Starts the timer to hover over an action for the current tool tip. :param action | <QAction> """ self._toolTipTimer.stop() QToolTip.hideText() if not action.toolTip(): return self._toolTipAction = action self._toolTipTimer.start() def titleHeight(self): """ Returns the height for the title of this menu bar and sections. :return <int> """ return self._titleHeight def updateCustomData(self, data): """ Updates the custom data dictionary with the inputed data. :param data | <dict> """ if (not data): return self._customData.update(data) @staticmethod def fromString(parent, xmlstring, actions=None): """ Loads the xml string as xml data and then calls the fromXml method. :param parent | <QWidget> xmlstring | <str> actions | {<str> name: <QAction>, .. } || None :return <XMenu> || None """ try: xdata = ElementTree.fromstring(xmlstring) except ExpatError, e: logger.exception(e) return None return XMenu.fromXml(parent, xdata, actions)