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
