pythonpyqt5qt-designerqpushbutton

When a QPushButton is clicked, it fires twice


I used PyQt5 for a project and have the following snippet (button is a QPushButton)

def on_receive(self, query):
    print("receiving", query)
    datapackages = json.loads(query)

    for button, datapackage in zip(self.buttonArray, datapackages):
        self.wire_up_button(datapackage, button) 

def wire_up_button(self, datapackage, button):
    title, songid = datapackage["title"], datapackage["songid"]
    button.setText(title + " (" + str(datapackage["votes"]) + ")")
    button.clicked.connect(lambda: self.upvote(songid))

def upvote(self, sid):
    text = '{"action":"upvote", "value":"' + sid + '"}\n'
    print(text)
    self.send(text)

def send(self, text):
    print("Sending")

The on_receive function is connected to a soccet client and will be called wheneever a data package is received. The layout is a bit complicated because my UI has so many buttons it's handier to iterate over them than to hard-code every single one.

Whenever I click the button, the wire-up function wires the button to the upvote function, which creates a json protocl and sends it to the socket server. However, the wireup-function is called twice per click. (I am certain of this because of the debug print commands). There is no other call in the send function in my program.

I speculate that this might be due to how clicked.connect works (maybe it fires upon click and release).

I used the QtDesigner to create the UI and loaded the .uic in my main.py


Solution

  • Every time you receive anything from socket you do

    for button, datapackage in zip(self.buttonArray, datapackages):
        self.wire_up_button(datapackage, button)
    

    and in self.wire_up_button you connect to button clicked event. Note, that self.buttonArray is always the same list of buttons, so every time on_receive is called you add 1 new subscription to each button click. But previous subscription to button click still exists, so on button click upvote will be called multiple times with different sid. You need to disconnect from button click event before adding new one:

    def wire_up_button(self, datapackage, button):
        try:
            button.clicked.disconnect()
        except:
            pass
        title, songid = datapackage["title"], datapackage["songid"]
        button.setText(title + " (" + str(datapackage["votes"]) + ")")
        button.clicked.connect(lambda: self.upvote(songid))
    

    try ... except block is required, because button.clicked.disconnect() raises exception if no functions were connected to click event.