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 <[email protected]>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 <[email protected]>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 <[email protected] >> > 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 >>> <[email protected]>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 < >>>> [email protected]> 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 >>>>> [email protected] >>>>> http://lists.qt-project.org/mailman/listinfo/pyside >>>>> >>>>> >>>> >>>> >>>> >>> >>> >> >> > >
_______________________________________________ PySide mailing list [email protected] http://lists.qt-project.org/mailman/listinfo/pyside
