Qt: Dynamically created buttons/popups? Passing argument data to button's original code?

Hey there, I have a previous post here, but didn’t know if I should put this there or not.
USING __import__() for variables - modules from subfolders

@tfox_TD absolutely nailed the answer for me on that previous post. Perhaps you could share how you would approach this, please?

I’ve updated said code but my layout of folders still stands, perhaps check the previous post for that.

Summary:
I am creating a FlowLayout with dynamically created buttons and passing them functions. When it comes to right clicking the button however, I am wondering where I would put that code if not the button’s .py file? I don’t understand how to pass those arguments from one class to the other.

I am creating my button in my ToolKit() class,
I have my functional code for that button in another class in another file.
During creation of the button it is importing that button’s MainFunc() class that has 3 functions: lmb(), mmb(), and rmb() for different mouse clicks.
On Right Mouse Button click (rmb) I would like to have a menu pop up for that button’s code.
That menu would be different depending on the button. So anything from text inputs to radio buttons and check boxes.

How would I go about making that menu code?

Keep it in the button’s rmb()? Or somehow create it dynamically from the UI code? I feel like my only option is from the button’s MainFunc(). But then how would I query that info if the UI exists now in another class? Rather confusing for me.

I’m unsure how to even write some example code for this as the overall concept eludes me.

I mean, I can think of a couple ways to handle this. The one I’d recommend depends on the day of the week and the current mood of my cat :cat2:. But they all come down to “Store the right-click function so you can access it given the button you right-clicked”.


If it was the weekend, and my cat was hungry and meowing at me, I might recommend you just set a custom attribute on the button to the right function
buttonInstance.myCustomRightClick = mainFuncInstance.rmb
Then you would setup a custom context menu (see customContextMenuRequested) on your main widget, and write a simple search in that slot for whatever button is at the point that was right-clicked (I think you’ll have to use QWidget.mapFrom).


If it was Tuesday, and my cat was knocking things off my dresser, you could store a dictionary on your main class like this:
self.rightClicks[buttonInstance] = mainFuncInstance.rmb
Then use a similar search from the prevous example, and just check if the clicked button is in the dictonary


But since it’s still Wednesday, and my cat is chasing things in his sleep, you could do something completely different, and subclass the QPushButton and override the mousePressEvent method so it checks which button was clicked and calls the proper method of the MainFunc instance you store on it in its __init__ method. Then make your button creation code build your custom subclass instead of the normal QPushButton.

Haha so it turns out that Wednesday’s are my jam. As right now I have a custom QPushButton that is calling my method’s MainFunc, but then where would the little popup window be stored? In my MainFunc? If so, how would you check that window’s selected radio buttons/widgets for the data?

class ToolPushButton(QtWidgets.QPushButton):
    """
    Custom QPushButton that adds Left/Middle/Right
    Button functionality.
    """
    def __init__(self, 
				 module=None, 
                 layout=None, 
                 img_path=None, 
                 parent=None):
        
        """
        :param module:
            Module to import.
        :param layout:
            Layout to .addWidget button to.
        :param img_path:
            Directory for button image to be applied.
        :param parent:
            Parent of module.
        """
        
        super(ToolPushButton, self).__init__(parent)
        
        btn_code = import_module(module)
        self.btn_main = btn_code.MainFunc()
        
        if self.btn_main.tooltip:
            self.setToolTip(self.btn_main.tooltip)
        else:
            print("Info: %s has no attribute called: "
				  "'tooltip" % module)
        
        if img_path:
            self.setIcon(QtGui.QIcon(img_path))
            self.setIconSize(self.rect().size())
        
        if layout:
            layout.addWidget(self)
        
    def mousePressEvent(self, mouse_event):
        if mouse_event.button() == QtCore.Qt.LeftButton:
            self.btn_main.lmb()
        if mouse_event.button() == QtCore.Qt.MiddleButton:
            self.btn_main.mmb()
        if mouse_event.button() == QtCore.Qt.RightButton:
            self.btn_main.rmb()

You’ve gotta pass the mouse_event through to the methods in your mousePressEvent that you want to open context menus. Then, in those methods you build and show the context menu like this-ish (I haven’t tested this code, but it’ll hopefully give you a decent idea how to handle this)

    def rmb(self, mouse_event):
        menu = QMenu()
        act1 = QAction("Thing 1", self)
        menu.addAction(act1)
        act1.triggered.connect(self.thing1)
        menu.exec_(self.mapToGlobal(mouse_event.pos()))

    def thing1(self):
        print "right clicked on thing 1"

[Edit]
You asked how you could check the window’s selected radio buttons.
Well, you’d pass those too … Or at least you’d pass something that would let you access that data. Probably the easiest, most future-proof way would be to just pass self along to the button presses as well. That way, as the tool changes and updates, you don’t have to mess with the main UI code, you just access the new widget added to the UI directly.

    def mousePressEvent(self, mouse_event):
        if mouse_event.button() == QtCore.Qt.LeftButton:
            self.btn_main.lmb((mouse_event, self)
        if mouse_event.button() == QtCore.Qt.MiddleButton:
            self.btn_main.mmb(mouse_event, self)
        if mouse_event.button() == QtCore.Qt.RightButton:
            self.btn_main.rmb(mouse_event, self

Thanks for the insight again tFox!

I have decided to do it in a way where I create the dialog I want, in my button’s functional code. That way the UI is created on right click, but is still queried by the button’s functional code without needing to pass any arguments. (at least to my understanding).

Positives, is that I can create unique dialogs/popups per button.
Pro/Con, you have to maintain each button’s dialog separately.