Qt: Dead lock detected – Sender is QPushButton(), receiver is PyQtSlotProxy() – for specific indexes in a QPushButton list
Question:
I am new to pyqt, and I tried to make an application window that contains a list of buttons that are able to toggle a different window. Since I want the number of these buttons to be of a varying quantity, I created a list of QPushButton elements for iterating over them, creating as many as defined by the length of the list, nevertheless I noticed a very weird behavior :
The following code …
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
"""
This "window" is a QWidget. If it has no parent,
it will appear as a free-floating window.
"""
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window % d" % randint(0, 100))
layout.addWidget(self.label)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,windows):
super().__init__()
self.windows=[]
self.buttons=[]
l=QtWidgets.QVBoxLayout()
for i in range(len(windows)):
window=AnotherWindow()
self.windows.append(window)
button=QtWidgets.QPushButton(f'window {windows[i]}')
print(i," ",button)
self.buttons.append(button)
self.buttons[i].clicked.connect(self.toggle_window,i)
l.addWidget(self.buttons[i])
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
print(len(self.windows))
def toggle_window(self,i):
if self.windows[i].isVisible():
self.windows[i].hide()
else:
self.windows[i].show()
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
windows=[0,1,2,3]
windows=[str(i) for i in windows]
print(windows)
w = MainWindow(windows)
w.show()
app.exec()
produced the following error but only when the 4rth button (window 3) is pressed.
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is QPushButton( ... ), receiver is PyQtSlotProxy( ... )
In effort to validate the code, I tried to narrow the list into a linear declaration of a static number of QPushButton instances, indicating that the issue occurs, only when I try to put the buttons on a list. For instance, the following script does not present any similar unpredictable behavior:
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
"""
This "window" is a QWidget. If it has no parent,
it will appear as a free-floating window.
"""
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window % d" % randint(0, 100))
layout.addWidget(self.label)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.window0 = AnotherWindow()
self.window1 = AnotherWindow()
self.window2 = AnotherWindow()
self.window3 = AnotherWindow()
l = QtWidgets.QVBoxLayout()
button0 = QtWidgets.QPushButton("window 0")
button0.clicked.connect(self.toggle_window0)
l.addWidget(button0)
button1 = QtWidgets.QPushButton("window 1")
button1.clicked.connect(self.toggle_window1)
l.addWidget(button1)
button2 = QtWidgets.QPushButton("window 2")
button2.clicked.connect(self.toggle_window2)
l.addWidget(button2)
button3 = QtWidgets.QPushButton("window 3")
button3.clicked.connect(self.toggle_window3)
l.addWidget(button3)
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def toggle_window0(self, checked):
if self.window0.isVisible():
self.window0.hide()
else:
self.window0.show()
def toggle_window1(self):
if self.window1.isVisible():
self.window1.hide()
else:
self.window1.show()
def toggle_window2(self):
if self.window2.isVisible():
self.window2.hide()
else:
self.window2.show()
def toggle_window3(self, checked):
if self.window3.isVisible():
self.window3.hide()
else:
self.window3.show()
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()
To test it further, I extended the list to a list of random lengths (more than 10), where I reassured that the issue persist for specific indexes each time. For example if I create 20 buttons using the first approach, the same bug appears for – the 4rth, the 12fth and the last index exclusively – but not for the rest of them. I even tested it on a different machine. Having also searched in forums, I could not find a solution.
Do I do anything completely wrong here? Does anyone understands better to indicate why is this happening?
I kindly thank you in advance!
Environment: Ubuntu 22.04
Pyqt version : 1.9 (under conda)
Answers:
Your problem is the following:
self.buttons[i].clicked.connect(self.toggle_window,i)
You are passing i
as second argument to connect and expect the toggle_window
function to be called with this argument. This is not happening. In toggle_window, i
will always be False
. See musicamente’s comment regarding what this second argument to connect does.
What you should do instead is connect the button click to a function of your window object. From there, you can of course do a callback to a function of your main window as illustrated below:
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
def __init__(self, parent, i):
super().__init__()
self.parent = parent
self.i = i
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window {}".format(i))
layout.addWidget(self.label)
self.setLayout(layout)
def toggle(self):
print("Toggling windows {}".format(self.i))
if self.isVisible():
self.hide()
self.parent.window_toggled(self.i, False)
else:
self.show()
self.parent.window_toggled(self.i, True)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, windows):
super().__init__()
self.windows=[]
self.buttons=[]
l=QtWidgets.QVBoxLayout()
for i,title in enumerate(windows):
window=AnotherWindow(self, i)
self.windows.append(window)
button=QtWidgets.QPushButton(title)
button.clicked.connect(window.toggle)
l.addWidget(button)
self.buttons.append(button)
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def window_toggled(self, i, visible):
print("Window {} is now {}".format(i, "visible" if visible else "hidden"))
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
windows = ["window {}".format(i) for i in range(12)]
w = MainWindow(windows)
w.show()
app.exec()
I am new to pyqt, and I tried to make an application window that contains a list of buttons that are able to toggle a different window. Since I want the number of these buttons to be of a varying quantity, I created a list of QPushButton elements for iterating over them, creating as many as defined by the length of the list, nevertheless I noticed a very weird behavior :
The following code …
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
"""
This "window" is a QWidget. If it has no parent,
it will appear as a free-floating window.
"""
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window % d" % randint(0, 100))
layout.addWidget(self.label)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,windows):
super().__init__()
self.windows=[]
self.buttons=[]
l=QtWidgets.QVBoxLayout()
for i in range(len(windows)):
window=AnotherWindow()
self.windows.append(window)
button=QtWidgets.QPushButton(f'window {windows[i]}')
print(i," ",button)
self.buttons.append(button)
self.buttons[i].clicked.connect(self.toggle_window,i)
l.addWidget(self.buttons[i])
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
print(len(self.windows))
def toggle_window(self,i):
if self.windows[i].isVisible():
self.windows[i].hide()
else:
self.windows[i].show()
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
windows=[0,1,2,3]
windows=[str(i) for i in windows]
print(windows)
w = MainWindow(windows)
w.show()
app.exec()
produced the following error but only when the 4rth button (window 3) is pressed.
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is QPushButton( ... ), receiver is PyQtSlotProxy( ... )
In effort to validate the code, I tried to narrow the list into a linear declaration of a static number of QPushButton instances, indicating that the issue occurs, only when I try to put the buttons on a list. For instance, the following script does not present any similar unpredictable behavior:
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
"""
This "window" is a QWidget. If it has no parent,
it will appear as a free-floating window.
"""
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window % d" % randint(0, 100))
layout.addWidget(self.label)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.window0 = AnotherWindow()
self.window1 = AnotherWindow()
self.window2 = AnotherWindow()
self.window3 = AnotherWindow()
l = QtWidgets.QVBoxLayout()
button0 = QtWidgets.QPushButton("window 0")
button0.clicked.connect(self.toggle_window0)
l.addWidget(button0)
button1 = QtWidgets.QPushButton("window 1")
button1.clicked.connect(self.toggle_window1)
l.addWidget(button1)
button2 = QtWidgets.QPushButton("window 2")
button2.clicked.connect(self.toggle_window2)
l.addWidget(button2)
button3 = QtWidgets.QPushButton("window 3")
button3.clicked.connect(self.toggle_window3)
l.addWidget(button3)
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def toggle_window0(self, checked):
if self.window0.isVisible():
self.window0.hide()
else:
self.window0.show()
def toggle_window1(self):
if self.window1.isVisible():
self.window1.hide()
else:
self.window1.show()
def toggle_window2(self):
if self.window2.isVisible():
self.window2.hide()
else:
self.window2.show()
def toggle_window3(self, checked):
if self.window3.isVisible():
self.window3.hide()
else:
self.window3.show()
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()
To test it further, I extended the list to a list of random lengths (more than 10), where I reassured that the issue persist for specific indexes each time. For example if I create 20 buttons using the first approach, the same bug appears for – the 4rth, the 12fth and the last index exclusively – but not for the rest of them. I even tested it on a different machine. Having also searched in forums, I could not find a solution.
Do I do anything completely wrong here? Does anyone understands better to indicate why is this happening?
I kindly thank you in advance!
Environment: Ubuntu 22.04
Pyqt version : 1.9 (under conda)
Your problem is the following:
self.buttons[i].clicked.connect(self.toggle_window,i)
You are passing i
as second argument to connect and expect the toggle_window
function to be called with this argument. This is not happening. In toggle_window, i
will always be False
. See musicamente’s comment regarding what this second argument to connect does.
What you should do instead is connect the button click to a function of your window object. From there, you can of course do a callback to a function of your main window as illustrated below:
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
def __init__(self, parent, i):
super().__init__()
self.parent = parent
self.i = i
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window {}".format(i))
layout.addWidget(self.label)
self.setLayout(layout)
def toggle(self):
print("Toggling windows {}".format(self.i))
if self.isVisible():
self.hide()
self.parent.window_toggled(self.i, False)
else:
self.show()
self.parent.window_toggled(self.i, True)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, windows):
super().__init__()
self.windows=[]
self.buttons=[]
l=QtWidgets.QVBoxLayout()
for i,title in enumerate(windows):
window=AnotherWindow(self, i)
self.windows.append(window)
button=QtWidgets.QPushButton(title)
button.clicked.connect(window.toggle)
l.addWidget(button)
self.buttons.append(button)
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def window_toggled(self, i, visible):
print("Window {} is now {}".format(i, "visible" if visible else "hidden"))
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
windows = ["window {}".format(i) for i in range(12)]
w = MainWindow(windows)
w.show()
app.exec()