<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p>Actually QPixMap is not only not Thread safe per-sae but elements
      of it are Thread prohibited as are all QtWidgets<br>
    </p>
    <div class="moz-cite-prefix">On 10/6/2019 2:59 PM, Maurizio Berti
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAPn+-XR0--aNj22W0Ybdt8wx_XDcO1t102wkLaEcmpuFrjgEeA@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="ltr">
        <div dir="ltr">
          <div dir="ltr">
            <div dir="ltr">
              <div dir="ltr">
                <div dir="ltr">
                  <div dir="ltr">
                    <div dir="ltr">After some tests, I found that the
                      problem is not about modal windows at all.
                      <div>If you check carefully, even after the
                        QThread.sleep, the dialog interaction is blocked
                        until the image is saved.</div>
                      <div><br>
                        It turns out that the issue here is that QPixmap
                        is *not* thread safe (at least, not in all
                        platforms).</div>
                      <div>It does not seem to be any official
                        documentation about this, but I found some
                        information on this thread:</div>
                      <div><a
href="https://forum.qt.io/topic/52397/not-safe-to-use-qpixmap-outside-the-gui-thread/4"
                          moz-do-not-send="true">https://forum.qt.io/topic/52397/not-safe-to-use-qpixmap-outside-the-gui-thread/4</a><br>
                      </div>
                      <div><br>
                      </div>
                      <div>A simple solution is to convert the pixmap to
                        a QImage and use its save() function, which
                        seems to be thread safe and doesn't block the
                        GUI:</div>
                      <div><br>
                      </div>
                      <div><font face="monospace">   
                          WIN.pixmap.toImage().save('/tmp/nonmodal_test.png')</font><br>
                      </div>
                      <div><br>
                      </div>
                      <div>A couple of slightly unrelated suggestions,
                        if I may.</div>
                      <div>- 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</div>
                      <div>- Avoid using object names that already are
                        existing properties or methods (like
                        self.thread)</div>
                      <div>- You can connect the finished signal
                        directly to the close (or, better, accept) slot
                        of the popup and delete the thread itelf:</div>
                      <div><br>
                      </div>
                      <div>
                        <div><font face="monospace">class
                            WaitMessage(QMessageBox):</font></div>
                        <div><font face="monospace">    ''' a message
                            box that can't be closed by the user</font></div>
                        <div><font face="monospace">    '''</font></div>
                        <div><font face="monospace">    def
                            __init__(self, parent):</font></div>
                        <div><font face="monospace">       
                            super(WaitMessage,
                            self).__init__(QMessageBox.Information,
                            'Wait', </font></div>
                        <div><font face="monospace">            'This is
                            a test, please wait', parent=parent)</font></div>
                        <div><font face="monospace">        # setting
                            NoButton in the constructor won't be enough,
                            it must be set explicitly</font></div>
                        <div><font face="monospace">        # in this
                            way the Escape key won't hide the dialog</font></div>
                        <div><font face="monospace">       
                            self.setStandardButtons(QMessageBox.NoButton)</font></div>
                        <div><font face="monospace"><br>
                          </font></div>
                        <div><font face="monospace">    def
                            closeEvent(self, event):</font></div>
                        <div><font face="monospace">        # ignore any
                            attempt to close the dialog via the title
                            bar buttoni</font></div>
                        <div><font face="monospace">       
                            event.ignore()</font></div>
                      </div>
                      <div><font face="monospace"><br>
                        </font></div>
                      <div>
                        <div><font face="monospace">class
                            WinMain(QMainWindow):</font></div>
                      </div>
                      <div><font face="monospace">    # ...</font></div>
                      <div>
                        <div><font face="monospace">    def
                            test_part_1(self):</font></div>
                        <div><font face="monospace">        popup =
                            WaitMessage(self)<br>
                          </font></div>
                        <div><font face="monospace">        worker =
                            ThdWorker(self)</font></div>
                        <div><font face="monospace">       
                            worker.started.connect(popup.exec_)</font></div>
                        <div><span style="font-family:monospace">       
                            worker.finished.connect(worker.deleteLater)</span><br>
                        </div>
                        <div><font face="monospace">       
                            worker.finished.connect(popup.deleteLater)</font></div>
                        <div><font face="monospace">       
                            worker.start()</font></div>
                      </div>
                      <div><br>
                      </div>
                      <div>With this approach you won't need another
                        function to delete the popup nor the thread, as
                        they will be deleted in Qt "scope" with
                        deleteLater, and will be deleted by python
                        followingly, since they're not instance
                        attributes.</div>
                      <div>Just ensure that both the popup and the
                        worker have a parent, otherwise they will be
                        garbage collected as soon as the function
                        returns.</div>
                      <div><br>
                      </div>
                      <div>Cheers,</div>
                      <div>Maurizio</div>
                      <div><br>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <br>
      <div class="gmail_quote">
        <div dir="ltr" class="gmail_attr">Il giorno dom 6 ott 2019 alle
          ore 18:20 Chuck Rhode <<a
            href="mailto:CRhode@lacusveris.com" moz-do-not-send="true">CRhode@lacusveris.com</a>>
          ha scritto:<br>
        </div>
        <blockquote class="gmail_quote" style="margin:0px 0px 0px
          0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">-----BEGIN
          PGP SIGNED MESSAGE-----<br>
          Hash: SHA1<br>
          <br>
          In the deep past, I've worked with VB, Tk, Pascal Delphi, and
          Gtk.  I<br>
          seem to recall that all had non-modal dialog boxes for
          situations where<br>
          you wanted to inform the user through the user interface that
          a<br>
          long-running task was in progress.  Is this not done anymore?<br>
          <br>
          I assume there are still long running tasks such as QPixmap
          *save*s.<br>
          <br>
          These tasks cannot emit progress signals for powering a
          progress bar.<br>
          What alternatives are there?<br>
          <br>
          I can't get a cursor change to show up.<br>
          <br>
          Does one nowadays throw up a semitransparent overlay with a
          spinner?<br>
          That is not so simple or informative as a non-modal dialog, I
          think.<br>
          <br>
          I've pored over Stackoverflow posts about Qt from the last
          decade, and<br>
          I don't see a lot that I can use.  Most say its as simple as<br>
          QMessageBox *open* instead of *exec_*, but this has not been
          my<br>
          experience with PyQt.  Here is code that works, however:<br>
          <br>
          #!/usr/bin/python<br>
          # -*- coding: utf-8 -*-<br>
          <br>
          # nonmodal_example.py<br>
          # 2019 Oct 06 . ccr<br>
          <br>
          """Demonstrate how to show a non-modal dialog box before
          starting a<br>
          long-running task.<br>
          <br>
          """<br>
          <br>
          from __future__ import division<br>
          import sys<br>
          from PyQt5.QtWidgets import (<br>
              QApplication,<br>
              QMainWindow,<br>
              QWidget,<br>
              QGridLayout,<br>
              QPushButton,<br>
              QMessageBox,<br>
              )<br>
          from PyQt5.QtGui import (<br>
              QPixmap,<br>
              QTransform,<br>
              )<br>
          from PyQt5.QtCore import(<br>
              QThread,<br>
              )<br>
          <br>
          ZERO = 0<br>
          SPACE = ' '<br>
          NULL = ''<br>
          NUL = '\x00'<br>
          NA = -1<br>
          <br>
          <br>
          class ThdWorker(QThread):<br>
          <br>
              """An (arbitrarily) long-running task.<br>
          <br>
              """<br>
          <br>
              def run(self):<br>
                  QThread.sleep(1)<br>
                  WIN.pixmap.save('/tmp/nonmodal_test.png')<br>
                  return<br>
          <br>
          <br>
          class WinMain(QMainWindow):<br>
          <br>
              """The (trivial) main window of the graphical user
          interface.<br>
          <br>
              """<br>
          <br>
              def __init__(self):<br>
                  super(WinMain, self).__init__()<br>
                  self.resize(800, 600)<br>
                  self.central_widget = QWidget(self)<br>
                  self.setCentralWidget(self.central_widget)<br>
                  self.layout = QGridLayout()<br>
                  self.central_widget.setLayout(self.layout)<br>
                  self.btn_run = QPushButton('Run Next Test Scenario',
          self.central_widget)<br>
                  self.btn_run.setMaximumSize(200, 30)<br>
                  self.btn_run.clicked.connect(self.test_part_1)<br>
                  self.layout.addWidget(self.btn_run)<br>
                  figure = QPixmap()<br>
                  result =
figure.load('/usr/share/qt5/doc/qtdesigner/images/designer-screenshot.png')<br>
                  if result:<br>
                      pass<br>
                  else:<br>
                      raise NotImplementedError<br>
                  transformation = QTransform()<br>
                  transformation.scale(5.0, 5.0)<br>
                  self.pixmap = figure.transformed(transformation)<br>
                  return<br>
          <br>
              def test_part_1(self):<br>
          <br>
                  """Fork a thread.<br>
          <br>
                  """<br>
          <br>
                  self.popup = QMessageBox(QMessageBox.Information,
          None, 'This is a test.  Please wait.')<br>
                  self.popup.show()<br>
                  self.thread = ThdWorker(self)<br>
                  self.thread.finished.connect(self.test_part_2)<br>
                  self.thread.start()<br>
                  return<br>
          <br>
              def test_part_2(self):<br>
                  self.popup.close()<br>
                  del self.popup<br>
                  del self.thread<br>
                  return<br>
          <br>
          <br>
          if __name__ == "__main__":<br>
              APP = QApplication(sys.argv)<br>
              WIN = WinMain()<br>
              WIN.show()<br>
              result = APP.exec_()<br>
              sys.exit(result)<br>
          <br>
          <br>
          # Fin<br>
          <br>
          What really gripes me about this example, despite the fact
          that it<br>
          works for me, is the sleep at the beginning of the thread that
          starts<br>
          the long running thread.  The sleep is essential.  Although
          the<br>
          QMessageBox *show* paints its frame, it doesn't have time to
          paint its<br>
          contents unless the thread pauses before it even gets going.<br>
          <br>
          There HAS TO BE a more elegant way to allow non-modal dialogs
          to paint<br>
          completely.  I've tried lots of different Stackoverflow<br>
          recommendations, and nothing works.  I have a more complete
          (and<br>
          considerably longer) test suite ready to show you that is
          available upon<br>
          request.<br>
          <br>
          - -- <br>
          .. Be Seeing You,<br>
          .. Chuck Rhode, Sheboygan, WI, USA<br>
          .. Weather:  <a href="http://LacusVeris.com/WX"
            rel="noreferrer" target="_blank" moz-do-not-send="true">http://LacusVeris.com/WX</a><br>
          .. 55° — Wind WSW 10 mph<br>
          <br>
          -----BEGIN PGP SIGNATURE-----<br>
          Version: GnuPG v2<br>
          <br>
iEYEARECAAYFAl2aE+IACgkQYNv8YqSjllJHuwCfW+tQv04X3s8e6jE5gWZPqbeN<br>
          kZgAn2nbhXFERp5rmIcEuO6yEvC8+HVF<br>
          =bE0d<br>
          -----END PGP SIGNATURE-----<br>
          _______________________________________________<br>
          PyQt mailing list    <a
            href="mailto:PyQt@riverbankcomputing.com" target="_blank"
            moz-do-not-send="true">PyQt@riverbankcomputing.com</a><br>
          <a
            href="https://www.riverbankcomputing.com/mailman/listinfo/pyqt"
            rel="noreferrer" target="_blank" moz-do-not-send="true">https://www.riverbankcomputing.com/mailman/listinfo/pyqt</a><br>
        </blockquote>
      </div>
      <br clear="all">
      <div><br>
      </div>
      -- <br>
      <div dir="ltr" class="gmail_signature">È difficile avere una
        convinzione precisa quando si parla delle ragioni del cuore. -
        "Sostiene Pereira", Antonio Tabucchi<br>
        <a href="http://www.jidesk.net" target="_blank"
          moz-do-not-send="true">http://www.jidesk.net</a></div>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <pre class="moz-quote-pre" wrap="">_______________________________________________
PyQt mailing list    <a class="moz-txt-link-abbreviated" href="mailto:PyQt@riverbankcomputing.com">PyQt@riverbankcomputing.com</a>
<a class="moz-txt-link-freetext" href="https://www.riverbankcomputing.com/mailman/listinfo/pyqt">https://www.riverbankcomputing.com/mailman/listinfo/pyqt</a>
</pre>
    </blockquote>
  </body>
</html>