I am just trying to send a signal from the class A to class B but it does not work, I don't see the print.
I am certainly doing something wrong but I did know what. Here is a quick code to show you the problem, thank you.
from PyQt5 import QtWidgets, QtCore
import sys
class B(object):
def __init__(self, parent=None):
super(B, self).__init__(parent)
self._initSlot()
def _initSlot(self):
a = A()
a.assetSelectionChanged.connect(self._doSomething)
#QtCore.pyqtSlot()
def _doSomething(self):
print('do something')
class A(QtWidgets.QWidget):
assetSelectionChanged = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(A, self).__init__(parent)
self._initUI()
def _initUI(self):
self.treeWidgetAssets = QtWidgets.QTreeWidget()
for i in range(1, 11, 1):
QtWidgets.QTreeWidgetItem(self.treeWidgetAssets, [str(i)])
self.mainLayout = QtWidgets.QVBoxLayout()
self.setLayout(self.mainLayout)
self.mainLayout.addWidget(self.treeWidgetAssets)
self.treeWidgetAssets.itemSelectionChanged.connect(self.onAssetSelectionChanged)
def onAssetSelectionChanged(self):
self.assetSelectionChanged.emit()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dlg = A()
dlg.show()
sys.exit(app.exec_())
There are various problems with that code:
No instance of B is ever created
direct object subclasses don't require calling super().__init__(), and even if you do that you should certainly not add arbitrary arguments, as they won't be accepted, resulting in a crash
B should inherit from QObject
No new instance should be declared in _initSlot: there already exists one, and the one you're creating there will be immediately garbage collected because its reference is local and will be deleted immediately after _initSlot returns
class B(QtCore.QObject):
def __init__(self, parent=None):
super(B, self).__init__(parent)
self._initSlot()
def _initSlot(self):
if self.parent():
self.parent().assetSelectionChanged.connect(self._doSomething)
#QtCore.pyqtSlot()
def _doSomething(self):
print('do something')
class A(QtWidgets.QWidget):
assetSelectionChanged = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(A, self).__init__(parent)
self._initUI()
self.b = B(self)
# ...
As an variant. I've marked the lines that I changed for you.
import sys
from PyQt5 import QtWidgets, QtCore
class B(QtCore.QObject):
def __init__(self, parent=None):
super(B, self).__init__(parent)
# self._initSlot()
# def _initSlot(self):
# a = A()
# a.assetSelectionChanged.connect(self._doSomething)
#QtCore.pyqtSlot(str)
def _doSomething(self, text):
print(f'do something: clicked -> {text}')
class A(QtWidgets.QWidget):
assetSelectionChanged = QtCore.pyqtSignal(str) # 1. + str
def __init__(self, parent=None):
super(A, self).__init__(parent)
self._initUI()
self.b = B(self) # !!!
self.assetSelectionChanged[str].connect(self.b._doSomething) # 3. + str
def _initUI(self):
self.treeWidgetAssets = QtWidgets.QTreeWidget()
for i in range(1, 11, 1):
QtWidgets.QTreeWidgetItem(self.treeWidgetAssets, [str(i)])
self.mainLayout = QtWidgets.QVBoxLayout()
self.setLayout(self.mainLayout)
self.mainLayout.addWidget(self.treeWidgetAssets)
self.treeWidgetAssets.itemSelectionChanged.connect(self.onAssetSelectionChanged)
def onAssetSelectionChanged(self):
text = self.treeWidgetAssets.selectedItems()[0].text(0)
self.assetSelectionChanged.emit(text) # 2. + text
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dlg = A()
dlg.show()
sys.exit(app.exec_())
Related
I have the following code but it's complaining that I cannot access the UI data from my thread. In my example code below, What is the best way I can access the userInputString value so my threading can run?
self.nameField is a PyQt QLineEdit.
QObject::setParent: Cannot set parent, new parent is in a different thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QWidget::repaint: Recursive repaint detected
import myUI
class MainUIClass(QtGui.QMainWindow, myUI.Ui_MainWindow):
def __init__(self, parent=None):
super(MainUIClass, self).__init__(parent)
self.setupUi(self)
self.startbutton.clicked.connect(self.do_work)
self.workerThread = WorkerThread()
self.connect(self.workerThread, SIGNAL("myThreading()"), self.myThreading, Qt.DirectConnection)
def do_work(self):
self.userInputString = self.nameField.Text()
self.workerThread.start()
def myThreading(self):
if userInputString is not None:
#Do something
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
self.emit(SIGNAL("myThreading()"))
if __name__ == '__main__':
a = QtGui.QApplication(sys.argv)
app = MainUIClass()
app.show()
a.exec_()
Not sure if it's what you need but here is a working QThread exemple using Qt5
import time
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.worker_thread = WorkerThread()
self.worker_thread.job_done.connect(self.on_job_done)
self.create_ui()
def create_ui(self):
self.button = QtWidgets.QPushButton('Test', self)
self.button.clicked.connect(self.start_thread)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
def start_thread(self):
self.worker_thread.gui_text = self.button.text()
self.worker_thread.start()
def on_job_done(self, generated_str):
print("Generated string : ", generated_str)
self.button.setText(generated_str)
class WorkerThread(QtCore.QThread):
job_done = QtCore.pyqtSignal('QString')
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
self.gui_text = None
def do_work(self):
for i in range(0, 1000):
print(self.gui_text)
self.job_done.emit(self.gui_text + str(i))
time.sleep(0.5)
def run(self):
self.do_work()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
test = MainWindow()
test.show()
app.exec_()
I must say it is a very beginner's question. I have read and tried a lot, but still don't understand how Slot+Signal work.
In my following code I want to transfer three variables from MyApp Class into Worker Class, when the button is clicked.
The code does not work.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Worker(QObject):
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
#pyqtSlot(str, str, int)
def onJob(self, strA, strB, int1):
print(strA, strB, int1)
for i in range(40):
print(i)
class MyApp(QWidget):
def __init__(self, parent= None):
super(MyApp, self).__init__(parent)
self.initUI()
def initUI(self):
self.btn = QPushButton("start", self)
self.btn.clicked.connect(self.start)
self.show()
def start(self):
otherClass = Worker()
self.signal = pyqtSignal(str, str, int)
self.signal.emit("foo", "baz", 10)
self.signal.connect(otherClass.onJob)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Your code has the following errors:
A signal must not be declared in any method of the class, it must be on the same level as the methods.
If I send a signal before connecting it to any slot then nobody will listen to the information so the data will be lost, that is, the transmission of data is almost instantaneous.
In the following code I have implemented the necessary modifications to make it work:
class MyApp(QWidget):
signal = pyqtSignal(str, str, int)
def __init__(self, parent= None):
super(MyApp, self).__init__(parent)
self.initUI()
def initUI(self):
self.btn = QPushButton("start", self)
self.btn.clicked.connect(self.start)
self.show()
def start(self):
otherClass = Worker()
self.signal.connect(otherClass.onJob)
self.signal.emit("foo", "baz", 10)
I have the following code but it's complaining that I cannot access the UI data from my thread. In my example code below, What is the best way I can access the userInputString value so my threading can run?
self.nameField is a PyQt QLineEdit.
QObject::setParent: Cannot set parent, new parent is in a different thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QWidget::repaint: Recursive repaint detected
import myUI
class MainUIClass(QtGui.QMainWindow, myUI.Ui_MainWindow):
def __init__(self, parent=None):
super(MainUIClass, self).__init__(parent)
self.setupUi(self)
self.startbutton.clicked.connect(self.do_work)
self.workerThread = WorkerThread()
self.connect(self.workerThread, SIGNAL("myThreading()"), self.myThreading, Qt.DirectConnection)
def do_work(self):
self.userInputString = self.nameField.Text()
self.workerThread.start()
def myThreading(self):
if userInputString is not None:
#Do something
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
self.emit(SIGNAL("myThreading()"))
if __name__ == '__main__':
a = QtGui.QApplication(sys.argv)
app = MainUIClass()
app.show()
a.exec_()
Not sure if it's what you need but here is a working QThread exemple using Qt5
import time
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.worker_thread = WorkerThread()
self.worker_thread.job_done.connect(self.on_job_done)
self.create_ui()
def create_ui(self):
self.button = QtWidgets.QPushButton('Test', self)
self.button.clicked.connect(self.start_thread)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
def start_thread(self):
self.worker_thread.gui_text = self.button.text()
self.worker_thread.start()
def on_job_done(self, generated_str):
print("Generated string : ", generated_str)
self.button.setText(generated_str)
class WorkerThread(QtCore.QThread):
job_done = QtCore.pyqtSignal('QString')
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
self.gui_text = None
def do_work(self):
for i in range(0, 1000):
print(self.gui_text)
self.job_done.emit(self.gui_text + str(i))
time.sleep(0.5)
def run(self):
self.do_work()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
test = MainWindow()
test.show()
app.exec_()
I wrote a few customized widget classes by subclassing QWidget. I created a few customized widgets using them and added them to a QToolBox.
class BaseWidget(QtGui.QWidget):
def __init__(self, parent=None):
# initialize
def mymethod():
pass
class AWidget(BaseWidget):
def __init__(self, parent=None):
# initialize
def mymethod():
print "A"
class BWidget(BaseWidget):
def __init__(self, parent=None):
# initialize
def mymethod():
print "B"
Now I want to loop over all the widgets added to the QToolBox and call a method of these customized widgets:
class toolboxWidget(QtGui.QToolBox):
def __init__(self, parent=None):
super(toolboxWidget, self).__init__(parent=parent)
a = AWidget(self)
b = BWidget(self)
self.addItem(a, "A")
self.addItem(b, "B")
def printMethod(self):
for i in range(self.count()):
self.widget(i).mymethod()
However, since widget() method of QToolBox only returns objects of QWidget type, when calling printMethod() of toolboxWidget object, it gives the following error:
AttributeError: 'QWidget' object has no attribute 'mymethod'.
Is there a way I can convert the QWidget returned by widget() to BaseWidget objects? Thanks.
After fixing all the obvious errors and omissions in your example code, I was able to get it working without any problems.
If the QToolBox.widget method didn't return an instance of one of your BaseWidget subclasses, it would be a bug in PyQt (or sip).
Here is a script that works for me using sip 4.15.4 and PyQt 4.10.3:
from PyQt4 import QtCore, QtGui
class BaseWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
def mymethod(self):
pass
class AWidget(BaseWidget):
def __init__(self, parent=None):
BaseWidget.__init__(self, parent)
def mymethod(self):
print "A"
class BWidget(BaseWidget):
def __init__(self, parent=None):
BaseWidget.__init__(self, parent)
def mymethod(self):
print "B"
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.toolbox = QtGui.QToolBox(self)
a = AWidget(self.toolbox)
b = BWidget(self.toolbox)
self.toolbox.addItem(a, "A")
self.toolbox.addItem(b, "B")
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.printMethod)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.toolbox)
layout.addWidget(self.button)
def printMethod(self):
for i in range(self.toolbox.count()):
self.toolbox.widget(i).mymethod()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 300)
window.show()
sys.exit(app.exec_())
i have a requirement where my execution will happen in threads and i want to emit signal from that thread to main class. below is my example code snippet, where i created the an instance of MainClass and assigned it to temp. where MainClass internally is connected to a thread Thread1 where the actual work will happen. when the signal is emitted from Thread1 i want the signalcalled definition to be executed which is connected from temp
Code snippet
class Thread1(QtCore.QThread):
ThreadSignal = QtCore.pyqtSignal(str)
While True:
#some statments
if Condition:
ThreadSignal.emit('Yes')
else:
ThreadSignal.emit('No')
class MainClass(QtCore.QObject):
MainSignal = QtCore.pyqtSignal(str)
Testinstance = Thread1()
def signalcalled(s):
print s
if __name__=='__main__':
app = AtGui.QApplication(sys.argv)
temp = MainClass()
temp.MainSignal.connect(signalcalled)
sys.exit(app.exec()_)
This should work:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtCore, QtGui
class myThread(QtCore.QThread):
threadSignal = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(myThread, self).__init__(parent)
def run(self):
while True:
if True:
self.threadSignal.emit('Yes')
else:
self.threadSignal.emit('No')
break
class myMainClass(QtCore.QObject):
mainSignal = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(myMainClass, self).__init__(parent)
testInstance = myThread(self)
testInstance.threadSignal.connect(self.mainSignal.emit)
testInstance.start()
def signalcalled(s):
print s
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
main = myMainClass()
main.mainSignal.connect(signalcalled)
sys.exit(app.exec_())
class Thread1(QtCore.QThread):
ThreadSignal = QtCore.pyqtSignal(str)
def __init__(self):
super(myThread, self).__init__()
def run(self):
While True:
#some statments
if Condition:
ThreadSignal.emit('Yes')
else:
ThreadSignal.emit('No')
class MainClass(QtCore.QObject):
MainSignal = QtCore.pyqtSignal(str)
def __init__(self):
super(myThread, self).__init__()
Testinstance = Thread1()
Testinstance.ThreadSignal.connect(MainSignal)
Testinstance.start()
def signalcalled(s):
print s
if __name__=='__main__':
app = AtGui.QApplication(sys.argv)
temp = MainClass()
temp.MainSignal.connect(signalcalled)
sys.exit(app.exec()_)