How to get Qt signals delivered to a pure python thread.

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

How to get Qt signals delivered to a pure python thread.

Ilya Kulakov
I have a python thread that mainly serves a network connection. In this thread (due to the logic/architecture of my app) I need connect to a slot of QApplication clipboard.
Something straightforward like connect does not work because my thread does not have a Qt event loop. However if I connect with a DirectConnect, I'll get my callback called, but sometimes it simply stops to work: no events are delivered.

Is there any utility classes in PyQt to subscribe to Qt events from non-Qt threads?

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

Re: How to get Qt signals delivered to a pure python thread.

Mathias Born
On 07.01.2015, 16:11:36 Ilya Kulakov wrote:
> I have a python thread that mainly serves a network connection. In
> this thread (due to the logic/architecture of my app) I need connect
> to a slot of QApplication clipboard.
> Something straightforward like connect does not work because my
> thread does not have a Qt event loop. However if I connect with a
> DirectConnect, I'll get my callback called, but sometimes it simply
> stops to work: no events are delivered.

> Is there any utility classes in PyQt to subscribe to Qt events from non-Qt threads?

I can't answer your last question, but I'd like to comment about the rest.

http://doc.qt.io/qt-5/qt.html#ConnectionType-enum
clearly describes "DirectConnect" as:

"The slot is invoked immediately when the signal is emitted.
The slot is executed in the signalling thread."

This means your callback is executed in the main thread. I suspect
it accesses objects of the other python thread without proper
synchronization. This may cause subtle multi-threading bugs, where
things "sometimes" happen.

Best Regards,
Mathias




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

Re: How to get Qt signals delivered to a pure python thread.

Alan Ezust-3
You can start up your own QEventLoop in the other thread...


On Wed, Jan 7, 2015 at 1:27 PM,  <[hidden email]> wrote:

> On 07.01.2015, 16:11:36 Ilya Kulakov wrote:
>> I have a python thread that mainly serves a network connection. In
>> this thread (due to the logic/architecture of my app) I need connect
>> to a slot of QApplication clipboard.
>> Something straightforward like connect does not work because my
>> thread does not have a Qt event loop. However if I connect with a
>> DirectConnect, I'll get my callback called, but sometimes it simply
>> stops to work: no events are delivered.
>
>> Is there any utility classes in PyQt to subscribe to Qt events from non-Qt threads?
>
> I can't answer your last question, but I'd like to comment about the rest.
>
> http://doc.qt.io/qt-5/qt.html#ConnectionType-enum
> clearly describes "DirectConnect" as:
>
> "The slot is invoked immediately when the signal is emitted.
> The slot is executed in the signalling thread."
>
> This means your callback is executed in the main thread. I suspect
> it accesses objects of the other python thread without proper
> synchronization. This may cause subtle multi-threading bugs, where
> things "sometimes" happen.
>
> Best Regards,
> Mathias
>
>
>
>
> _______________________________________________
> PyQt mailing list    [hidden email]
> http://www.riverbankcomputing.com/mailman/listinfo/pyqt
_______________________________________________
PyQt mailing list    [hidden email]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: How to get Qt signals delivered to a pure python thread.

Andreas Pakulat
In reply to this post by Ilya Kulakov
Hi,

On Wed, Jan 7, 2015 at 4:11 PM, Ilya Kulakov <[hidden email]> wrote:
I have a python thread that mainly serves a network connection. In this thread (due to the logic/architecture of my app) I need connect to a slot of QApplication clipboard.
Something straightforward like connect does not work because my thread does not have a Qt event loop. However if I connect with a DirectConnect, I'll get my callback called, but sometimes it simply stops to work: no events are delivered.

Is there any utility classes in PyQt to subscribe to Qt events from non-Qt threads?

Delivery of signals to an objects slot when the object lives in another thread requires a Qt event loop running in that thread, you won't get around that if you really need a cross-thread signal/slot connection.

However an alternative is to keep the slot in the main thread and use standard inter-thread communication (Lock/Condition or Queue from python) to signal whatever you need to communicate to the thread.

Qt and PyQt offer some threading API as well (QThread, QMutex, QSemaphore, QFuture) but I think you'd have to use QThread for those to work correctly for your thread (and it sounds like that is also a significant change in your design).

Andreas

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

Re: How to get Qt signals delivered to a pure python thread.

Ilya Kulakov
In reply to this post by Mathias Born
Thank you for all your response!

So you're saying that DirectConnect is perfectly safe when connecting from a thread without qeventloop?

More Qt question rather than pyqt, but is it possible to control run time of an event loop? According to docs, there is just a blocking exec function and it's not possible to run qeventloop for N seconds  (as e.g. Cocoa's CFRunLoop).

Another approach that I tried is to have a proxy qobject that is moved to the main thread just after creation and then uses simple python callbacks to invoke methods of my other-thread object. Is it better than DirectConnect?

Sent from my iPhone

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

Re: How to get Qt signals delivered to a pure python thread.

Giuseppe Corbelli-3
On 08/01/2015 09:00, Ilya Kulakov wrote:
> Thank you for all your response!
>
> So you're saying that DirectConnect is perfectly safe when connecting from
> a  thread without qeventloop?

If you connect a slot of ThreadB to the (ie) QClipboard.changed signal with
DirectConnection, the slot will be called (as direct function call) in the
thread where QClipboard lives in, likely the main thread. Synchronization must
be handled manually.

> More Qt question rather than pyqt, but is it possible to control run time
> of  an event loop? According to docs, there is just a blocking exec function and
> it's not possible to run qeventloop for N seconds (as e.g. Cocoa's CFRunLoop).

Yes, but you can repeatedly call processEvents() in a while loop and
constantly check the elapsed time.

> Another approach that I tried is to have a proxy qobject that is moved to
> the main thread just after creation and then uses simple python callbacks to
> invoke methods of my other-thread object. Is it better than DirectConnect?

Again, synchronization must be handled. You may also consider a mailbox
(synchronized queue). Push data from the main thread and pop from the secondary.

--
             Giuseppe Corbelli
WASP Software Engineer, Copan Italia S.p.A
Phone: +390303666318  Fax: +390302659932
E-mail: [hidden email]
_______________________________________________
PyQt mailing list    [hidden email]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: How to get Qt signals delivered to a pure python thread.

Florian Bruhin
* Giuseppe Corbelli <[hidden email]> [2015-01-08 10:13:50 +0100]:
> On 08/01/2015 09:00, Ilya Kulakov wrote:
> >More Qt question rather than pyqt, but is it possible to control run time
> >of  an event loop? According to docs, there is just a blocking exec function and
> >it's not possible to run qeventloop for N seconds (as e.g. Cocoa's CFRunLoop).
>
> Yes, but you can repeatedly call processEvents() in a while loop and
> constantly check the elapsed time.

processEvents has an overload with a maxTime argument, isn't that
exactly what Ilya is looking for?

    Process pending events that match flags for a maximum of maxTime
    milliseconds, or until there are no more events to process,
    whichever is shorter.

http://doc.qt.io/qt-5/qeventloop.html#processEvents-2

Florian

--
http://www.the-compiler.org | [hidden email] (Mail/XMPP)
   GPG: 916E B0C8 FD55 A072 | http://the-compiler.org/pubkey.asc
         I love long mails! | http://email.is-not-s.ms/

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

attachment0 (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: How to get Qt signals delivered to a pure python thread.

Ilya Kulakov
processEvents has an overload with a maxTime argument, isn't that
exactly what Ilya is looking for?

   Process pending events that match flags for a maximum of maxTime
   milliseconds, or until there are no more events to process,
   whichever is shorter.

http://doc.qt.io/qt-5/qeventloop.html#processEvents-2

Unfortunately it does not work as expected. processEvents should be running _within_ qeventloop. As I understand it's designed to process events during some long term task which do not return execution.
But it requires a blocking qeventloop.exec in the first place which I cannot afford to have.

What I'm looking for, is something like qeventloop.exec(10ms).


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

Re: How to get Qt signals delivered to a pure python thread.

Florian Bruhin
* Ilya Kulakov <[hidden email]> [2015-01-08 16:25:54 +0600]:

> processEvents has an overload with a maxTime argument, isn't that
> > exactly what Ilya is looking for?
> >
> >    Process pending events that match flags for a maximum of maxTime
> >    milliseconds, or until there are no more events to process,
> >    whichever is shorter.
> >
> > http://doc.qt.io/qt-5/qeventloop.html#processEvents-2 <http://doc.qt.io/qt-5/qeventloop.html#processEvents-2>
>
> Unfortunately it does not work as expected. processEvents should be running _within_ qeventloop. As I understand it's designed to process events during some long term task which do not return execution.
> But it requires a blocking qeventloop.exec in the first place which I cannot afford to have.
>
> What I'm looking for, is something like qeventloop.exec(10ms).
Hmm. Couldn't you use a QTimer to call qeventloop.quit after 10ms? Not
sure if that'd work though.

Florian

--
http://www.the-compiler.org | [hidden email] (Mail/XMPP)
   GPG: 916E B0C8 FD55 A072 | http://the-compiler.org/pubkey.asc
         I love long mails! | http://email.is-not-s.ms/

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

attachment0 (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: How to get Qt signals delivered to a pure python thread.

Giuseppe Corbelli-3
In reply to this post by Ilya Kulakov
On 08/01/2015 11:25, Ilya Kulakov wrote:

> processEvents has an overload with a maxTime argument, isn't that
>> exactly what Ilya is looking for?
>>
>>    Process pending events that match flags for a maximum of maxTime
>>    milliseconds, or until there are no more events to process,
>>    whichever is shorter.
>>
>> http://doc.qt.io/qt-5/qeventloop.html#processEvents-2
>
> Unfortunately it does not work as expected. processEvents should be running
> _within_ qeventloop. As I understand it's designed to process events during
> some long term task which do not return execution.
> But it requires a blocking qeventloop.exec in the first place which I cannot
> afford to have.
>
> What I'm looking for, is something like qeventloop.exec(10ms).

Don't understand what "within qeventloop" means. You can create an eventloop
and call processEvents repeatedly instead of exec_().
If you look at qeventloop.cpp QT sources you can see that exec() is based on a
while (!d->exit)
   processEvents(flags | WaitForMoreEvents | EventLoopExec);

--
             Giuseppe Corbelli
WASP Software Engineer, Copan Italia S.p.A
Phone: +390303666318  Fax: +390302659932
E-mail: [hidden email]
_______________________________________________
PyQt mailing list    [hidden email]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: How to get Qt signals delivered to a pure python thread.

Ilya Kulakov

> Don't understand what "within qeventloop" means. You can create an eventloop and call processEvents repeatedly instead of exec_().
> If you look at qeventloop.cpp QT sources you can see that exec() is based on a
> while (!d->exit)
>  processEvents(flags | WaitForMoreEvents | EventLoopExec);

Oh then it looks like I did something wrong… Then if not exec, how QEventLoop registers in the thread?
When it's time to deliver anything to my thread, how Qt would find out where to send them?

As of now I'm using a qobject that proxies communication.

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

Re: How to get Qt signals delivered to a pure python thread.

Giuseppe Corbelli-3
On 08/01/2015 11:59, Ilya Kulakov wrote:

>> Don't understand what "within qeventloop" means. You can create an
>> eventloop and call processEvents repeatedly instead of exec_().
>> If you look at qeventloop.cpp QT sources you can see that exec() is based on a
>> while (!d->exit)
>>   processEvents(flags | WaitForMoreEvents | EventLoopExec);
>
> Oh then it looks like I did something wrong… Then if not exec, how
> QEventLoop registers in the thread?
> When it's time to deliver anything to my thread, how Qt would find out
> where  to send them?

Good question. I don't know.
Guess: event queues for each thread plus some thread-specific data? Sorry,
can't really help you.

--
             Giuseppe Corbelli
WASP Software Engineer, Copan Italia S.p.A
Phone: +390303666318  Fax: +390302659932
E-mail: [hidden email]
_______________________________________________
PyQt mailing list    [hidden email]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt