[PyQt] Non-Modal Dialog

Maurizio Berti maurizio.berti at gmail.com
Tue Oct 8 05:13:09 BST 2019


>
> That didn't change things for me.
>
> ... which is good.  For my test, I require a long-running task that
> locks the GUI.  Both QPixmap *save* and QImage *save* seem to fill the
> bill.
>
> I changed QMessageBox to *exec_* from *show* and fired it from the
> *started* signal of the thread per your example, which seems to be a
> straightforward approach, but that couldn't manage to display anything
> unless the thread paused with a *sleep* first.
>

I *think* that it depends on "where" (as in "which thread") the task occurs.
The issue with QPixmap.save() comes from the fact that, being it not thread
safe, it would lock the main loop.
So, I suppose that your "threaded" function might not be actually thread
safe with Qt's main loop. I think that a good way to find out if it is is
to check
if the GUI actually responds after the QThread.sleep (in the code you gave,
it means testing if the button responds to mouse events while saving).
In my tests, using QPixmap.save it didn't: the "Ok" button didn't react
until
the saving was completed. If that's the case, you might need to do some
kind of introspection about what your function actually does. Keep in mind
that even some "declared" thread safe functions might lock the GUI, even
if run from a separated QThread.
Unfortunately I'm not such an expert about threading, and I cannot give
you a fair and complete answer about that. Sorry.



> Returning to your kind reply....
>
> > - You don't need to return every function if the returned value is
> > not required, as Python implicitly returns None if no explicit
> > return exists
>
> Hah!  You're not the first to be bugged by this habit of mine.

[...]

When I'm writing my own classes, I'll usually make the *set* methods

return *self*, for example.
>

Well, as I wrote, "you don't need": nobody says you couldn't try :-)
As long as you're "comfortable" with your code, do what you want with it.
I'm just saying it because, when analyzing somebody else's code, people
could be annoyed about unnecessary lines. It's just not about "duh, who
cares", but possibly something like "I'm eager to help you, but, please,
don't bugger me with unnecessary code that only distracts me from
actually helping you and finally just ignore it".
When I'm asking out for help I usually spend a *lot* of time making my
code as much as "standard" as possible: it's something that can take
a *long* time to be achieved, but I know that it's also something that
would dramatically increase the possibility of somebody helping me: if
somebody see my code and can understand it on the fly, he/she might
be able to answer me instantly, or, at least, better consider to take some
of his/her time to do so.
I've seen plenty of "custom-styled-code" in both questions and answers
that people would just ignore because they were just too hard to read,
even for the most "meaningless" reason (such as variable namings not
following PEP or framework standards like using capitalized names for
variables or class/instance properties; believe me, that's just confusing,
no matter what you do with your own code).
The result is pretty simple: even if I spend an hour to write a more
readable question, I know that I'm increasing the possibility of someone
answering me (and it goes the same with answers too). The alternative
is that I don't care about it, losing the even slight possibility of a
single
person being able or eager to help me, leaving me without any answer
at all.
And, at the same time and maybe even more important, leaving
somebody else, with the same kind of doubt, without an helpful answer.

Everybody has his/her habits, that's no harm in that. But when you're
asking a question or giving an answer, you cannot expect that
everybody follows your own standards, no matter how they fit you.

[end of my uncalled rant; sorry, but I really think that it's important,
and I know I'm not the only one :-) ]

> - Avoid using object names that already are existing properties or
> > methods (like self.thread)
>
> Oh, heck!
>
> I remember, writing Pascal, where it was fairly common to prefix
> labels to indicate their type: x-strings, i-integers, f-functions, and
> xxx-methods, where xxx was the abbreviated name of the class.  This
> had the virtue of avoiding tracking-over implementor's names during
> subclassing and having your own tracked-over in turn.  In Python, it's
> much more chic to rely on implicit typing, and best practice is to
> avoid such prefixing, and I get into trouble.
>

Well, Python has its ups and downs.
The dynamic typing and its property/attribute are a bless as they're
some kind of a disgrace.
[Un]fortunately, the properties and function/methods of Qt are quite
standardized, but I can understand that there can be some "mishap"
here and there, because they are *a lot* and sometimes you might just
forget about them. Just yesterday I finally found out that I had a bug
with a calendar function because I was using a function named
"moveEvent" (which made sense, as it was necessary to "move" the
start date of a calendar event), until I remembered that "moveEvent"
is a standard QWidget protected function.

Sometimes I even don't care about them myself, as long as I'm sure
that I don't need them, but I try to avoid that practice (especially in
shared code). As a rule of thumb, just really think when you're naming
functions, methods, properties or attributes.
>From time to time you'll get it wrong anyway, but that's just part of the
process :-)

> - You can connect the finished signal directly to the close (or,
> > better, accept) slot of the popup and delete the thread itelf:
> [...]
> That's cool!  Didn't think of that.  The modal dialog can be harnessed
> for non-modal duty.
>
> I dithered about whether to *del* the thread and popup structures.  In
> my extended test suite, they are replaced by new ones for each
> scenario, and presumably the old ones are garbage-collected soon
> thereafter.  Not bothering about garbage collection is one of the
> principal virtues of coding in Python, and I shouldn't have worried
> about trying to show their destruction explicitly.  Aren't explicit
> deletes like *del* or *deleteLater* truely rundundant?
>

Well.
One of the main issues about PyQt is that the Python and "C++"
counterparts of Qt objects are not always "sync'd", and that's due to
the fact that PyQt is a "binding" to "C++" objects.
You can "delete" a python "Qt" object, leaving its C++ counterpart still
existing, and viceversa.
For example:

class SomeObject(QtCore.QObject):
    def __init__(self):
        super(SomeObject, self).__init__()
        child = QtCore.QObject(self)

will garbage collect the "child" *python* object as soon as the function
returns, since it's outside the function's scope, but, technically, the
object still exists as a C++ counterpart:

obj = SomeObject()
print('Object children:', obj.children())
>>> Object children: [<PyQt5.QtCore.QObject object at 0xb21540bc>]

This would be a seriuos issue on the C++ side of your project, but it's
something that (thinking from a Python perspective) we "could" just
ignore.
The same goes on the other way:

class SomeObject(QtCore.QObject):
    def __init__(self):
        super(SomeObject, self).__init__()
        self.child = QtCore.QObject(self)
        self.child.deleteLater()

def showChildren():
    print('self.child: {}'.format(obj.child))
    print('Object children:', obj.children())

import sys
obj = SomeObject()
app = QtWidgets.QApplication(sys.argv)
QtCore.QTimer.singleShot(0, showChildren)
sys.exit(app.exec_())

>>> self.child: <PyQt5.QtCore.QObject object at 0xb218d14c>
Object children: []

In this case the python object still "exists", but its C++ "Qt object"
doesn't.

You can read more about this in here also:
http://enki-editor.org/2014/08/23/Pyqt_mem_mgmt.html

So, finally, I'd suggest you to look into what your separated thread
actually does, because I think that it's possible that you're dealing
with thread-unsafe functions. I had a similar problem just days ago,
when I was trying to deal with a python object run through python's
threading.Thread(). When I implemented a "fake" signal/slot system,
which obviously wasn't really compatible with Qt connection system,
I had to face unexpected behaviour from a looped QAbstractAnimation
descendant (strangely enough, it started to run faster and faster
each time it was started).
As soon as I made that object a QObject, everything worked as
expected, obviously, since its [sub]thread was correctly treated as Qt
would expect.


Cheers,
Maurizio

-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del
cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20191008/63183649/attachment-0001.html>


More information about the PyQt mailing list