[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