[PySide] wait for QFileDialog to close
Frank Rueter | OHUfx
frank at ohufx.com
Wed Dec 11 05:49:11 CET 2013
Hi Sean,
actually the progress bar works just fine for me in my example. How come?
On 11/12/13 17: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/20131211/aace1eb6/attachment.html>
More information about the PySide
mailing list