[PySide] wait for QFileDialog to close

Sean Fisk sean at seanfisk.com
Thu Dec 12 00:07:51 CET 2013


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>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>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/a161a155/attachment.html>


More information about the PySide mailing list