Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

[pyqt5 / python 3.7] How to dynamically define a ToolBar?



  • I know how to set up a toolbar based on the menu but I am trying to make the toolbar adjustable by the user which means I will have to store the layout in my database and then create it based on the query results -- so here is my test program and while it kind of works the most important part does not work as I have denoted -- this works except you will have to make an images directory and put the 3 images you want to use within it:

    import sys
    
    from PyQt5.QtCore    import *
    from PyQt5.QtGui     import *
    from PyQt5.QtWidgets import *
    
    class CenterPanel(QWidget):
        def __init__(self, MainWin):
            QWidget.__init__(self)
            self.MainWin = MainWin
    
            CntrPane = QSplitter(Qt.Horizontal, self)
            CntrPane.addWidget(QTextEdit())
            CntrPane.addWidget(QTextEdit())
            CntrPane.setSizes([75,200])
    
            hbox = QHBoxLayout(self)
            hbox.addWidget(CntrPane)
    
            self.setLayout(hbox)
    
        @property
        def MainWin(self):
            return self.__parent
    
        @MainWin.setter
        def MainWin(self, value):
            self.__parent = value
    
    class MenuToolBar(QDockWidget):
        def __init__(self, MainWin):
            QDockWidget.__init__(self)
            self.MainWin = MainWin
            self.MainMenu = MainWin.menuBar()
            
            # ******* Create the File Menu *******
            self.FileMenu  = self.MainMenu.addMenu('File')
    
            self.NewFileAct = QAction(QIcon('images/new.png'), 'New File', self)
            self.NewFileAct.setShortcut("Ctrl+N")
            self.NewFileAct.setStatusTip('Create a New Project File')
            self.NewFileAct.triggered.connect(self.NewProjFile)
    
            # ******* Create File Menu Items *******
            self.OpnFileAct = QAction(QIcon('images/open.png'), 'Open File', self)
            self.OpnFileAct.setShortcut("Ctrl+O")
            self.OpnFileAct.setStatusTip('Open an Existing Project File')
            self.OpnFileAct.triggered.connect(self.OpenProjFile)
    
            self.SavFileAct = QAction(QIcon('images/save.png'), 'Save File', self)
            self.SavFileAct.setShortcut("Ctrl+S")
            self.SavFileAct.setStatusTip('Save Current Project File')
            self.SavFileAct.triggered.connect(self.SaveProjFile)
    
            # ******* Setup the File Menu *******
            self.FileMenu.addAction(self.NewFileAct)
            self.FileMenu.addSeparator()
            self.FileMenu.addAction(self.OpnFileAct)
            self.FileMenu.addSeparator()
            self.FileMenu.addAction(self.SavFileAct)
    
            self.InitToolBar(MainWin)
    
        def InitToolBar(self, MainWin):
            # Add Items to the Toolbar
            # This needs to be dynamically based on user adjustments if any
            self.mainToolBar = MainWin.addToolBar("Quick Access")
    
    # This how I would do it normally and it works just fine
    #        self.mainToolBar.addAction(self.OpnFileAct)
    #        self.mainToolBar.addAction(self.SavFileAct)
    #        self.mainToolBar.addAction(self.NewFileAct)
    
    # Here I am trying to figure out how to load the various items dynamically if stored as follows:
    # It sort of works I just need to translate the Action string
    #
            NewToolBarLayout = {0:{'addAction': 'self.NewFileAct'},
                                1:{'addSeparator': ''},
                                2:{'addAction': 'self.OpnFileAct'},
                                3:{'addSeparator': ''},
                                4:{'addAction': 'self.SavFileAct'}}
    #
            for idx in NewToolBarLayout:
                item = NewToolBarLayout[idx]
    
                if 'addAction' in item.keys():
                    value = item['addAction']
    #               How to translate the above so that the stored 'self.OpnProjAct' string
    #               for instance becomes the same internal value it is defined as
                    self.mainToolBar.addAction(value)  
    
                elif 'addSeparator' in item.keys():
                    self.mainToolBar.addSeparator()
    #
    
        def NewProjFile(self):
            print("Added New Project File")
    
        def OpenProjFile(self):
            print("Opened Existing Project File")
    
        def SaveProjFile(self):
            print("Saved Current Project File")
    
    class Window(QMainWindow):
        def __init__(self, parent=None):
            super(Window, self).__init__(parent)
    
            self.title = 'New Project'
            self.LeftEdge  = 100
            self.TopEdge   = 100
            self.WinWidth  = 800
            self.WinHeight = 600
    
            self.setWindowTitle(self.title)
            self.setGeometry(self.LeftEdge, self.TopEdge, self.WinWidth, self.WinHeight)
            self.CenterPane = CenterPanel(self)
            self.setCentralWidget(self.CenterPane)
            self.MenuToolBar = MenuToolBar(self)
            self.setStyle(QStyleFactory.create('Cleanlooks'))
    
    if __name__ == '__main__':
        newProj = QApplication([])
    
        GUI = Window()
        GUI.show()
    
        sys.exit(newProj.exec_())
    

  • Lifetime Qt Champion

    @Denni You can use https://doc.qt.io/qt-5/resources.html to embed the images with your application.



  • They are just generic images no need to take up extra space my sending them hither and yon -- heck you do not even need them you just do not have a visual of the box without an image is all and the directory would still need to be set up as I cannot include that with the images (I do not think?).

    Also having looked at the Link it appears that is something you combine with the executable and what I supplied is not an executable but source code? Then again perhaps I am wrong if so please explain.


  • Lifetime Qt Champion

    @Denni Then I don't understand what the problem is.
    You stated: "this works except you will have to make an images directory and put the 3 images you want to use within it". So, I thought that the problem was to provide the images along the application. If this is not the actual issue then please explain better what the issue is.



  • I said "while it kind of works" ... if you had ran it you would have seen what the true issue was. I thought simply looking at the code would be sufficient my bad.

    To clarify the program works but it fails to implement the proper implementation of the toolbar. Instead of displaying the icons with their code behind trigger it shows the string of the name of that object with no code behind trigger. Note I have also included a static proper implementation of the toolbar but have re-marked it out it is there in case you want to see what I am trying to implement dynamically

    What I am attempting to do is implement the icon with code-behind trigger dynamically which means I need to be able to implement the object that contains that dynamically which is not what is occurring. I have the name of the object but as the comment states -- how do I go about converting the name of the object into the actual object and/or is there another way to go about doing this that I am not aware of.



  • Okay a suggestion which would not work directly from someone else on a different forum got me thinking down a different path and I was able to figure out a viable solution. Granted, while it might not be as pretty of a solution as I would like it to have been, it will still work and might be the only one that would. So instead of storing a reference to the pointer to the object between instances I am hard-coding (hate doing this) a reference to the name during the creation of that object and storing these two within a dictionary. Then upon retrieving the named layout of the toolbar I use that name-reference to get my object-reference and she works just fine. Below is an edited version of the previous code with the working version in case anyone else is interested in doing this.

    import sys
    
    from PyQt5.QtCore    import *
    from PyQt5.QtGui     import *
    from PyQt5.QtWidgets import *
    
    class CenterPanel(QWidget):
        def __init__(self, MainWin):
            QWidget.__init__(self)
            self.MainWin = MainWin
    
            CntrPane = QSplitter(Qt.Horizontal, self)
            CntrPane.addWidget(QTextEdit())
            CntrPane.addWidget(QTextEdit())
            CntrPane.setSizes([75,200])
    
            hbox = QHBoxLayout(self)
            hbox.addWidget(CntrPane)
    
            self.setLayout(hbox)
    
        @property
        def MainWin(self):
            return self.__parent
    
        @MainWin.setter
        def MainWin(self, value):
            self.__parent = value
    
    class MenuToolBar(QDockWidget):
        def __init__(self, MainWin):
            QDockWidget.__init__(self)
            self.MainWin = MainWin
            self.MainMenu = MainWin.menuBar()
            
            self.MenuActRef = {'NewFileAct':0,
                               'OpnFileAct':0,
                               'SavFileAct':0}
    
            # ******* Create the File Menu *******
            self.FileMenu  = self.MainMenu.addMenu('File')
    
            self.NewFileAct = QAction(QIcon('img/new.png'), 'New File', self)
            self.NewFileAct.setShortcut("Ctrl+N")
            self.NewFileAct.setStatusTip('Create a New Project File')
            self.NewFileAct.triggered.connect(self.NewProjFile)
            self.MenuActRef['NewFileAct'] = self.NewFileAct
    
            # ******* Create File Menu Items *******
            self.OpnFileAct = QAction(QIcon('img/open.png'), 'Open File', self)
            self.OpnFileAct.setShortcut("Ctrl+O")
            self.OpnFileAct.setStatusTip('Open an Existing Project File')
            self.OpnFileAct.triggered.connect(self.OpenProjFile)
            self.MenuActRef['OpnFileAct'] = self.OpnFileAct
    
            self.SavFileAct = QAction(QIcon('img/save.png'), 'Save File', self)
            self.SavFileAct.setShortcut("Ctrl+S")
            self.SavFileAct.setStatusTip('Save Current Project File')
            self.SavFileAct.triggered.connect(self.SaveProjFile)
            self.MenuActRef['SavFileAct'] = self.SavFileAct
    
            # ******* Setup the File Menu *******
            self.FileMenu.addAction(self.NewFileAct)
            self.FileMenu.addSeparator()
            self.FileMenu.addAction(self.OpnFileAct)
            self.FileMenu.addSeparator()
            self.FileMenu.addAction(self.SavFileAct)
    
            self.InitToolBar(MainWin)
    
        def InitToolBar(self, MainWin):
            # Dynamically Add Items to the Toolbar
            self.mainToolBar = MainWin.addToolBar("Quick Access")
    
            # This represents reading these values in via a Query
            NewToolBarLayout = {0:'NewFileAct',
                                1:'Spacer',
                                2:'OpnFileAct',
                                3:'Spacer',
                                4:'SavFileAct'}
    
            for idx in NewToolBarLayout:
                item = NewToolBarLayout[idx]
    
                if item == 'Spacer':
                    self.mainToolBar.addSeparator()
                
                else:
                    self.mainToolBar.addAction(self.MenuActRef[item])  
    
        def NewProjFile(self):
            print("Added New Project File")
    
        def OpenProjFile(self):
            print("Opened Existing Project File")
    
        def SaveProjFile(self):
            print("Saved Current Project File")
    
    class Window(QMainWindow):
        def __init__(self, parent=None):
            super(Window, self).__init__(parent)
    
            self.title = 'New Project'
            self.LeftEdge  = 100
            self.TopEdge   = 100
            self.WinWidth  = 800
            self.WinHeight = 600
    
            self.setWindowTitle(self.title)
            self.setGeometry(self.LeftEdge, self.TopEdge, self.WinWidth, self.WinHeight)
            self.CenterPane = CenterPanel(self)
            self.setCentralWidget(self.CenterPane)
            self.MenuToolBar = MenuToolBar(self)
            self.setStyle(QStyleFactory.create('Cleanlooks'))
    
    if __name__ == '__main__':
        newProj = QApplication([])
    
        GUI = Window()
        GUI.show()
    
        sys.exit(newProj.exec_())
    

Log in to reply