PyQt signals priority

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

PyQt signals priority

kristof.mulier
Hi,
Is there a way to set a priority on programmatically fired pyqt signals?
I've posted a StackOverflow question about the matter:
https://stackoverflow.com/questions/55442777/pyqt5-how-to-set-a-priority-to-a-pyqtsignal
Please leave your reply on the StackOverflow page (or send it over mail).

Thank you very much :-)
Kind greetings,

Kristof Mulier

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: PyQt signals priority

Phil Thompson-5
On 31 Mar 2019, at 5:26 pm, <[hidden email]> <[hidden email]> wrote:
>
> Hi,
> Is there a way to set a priority on programmatically fired pyqt signals?
> I've posted a StackOverflow question about the matter:
> https://stackoverflow.com/questions/55442777/pyqt5-how-to-set-a-priority-to-a-pyqtsignal
> Please leave your reply on the StackOverflow page (or send it over mail).

You have misunderstood how signals work. Unless you are using a queued connection emitting a signal simply invokes a list of callbacks (ie. the slots).

If your GUI is freezing then your slots are taking too long to run without giving the event loop a chance. Call QCoreApplication.processEvents() in your slot.

Phil
_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: PyQt signals priority

Maurizio Berti
In reply to this post by kristof.mulier
As far as I know, slots are called in the same sequence they're connected to.
But I'm afraid that's not the issue here. As pointed out by Phil and the good and omnipresent eyllanesc on stackoverflow, you're misunderstanding how signal/slot work: once a signal is fired, the "control" is returned only as soon as the slot returns it.

If you've a long/heavy process in the slot, the UI won't react until that process has finished. The only alternative is to use threads (both via Python or QThreads).

In similar scenarios I usually create a subclassed QObject with a Python Queue, create a QThread in the parent object, use object.moveToThread(createdThread), then connect the started() signal of the thread to the "worker" method of the QObject and finally push "messages" to the Queue to let the QObject relate with them.

Let's see a very simple example:

from time import sleep
from Queue import Queue
from PyQt5 import QtCore, QtWidgets

class Worker(QtCore.QObject):
    working = QtCore.Signal(bool)

    def __init__(self):
        QtCore.QObject.__init__(self)
        self.queue = Queue()

    def run(self):
        while True:
            res = self.queue.get(True)
            if res is None:
                break
            self.process(*res)
        print('quitting thread')

    def doCommand(self, cmd, *args):
        self.queue.put((cmd, args))

    def process(self, cmd, *args):
        getattr(self, cmd)(*args)

    def myCommand(self, *args):
        print('doing some long process with {}'.format(*args))
        self.working.emit(True)
        sleep(2)
        print('processing complete')
        self.working.emit(False)

    def quit(self):
        self.queue.put(None)


class Test(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        l = QtWidgets.QGridLayout()
        self.setLayout(l)
        b = QtWidgets.QPushButton('cmd')
        l.addWidget(b)

        q = QtWidgets.QPushButton('quit')
        l.addWidget(q)

        self.worker = Worker()
        b.clicked.connect(lambda: self.worker.doCommand('myCommand', 'Argument 1', 'Argument 2'))
        q.clicked.connect(self.quit)
        self.worker.working.connect(b.setDisabled)
        self.workerThread = QtCore.QThread()
        self.worker.moveToThread(self.workerThread)
        self.workerThread.started.connect(self.worker.run)
        self.workerThread.start()

    def quit(self):
        self.worker.quit()
        QtWidgets.QApplication.quit()


This will show a simple window with two buttons. The latter will just quit the program and the thread with it, which is suggested in most cases (expecially when using external modules, and if that's the case add a custom signal and connect it to the quit() slot of the QThread).
The former button will call some virtually long process (taking 2 seconds in this case) but will not block the user interface; instead, it will send a "working" signal itself, disabling the button to avoid further processing (since the process is in the same thread, further clicking will "queue" the processing) until the process is actually finished.

Some special care is required with the signal connection: signals have to be connected *before* starting the thread, otherwise they won't be received by the QObject (since its thread is already running and waiting for the queue to be filled, thus blocking any other interaction - including signal connection).
If, for any reason, you need to connect signals afterwards, just use the timeout argument of the Queue.get() method to a reasonable amount and customize the "quit" message to put into the queue, leaving the "None" result to continue the while cycle.

Regards,
Maurizio


Il giorno dom 31 mar 2019 alle ore 18:28 <[hidden email]> ha scritto:
Hi,
Is there a way to set a priority on programmatically fired pyqt signals?
I've posted a StackOverflow question about the matter:
https://stackoverflow.com/questions/55442777/pyqt5-how-to-set-a-priority-to-a-pyqtsignal
Please leave your reply on the StackOverflow page (or send it over mail).

Thank you very much :-)
Kind greetings,

Kristof Mulier

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt