[PySide2][Maya] Batch Create and Connect Button Widgets?

Hi,

Is there I can batch create and connect button widgets?
My main usage is

  1. List a scripts of a directory
  2. Create buttons based on those scripts
  3. Connect the buttons where if a button is clicked the corresponding script is executed.

I can manage #1 and #2 but not #3.
In C4D, this was easy since every button creation needs a unique ID. So I just need to mapped the unique ID to a script/command.

In PySide2 Maya, I can’t attached a unique ID to the button. I also can’t create a variable for every button since there are a lot and it would be hard to maintain.

Is there a way around this?

Here is a snippet of the working code:


# Assume these are the list of scripts with the corresponding commands from #1
general_cmds = [
    ("Hello Universe", {"id": 111120, "cmds": 'util.universe.main()'} ),
    ("Hello World", {"id": 111130, "cmds": 'util.print_world.main()'} ),
    ("Hello Milkyway", {"id": 111140, "cmds": 'util.print_milyway.main()'} ),
    ] 

button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch(3)

self.command = None
for command in general_cmds:
    self.command = command
    btn = QtWidgets.QPushButton(command[0])
    button_layout.addWidget(btn) 
    btn.clicked.connect(self.btn_command) 

main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addLayout(button_layout)


# This is my problem 
# The function works but it is stuck with latest self.command loop. 
# So even if I press "Hello Universe" or "Hello World", its always the "Hello Milkway" command that is executed.
def btn_command(self):
    eval (self.command[1]['cmds']) 

You’re looping over general_cmds, and every loop setting self.command = command. That means that the last time through the loop, self.command is set to “Hello Milkyway”. So when the loop is done and you get down to btn_command it will only ever be the last item in the list: “Hello Milkyway”.

And there’s no reason to have btn_command. You can just connect the button directly to the function you want called. That also means you don’t need the ID at all.


button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch(3)

general_cmds = [
    ("Hello Universe", "util.universe.main"),
    ("Hello World", "util.print_world.main"),
    ("Hello Milkyway", "util.print_milyway.main"),
] 

for cmd_text, cmd_func in general_cmds:
    btn = QtWidgets.QPushButton(cmd_text)
    button_layout.addWidget(btn) 
    btn.clicked.connect(eval(cmd_func))

main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addLayout(button_layout)

1 Like

Ah gotcha.
Thanks for the help. Never though binding the clicked.connect directly to the function rather than creating one.

Just an alternative, went to a rabbit hole and found that sender() can also do the job but of course, your code is much more elegant :slight_smile:

For reference, here is the code solution for the sender() route.


       self.general_cmds = {"Hello Universe" : {"id": 111121, "cmds": 'util.universe.main()'}, 
            "Hello World": {"id": 111121, "cmds": 'util.print_world.main()'} }

        for key, value in self.general_cmds.items():
            #self.command = command
            btn = QtWidgets.QPushButton(key)
            button_layout.addWidget(btn) 
            btn.clicked.connect(self.general_command) 

    def general_command(self):

        sender = self.sender()
        key = sender.text()
        eval (self.general_cmds[key]["cmds"])