E5Gui/E5ToolButton.py

changeset 5745
4f4316e83318
child 6048
82ad8ec9548c
diff -r d53474dcb9e6 -r 4f4316e83318 E5Gui/E5ToolButton.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/E5Gui/E5ToolButton.py	Sun May 28 16:59:18 2017 +0200
@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a specialized tool button subclass.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QTimer, QSize
+from PyQt5.QtWidgets import QToolButton, QStyle, QStyleOptionToolButton, \
+    QStyleOption, QApplication
+
+
+class E5ToolButton(QToolButton):
+    """
+    Class implementing a specialized tool button subclass.
+    
+    @signal aboutToShowMenu() emitted before the tool button menu is shown
+    @signal aboutToHideMenu() emitted before the tool button menu is hidden
+    @signal middleClicked() emitted when the middle mouse button was clicked
+    @signal controlClicked() emitted when the left mouse button was
+        clicked while pressing the Ctrl key
+    @signal doubleClicked() emitted when the left mouse button was
+        double clicked
+    """
+    NoOptions = 0
+    ShowMenuInsideOption = 1
+    ToolBarLookOption = 2
+    
+    aboutToShowMenu = pyqtSignal()
+    aboutToHideMenu = pyqtSignal()
+    middleClicked = pyqtSignal()
+    controlClicked = pyqtSignal()
+    doubleClicked = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(E5ToolButton, self).__init__(parent)
+        
+        self.setMinimumWidth(16)
+        
+        self.__menu = None
+        self.__options = E5ToolButton.NoOptions
+        
+        opt = QStyleOptionToolButton()
+        self.initStyleOption(opt)
+        
+        self.__pressTimer = QTimer()
+        self.__pressTimer.setSingleShot(True)
+        self.__pressTimer.setInterval(
+            QApplication.style().styleHint(
+                QStyle.SH_ToolButton_PopupDelay, opt, self))
+        self.__pressTimer.timeout.connect(self.__showMenu)
+    
+    ##################################################################
+    ## Menu handling methods below.
+    ##
+    ## The menu is handled in E5ToolButton and is not passed to
+    ## QToolButton. No menu indicator will be shown in the button.
+    ##################################################################
+    
+    def menu(self):
+        """
+        Public method to get a reference to the tool button menu.
+        
+        @return reference to the tool button menu
+        @rtype QMenu
+        """
+        return self.__menu
+    
+    def setMenu(self, menu):
+        """
+        Public method to set the tool button menu.
+        
+        @param menu reference to the tool button menu
+        @type QMenu
+        """
+        assert menu is not None
+        
+        if self.__menu:
+            self.__menu.aboutToHide.disconnect(self.__menuAboutToHide)
+        
+        self.__menu = menu
+        self.__menu.aboutToHide.connect(self.__menuAboutToHide)
+    
+    def showMenuInside(self):
+        """
+        Public method to check, if the menu edge shall be aligned with
+        the button.
+        
+        @return flag indicating that the menu edge shall be aligned
+        @rtype bool
+        """
+        return bool(self.__options & E5ToolButton.ShowMenuInsideOption)
+    
+    def setShowMenuInside(self, enable):
+        """
+        Public method to set a flag to show the menu edge aligned with
+        the button.
+        
+        @param enable flag indicating to align the menu edge to the button
+        @type bool
+        """
+        if enable:
+            self.__options |= E5ToolButton.ShowMenuInsideOption
+        else:
+            self.__options &= ~E5ToolButton.ShowMenuInsideOption
+    
+    @pyqtSlot()
+    def __showMenu(self):
+        """
+        Private slot to show the tool button menu.
+        """
+        if self.__menu is None or self.__menu.isVisible():
+            return
+        
+        self.aboutToShowMenu.emit()
+        
+        if self.__options & E5ToolButton.ShowMenuInsideOption:
+            pos = self.mapToGlobal(self.rect().bottomRight())
+            if QApplication.layoutDirection() == Qt.RightToLeft:
+                pos.setX(pos.x() - self.rect().width())
+            else:
+                pos.setX(pos.x() - self.__menu.sizeHint().width())
+        else:
+            pos = self.mapToGlobal(self.rect().bottomLeft())
+        
+        self.__menu.popup(pos)
+    
+    @pyqtSlot()
+    def __menuAboutToHide(self):
+        """
+        Private slot to handle the tool button menu about to be hidden.
+        """
+        self.setDown(False)
+        self.aboutToHideMenu.emit()
+    
+    ##################################################################
+    ## Methods to handle the tool button look
+    ##################################################################
+    
+    def toolbarButtonLook(self):
+        """
+        Public method to check, if the button has the toolbar look.
+        
+        @return flag indicating toolbar look
+        @rtype bool
+        """
+        return bool(self.__options & E5ToolButton.ToolBarLookOption)
+    
+    def setToolbarButtonLook(self, enable):
+        """
+        Public method to set the toolbar look state.
+        
+        @param enable flag indicating toolbar look
+        @type bool
+        """
+        if enable:
+            self.__options |= E5ToolButton.ToolBarLookOption
+            
+            opt = QStyleOption()
+            opt.initFrom(self)
+            size = self.style().pixelMetric(
+                QStyle.PM_ToolBarIconSize, opt, self)
+            self.setIconSize(QSize(size, size))
+        else:
+            self.__options &= ~E5ToolButton.ToolBarLookOption
+        
+        self.setProperty("toolbar-look", enable)
+        self.style().unpolish(self)
+        self.style().polish(self)
+    
+    ##################################################################
+    ## Methods to handle some event types
+    ##################################################################
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method to handle mouse press events.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        if self.popupMode() == QToolButton.DelayedPopup:
+            self.__pressTimer.start()
+        
+        if evt.buttons() == Qt.LeftButton and \
+           self.__menu is not None and \
+           self.popupMode() == QToolButton.InstantPopup:
+            self.setDown(True)
+            self.__showMenu()
+        elif evt.buttons() == Qt.RightButton and \
+                self.__menu is not None:
+            self.setDown(True)
+            self.__showMenu()
+        else:
+            super(E5ToolButton, self).mousePressEvent(evt)
+    
+    def mouseReleaseEvent(self, evt):
+        """
+        Protected method to handle mouse release events.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        self.__pressTimer.stop()
+        
+        if evt.button() == Qt.MiddleButton and \
+           self.rect().contains(evt.pos()):
+            self.middleClicked.emit()
+            self.setDown(False)
+        elif evt.button() == Qt.LeftButton and \
+            self.rect().contains(evt.pos()) and \
+                evt.modifiers() == Qt.ControlModifier:
+            self.controlClicked.emit()
+            self.setDown(False)
+        else:
+            super(E5ToolButton, self).mouseReleaseEvent(evt)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method to handle mouse double click events.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        super(E5ToolButton, self).mouseDoubleClickEvent(evt)
+        
+        self.__pressTimer.stop()
+        
+        if evt.buttons() == Qt.LeftButton:
+            self.doubleClicked.emit()
+    
+    def contextMenuEvent(self, evt):
+        """
+        Protected method to handle context menu events.
+        
+        @param evt reference to the context menu event
+        @type QContextMenuEvent
+        """
+        # block to prevent showing the context menu and the tool button menu
+        if self.__menu is not None:
+            return
+        
+        super(E5ToolButton, self).contextMenuEvent(evt)

eric ide

mercurial