[PyKDE] My thinking still wrong?

David Boddie david at boddie.org.uk
Sun Feb 26 17:24:27 GMT 2006


On Sat, 25 Feb 2006, Andreas Pakulat wrote:

> On 25.02.06 09:55:18, Tina Isaksen wrote:
>
> > So I guess I've fallen in the trap of old-time thinking again...
>
> This time it's actually not python-specific "wrong-thinking" ;-);-) This
> would've happend to you in C++, Java and any other threaded-language
> with a threaded-toolkit like Qt.

I'm not sure I'd put it that way, but I know what Andreas is saying.
Basically, in the doUpdateSources() method, Qt doesn't update any of the
widgets until you return from the method. Actually, it could take the
opportunity to refresh them, but I suspect that the updates are queued
until it regains control (in the event loop).

Here's the code again:

def doUpdateSources(self):
       self.sourcesMessage.setEnabled(1)
       self.sourcesMessage.setText("Updating your sources, please wait...")
       os.chdir("/etc/apt/")
       self.mainTextWindow.setEnabled(1)
       self.mainTextWindow.setText(commands.getoutput("apt-get update"))
       self.mainTextWindow.append("Done!")
       self.mainTextWindow.scrollToBottom()
       self.pbSaveMain.setEnabled(0)
       self.mainTextWindow.setReadOnly(1)

You will notice that the GUI will appear to freeze if the "apt-get update"
operation takes a long time. With PyQt and Qt 3, you can drag other windows
over your main window and "wipe away" the GUI because it isn't being updated.

> You now have 2 Options: 
>
> 1) Call QApplication::processEvents (IIRC that was the name of the
> function) after setting the text. This makes Qt process any pending
> events (like the redraw of the label) and then returning to your
> code-part, AFAIK.

Yes, this works quite well. However, it still doesn't solve the problem
of long delays if "apt-get" takes a long time to finish. There's a way
to fix this that _shouldn't_ be too hard (see option 3).

> 2) put the "work" into a separate Thread (i.e. the hole
> commands.getoutput which takes time) and communicate between the
> mainTextWindow and the new Thread via Event's. But befor doing so (and
> that is why I'm so brief here) you need to read some basics about
> threading, so you don't shoot yourself in the foot with it (for example
> never trigger drawing-operations outside the gui-thread). I don't have
> any book title or tutorial or some such at hand to recommend, sorry.

Many people regard threading as a fairly advanced technique, although
Python's own threading module makes it look almost trivial. However, it
can introduce even more ways to shoot yourself in the foot. Another
option is:

3) Run "apt-get update" in another process, using either QProcess or
   a Python standard library module. QProcess will execute a command
   with arguments, and lets you read the output and errors as they
   become available. There are ways of monitoring the command's
   status and collecting the output.

One of the easiest ways to use QProcess in this way is to do something like
the following. (Note: this is untested.)

def doUpdateSources(self):
       self.sourcesMessage.setEnabled(1)
       self.sourcesMessage.setText("Updating your sources, please wait...")
       os.chdir("/etc/apt/")
       self.mainTextWindow.setEnabled(1)
       process = QProcess("apt-get")
       process.addArgument("update")
       process.start()
       while process.canReadLineStdout():
           self.mainTextWindow.append(process.readLineStdout())
           qApp.processEvents()
       self.mainTextWindow.append("Done!")
       self.mainTextWindow.scrollToBottom()
       self.pbSaveMain.setEnabled(0)
       self.mainTextWindow.setReadOnly(1)

Note that you need to import qApp and QProcess from the qt module. This
doesn't cover things like error reporting, what happens when the process
is killed, etc.

Anyway, I'll put a more complete example on the Wiki because I
encountered a few more problems with this approach, and I want to show
some code that's as clear as possible.

> > So what I want it to do is to display the 'please wait' in the
> > sourceMessage *before* doing the 'commands.output', just as this little
> > console script does:
>
> This works, because print does output the text directly, without the
> need of a redraw-event processed and thus it works.

Yes, this is the difference between a framework with its own event loop
and one without. You need to let the event loop run every so often, so that
redraws and other events can be processed.

David




More information about the PyQt mailing list