PyQt6 Connecting a slot to a button in QDialogButtonBox - pyqt6

How to connect slots on QDialogButtonBox in PyQt6? I can connect reject and accept signal, but i can't connected another buttons (e.g. Reset).
class Test(QtWidgets.QDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
uic.loadUi("test.ui", self)
self.buttonBox.accepted.connect(lambda: print(1))
self.buttonBox.rejected.connect(lambda: print(2))
# AttributeError: clicked
self.buttonBox.StandardButton.Reset.clicked.connect(lambda: print(3))

you can use this code
self.buttonBox.button(QDialogButtonBox.Reset).clicked.connect(self.close)

Related

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

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())

PyQt: QThreadPool with QRunnables taking time to quit

I've a class who create QRunnables and start them in a QThreadPool instance.
My threads are working well, but in case user want to quit application, the application takes a long time to stop. Certainly due to the fact that the requests launched take time.
Here is a snippet code of how I use QThreadPool, QRunnables:
import sys
from PyQt5.Qt import QThreadPool, QApplication, QWidget, QVBoxLayout
from PyQt5.Qt import QTimer, QObject, QPushButton, QLabel
from PyQt5.Qt import QRunnable
class BackendQRunnable(QRunnable):
"""
Class who create a QThread to trigger requests
"""
def __init__(self, task):
super(BackendQRunnable, self).__init__()
self.task = task
def run(self):
"""
Run the QRunnable. Trigger actions depending on the selected task
"""
# Here I make long requests
if 'user' in self.task:
self.query_user_data()
elif 'host' in self.task:
self.query_hosts_data()
elif 'service' in self.task:
self.query_services_data()
elif 'alignakdaemon' in self.task:
self.query_daemons_data()
elif 'livesynthesis' in self.task:
self.query_livesynthesis_data()
elif 'history' in self.task:
self.query_history_data()
elif 'notifications' in self.task:
self.query_notifications_data()
else:
pass
#staticmethod
def query_user_data():
"""
Launch request for "user" endpoint
"""
print('Query user data')
#staticmethod
def query_hosts_data():
"""
Launch request for "host" endpoint
"""
print('Query hosts')
#staticmethod
def query_services_data():
"""
Launch request for "service" endpoint
"""
print("Query services")
#staticmethod
def query_daemons_data():
"""
Launch request for "alignakdaemon" endpoint
"""
print('Query daemons')
#staticmethod
def query_livesynthesis_data():
"""
Launch request for "livesynthesis" endpoint
"""
print('query livesynthesis')
#staticmethod
def query_history_data():
"""
Launch request for "history" endpoint but only for hosts in "data_manager"
"""
print('Query history')
#staticmethod
def query_notifications_data():
"""
Launch request for "history" endpoint but only for notifications of current user
"""
print('Query notifications')
class ThreadManager(QObject):
"""
Class who create BackendQRunnable to periodically request on a Backend
"""
def __init__(self, parent=None):
super(ThreadManager, self).__init__(parent)
self.backend_thread = BackendQRunnable(self)
self.pool = QThreadPool.globalInstance()
self.tasks = self.get_tasks()
def start(self):
"""
Start ThreadManager
"""
print("Start backend Manager...")
# Make a first request
self.create_tasks()
# Then request periodically
timer = QTimer(self)
timer.setInterval(10000)
timer.start()
timer.timeout.connect(self.create_tasks)
#staticmethod
def get_tasks():
"""
Return the tasks to run in BackendQRunnable
:return: tasks to run
:rtype: list
"""
return [
'notifications', 'livesynthesis', 'alignakdaemon', 'history', 'service', 'host', 'user',
]
def create_tasks(self):
"""
Create tasks to run
"""
for cur_task in self.tasks:
backend_thread = BackendQRunnable(cur_task)
# Add task to QThreadPool
self.pool.start(backend_thread)
def exit_pool(self):
"""
Exit all BackendQRunnables and delete QThreadPool
"""
# When trying to quit, the application takes a long time to stop
self.pool.globalInstance().waitForDone()
self.pool.deleteLater()
sys.exit(0)
if __name__ == '__main__':
app = QApplication(sys.argv)
thread_manager = ThreadManager()
thread_manager.start()
layout = QVBoxLayout()
label = QLabel("Start")
button = QPushButton("DANGER!")
button.pressed.connect(thread_manager.exit_pool)
layout.addWidget(label)
layout.addWidget(button)
w = QWidget()
w.setLayout(layout)
w.show()
sys.exit(app.exec_())
In function exit_pool, I wait until threads are finished and delete the QThreadPool instance...
Is there a way not to wait for each thread and stop everything directly ?
EDIT Solution:
So I have approached the subject differently. I replaced my QRunnable with a QThread. I removed QThreadPool and I manage threads myself in a list. I also added a pyqtSignal in order to stop the QTimer and close the running threads by quit() function.
Like that all my thread quit without problem.
import sys
from PyQt5.Qt import QThread, QApplication, QWidget, QVBoxLayout
from PyQt5.Qt import QTimer, QObject, QPushButton, QLabel, pyqtSignal
class BackendQThread(QThread):
"""
Class who create a QThread to trigger requests
"""
quit_thread = pyqtSignal(name='close_thread')
def __init__(self, task):
super(BackendQThread, self).__init__()
self.task = task
def run(self):
"""
Run the actions depending on the selected task
"""
# Here I make long requests
if 'user' in self.task:
self.query_user_data()
elif 'host' in self.task:
self.query_hosts_data()
elif 'service' in self.task:
self.query_services_data()
elif 'alignakdaemon' in self.task:
self.query_daemons_data()
elif 'livesynthesis' in self.task:
self.query_livesynthesis_data()
elif 'history' in self.task:
self.query_history_data()
elif 'notifications' in self.task:
self.query_notifications_data()
else:
pass
#staticmethod
def query_user_data():
"""
Launch request for "user" endpoint
"""
print('Query user data')
#staticmethod
def query_hosts_data():
"""
Launch request for "host" endpoint
"""
print('Query hosts')
#staticmethod
def query_services_data():
"""
Launch request for "service" endpoint
"""
print("Query services")
#staticmethod
def query_daemons_data():
"""
Launch request for "alignakdaemon" endpoint
"""
print('Query daemons')
#staticmethod
def query_livesynthesis_data():
"""
Launch request for "livesynthesis" endpoint
"""
print('query livesynthesis')
#staticmethod
def query_history_data():
"""
Launch request for "history" endpoint but only for hosts in "data_manager"
"""
print('Query history')
#staticmethod
def query_notifications_data():
"""
Launch request for "history" endpoint but only for notifications of current user
"""
print('Query notifications')
class ThreadManager(QObject):
"""
Class who create BackendQThread to periodically request on a Backend
"""
def __init__(self, parent=None):
super(ThreadManager, self).__init__(parent)
self.tasks = self.get_tasks()
self.timer = QTimer()
self.threads = []
def start(self):
"""
Start ThreadManager
"""
print("Start backend Manager...")
# Make a first request
self.create_tasks()
# Then request periodically
self.timer.setInterval(10000)
self.timer.start()
self.timer.timeout.connect(self.create_tasks)
#staticmethod
def get_tasks():
"""
Return the available tasks to run
:return: tasks to run
:rtype: list
"""
return [
'notifications', 'livesynthesis', 'alignakdaemon', 'history', 'service', 'host', 'user',
]
def create_tasks(self):
"""
Create tasks to run
"""
# Here I reset the list of threads
self.threads = []
for cur_task in self.tasks:
backend_thread = BackendQThread(cur_task)
# Add task to QThreadPool
backend_thread.start()
self.threads.append(backend_thread)
def stop(self):
"""
Stop the manager and close all QThreads
"""
print("Stop tasks")
self.timer.stop()
for task in self.threads:
task.quit_thread.emit()
print("Tasks finished")
if __name__ == '__main__':
app = QApplication(sys.argv)
layout = QVBoxLayout()
widget = QWidget()
widget.setLayout(layout)
thread_manager = ThreadManager()
start_btn = QPushButton("Start")
start_btn.clicked.connect(thread_manager.start)
layout.addWidget(start_btn)
stop_btn = QPushButton("Stop")
stop_btn.clicked.connect(thread_manager.stop)
layout.addWidget(stop_btn)
widget.show()
sys.exit(app.exec_())
You cannot stop a QRunnable once it's started. However, there are a few simple things you can do to reduce the wait time in your example.
Firstly, you can stop the timer, so that it doesn't add any more tasks. Secondly, you can clear the thread-pool so that it removes any pending tasks. Thirdly, you could try setting a smaller maximum thread count to see whether it still achieves acceptable performance. By default, the thread-pool will use QThread.idealThreadCount() to set the maximum number of threads - which usually means one for each processor core on the system.
A final option is to provide a way to interrupt the code that executes in your runnables. This will only really be possible if the code runs a loop which can periodically check a flag to see if it should continue. In your example, it looks like you could use a single shared class attribute for the flag, since all the tasks call static methods. But if the code is not interruptable in this way, there is nothing else you can do - you will just have to wait for the currently running tasks to finish.

QMdiSubWindow not accepting drops

I have this small QT program:
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import sys
class QtZListView(QtGui.QListView):
def __init__(self, *args, **kwargs):
QtGui.QListView.__init__(self, *args, **kwargs)
self.model = QtGui.QStringListModel(['a','b','c'])
self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.setModel(self.model)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setDragEnabled(True)
def setStringList(self, *args, **kwargs):
return self.model.setStringList(*args, **kwargs)
class mplsubwindow(QtGui.QMdiSubWindow):
def __init__(self, *args, **kwargs):
QtGui.QMdiSubWindow.__init__(self, *args, **kwargs)
self.setWindowTitle("testing")
self.setAcceptDrops(True)
fig = Figure(figsize=(5, 4), dpi=100,
facecolor = self.palette().color(QtGui.QPalette.Background).name()
)
p = FigureCanvas(fig)
self.axes = fig.add_subplot(111)
self.axes.hold(False)
FigureCanvas.setSizePolicy(
self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding
)
FigureCanvas.updateGeometry(self)
fig.tight_layout()
toolbar = NavigationToolbar(p, self)
self.layout().addWidget(toolbar)
self.layout().addWidget(p)
self.resize(400,400)
self.show()
def dragEnterEvent(self, event):
print('entering')
super(mplsubwindow, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
print('drag moving')
super(mplsubwindow, self).dragMoveEvent(event)
def dropEvent(self, event):
print('dropped')
super(mplsubwindow, self).dropEvent(event)
class ExampleApp(QtGui.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
mainwid = QtGui.QWidget()
layout = QtGui.QGridLayout()
mainwid.setLayout(layout)
self.mdiarea = QtGui.QMdiArea()
self.setCentralWidget(mainwid)
layout.addWidget(self.mdiarea)
sub = mplsubwindow(self.mdiarea)
fig = Figure()
p = FigureCanvas(fig)
sub.layout().addWidget(p)
sub.show()
layout.addWidget(QtZListView())
def main():
app = QtGui.QApplication(sys.argv)
form = ExampleApp()
form.show()
app.exec_()
if __name__ == '__main__':
main()
I want to be able to drag one or more items from the list in the bottom into the matplotlib canvas. For some reason only the enter-event is invoked...the remaining drag/drop events seems to be ignored...and furthermore it seems like the QMdiSubWindow does not accept drops even though i set setAcceptDrops(True).
What am I missing here?
You need to accept the event in the dragEnterEvent method or else move and drop events are ignored.
def dragEnterEvent(self, event):
print('entering')
event.accept()
super(mplsubwindow, self).dragEnterEvent(event)
Note that I really want to emphasise you should be adding the widget via QMdiSubWindow.setWidget(). Any of the other methods (like using QMdiSubWindow.layout() or QMdiSubWindow.setCentralWidget()) are not fully supported by MDI windows and will likely lead to other issues down the road. If you feel that setWidget() is not doing what you want, ask a new question detailing the issue so that it can be resolved while still using setWidget().

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_())

Resources