[PySide] wait for QFileDialog to close
Sean Fisk
sean at seanfisk.com
Wed Dec 11 21:56:13 CET 2013
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>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 sysimport osimport time
> from PySide import 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, filename in 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>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
>> > 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
>>> http://lists.qt-project.org/mailman/listinfo/pyside
>>>
>>>
>>
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/pyside/attachments/20131211/3ba38686/attachment.html>
More information about the PySide
mailing list