Talking about PyQt4 - Second gathering

    image

    Welcome!


        Last time we discussed how you can write your PyQt4 application based on the logic of the file generated by the pyuic4 program . As it often happens - after writing the topic received a lot of interesting and, most importantly, informative comments explaining why in some cases I am right and in others wrong.
        The most interesting thing is that the discussion is interesting both for pythonists and C ++ adherents, because in this case the difference is small, basically only insignificant things in the syntax. All of this is because PyQt4, in its essence, is a simple wrapper around cached Qt classes that preserves all names and methods. So, here’s a cup of tea or coffee, make yourself comfortable, let's start our conversation.

    Right off the bat


        So, we are writing a 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 buttons. Thus, we use the console for debugging and catching errors. But why console to a 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 in the logs and asking whether to send a report on the operation of the application) or redirect the console output to the widget on the form to see what deal with. And if the first is done quite easily:
    Copy Source | Copy HTML
    1. import sys
    2. # открываем файлы для записи с возможностью аппендинга нужной информации
    3. sys.stdout = open("log.txt", "a+")
    4. sys.stderr = open("errors.txt", "a+")

    then with the second, many have questions. In fact, the clue is hidden just in this example. What we have? A file-like object that allows you to add 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 to it!
    Copy Source | Copy HTML
    1. class Logger(object):
    2.     def __init__(self, output):
    3.         self.output = output
    4.  
    5.     def write(self, string):
    6.         if not (string == "\n"):
    7.             trstring = QtGui.QApplication.translate("MainWindow", string.strip(), None, QtGui.QApplication.UnicodeUTF8)
    8.             self.output.append(trstring)

    at the same time, you often have to cut off everything unnecessary from the line and check for the carriage return character so that half the log is not occupied by empty lines.
        Now, to redirect the console output to the widget, we need to register it in the window class and bind it through the logger class to the standard output:
    Copy Source | Copy HTML
    1. self.logText = QtGui.QTextEdit(MainWindow)
    2. # даем виджету свойство read-only
    3. self.logText.setReadOnly(True)
    4. # делаем полосу вертикальной прокрутки видимой всегда, на мой взгляд, так удобнее
    5. self.logText.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
    6. # начальное сообщение
    7. self.logText.append("Log started")
    8. # можно как перенаправлять все в один виджет, так и разделить ошибки и 
    9. # стандартный вывод
    10. self.logger = Logger(self.logText)
    11. self.errors = Logger(self.logText)
    12. sys.stdout = self.logger
    13. 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 conclusion for try-except 's, we see everything right in the window. Here, an even greater plus is the very separation of Python logic with Qt logic - the form hangs in a separate thread and the application does not end with errors in external functions, but the error immediately falls into the log. And yes, it’s worth talking about this in a little more detail.

    A little bit about the threads


        I think you have noticed more than once that if you register a function that requires a lot of time and resources to execute, the form will “freeze” and you will not receive any data about the state of the function. I myself came across this when I wanted to make a sequential output of the values ​​returned from functions to a list on the form. The solution is obvious - use threads. And we will have a choice - to use threads with Python logic or Qt threads? Personally, in this case I prefer the second - less trouble in some aspects.
        In order for us to be beautiful, we must again create another class with inheritance from Qt. And what to do, such a “class logic” :) There are pros and cons, but this is not about that now.
    Copy Source | Copy HTML
    1. class myQThread(QtCore.QThread):
    2.     def __init__(self, output):
    3.         QtCore.QThread.__init__(self)
    4.         self.output = output
    5.  
    6.     def run(self):
    7.         some_big_function_with_output()

        As you can see, first we initialize the Qt-th stream class (I also added an output object to the constructor parameters - this may be the very same widget on the form). Then we redefine the standard run () method , which just contains what the stream should do. This function or functions can be connected even from external modules - in this case it does not play a role. In this case, its output can and should be done through the output function passed to it using the same append () method . For greater beauty, you can add a progress bar or other visualizer so that the user sees that the stream is working.
        And how do we know that the thread has completed its work? To do this, we’ll hook up one small hack - we will create a slot for the signal processor from the stream, but we will not use connect , but use the decorator:
    Copy Source | Copy HTML
    1. @QtCore.pyqtSignature("")
    2. def threadFinished(self):
    3.     self.logger.write("Поток завершен!")

        Yes, yes, you understood correctly, this is written exactly to the form class, and when the threadFinished () signal is generated , the program tells us about it. These are the pies.

        Actually, work with streams, slots and signals could be considered in more detail - but for a start it’s enough. I hope you enjoyed our little tea party, before communication! Write comments :)

    Also popular now: