[PyQt] Non-Modal Dialog

Chuck Rhode CRhode at LacusVeris.com
Sun Oct 6 17:18:42 BST 2019


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

In the deep past, I've worked with VB, Tk, Pascal Delphi, and Gtk.  I
seem to recall that all had non-modal dialog boxes for situations where
you wanted to inform the user through the user interface that a
long-running task was in progress.  Is this not done anymore?

I assume there are still long running tasks such as QPixmap *save*s.

These tasks cannot emit progress signals for powering a progress bar.
What alternatives are there?

I can't get a cursor change to show up.

Does one nowadays throw up a semitransparent overlay with a spinner?
That is not so simple or informative as a non-modal dialog, I think.

I've pored over Stackoverflow posts about Qt from the last decade, and
I don't see a lot that I can use.  Most say its as simple as
QMessageBox *open* instead of *exec_*, but this has not been my
experience with PyQt.  Here is code that works, however:

#!/usr/bin/python
# -*- coding: utf-8 -*-

# nonmodal_example.py
# 2019 Oct 06 . ccr

"""Demonstrate how to show a non-modal dialog box before starting a
long-running task.

"""

from __future__ import division
import sys
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QGridLayout,
    QPushButton,
    QMessageBox,
    )
from PyQt5.QtGui import (
    QPixmap,
    QTransform,
    )
from PyQt5.QtCore import(
    QThread,
    )

ZERO = 0
SPACE = ' '
NULL = ''
NUL = '\x00'
NA = -1


class ThdWorker(QThread):

    """An (arbitrarily) long-running task.

    """

    def run(self):
        QThread.sleep(1)
        WIN.pixmap.save('/tmp/nonmodal_test.png')
        return


class WinMain(QMainWindow):

    """The (trivial) main window of the graphical user interface.

    """
    
    def __init__(self):
        super(WinMain, self).__init__()
        self.resize(800, 600)
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)
        self.layout = QGridLayout()
        self.central_widget.setLayout(self.layout)
        self.btn_run = QPushButton('Run Next Test Scenario', self.central_widget)
        self.btn_run.setMaximumSize(200, 30)
        self.btn_run.clicked.connect(self.test_part_1)
        self.layout.addWidget(self.btn_run)
        figure = QPixmap()
        result = figure.load('/usr/share/qt5/doc/qtdesigner/images/designer-screenshot.png')
        if result:
            pass
        else:
            raise NotImplementedError
        transformation = QTransform()
        transformation.scale(5.0, 5.0)
        self.pixmap = figure.transformed(transformation)
        return

    def test_part_1(self):

        """Fork a thread.

        """

        self.popup = QMessageBox(QMessageBox.Information, None, 'This is a test.  Please wait.')
        self.popup.show()
        self.thread = ThdWorker(self)
        self.thread.finished.connect(self.test_part_2)
        self.thread.start()
        return

    def test_part_2(self):
        self.popup.close()
        del self.popup
        del self.thread
        return


if __name__ == "__main__":
    APP = QApplication(sys.argv)
    WIN = WinMain()
    WIN.show()
    result = APP.exec_()
    sys.exit(result)


# Fin

What really gripes me about this example, despite the fact that it
works for me, is the sleep at the beginning of the thread that starts
the long running thread.  The sleep is essential.  Although the
QMessageBox *show* paints its frame, it doesn't have time to paint its
contents unless the thread pauses before it even gets going.

There HAS TO BE a more elegant way to allow non-modal dialogs to paint
completely.  I've tried lots of different Stackoverflow
recommendations, and nothing works.  I have a more complete (and
considerably longer) test suite ready to show you that is available upon
request.

- -- 
.. Be Seeing You,
.. Chuck Rhode, Sheboygan, WI, USA
.. Weather:  http://LacusVeris.com/WX
.. 55° — Wind WSW 10 mph

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iEYEARECAAYFAl2aE+IACgkQYNv8YqSjllJHuwCfW+tQv04X3s8e6jE5gWZPqbeN
kZgAn2nbhXFERp5rmIcEuO6yEvC8+HVF
=bE0d
-----END PGP SIGNATURE-----


More information about the PyQt mailing list