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