[PySide] wait for QFileDialog to close

Frank Rueter | OHUfx frank at ohufx.com
Thu Dec 12 03:12:48 CET 2013


Thanks for the update Sean!

On 12/12/13 12:37, Sean Fisk wrote:
>
> I just tried it on CentOS with PySide 1.2.1. The progress bar does 
> indeed work. However, I do see some visual glitches: the layout 
> doesn’t readjust if the window is resized; the button sometimes 
> disappears halfway or fully; the button can’t be clicked. Throwing in 
> a call to |QtGui.QApplication.processEvents()| before |time.sleep()| 
> helps the situation, but the response is laggy and sluggish because 
> the GUI is still only able to update every half-second.
>
> While threads are “evil” in many ways, I do think that for delays of 
> half a second or greater they are a good solution.
>
>
>
> --
> Sean Fisk
>
>
> On Wed, Dec 11, 2013 at 6:16 PM, Frank Rueter | OHUfx <frank at ohufx.com 
> <mailto:frank at ohufx.com>> wrote:
>
>     Ok, will report back if I stumble across anything interesting. For
>     now I will just assume that my example simply shouldn't work :-D
>
>
>
>     On 12/12/13 12:07, Sean Fisk wrote:
>>     Frank,
>>
>>     Understood. I'm going to try your example on CentOS 6.4 with
>>     PySide 1.2.1 and see what happens. Let me know what happens with
>>     future progress bars. I'm going to start doing a lot of
>>     asynchronous stuff soon, so it would be helpful to know.
>>
>>     Thanks,
>>
>>
>>     --
>>     Sean Fisk
>>
>>
>>     On Wed, Dec 11, 2013 at 4:45 PM, Frank Rueter | OHUfx
>>     <frank at ohufx.com <mailto:frank at ohufx.com>> wrote:
>>
>>         Hi Sean,
>>
>>         I totally agree that it shouldn't work, and I paid little
>>         attention to it as I just wanted to provide an example for
>>         the signal.
>>
>>         I am on Kubuntu 12.10 using PySide 1.2.1.
>>         I just ran the code on an OSX box with an old PySide
>>         installation (1.0.9) and the progressbar didn't move there.
>>
>>         I have a few simple apps to write which will involve proper
>>         use of progressbar to indicate background image processing,
>>         so I'm sure I will run into it again.
>>
>>
>>         Cheers,
>>         frank
>>
>>
>>
>>         On 12/12/13 09:56, Sean Fisk wrote:
>>>         Janwillem,
>>>
>>>         I'm glad you were able to able to figure out the problem.
>>>         Sorry for my red herrings!
>>>
>>>         Frank,
>>>
>>>         I thought about it, and I have no idea why the progress bar
>>>         works fine for you and not for me. It hangs for me, which is
>>>         exactly what I expected to happen. Maybe some platform
>>>         difference? I'm on Mac OS 10.6 with PySide 1.2.1.
>>>
>>>
>>>         --
>>>         Sean Fisk
>>>
>>>
>>>         On Wed, Dec 11, 2013 at 9:16 AM, Janwillem van Dijk
>>>         <jwevandijk at xs4all.nl <mailto:jwevandijk at xs4all.nl>> wrote:
>>>
>>>             The solution Frank proposed yesterday works (after I
>>>             found out that you can get the output using
>>>             selectedFiles()[0]).
>>>             No problems with the progressbar.
>>>             The actual processing can take a bit long because the
>>>             exif's of digital camera shots are analysed (GExiv2 for
>>>             photos and exiftool for movies ) and than copied to
>>>             folders as
>>>             /camara_make/year/month/imagetype/yyyymmddhhmmss_fname.
>>>             When copying more than say 50 16MB raw photos the gui
>>>             becomes blocked. In other apps I indeed solved that with
>>>             threading but, although not elegant, I decided to live
>>>             with that for this one.
>>>             Many thanks for teaching me this extra bit of Python.
>>>             Cheers, Janwillem
>>>
>>>
>>>             On 11/12/13 05:45, Sean Fisk wrote:
>>>>
>>>>             Frank,
>>>>
>>>>             Your example is a good demonstration of |QFileDialog|‘s
>>>>             signals. However, since the processing runs in the GUI
>>>>             thread, the progress bar is virtually useless as the
>>>>             GUI has no time to update it. It starts empty, the
>>>>             application hangs, and then it is filled when the
>>>>             processing is done.
>>>>
>>>>             Janwillem,
>>>>
>>>>             As I see it, if you would like a progress bar, you have
>>>>             three options:
>>>>
>>>>              1. Call |QCoreApplication.processEvents()|
>>>>                 <http://seanfisk.github.io/pyside-docs/pyside/PySide/QtCore/QCoreApplication.html#PySide.QtCore.PySide.QtCore.QCoreApplication.processEvents>
>>>>                 during your processing code. This is not always a
>>>>                 great idea, and more of a hack than a solution. But
>>>>                 it usually works.
>>>>              2. Split your processing into chunks as in this
>>>>                 example
>>>>                 <http://qt-project.org/wiki/Threads_Events_QObjects#72c9aabadf52900fbf3d4c1ff2b6008c>.
>>>>                 However, the code is a bit convoluted and it still
>>>>                 runs in the GUI thread. The whole page that
>>>>                 contains that example is a great read for
>>>>                 asynchronous programming.
>>>>              3. Send your processing to a thread, and dispatch
>>>>                 events from the thread indicating the progress.
>>>>
>>>>             The first two solutions involve running processing code
>>>>             within the GUI thread. If any step of the processing
>>>>             takes longer than a second, then it’s probably not a
>>>>             good idea as the user will see the application hang.
>>>>             Here is an example implementation of the third solution:
>>>>
>>>>             |#!/usr/bin/env python
>>>>
>>>>             # Example: Asynchronously process a directory of files with a progress bar.
>>>>
>>>>             import  sys
>>>>             import  os
>>>>             import  time
>>>>
>>>>             from  PySideimport  QtCore, QtGui
>>>>
>>>>             class  ProcessingThread(QtCore.QThread):
>>>>                  # Fired when each file is processed.
>>>>                  file_processed = QtCore.Signal(int, str)
>>>>
>>>>                  def  __init__(self, parent=None):
>>>>                      super(ProcessingThread, self).__init__(parent)
>>>>                      self.files = []
>>>>
>>>>                  def  run(self):
>>>>                      # Code that's run in the thread.
>>>>                      for  i, filenamein  enumerate(self.files):
>>>>                          # The actual code for one file goes here. Stubbed out with
>>>>                          # time.sleep() for now.
>>>>                          time.sleep(0.5)
>>>>                          print  'Processed:', filename
>>>>                          # Send update to the GUI thread.
>>>>                          self.file_processed.emit(i +1, filename)
>>>>
>>>>             class  MyWidget(QtGui.QWidget):
>>>>                  def  __init__(self, parent=None):
>>>>                      super(MyWidget, self).__init__(parent)
>>>>
>>>>                      # Setup UI.
>>>>                      self._layout = QtGui.QVBoxLayout(self)
>>>>                      self._button = QtGui.QPushButton('Open files...')
>>>>                      self._progress = QtGui.QProgressBar()
>>>>                      self._filelist = QtGui.QPlainTextEdit()
>>>>                      self._layout.addWidget(self._button)
>>>>                      self._layout.addWidget(self._filelist)
>>>>                      self._layout.addWidget(self._progress)
>>>>
>>>>                      # Setup events.
>>>>                      self._button.clicked.connect(self._button_clicked)
>>>>
>>>>                      # Create the thread. Note that this doesn't actually _start_ it.
>>>>                      self._thread = ProcessingThread()
>>>>                      self._thread.file_processed.connect(self._file_processed)
>>>>
>>>>                      # We need to wait for the thread before exiting. Either use this or
>>>>                      # don't let the user close the window if processing is happening. See
>>>>                      # the next method in this class.
>>>>                      QtCore.QCoreApplication.instance().aboutToQuit.connect(
>>>>                          self._thread.wait)
>>>>
>>>>                  # def closeEvent(self, event):
>>>>                  #     # This is an alternative to waiting for the threads. Just don't let
>>>>                  #     # the user close the window.
>>>>                  #     if self._thread.isRunning():
>>>>                  #         QtGui.QMessageBox.critical(
>>>>                  #             self, 'Processing',
>>>>                  #             'Cannot exit while processing is happening.')
>>>>                  #         event.ignore()
>>>>                  #     else:
>>>>                  #         event.accept()
>>>>
>>>>                  def  _button_clicked(self):
>>>>                      # If we are already running the processing, produce an error.
>>>>                      if  self._thread.isRunning():
>>>>                          QtGui.QMessageBox.critical(
>>>>                              self,'Processing',
>>>>                              'Can only process one directory at a time.')
>>>>                          return
>>>>
>>>>                      # Get the directory name from the user.
>>>>                      dir_name = QtGui.QFileDialog.getExistingDirectory(
>>>>                          parent=self,
>>>>                          caption='Choose files...',
>>>>                          dir=os.getcwd())
>>>>
>>>>                      # Activate the main dialog as it will be deactivated for some reason
>>>>                      # after the file dialog closes (at least on my machine).
>>>>                      self.activateWindow()
>>>>
>>>>                      # Get the list of files in the directory and prime the progress bar.
>>>>                      files = os.listdir(dir_name)
>>>>
>>>>                      # Set values for progress bar.
>>>>                      self._progress.setRange(0, len(files))
>>>>                      self._progress.setValue(0)
>>>>
>>>>                      # Create and start the thread.
>>>>                      self._thread.files = files
>>>>                      self._thread.start()
>>>>
>>>>                  def  _file_processed(self, num_files_processed, filename):
>>>>                      # Called for each file that is processed.
>>>>                      self._filelist.appendPlainText(filename)
>>>>                      self._progress.setValue(num_files_processed)
>>>>
>>>>             if  __name__ =='__main__':
>>>>                  app = QtGui.QApplication(sys.argv)
>>>>                  w = MyWidget()
>>>>                  w.show()
>>>>                  w.raise_()
>>>>                  raise  SystemExit(app.exec_())|
>>>>
>>>>             This is all fine, but it might not solve your original
>>>>             problem of the file dialog not closing. On my Mac, the
>>>>             file dialog is gone as soon as the call to
>>>>             |getExistingDirectory()| finishes. However, since I
>>>>             don’t have a runnable portion of your code, I can’t
>>>>             really test it. I would recommend attempting to run my
>>>>             example to see if it exhibits the same problem as your
>>>>             program. Hope this helps!
>>>>
>>>>             Cheers,
>>>>
>>>>
>>>>
>>>>
>>>>             --
>>>>             Sean Fisk
>>>>
>>>>
>>>>             On Tue, Dec 10, 2013 at 4:43 PM, Frank Rueter | OHUfx
>>>>             <frank at ohufx.com <mailto:frank at ohufx.com>> wrote:
>>>>
>>>>                 Here is an example using signals/slots
>>>>
>>>>
>>>>                 On 11/12/13 09:56, Janwillem van Dijk wrote:
>>>>>
>>>>>                 Here is the snippet: It reads the filenames in a
>>>>>                 folder and determines new names for photo's based
>>>>>                 on the exif info.
>>>>>
>>>>>                 I apreciate that threading might be a solution but
>>>>>                 the problem seems too simple for that. Can you
>>>>>                 give an example on how to use the signal concept?
>>>>>
>>>>>
>>>>>                 self.outFolder = QFileDialog.getExistingDirectory(
>>>>>
>>>>>                 caption='Destination folder', dir=self.defOutFolder)
>>>>>
>>>>>                 self.outFiles = []
>>>>>
>>>>>                 if self.outFolder:
>>>>>
>>>>>                 self.outFolder = self.outFolder.replace('\\', '/')
>>>>>
>>>>>                 self.lineEdit_dest.setText(self.outFolder)
>>>>>
>>>>>                 self.progressBar.setRange(0, self.numFiles)
>>>>>
>>>>>                 for i, fname in enumerate(self.inFiles):
>>>>>
>>>>>                 self.progressBar.setValue(i + 1)
>>>>>
>>>>>                 newpath, newfname = rename_photo(self.inFolder, fname)
>>>>>
>>>>>                 newpath = path.join(self.outFolder, newpath)
>>>>>
>>>>>                 self.outFiles.append([fname, newpath, newfname])
>>>>>
>>>>>                 s = fname + ' --> ' + self.outFolder + '\n'
>>>>>
>>>>>                 s += path.join(newpath,
>>>>>                 newfname).replace(self.outFolder, '')
>>>>>
>>>>>                 self.plainTextEdit_dest.appendPlainText(s)
>>>>>
>>>>>
>>>>>
>>>>>                 On 10/12/13 21:35, Sean Fisk wrote:
>>>>>>
>>>>>>                 Hi Janwillem,
>>>>>>
>>>>>>                 Are you running the “lengthy part that processes
>>>>>>                 a files list” within the GUI thread? If so, you
>>>>>>                 will probably see your GUI hang while this is
>>>>>>                 happening (you won’t be able to click or do
>>>>>>                 anything). In this case, you should consider
>>>>>>                 running the processing in a different thread
>>>>>>                 using QThread
>>>>>>                 <http://seanfisk.github.io/pyside-docs/pyside/PySide/QtCore/QThread.html>
>>>>>>                 or QThreadPool
>>>>>>                 <http://seanfisk.github.io/pyside-docs/pyside/PySide/QtCore/QThreadPool.html>.
>>>>>>
>>>>>>                 Can you post the relevant part of the code?
>>>>>>
>>>>>>                 Thanks,
>>>>>>
>>>>>>
>>>>>>
>>>>>>                 --
>>>>>>                 Sean Fisk
>>>>>>
>>>>>>
>>>>>>                 On Tue, Dec 10, 2013 at 3:17 PM, Janwillem van
>>>>>>                 Dijk <jwevandijk at xs4all.nl
>>>>>>                 <mailto:jwevandijk at xs4all.nl>> wrote:
>>>>>>
>>>>>>                     Hi, I have a PySide script that uses
>>>>>>                     QFileDialog.getExistingDirectory(). After
>>>>>>                     clicking the Open button the script proceeds
>>>>>>                     with a lengthy part that processes a files
>>>>>>                     list and writes to a QPlainTextEdit.
>>>>>>                     Unfortunately the QFileDialog widget does
>>>>>>                     only disappear after this processing is
>>>>>>                     finished, hiding the QPlainTextEdit.
>>>>>>
>>>>>>                     How can I make that the QFileDialog widget is
>>>>>>                     gone before the processing starts?
>>>>>>
>>>>>>                     Cheers, Janwillem
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>                     _______________________________________________
>>>>>>                     PySide mailing list
>>>>>>                     PySide at qt-project.org
>>>>>>                     <mailto:PySide at qt-project.org>
>>>>>>                     http://lists.qt-project.org/mailman/listinfo/pyside
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/pyside/attachments/20131212/a1d97b93/attachment.html>


More information about the PySide mailing list