eric7/EricWidgets/EricToolBarManager.py

branch
eric7
changeset 8358
144a6b854f70
parent 8356
68ec9c3d4de5
child 8881
54e42bc2437a
diff -r a081458cc57b -r 144a6b854f70 eric7/EricWidgets/EricToolBarManager.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/EricWidgets/EricToolBarManager.py	Sat May 22 19:58:24 2021 +0200
@@ -0,0 +1,728 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a toolbar manager class.
+"""
+
+from PyQt6.QtCore import QObject, QByteArray, QDataStream, QIODevice
+from PyQt6.QtWidgets import QToolBar
+
+import Utilities
+
+
+class EricToolBarManager(QObject):
+    """
+    Class implementing a toolbar manager.
+    """
+    VersionMarker = 0xffff
+    ToolBarMarker = 0xfefe
+    CustomToolBarMarker = 0xfdfd
+    
+    def __init__(self, ui=None, parent=None):
+        """
+        Constructor
+        
+        @param ui reference to the user interface object (UI.UserInterface)
+        @param parent reference to the parent object (QObject)
+        """
+        super().__init__(parent)
+        
+        self.__mainWindow = None
+        self.__ui = ui
+        
+        self.__toolBars = {}
+        # maps toolbar IDs to actions
+        self.__toolBarsWithSeparators = {}
+        # maps toolbar IDs to actions incl. separators
+        self.__defaultToolBars = {}
+        # maps default toolbar IDs to actions
+        self.__customToolBars = []
+        # list of custom toolbars
+        self.__allToolBars = {}
+        # maps toolbar IDs to toolbars
+        
+        self.__categoryToActions = {}
+        # maps categories to actions
+        self.__actionToCategory = {}
+        # maps action IDs to categories
+        self.__allActions = {}
+        # maps action IDs to actions
+        self.__actionToToolBars = {}
+        # maps action IDs to toolbars
+        
+        self.__widgetActions = {}
+        # maps widget action IDs to toolbars
+        self.__allWidgetActions = {}
+        # maps widget action IDs to widget actions
+    
+    ######################################################
+    ## Private methods
+    ######################################################
+    
+    def __toolBarByName(self, name):
+        """
+        Private slot to get a toolbar by its object name.
+        
+        @param name object name of the toolbar (string)
+        @return reference to the toolbar (QToolBar)
+        """
+        for toolBar in list(self.__allToolBars.values()):
+            if toolBar.objectName() == name:
+                return toolBar
+        return None
+    
+    def __findAction(self, name):
+        """
+        Private method to find an action by name.
+        
+        @param name name of the action to search for (string)
+        @return reference to the action (QAction)
+        """
+        # check objectName() first
+        for action in list(self.__allActions.values()):
+            if action.objectName() == name:
+                return action
+        
+        # check text() next
+        for action in list(self.__allActions.values()):
+            if action.text() == name:
+                return action
+        
+        return None
+    
+    def __findDefaultToolBar(self, name):
+        """
+        Private method to find a default toolbar by name.
+        
+        @param name name of the default toolbar to search for (string)
+        @return reference to the default toolbar (QToolBar)
+        """
+        # check objectName() first
+        for tbID in self.__defaultToolBars:
+            tb = self.__allToolBars[tbID]
+            if tb.objectName() == name:
+                return tb
+        
+        # check windowTitle() next
+        for tbID in self.__defaultToolBars:
+            tb = self.__allToolBars[tbID]
+            if tb.windowTitle() == name:
+                return tb
+        
+        return None
+    
+    ######################################################
+    ## Public methods
+    ######################################################
+    
+    def setMainWindow(self, mainWindow):
+        """
+        Public method to set the reference to the main window.
+        
+        @param mainWindow reference to the main window (QMainWindow)
+        """
+        self.__mainWindow = mainWindow
+    
+    def mainWindow(self):
+        """
+        Public method to get the reference to the main window.
+        
+        @return reference to the main window (QMainWindow)
+        """
+        return self.__mainWindow
+    
+    def addToolBar(self, toolBar, category):
+        """
+        Public method to add a toolbar to be managed.
+        
+        @param toolBar reference to the toolbar to be managed (QToolBar)
+        @param category category for the toolbar (string)
+        """
+        if toolBar is None:
+            return
+        
+        newActions = []
+        newActionsWithSeparators = []
+        actions = toolBar.actions()
+        for action in actions:
+            actID = id(action)
+            self.addAction(action, category)
+            if actID in self.__widgetActions:
+                self.__widgetActions[actID] = toolBar
+            newActionsWithSeparators.append(action)
+            if action.isSeparator():
+                action = None
+            else:
+                if toolBar not in self.__actionToToolBars[actID]:
+                    self.__actionToToolBars[actID].append(toolBar)
+            newActions.append(action)
+        tbID = id(toolBar)
+        self.__defaultToolBars[tbID] = newActions
+        self.__toolBars[tbID] = newActions
+        self.__toolBarsWithSeparators[tbID] = newActionsWithSeparators
+        self.__allToolBars[tbID] = toolBar
+    
+    def removeToolBar(self, toolBar):
+        """
+        Public method to remove a toolbar added with addToolBar().
+        
+        @param toolBar reference to the toolbar to be removed (QToolBar)
+        """
+        if toolBar is None:
+            return
+        
+        tbID = id(toolBar)
+        
+        if tbID not in self.__defaultToolBars:
+            return
+        
+        defaultActions = self.__defaultToolBars[tbID][:]
+        self.setToolBar(toolBar, [])
+        for action in defaultActions:
+            self.removeAction(action)
+        
+        del self.__defaultToolBars[tbID]
+        del self.__toolBars[tbID]
+        del self.__toolBarsWithSeparators[tbID]
+        del self.__allToolBars[tbID]
+        
+        for action in defaultActions:
+            if action is None:
+                toolBar.addSeparator()
+            else:
+                toolBar.addAction(action)
+    
+    def setToolBars(self, toolBars):
+        """
+        Public method to set the actions of several toolbars.
+        
+        @param toolBars dictionary with toolbar id as key and
+            a list of actions as value
+        """
+        for key, actions in list(toolBars.items()):
+            tb = self.__allToolBars[key]
+            self.setToolBar(tb, actions)
+    
+    def setToolBar(self, toolBar, actions):
+        """
+        Public method to set the actions of a toolbar.
+        
+        @param toolBar reference to the toolbar to configure (QToolBar)
+        @param actions list of actions to be set (list of QAction)
+        """
+        if toolBar is None:
+            return
+        
+        tbID = id(toolBar)
+        if tbID not in self.__toolBars:
+            return
+        if self.__toolBars[tbID] == actions:
+            return
+        
+        # step 1: check list of actions
+        toRemove = {}
+        newActions = []
+        for action in actions:
+            if (
+                action is None or
+                (action not in newActions and
+                 id(action) in self.__allActions)
+            ):
+                newActions.append(action)
+            oldTB = self.toolBarWidgetAction(action)
+            if oldTB is not None and oldTB != toolBar:
+                if id(oldTB) not in toRemove:
+                    toRemove[id(oldTB)] = []
+                toRemove[id(oldTB)].append(action)
+        self.removeWidgetActions(toRemove)
+        
+        # step 2: remove all toolbar actions
+        for action in self.__toolBarsWithSeparators[tbID]:
+            if self.toolBarWidgetAction(action) == tbID:
+                self.__widgetActions[id(action)] = None
+            toolBar.removeAction(action)
+            if action.isSeparator():
+                del action
+            else:
+                self.__actionToToolBars[id(action)].remove(toolBar)
+        
+        # step 3: set the actions as requested
+        newActionsWithSeparators = []
+        for action in newActions:
+            newAction = None
+            if action is None:
+                newAction = toolBar.addSeparator()
+            elif id(action) in self.__allActions:
+                toolBar.addAction(action)
+                newAction = action
+                self.__actionToToolBars[id(action)].append(toolBar)
+                if id(action) in self.__widgetActions:
+                    self.__widgetActions[id(action)] = toolBar
+            else:
+                continue
+            newActionsWithSeparators.append(newAction)
+        
+        if toolBar.isVisible():
+            toolBar.hide()
+            toolBar.show()
+        self.__toolBars[tbID] = newActions
+        self.__toolBarsWithSeparators[tbID] = newActionsWithSeparators
+    
+    def resetToolBar(self, toolBar):
+        """
+        Public method to reset a toolbar to its default state.
+        
+        @param toolBar reference to the toolbar to configure (QToolBar)
+        """
+        if not self.isDefaultToolBar():
+            return
+        self.setToolBar(toolBar, self.__defaultToolBars[id(toolBar)])
+    
+    def resetAllToolBars(self):
+        """
+        Public method to reset all toolbars to their default state.
+        """
+        self.setToolBars(self.__defaultToolBars)
+        for toolBar in self.__customToolBars[:]:
+            self.deleteToolBar(toolBar)
+        
+    def defaultToolBars(self):
+        """
+        Public method to get all toolbars added with addToolBar().
+        
+        @return list of all default toolbars (list of QToolBar)
+        """
+        return list(self.__defaultToolBars.values())
+    
+    def isDefaultToolBar(self, toolBar):
+        """
+        Public method to check, if a toolbar was added with addToolBar().
+        
+        @param toolBar reference to the toolbar to be checked (QToolBar)
+        @return flag indicating an added toolbar (boolean)
+        """
+        return (
+            toolBar is not None and
+            id(toolBar) in self.__defaultToolBars
+        )
+    
+    def createToolBar(self, title, name=""):
+        """
+        Public method to create a custom toolbar.
+        
+        @param title title to be used for the toolbar (string)
+        @param name optional name for the new toolbar (string)
+        @return reference to the created toolbar (QToolBar)
+        """
+        if self.__mainWindow is None:
+            return None
+        
+        toolBar = QToolBar(title, self.__mainWindow)
+        toolBar.setToolTip(title)
+        if not name:
+            index = 1
+            customPrefix = "__CustomPrefix__"
+            name = "{0}{1:d}".format(customPrefix, index)
+            while self.__toolBarByName(name) is not None:
+                index += 1
+                name = "{0}{1:d}".format(customPrefix, index)
+        toolBar.setObjectName(name)
+        self.__mainWindow.addToolBar(toolBar)
+        
+        tbID = id(toolBar)
+        self.__customToolBars.append(toolBar)
+        self.__allToolBars[tbID] = toolBar
+        self.__toolBars[tbID] = []
+        self.__toolBarsWithSeparators[tbID] = []
+        
+        if self.__ui is not None:
+            toolBar.setIconSize(self.__ui.getToolBarIconSize())
+            self.__ui.registerToolbar(name, title, toolBar)
+        
+        return toolBar
+    
+    def deleteToolBar(self, toolBar):
+        """
+        Public method to remove a custom toolbar created with createToolBar().
+        
+        @param toolBar reference to the toolbar to be managed (QToolBar)
+        """
+        if toolBar is None:
+            return
+        
+        tbID = id(toolBar)
+        if tbID not in self.__toolBars:
+            return
+        if tbID in self.__defaultToolBars:
+            return
+        
+        if self.__ui is not None:
+            self.__ui.unregisterToolbar(toolBar.objectName())
+        
+        self.setToolBar(toolBar, [])
+        
+        del self.__allToolBars[tbID]
+        del self.__toolBars[tbID]
+        del self.__toolBarsWithSeparators[tbID]
+        self.__customToolBars.remove(toolBar)
+        self.__mainWindow.removeToolBar(toolBar)
+        del toolBar
+    
+    def renameToolBar(self, toolBar, title):
+        """
+        Public method to give a toolbar a new title.
+        
+        @param toolBar reference to the toolbar to be managed (QToolBar)
+        @param title title to be used for the toolbar (string)
+        """
+        if toolBar is None:
+            return
+        
+        toolBar.setWindowTitle(title)
+        
+        if self.__ui is not None:
+            self.__ui.reregisterToolbar(toolBar.objectName(), title)
+    
+    def toolBars(self):
+        """
+        Public method to get all toolbars.
+        
+        @return list of all toolbars (list of QToolBar)
+        """
+        return list(self.__allToolBars.values())
+    
+    def addAction(self, action, category):
+        """
+        Public method to add an action to be managed.
+        
+        @param action reference to the action to be managed (QAction)
+        @param category category for the toolbar (string)
+        """
+        if action is None:
+            return
+        if action.isSeparator():
+            return
+        if id(action) in self.__allActions:
+            return
+        
+        if action.metaObject().className() == "QWidgetAction":
+            self.__widgetActions[id(action)] = None
+            self.__allWidgetActions[id(action)] = action
+        self.__allActions[id(action)] = action
+        if category not in self.__categoryToActions:
+            self.__categoryToActions[category] = []
+        self.__categoryToActions[category].append(action)
+        self.__actionToCategory[id(action)] = category
+        self.__actionToToolBars[id(action)] = []
+    
+    def addActions(self, actions, category):
+        """
+        Public method to add actions to be managed.
+        
+        @param actions list of actions to be managed
+        @type list of QAction
+        @param category category for the toolbar
+        @type str
+        """
+        for action in actions:
+            self.addAction(action, category)
+    
+    def removeAction(self, action):
+        """
+        Public method to remove an action from the manager.
+        
+        @param action reference to the action to be removed (QAction)
+        """
+        aID = id(action)
+        
+        if aID not in self.__allActions:
+            return
+        
+        toolBars = self.__actionToToolBars[aID]
+        for toolBar in toolBars:
+            tbID = id(toolBar)
+            self.__toolBars[tbID].remove(action)
+            self.__toolBarsWithSeparators[tbID].remove(action)
+            toolBar.removeAction(action)
+            if toolBar.isVisible():
+                toolBar.hide()
+                toolBar.show()
+        
+        for tbID in self.__defaultToolBars:
+            if action in self.__defaultToolBars[tbID]:
+                self.__defaultToolBars[tbID].remove(action)
+        
+        del self.__allActions[aID]
+        if aID in self.__widgetActions:
+            del self.__widgetActions[aID]
+            del self.__allWidgetActions[aID]
+        del self.__actionToCategory[aID]
+        del self.__actionToToolBars[aID]
+        
+        for category in self.__categoryToActions:
+            if action in self.__categoryToActions[category]:
+                self.__categoryToActions[category].remove(action)
+    
+    def removeCategoryActions(self, category):
+        """
+        Public method to remove the actions belonging to a category.
+        
+        @param category category for the actions (string)
+        """
+        for action in self.categoryActions(category):
+            self.removeAction(action)
+    
+    def saveState(self, version=0):
+        """
+        Public method to save the state of the toolbar manager.
+        
+        @param version version number stored with the data (integer)
+        @return saved state as a byte array (QByteArray)
+        """
+        data = QByteArray()
+        stream = QDataStream(data, QIODevice.OpenModeFlag.WriteOnly)
+        stream.setVersion(QDataStream.Version.Qt_4_6)
+        stream.writeUInt16(EricToolBarManager.VersionMarker)
+        stream.writeUInt16(version)
+        
+        # save default toolbars
+        stream.writeUInt16(EricToolBarManager.ToolBarMarker)
+        stream.writeUInt16(len(self.__defaultToolBars))
+        for tbID in self.__defaultToolBars:
+            tb = self.__allToolBars[tbID]
+            if tb.objectName():
+                stream.writeString(tb.objectName().encode("utf-8"))
+            else:
+                stream.writeString(tb.windowTitle().encode("utf-8"))
+            stream.writeUInt16(len(self.__toolBars[tbID]))
+            for action in self.__toolBars[tbID]:
+                if action is not None:
+                    if action.objectName():
+                        stream.writeString(action.objectName().encode("utf-8"))
+                    else:
+                        stream.writeString(action.text().encode("utf-8"))
+                else:
+                    stream.writeString("".encode("utf-8"))
+        
+        # save the custom toolbars
+        stream.writeUInt16(EricToolBarManager.CustomToolBarMarker)
+        stream.writeUInt16(len(self.__toolBars) - len(self.__defaultToolBars))
+        for tbID in self.__toolBars:
+            if tbID not in self.__defaultToolBars:
+                tb = self.__allToolBars[tbID]
+                stream.writeString(tb.objectName().encode("utf-8"))
+                stream.writeString(tb.windowTitle().encode("utf-8"))
+                stream.writeUInt16(len(self.__toolBars[tbID]))
+                for action in self.__toolBars[tbID]:
+                    if action is not None:
+                        if action.objectName():
+                            stream.writeString(action.objectName()
+                                               .encode("utf-8"))
+                        else:
+                            stream.writeString(action.text().encode("utf-8"))
+                    else:
+                        stream.writeString("".encode("utf-8"))
+        
+        return data
+    
+    def restoreState(self, state, version=0):
+        """
+        Public method to restore the state of the toolbar manager.
+        
+        @param state byte array containing the saved state (QByteArray)
+        @param version version number stored with the data (integer)
+        @return flag indicating success (boolean)
+        """
+        if state.isEmpty():
+            return False
+        
+        data = QByteArray(state)
+        stream = QDataStream(data, QIODevice.OpenModeFlag.ReadOnly)
+        stream.setVersion(QDataStream.Version.Qt_4_6)
+        marker = stream.readUInt16()
+        vers = stream.readUInt16()
+        if marker != EricToolBarManager.VersionMarker or vers != version:
+            return False
+        
+        tmarker = stream.readUInt16()
+        if tmarker != EricToolBarManager.ToolBarMarker:
+            return False
+        
+        toolBarCount = stream.readUInt16()
+        for _i in range(toolBarCount):
+            objectName = Utilities.readStringFromStream(stream)
+            actionCount = stream.readUInt16()
+            actions = []
+            for _j in range(actionCount):
+                actionName = Utilities.readStringFromStream(stream)
+                if actionName:
+                    action = self.__findAction(actionName)
+                    if action is not None:
+                        actions.append(action)
+                else:
+                    actions.append(None)
+            toolBar = self.__findDefaultToolBar(objectName)
+            if toolBar is not None:
+                self.setToolBar(toolBar, actions)
+        
+        cmarker = stream.readUInt16()
+        if cmarker != EricToolBarManager.CustomToolBarMarker:
+            return False
+        
+        oldCustomToolBars = self.__customToolBars[:]
+        
+        toolBarCount = stream.readUInt16()
+        for _i in range(toolBarCount):
+            objectName = Utilities.readStringFromStream(stream)
+            toolBarTitle = Utilities.readStringFromStream(stream)
+            actionCount = stream.readUInt16()
+            actions = []
+            for _j in range(actionCount):
+                actionName = Utilities.readStringFromStream(stream)
+                if actionName:
+                    action = self.__findAction(actionName)
+                    if action is not None:
+                        actions.append(action)
+                else:
+                    actions.append(None)
+            toolBar = self.__toolBarByName(objectName)
+            if toolBar is not None:
+                toolBar.setWindowTitle(toolBarTitle)
+                oldCustomToolBars.remove(toolBar)
+            else:
+                toolBar = self.createToolBar(toolBarTitle, objectName)
+            if toolBar is not None:
+                toolBar.setObjectName(objectName)
+                self.setToolBar(toolBar, actions)
+        
+        for tb in oldCustomToolBars:
+            self.deleteToolBar(tb)
+        
+        return True
+    
+    def toolBarWidgetAction(self, action):
+        """
+        Public method to get the toolbar for a widget action.
+        
+        @param action widget action to check for (QAction)
+        @return reference to the toolbar containing action (QToolBar)
+        """
+        aID = id(action)
+        if aID in self.__widgetActions:
+            return self.__widgetActions[aID]
+        return None
+    
+    def removeWidgetActions(self, actions):
+        """
+        Public method to remove widget actions.
+        
+        @param actions dictionary with toolbar id as key and
+            a list of widget actions as value
+        """
+        for tbID in list(actions.keys())[:]:
+            toolBar = self.__allToolBars[tbID]
+            newActions = self.__toolBars[tbID][:]
+            newActionsWithSeparators = self.__toolBarsWithSeparators[tbID][:]
+            
+            removedActions = []
+            for action in actions[tbID]:
+                if (
+                    action in newActions and
+                    self.toolBarWidgetAction(action) == toolBar
+                ):
+                    newActions.remove(action)
+                    newActionsWithSeparators.remove(action)
+                    removedActions.append(action)
+            
+            self.__toolBars[tbID] = newActions
+            self.__toolBarsWithSeparators[tbID] = newActionsWithSeparators
+            
+            for action in removedActions:
+                self.__widgetActions[id(action)] = None
+                self.__actionToToolBars[id(action)].remove(toolBar)
+                toolBar.removeAction(action)
+    
+    def isWidgetAction(self, action):
+        """
+        Public method to check, if action is a widget action.
+        
+        @param action reference to the action to be checked (QAction)
+        @return flag indicating a widget action (boolean)
+        """
+        return id(action) in self.__allWidgetActions
+    
+    def categories(self):
+        """
+        Public method to get the list of categories.
+        
+        @return list of categories (list of string)
+        """
+        return list(self.__categoryToActions.keys())
+    
+    def categoryActions(self, category):
+        """
+        Public method to get the actions belonging to a category.
+        
+        @param category category for the actions (string)
+        @return list of actions (list of QAction)
+        """
+        if category not in self.__categoryToActions:
+            return []
+        
+        return self.__categoryToActions[category][:]
+    
+    def actionById(self, aID):
+        """
+        Public method to get an action given its id.
+        
+        @param aID id of the action object (integer)
+        @return reference to the action (QAction)
+        """
+        if aID not in self.__allActions:
+            return None
+        return self.__allActions[aID]
+    
+    def toolBarById(self, tbID):
+        """
+        Public method to get a toolbar given its id.
+        
+        @param tbID id of the toolbar object (integer)
+        @return reference to the toolbar (QToolBar)
+        """
+        if tbID not in self.__allToolBars:
+            return None
+        return self.__allToolBars[tbID]
+    
+    def toolBarActions(self, tbID):
+        """
+        Public method to get a toolbar's actions given its id.
+        
+        @param tbID id of the toolbar object (integer)
+        @return list of actions (list of QAction)
+        """
+        if tbID not in self.__toolBars:
+            return []
+        return self.__toolBars[tbID][:]
+    
+    def toolBarsActions(self):
+        """
+        Public method to get all toolbars and their actions.
+        
+        @return reference to dictionary of toolbar IDs as key and list
+            of actions as values
+        """
+        return self.__toolBars
+    
+    def defaultToolBarActions(self, tbID):
+        """
+        Public method to get a default toolbar's actions given its id.
+        
+        @param tbID id of the default toolbar object (integer)
+        @return list of actions (list of QAction)
+        """
+        if tbID not in self.__defaultToolBars:
+            return []
+        return self.__defaultToolBars[tbID][:]

eric ide

mercurial