Search code examples
pythonpysideqaction

Dynamically created QActions creating class object


I have a simple pyside QMenu which is being populated with QActions when the application starts. Each menu action represents a class object. How can I create a new instance of the class object based on the Menu Item clicked, and append that new object to a list, which in this example is called

ACTIVE_BAKERS = []

enter image description here

import sys
from PySide import QtGui, QtCore

################################################################################
# Bakers
################################################################################
class Baker(QtGui.QWidget):
    def __init__(self, name):
        self.name = name


class Baker_John(Baker):
    def __init__(self):
        Baker.__init__(self, name='John')


class Baker_Amy(Baker):
    def __init__(self):
        Baker.__init__(self, name='Amy')


class Baker_Makela(Baker):
    def __init__(self):
        Baker.__init__(self, name='Makela')


class Baker_Jeff(Baker):
    def __init__(self):
        Baker.__init__(self, name='Jeff')


################################################################################
# Action
################################################################################
class MyAction(QtGui.QAction):

    on_action = QtCore.Signal(dict)

    def __init__(self, user_info, *args, **kwargs):
        super(MyAction, self).__init__(*args, **kwargs)
        self.ui = user_info
        self.triggered.connect(self.on_triggered)

    def on_triggered(self):
        print('UI:', self.ui)
        self.on_action.emit(self.ui)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.resize(200, 300)

        # OBJECTS - variable containing list of class objects created
        ACTIVE_BAKERS = []

        # CONTROLS
        self.ui_items = QtGui.QListView()
        self.ui_items.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.ui_items.customContextMenuRequested.connect(self.open_tasks_contextmenu)
        self.setCentralWidget(self.ui_items)
        self.create_context_menu_ui()


    # dynamically create the menu
    def create_context_menu_ui(self):
        self.add_baker = QtGui.QMenu("Add")

        AVAILABLE_BAKERS = [Baker_John(), Baker_Amy(), Baker_Makela(), Baker_Jeff()]
        for x in AVAILABLE_BAKERS:
            new_action = MyAction(x, x.name, self)
            self.add_baker.addAction(new_action)

        self._cmenu = QtGui.QMenu()
        self._cmenu.addMenu(self.add_baker)


    def open_tasks_contextmenu(self, position):
        self._cmenu.exec_(QtGui.QCursor.pos())


def main():
    app = QtGui.QApplication(sys.argv)
    ex = MainWindow()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Solution

  • the exec_() method of QMenu returns the selected QAction, through that QAction that is a MyAction that has as attribute ui that gives us the associated Barker object, using the Barker we can access the class through __class__ and create another one object:

    class MainWindow(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            self.resize(200, 300)
    
            # OBJECTS - variable containing list of class objects created
            self.ACTIVE_BAKERS = []
    
            # CONTROLS
            self.ui_items = QtGui.QListView()
            self.ui_items.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            self.ui_items.customContextMenuRequested.connect(self.open_tasks_contextmenu)
            self.setCentralWidget(self.ui_items)
            self.create_context_menu_ui()
    
    
        # dynamically create the menu
        def create_context_menu_ui(self):
            self.add_baker = QtGui.QMenu("Add")
    
            AVAILABLE_BAKERS = [Baker_John(), Baker_Amy(), Baker_Makela(), Baker_Jeff()]
            for x in AVAILABLE_BAKERS:
                new_action = MyAction(x, x.name, self)
                self.add_baker.addAction(new_action)
    
            self._cmenu = QtGui.QMenu()
            self._cmenu.addMenu(self.add_baker)
    
    
        def open_tasks_contextmenu(self, position):
            action = self._cmenu.exec_(QtGui.QCursor.pos())
            if isinstance(action, MyAction):
                obj = action.ui.__class__()
                if obj not in self.ACTIVE_BAKERS:
                    self.ACTIVE_BAKERS.append(obj)