33

Depending on a conditions I would like to connect/re-connect a button to a different function.

Let's say I have a button:

myButton = QtGui.QPushButton()

For this example let's say I check if there is an internet connection.

if connected == True:
    myButton.clicked.connect(function_A)

elif connected == False:
    myButton.clicked.connect(function_B)

First of all I would like to disconnect a button from any function it was already connected before the button is being re-assigned/re-connected to another function (function_A or function_B). Secondly, I have already noticed that after the button is re-connected it takes an extra click for the button to pick up a new function. After the button is re-connected to another function it still attempts to run a previous function - a function to which a button was connected earlier (before a re-connection). Please advice. Thanks in advance!

EDITED LATER:

It appears a widget's .disconnect() method can be used to disconnect a button from a function it it is connected.

myButton.disconnect()

Unfortunately .disconnect() throws an error if a widget is not connected to any function. To get around it I am using Try/Except. But I would rather use a more elegant solution...

try: myButton.clicked.disconnect() 
except Exception: pass
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
alphanumeric
  • 17,967
  • 64
  • 244
  • 392
  • 1
    what is the exception raised? bit strange. NOte that myButton.disconnect() is different from myButton.clicked.disconnect(), which are you actually referring to? – Oliver Feb 05 '14 at 21:01
  • @Schollii. It raises a `TypeError`. And the exception is not any way strange: in fact, it's highly desirable. The reason why it was introduced, is because the old-style method of connecting and disconnecting signals fails silently, which can hide a lot of potential bugs. In short, raising an exception in just much more pythonic. – ekhumoro Feb 05 '14 at 21:15
  • 7
    Thanks for explanation. I just don't see why a function that is supposed to disconnect() all signals should treat "there are currently no connections" as exceptional behavior. OTOH if you tried disconnect(someSignal) and that signal does not *exist*, then I could see that as being exceptional condition that should be flagged. But anyhoo, it's neither here nor there, thanks for the info. – Oliver Feb 05 '14 at 21:22

3 Answers3

35

If you need to reconnect signals in many places, then you could define a generic utility function like this:

def reconnect(signal, newhandler=None, oldhandler=None):        
    try:
        if oldhandler is not None:
            while True:
                signal.disconnect(oldhandler)
        else:
            signal.disconnect()
    except TypeError:
        pass
    if newhandler is not None:
        signal.connect(newhandler)

...

if connected:
    reconnect(myButton.clicked, function_A)
else:
    reconnect(myButton.clicked, function_B)

(NB: the loop is needed for safely disconnecting a specific handler, because it may have been connected multple times, and disconnect(slot) only removes one connection at a time.).

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • 1
    So if you do `signal.disconnect()` - without giving a `slot` parameter - then this signal will disconnect from *all* its slots? In other words - no need for looping in such case? – K.Mulier Jan 30 '21 at 12:00
  • 2
    @K.Mulier That's [what the docs say](https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#disconnect), so if you see different behaviour, it would be a bug. It's easy enough to test this by inspecting the output of `sender.receivers(sender.signal)`. – ekhumoro Jan 30 '21 at 19:12
10

Try this:

from PyQt4 import QtGui as gui

app = gui.QApplication([])

myButton = gui.QPushButton()

def function_A():
    myButton.clicked.disconnect() #this disconnect all!
    myButton.clicked.connect(function_B)
    print 'function_A'

def function_B():
    myButton.clicked.disconnect(function_B) #this disconnect function_B
    myButton.clicked.connect(function_A)
    print 'function_B'

myButton.clicked.connect(function_A)
myButton.setText("Click me!")
myButton.show()

app.exec_()
Alvaro Fuentes
  • 16,937
  • 4
  • 56
  • 68
  • Do you know how to check if a widget is connected to a function? I found there is .isSignalConnected() widget method. Unfortunately I don't know how to use it... – alphanumeric Feb 05 '14 at 19:51
  • Well you should read [this](http://qt-project.org/forums/viewthread/6820), I think there is no hope, but indeed if you found a way let me know ;) – Alvaro Fuentes Feb 05 '14 at 20:10
0

Concise way for 3.4+ with contextlib.suppress:

with contextlib.suppress(RuntimeError):
    button.clicked.disconnect()
button.connect(func_a if condition else func_b)
johnson
  • 3,729
  • 3
  • 31
  • 32