
Welcome!
Last time, we discussed how to write your PyQt4 application based on the logic generated by the
pyuic4 file. As is often the case - after writing the topic, I received many interesting and, most importantly, informative comments explaining why in some cases I am right and in others wrong.
The most curious thing is that the discussion is interesting both for pythonists and for C ++ adherents, because in this case the difference is small, mostly only minor things in the syntax. All this is because PyQt4, in its essence, is a simple wrapper around sishnyh Qt-classes, preserving all the names and methods. So, here's a cup of tea or coffee for you, get comfortable, let's start our conversation.
Right off the bat
So, we write window application. The emphasis here is on the word "
window ", which means that we have a user-friendly interface that allows the end user to appreciate the beauty of clicking on buttons. Thus, we use the console for debugging and catching errors. But why the console to the user who already has a beautiful window? Here you can choose what to do for convenience - keep a log of work (this method just loves Microsoft, collecting all the most interesting logs and asking whether to send a report on the application's work) or redirect the console output to the widget on the form to see what have a deal. And if the first is done fairly easily:
Copy Source | Copy HTML import sys # sys .stdout = open ( "log.txt" , "a+" ) sys .stderr = open ( "errors.txt" , "a+" )
Copy Source | Copy HTML import sys # sys .stdout = open ( "log.txt" , "a+" ) sys .stderr = open ( "errors.txt" , "a+" )
Copy Source | Copy HTML import sys # sys .stdout = open ( "log.txt" , "a+" ) sys .stderr = open ( "errors.txt" , "a+" )
Copy Source | Copy HTML import sys # sys .stdout = open ( "log.txt" , "a+" ) sys .stderr = open ( "errors.txt" , "a+" )
Copy Source | Copy HTML import sys # sys .stdout = open ( "log.txt" , "a+" ) sys .stderr = open ( "errors.txt" , "a+" )
then with the second, many questions arise. Actually the otgadka is hidden just in this example. What we have? File-like object that allows the addition of lines to the end. This is done, as far as we remember from working with files in Python, using the
write () method. So let's make an object with the
write () method and pass the output widget as a parameter!
Copy Source | Copy HTML
- class Logger (object):
- def __init__ (self, output):
- self .output = output
- def write (self, string):
- if not (string == "\ n" ):
- trstring = QtGui.QApplication.translate ( "MainWindow" , string .strip (), None, QtGui.QApplication.UnicodeUTF8)
- self .output.append (trstring)
at the same time, it is often necessary to cut off all unnecessary from the line and make a check on the carriage transfer character so that half of the log is not occupied by empty lines.
Now, in order to redirect the console output to the widget, we need to register it in the window class and link it to the standard output via the logger class:
Copy Source | Copy HTML
- self .logText = QtGui.QTextEdit (MainWindow)
- # give the widget the read-only property
- self .logText.setReadOnly (True)
- # make the vertical scroll bar visible always, in my opinion, it is more convenient
- self .logText.setVerticalScrollBarPolicy (QtCore.Qt.ScrollBarAlwaysOn)
- # initial message
- self .logText.append ( "Log started" )
- # you can both redirect everything to one widget, or separate errors and
- # standard output
- self .logger = Logger ( self .logText)
- self .errors = Logger ( self .logText)
- sys .stdout = self .logger
- sys .stderr = self .errors
Now we can write to the log in two ways - either (in the window class) using
self.logger.write () , or simply using
print . Thus, having made a beautiful error output with
try-except 'ah, we see everything right in the window. There is still a big plus is the very separation of Python-logic with Qt logic - the form hangs in a separate thread and when errors in external functions the application does not end, but the error immediately falls into the log. And yes, it’s worth a little more to talk about.
A little bit about streams
I think you have repeatedly noticed that if you register a function that requires a lot of time and resources to execute, the form will “hang” and you will not receive any data on the status of the function. I myself faced this when I wanted to make a consistent output of the values ​​returned from the functions to the list on the form. The solution is obvious - to use threads. And before us there will be a choice - to use threads with Python-logic or Qt streams? Personally, in this case, I prefer the second - less hassle in some aspects.
In order for us to be beautiful, we must again create another class with inheritance from Qt-shnogo. And what to do, such a "class logic" :) There are pros and cons, but now this is not about that.
Copy Source | Copy HTML
- class myQThread (QtCore.QThread):
- def __init__ (self, output):
- QtCore.QThread. __init__ (self)
- self .output = output
- def run (self):
- some_big_function_with_output ()
As you can see, at first we initialize the Qt-shnogo class stream (I also added an output object to the constructor parameters - this could be the very widget on the form). Then we override the standard
run () method, which contains exactly what the thread should do. This function or functions can be connected even from external modules - in this case it does not matter. Its output in this case can and should be done through the
output passed to the function using the
append () method. You can, for greater beauty, add a progress bar or another visualizer so that the user can see that the stream is working.
And how do we know that the thread has completed its work? To do this, we will create one small hack - we will create a slot for the signal handler from the stream, but we will not use
connect , but use the decorator:
Copy Source | Copy HTML
- @ QtCore.pyqtSignature ("")
- def threadFinished (self):
- self .logger.write ( "Feed completed!" )
Yes, you understood correctly, it is written in the form class, and when the
threadFinished () signal is generated, the program informs us about it. These are the pies.
Actually, work with streams, slots and signals could be considered in more detail - but for starters, that's enough. I hope you enjoyed our little tea party, before the connection! Write comments :)