How do I pass a variable to a QThread object when creating an instance? I'm getting a type error in Qthread.__init__ - qthread

I found this sample code using QThread at https://doc.qt.io/qtforpython/examples/example_widgets__thread_signals.html
I modified the code in an attempt to pass a variable to the thread when instantiated based on info I found here: https://nikolak.com/pyqt-threading-tutorial/
Unfortunately I am getting the following error:
Traceback (most recent call last):
File "C:\Users\joe\python\ThreadTest.py", line 22, in start_thread
instanced_thread = WorkerThread(self, 'c:\testfile.xml')
File "C:\Users\joe\python\ThreadTest.py", line 44, in __init__
QThread.__init__(self, parent)
TypeError: 'PySide6.QtCore.QThread.__init__' called with wrong argument types:
PySide6.QtCore.QThread.__init__(str)
Supported signatures:
PySide6.QtCore.QThread.__init__(Optional[PySide6.QtCore.QObject] = None)
Can someone tell me the proper syntax for passing a variable to a QThread?
I'm using PySide6 and Python 3.9.10.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import sys
from PySide6.QtCore import QObject, QThread, Signal, Slot
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
# Create a basic window with a layout and a button
class MainForm(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("My Form")
self.layout = QVBoxLayout()
self.button = QPushButton("Click me!")
self.button.clicked.connect(self.start_thread)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
# Instantiate and start a new thread
def start_thread(self):
instanced_thread = WorkerThread(self, 'c:\testfile.xml')
instanced_thread.start()
# Create the Slots that will receive signals
#Slot(str)
def update_str_field(self, message):
print(message)
#Slot(int)
def update_int_field(self, value):
print(value)
# Signals must inherit QObject
class MySignals(QObject):
signal_str = Signal(str)
signal_int = Signal(int)
# Create the Worker Thread
class WorkerThread(QThread):
def __init__(self, filename, parent=None):
QThread.__init__(self, parent)
# Instantiate signals and connect signals to the slots
self.signals = MySignals()
self.signals.signal_str.connect(parent.update_str_field)
self.signals.signal_int.connect(parent.update_int_field)
self.filename = filename
def run(self):
# Do something on the worker thread
a = 1 + 1
# Emit signals whenever you want
self.signals.signal_int.emit(a)
self.signals.signal_str.emit("This text comes to Main thread from our Worker thread.")
print(self.filename)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainForm()
window.show()
sys.exit(app.exec())

Related

PyQt5 Qthread Create

AttributeError: type object 'QThread' has no attribute 'create'
Here is my code.
from PyQt5.QtCore import QThread
def fun(num):
print(num)
thread1 = QThread.create(fun)
thread1.start()
But Qt documentation says there is a function called create since Qt 5.10. I am using PyQt5 5.11.3. Someone please help me with this.
You can use worker objects by moving them to the thread using QObject::moveToThread().
from PyQt5.QtCore import QObject, pyqtSignal, QThread, QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QProgressBar, QPushButton
class Worker(QObject):
valueChanged = pyqtSignal(int) # Value change signal
def run(self):
print('thread id ->', int(QThread.currentThreadId()))
for i in range(1, 101):
print('value =', i)
self.valueChanged.emit(i)
QThread.msleep(100)
class Window(QWidget):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.progressBar = QProgressBar(self)
self.progressBar.setRange(0, 100)
layout.addWidget(self.progressBar)
layout.addWidget(QPushButton('Open thread', self, clicked=self.onStart))
# Current thread id
print('main id = ', int(QThread.currentThreadId()))
# Start thread update progress bar value
self._thread = QThread(self)
self._worker = Worker()
self._worker.moveToThread(self._thread) # Move to thread to execute
self._thread.finished.connect(self._worker.deleteLater)
self._worker.valueChanged.connect(self.progressBar.setValue)
def onStart(self):
print('main id -> ', int(QThread.currentThreadId()))
self._thread.start() # Start thread
QTimer.singleShot(1, self._worker.run)
def closeEvent(self, event):
if self._thread.isRunning():
self._thread.quit()
del self._thread
del self._worker
super(Window, self).closeEvent(event)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Window()
w.show()
w.setWindowTitle('Demo moveToThread')
sys.exit(app.exec_())

Get application quit signal before windows are closed in QT

I have a Qt Application with multiple main windows and want to get a signal when the App is about to quit while all windows are still up.
The QApplication.aboutToQuit signal doesn't work for me because it only fires after all windows have been closed.
All other answers I've seen suggest implementing the main window's closeEvent() function, but I have multiple main windows and I couldn't find any way to differentiate CloseEvents between a normal closeEvent of just a single window being closed and a close event from when the whole application is closing after QMD+Q or the using clicking quit in the task bar or for whatever reason it quits.
How do I get a signal for that only when the whole application is about to quit but before any windows are closed?
What happens when I press Cmd+Q or right click quit the task bar icon:
(I want a signal at this point) <-
all windows get a closeEvent()
aboutToQuit() fires
app quits
What I want is to get a signal before any of that happens and all the windows are still open.
Edit: minimal example
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget
class Win(QWidget):
def closeEvent(self, event: QCloseEvent):
# implementing a custom closeEvent doesn't help me
# this is called for every normal manual window close and when the application quits
# I only want to run something when the full application gets shut down, not just this window
# can't see any way to differentiate between the two
print("closeEvent", event.type(), event)
return super().closeEvent(event)
if __name__ == '__main__':
app = QApplication([])
app.aboutToQuit.connect(lambda :print("about to quit, this is too late, by now all windows are closed"))
#What I need
# app.beforeQuitSignal.connect(lambda :print("signal that it's gonna quit, before any of the windows are closed"))
#stuff I've tried that didn't work either
app.quit = lambda *args:print("quit, doesnt get called")
app.exit = lambda *args:print("exit, doesnt get called")
app.closeAllWindows = lambda *args:print("closeAllWindows, doesnt get called")
mainwins = []
for i in range(5):
win = Win()
win.setGeometry(100*i,100*i, 400,400), win.show(), win.raise_()
mainwins.append(win)
app.exec_()
If you want to fire a signal when the user close a window but do not exit the QApplication immediately after the last window is closed, you can use QApplication.setQuitOnLastWindowClosed()
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget
class Win(QWidget):
closing = pyqtSignal()
def closeEvent(self, event: QCloseEvent):
print("Window {} closed".format(self))
self.closing.emit()
return super().closeEvent(event)
class MyApp(QApplication):
def __init__(self, *args):
super(MyApp, self).__init__(*args)
self.setQuitOnLastWindowClosed(False)
self.lastWindowClosed.connect(self.onLastClosed)
self.mainwins = []
for i in range(5):
win = Win()
win.setGeometry(100 * i, 100 * i, 400, 400), win.show(), win.raise_()
self.mainwins.append(win)
def onLastClosed(self):
print("Last windows closed, exiting ...")
self.exit()
if __name__ == '__main__':
app = MyApp([])
app.exec_()
With this, when a window is closed, a closing() signal is emited
You can filter on the CloseEvent if it is spontaneous :
from PyQt5.QtCore import pyqtSignal, QEvent
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget
class Win(QWidget):
closing = pyqtSignal()
def closeEvent(self, event: QCloseEvent):
self.closing.emit()
if event.type() == QEvent.Close and event.spontaneous():
event.ignore()
else:
return super().closeEvent(event)
class MyApp(QApplication):
def __init__(self, *args):
super(MyApp, self).__init__(*args)
self.mainwins = []
for i in range(5):
win = Win()
win.closing.connect(self.beforeQuit)
win.setGeometry(100 * i, 100 * i, 400, 400), win.show(), win.raise_()
self.mainwins.append(win)
def beforeQuit(self):
print("Exiting")
# Do what you want here ...
self.exit() # Terminate the QApplication
if __name__ == '__main__':
app = MyApp([])
app.exec_()
Use installEventFilter to intercept the application's quit event before any windows are closed:
class AppManager(QObject):
def __init__(self):
super().__init__()
qApp.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QEvent.Quit:
self.saveWindowStates()
return False
def saveWindowStates(self):
# code to save the application's state

PyQt: connecting slots with custom objects via Qt Designer

I have a PyQt widget that sends signals with numpy.ndarray data. And I have another PyQt widget that has a slot with numpy.ndarray data.
Both widget are located on my main window, that is compiled from *.ui file. The widgets are set as promoted widgets.
Cannot I somehow connect the signal and slot in Qt Creator?
Just now it gives me the next error:
TypeError: C++ type 'ndarray' is not supported as a slot argument type
Reason for this is Qt only support the datatype defined in QMetaType passed as argument of signal, looks here http://pyqt.sourceforge.net/Docs/PyQt4/qmetatype.html#Q_DECLARE_METATYPE
According to ekhumoro's POST, I update the following code, it should work for PyQt4, not tested on PyQt5.
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from numpy import *
class MyThread(QThread):
mysignal = pyqtSignal(ndarray)
def __init__(self, parent=None):
super(MyThread, self).__init__(parent)
def run(self):
while True:
QThread.msleep(100)
self.mysignal.emit(array((1, 2, 3, 4)))
class MyWidget(QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.thread = MyThread()
self.thread.mysignal.connect(self.handleSignal)
self.thread.start()
def handleSignal(self, data):
print data
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = MyWidget()
w.show()
app.exec_()

Passing exception to slot while emitting signal

I have to pass what exception occured in one function to a slot inside a different class.
_qObject = QtCore.QObject()
except Exception, ex:
QtCore.QObject.emit(_qObject, QtCore.SIGNAL("error_occured"))
I want to pass ex to class that has
QtCore.QObject.connect(_qObject, QtCore.SIGNAL("error_occured"), self.__errorOccured)
This is my case
from PyQt4.QtCore import pyqtSignal, pyqtSlot
from PyQt4.QtGui import QWidget, QApplication
has_error = pyqtSignal(Exception)
class SomeOtherClass(QWidget):
# this is my UI class
def __init__(self, parent=None):
super(SomeOtherClass, self).__init__(parent)
# Initialise the Class and connect signal to slot
has_error.connect(self.thrown_error)
#pyqtSlot(Exception)
def thrown_error(self, my_err):
#Do Stuff with the Exception
print(type(my_err), my_err)
self.close()
def makeError():
try:
print 1/0
except ZeroDivisionError, ze:
has_error.emit(ze)
app = QApplication([])
SomeOtherClass()
See below code for example:
from PyQt4.QtCore import pyqtSignal, pyqtSlot
from PyQt4.QtGui import QWidget, QApplication
import sys
class SomeClass(QWidget):
# Declare a new signal - passes Exception
has_error = pyqtSignal(Exception)
def __init__(self, parent=None):
super(SomeClass, self).__init__(parent)
def run_something(self):
#Force an Error
try:
1 / 0
except ZeroDivisionError as ze:
#Emit the Signal
self.has_error.emit(ze)
class SomeOtherClass(QWidget):
def __init__(self, parent=None):
super(SomeOtherClass, self).__init__(parent)
# Initialise the Class and connect signal to slot
class1 = SomeClass()
class1.has_error.connect(self.thrown_error)
class1.run_something()
#pyqtSlot(Exception)
def thrown_error(self, my_err):
#Do Stuff with the Exception
print(type(my_err), my_err)
app = QApplication(sys.argv)
SomeOtherClass()
See the new way to connect signals to slots
How Shadow9043 suggested is also right but in comments things got twisted but this is what I found the correct and simplistic approach.
Long Story in Short!!!
except Exception, ex:
QtCore.QObject.emit(_qObject, QtCore.SIGNAL("error_occured"), str(ex))
and in the class where I connect this exception
i will take this ex string as argument
def __errorOccured(self, exStr):
print exStr
Here is how I have it working, if anyone has got better suggestion in how I achieved it please comment.
from PyQt4.QtCore import pyqtSignal, pyqtSlot
from PyQt4.QtGui import QWidget, QApplication
from PyQt4 import QtCore
import sys
_qObject = QtCore.QObject()
class SomeOtherClass(QWidget):
# this is my UI class
def __init__(self, parent=None):
super(SomeOtherClass, self).__init__(parent)
# Initialise the Class and connect signal to slot
QtCore.QObject.connect(_qObject, QtCore.SIGNAL("error_occured"), self.thrown_error)
def thrown_error(self, my_err):
#Do Stuff with the Exception
print(type(my_err), my_err)
def makeError():
try:
print 1/0
except ZeroDivisionError, ex:
QtCore.QObject.emit(_qObject, QtCore.SIGNAL("error_occured"), str(ex))
app = QApplication(sys.argv)
win = SomeOtherClass()
makeError()
win.show()
sys.exit(app.exec_())

Single QTreeWidgetItem instance on multiple QTreeWidget?

I'm trying to show a single QTreeWidgetItem instance on 2 QTreeWidgets, which ends up the item shown only on the 1st tree without being notified. I haven't seen its API doc talks about a limitation if any. Is there a way to workaround?
#!/usr/bin/python
import os
import sys
from PySide.QtCore import QFile
from PySide.QtUiTools import QUiLoader
from PySide.QtGui import QApplication, QTreeWidget, QTreeWidgetItem, QWidget
class MyTreeWidgetItem(QTreeWidgetItem):
def __init__(self, *args):
super(MyTreeWidgetItem, self).__init__()
class MyWidget(QWidget):
def __init__(self, *args):
super(MyWidget, self).__init__()
loader = QUiLoader()
file = QFile('./src/prove_qtreewidget/qtree_mainwidget.ui')
file.open(QFile.ReadOnly)
self.widget_ui = loader.load(file, self)
file.close()
item1 = MyTreeWidgetItem(self)
item1.setText(0, 'Item 1')
_tw1 = self.widget_ui.findChild(QTreeWidget, '_tree_widget_1')
_tw2 = self.widget_ui.findChild(QTreeWidget, '_tree_widget_2')
_tw1.addTopLevelItem(item1)
_tw2.addTopLevelItem(item1)
if __name__ == '__main__':
print("Running in " + os.getcwd() + " .\n")
app = QApplication(sys.argv)
win = MyWidget()
win.show()
app.exec_()
.ui file above is available here.
Using Qt 4.8, Ubuntu 12.04
I haven't tried PyQt binding but I just assume shouldn't be any different w/o proof.
What you need is a model and a QTreeView, that's for what they are for:
Model/View Programming: Widgets do not maintain internal data containers. They access external data through a standardized interface and therefore avoid data duplication.

Resources