[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