[PySide] wait for QFileDialog to close

Janwillem van Dijk jwevandijk at xs4all.nl
Wed Dec 11 22:30:36 CET 2013


On 11/12/13 21: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
>>>>
>>>>
>>>
>>
>>
>
>
I am on Linux (Ubuntu 13.10) and Python 3.2 developing using Spyder 
2.3.0beta (I am a numpy scipy matplotlib user). I am not aware that 
gexiv2 is available for Windows so I cannot test that.

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


More information about the PySide mailing list