(continued from previous page) (continues on next page) 45 # Let the thread run until interrupted. This allows reasonably clean # thread termination. while not QtCore.QThread .currentThread() .isInterruptionRequested(): delay =0.5 +random.random()*2 time.sleep(delay) level=random.choice(LEVELS) logger.log(level, 'Message after delay of %3.1f : %d ', delay, i, ␣ ↪ → extra=extra) i +=1 # # Implement a simple UI for this cookbook example. This contains: # # * A read-only text edit window which holds formatted log messages # * A button to start work and log stuff in a separate thread # * A button to log something from the main thread # * A button to clear the log window # classWindow (QtWidgets.QWidget): COLORS ={ logging.DEBUG:'black', logging.INFO: 'blue', logging.WARNING: 'orange', logging.ERROR: 'red', logging.CRITICAL: 'purple', } def __init__ (self, app): super() .__init__ () self.app =app self.textedit =te =QtWidgets.QPlainTextEdit( self) # Set whatever the default monospace font is for the platform f =QtGui .QFont('nosuchfont') f.setStyleHint(f .Monospace) te.setFont(f) te.setReadOnly( True ) PB =QtWidgets.QPushButton self.work_button=PB('Start background work ',self) self.log_button=PB('Log a message at a random level ',self) self.clear_button=PB('Clear log window',self) self.handler =h =QtHandler(self.update_status) # Remember to use qThreadName rather than threadName in the format string. fs =' %(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s ' formatter =logging.Formatter(fs) h .setFormatter(formatter) logger.addHandler(h) # Set up to terminate the QThread when we exit app.aboutToQuit.connect( self.force_quit) # Lay out all the widgets layout=QtWidgets.QVBoxLayout(self) layout.addWidget(te) layout.addWidget(self.work_button) layout.addWidget(self.log_button) layout.addWidget(self.clear_button) self.setFixedSize( 900 ,400 ) # Connect the non-worker slots and signals self.log_button .clicked.connect( self.manual_update) self.clear_button .clicked.connect( self.clear_display)