Python Forum
[PyQt] How to dynamically define a ToolBar?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] How to dynamically define a ToolBar?
#1
(Python 3.7 / pyqt5) 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('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)

        # ******* 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.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)

        # ******* 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_())
Reply
#2
I'm not sure to understand what you are trying do to. Do you want to add a pointer for your predefined QActions?

        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():
                self.mainToolBar.addAction(item['addAction'])

            elif 'addSeparator' in item.keys():
                self.mainToolBar.addSeparator()
Reply
#3
Okay to clarify what I am trying to do is get the toolbar to dynamically work just like what I can do statically. I have included the static implementation of the toolbar but re-marked it out and it is commented accordingly following that is what I am trying to do dynamically along with the explanation that I just need to convert the "name of the object" into that object or in other words I want the following:

self.mainToolBar.addAction('self.OpnFileAct') to some how become self.mainToolBar.addAction(self.OpnFileAct)

Currently the former displays just the string in the toolbar while the latter displays the icon w/trigger

Now perhaps what I am trying to do is not the correct way to go about this and there is another way to implement this that I am currently not aware of -- but in short this is what I am trying to do. Basically store the user's customized layout of the toolbar so that I can read it back in the next time the program is run and dynamically create that toolbar --- rather than hard-coding its layout.
Reply
#4
Ok so did you try the example I posted in the last post? All you would have to do is to store a pointer to your QAction into a dict, then add it as needed to your toolbar.
Reply
#5
No I had not noticed a difference however that value changes every time you run the program. For example here are the values of 2 separate back-to-back runs where I have printed out the value of the object in question:

1st Run: <PyQt5.QtWidgets.QAction object at 0x0000017D8D2AC798>
2nd Run: <PyQt5.QtWidgets.QAction object at 0x000002234DBCC798>

As you can see the internal pointer value changes with each instance and I was pretty sure that was the going to be the case when I posed my question as that is basically how pointers to memory locations (or objects in memory) works.

So to answer your question no storing a pointer to a memory location will not work from program instance to program instance as that value will change for each instance of a that program. I would have to somehow actually store the entire object.



Okay @Alfalfa while your suggestion would not work directly my response to your suggestion 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_())
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
Question [PyQt] Application desktop toolbar with PyQt6 bunz 4 1,506 Mar-09-2023, 08:09 PM
Last Post: bunz

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020