mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	Refined Qt GUI example in the logging cookbook. (GH-15045)
This commit is contained in:
		
							parent
							
								
									d04f8907ba
								
							
						
					
					
						commit
						472eced677
					
				
					 1 changed files with 39 additions and 17 deletions
				
			
		| 
						 | 
					@ -2760,8 +2760,8 @@ The following example shows how to log to a Qt GUI. This introduces a simple
 | 
				
			||||||
``QtHandler`` class which takes a callable, which should be a slot in the main
 | 
					``QtHandler`` class which takes a callable, which should be a slot in the main
 | 
				
			||||||
thread that does GUI updates. A worker thread is also created to show how you
 | 
					thread that does GUI updates. A worker thread is also created to show how you
 | 
				
			||||||
can log to the GUI from both the UI itself (via a button for manual logging)
 | 
					can log to the GUI from both the UI itself (via a button for manual logging)
 | 
				
			||||||
as well as a worker thread doing work in the background (here, just random
 | 
					as well as a worker thread doing work in the background (here, just logging
 | 
				
			||||||
short delays).
 | 
					messages at random levels with random short delays in between).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The worker thread is implemented using Qt's ``QThread`` class rather than the
 | 
					The worker thread is implemented using Qt's ``QThread`` class rather than the
 | 
				
			||||||
:mod:`threading` module, as there are circumstances where one has to use
 | 
					:mod:`threading` module, as there are circumstances where one has to use
 | 
				
			||||||
| 
						 | 
					@ -2769,7 +2769,7 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The code should work with recent releases of either ``PySide2`` or ``PyQt5``.
 | 
					The code should work with recent releases of either ``PySide2`` or ``PyQt5``.
 | 
				
			||||||
You should be able to adapt the approach to earlier versions of Qt. Please
 | 
					You should be able to adapt the approach to earlier versions of Qt. Please
 | 
				
			||||||
refer to the comments in the code for more detailed information.
 | 
					refer to the comments in the code snippet for more detailed information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. code-block:: python3
 | 
					.. code-block:: python3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2789,22 +2789,24 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
        Signal = QtCore.pyqtSignal
 | 
					        Signal = QtCore.pyqtSignal
 | 
				
			||||||
        Slot = QtCore.pyqtSlot
 | 
					        Slot = QtCore.pyqtSlot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    logger = logging.getLogger(__name__)
 | 
					    logger = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # Signals need to be contained in a QObject or subclass in order to be correctly
 | 
					    # Signals need to be contained in a QObject or subclass in order to be correctly
 | 
				
			||||||
    # initialized.
 | 
					    # initialized.
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    class Signaller(QtCore.QObject):
 | 
					    class Signaller(QtCore.QObject):
 | 
				
			||||||
        signal = Signal(str)
 | 
					        signal = Signal(str, logging.LogRecord)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # Output to a Qt GUI is only supposed to happen on the main thread. So, this
 | 
					    # Output to a Qt GUI is only supposed to happen on the main thread. So, this
 | 
				
			||||||
    # handler is designed to take a slot function which is set up to run in the main
 | 
					    # handler is designed to take a slot function which is set up to run in the main
 | 
				
			||||||
    # thread. In this example, the function takes a single argument which is a
 | 
					    # thread. In this example, the function takes a string argument which is a
 | 
				
			||||||
    # formatted log message. You can attach a formatter instance which formats a
 | 
					    # formatted log message, and the log record which generated it. The formatted
 | 
				
			||||||
    # LogRecord however you like, or change the slot function to take some other
 | 
					    # string is just a convenience - you could format a string for output any way
 | 
				
			||||||
    # value derived from the LogRecord.
 | 
					    # you like in the slot function itself.
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # You specify the slot function to do whatever GUI updates you want. The handler
 | 
					    # You specify the slot function to do whatever GUI updates you want. The handler
 | 
				
			||||||
    # doesn't know or care about specific UI elements.
 | 
					    # doesn't know or care about specific UI elements.
 | 
				
			||||||
| 
						 | 
					@ -2817,7 +2819,7 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def emit(self, record):
 | 
					        def emit(self, record):
 | 
				
			||||||
            s = self.format(record)
 | 
					            s = self.format(record)
 | 
				
			||||||
            self.signaller.signal.emit(s)
 | 
					            self.signaller.signal.emit(s, record)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # This example uses QThreads, which means that the threads at the Python level
 | 
					    # This example uses QThreads, which means that the threads at the Python level
 | 
				
			||||||
| 
						 | 
					@ -2827,6 +2829,13 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
    def ctname():
 | 
					    def ctname():
 | 
				
			||||||
        return QtCore.QThread.currentThread().objectName()
 | 
					        return QtCore.QThread.currentThread().objectName()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # Used to generate random levels for logging.
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    LEVELS = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
 | 
				
			||||||
 | 
					              logging.CRITICAL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # This worker class represents work that is done in a thread separate to the
 | 
					    # This worker class represents work that is done in a thread separate to the
 | 
				
			||||||
    # main thread. The way the thread is kicked off to do work is via a button press
 | 
					    # main thread. The way the thread is kicked off to do work is via a button press
 | 
				
			||||||
| 
						 | 
					@ -2851,7 +2860,8 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
            while not QtCore.QThread.currentThread().isInterruptionRequested():
 | 
					            while not QtCore.QThread.currentThread().isInterruptionRequested():
 | 
				
			||||||
                delay = 0.5 + random.random() * 2
 | 
					                delay = 0.5 + random.random() * 2
 | 
				
			||||||
                time.sleep(delay)
 | 
					                time.sleep(delay)
 | 
				
			||||||
                logger.debug('Message after delay of %3.1f: %d', delay, i, extra=extra)
 | 
					                level = random.choice(LEVELS)
 | 
				
			||||||
 | 
					                logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra)
 | 
				
			||||||
                i += 1
 | 
					                i += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
| 
						 | 
					@ -2864,10 +2874,18 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    class Window(QtWidgets.QWidget):
 | 
					    class Window(QtWidgets.QWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        COLORS = {
 | 
				
			||||||
 | 
					            logging.DEBUG: 'black',
 | 
				
			||||||
 | 
					            logging.INFO: 'blue',
 | 
				
			||||||
 | 
					            logging.WARNING: 'orange',
 | 
				
			||||||
 | 
					            logging.ERROR: 'red',
 | 
				
			||||||
 | 
					            logging.CRITICAL: 'purple',
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def __init__(self, app):
 | 
					        def __init__(self, app):
 | 
				
			||||||
            super(Window, self).__init__()
 | 
					            super(Window, self).__init__()
 | 
				
			||||||
            self.app = app
 | 
					            self.app = app
 | 
				
			||||||
            self.textedit = te = QtWidgets.QTextEdit(self)
 | 
					            self.textedit = te = QtWidgets.QPlainTextEdit(self)
 | 
				
			||||||
            # Set whatever the default monospace font is for the platform
 | 
					            # Set whatever the default monospace font is for the platform
 | 
				
			||||||
            f = QtGui.QFont('nosuchfont')
 | 
					            f = QtGui.QFont('nosuchfont')
 | 
				
			||||||
            f.setStyleHint(f.Monospace)
 | 
					            f.setStyleHint(f.Monospace)
 | 
				
			||||||
| 
						 | 
					@ -2880,7 +2898,7 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
            self.handler = h = QtHandler(self.update_status)
 | 
					            self.handler = h = QtHandler(self.update_status)
 | 
				
			||||||
            # Remember to use qThreadName rather than threadName in the format string.
 | 
					            # Remember to use qThreadName rather than threadName in the format string.
 | 
				
			||||||
            fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s'
 | 
					            fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s'
 | 
				
			||||||
            formatter = logging.Formatter(f)
 | 
					            formatter = logging.Formatter(fs)
 | 
				
			||||||
            h.setFormatter(formatter)
 | 
					            h.setFormatter(formatter)
 | 
				
			||||||
            logger.addHandler(h)
 | 
					            logger.addHandler(h)
 | 
				
			||||||
            # Set up to terminate the QThread when we exit
 | 
					            # Set up to terminate the QThread when we exit
 | 
				
			||||||
| 
						 | 
					@ -2932,14 +2950,17 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
        # that's where the slots are set up
 | 
					        # that's where the slots are set up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Slot(str)
 | 
					        @Slot(str)
 | 
				
			||||||
        def update_status(self, status):
 | 
					        def update_status(self, status, record):
 | 
				
			||||||
            self.textedit.append(status)
 | 
					            color = self.COLORS.get(record.levelno, 'black')
 | 
				
			||||||
 | 
					            s = '<pre><font color="%s">%s</font></pre>' % (color, status)
 | 
				
			||||||
 | 
					            self.textedit.appendHtml(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Slot()
 | 
					        @Slot()
 | 
				
			||||||
        def manual_update(self):
 | 
					        def manual_update(self):
 | 
				
			||||||
            levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
 | 
					            # This function uses the formatted message passed in, but also uses
 | 
				
			||||||
                      logging.CRITICAL)
 | 
					            # information from the record to format the message in an appropriate
 | 
				
			||||||
            level = random.choice(levels)
 | 
					            # color according to its severity (level).
 | 
				
			||||||
 | 
					            level = random.choice(LEVELS)
 | 
				
			||||||
            extra = {'qThreadName': ctname() }
 | 
					            extra = {'qThreadName': ctname() }
 | 
				
			||||||
            logger.log(level, 'Manually logged!', extra=extra)
 | 
					            logger.log(level, 'Manually logged!', extra=extra)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2947,6 +2968,7 @@ refer to the comments in the code for more detailed information.
 | 
				
			||||||
        def clear_display(self):
 | 
					        def clear_display(self):
 | 
				
			||||||
            self.textedit.clear()
 | 
					            self.textedit.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def main():
 | 
					    def main():
 | 
				
			||||||
        QtCore.QThread.currentThread().setObjectName('MainThread')
 | 
					        QtCore.QThread.currentThread().setObjectName('MainThread')
 | 
				
			||||||
        logging.getLogger().setLevel(logging.DEBUG)
 | 
					        logging.getLogger().setLevel(logging.DEBUG)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue