Source code for rcp.QtLog

"""PyQt5 widgets to show and control logging output.
"""

################################################################
# Written in 2017-2019 by Garth Zeglin <garthz@cmu.edu>

# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.

# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

################################################################
# standard Python libraries
from __future__ import print_function
import logging

# for documentation on the PyQt5 API, see http://pyqt.sourceforge.net/Docs/PyQt5/index.html
from PyQt5 import QtCore, QtGui, QtWidgets

# set up logger for module
log = logging.getLogger('QtLog')

################################################################
[docs]class QtLog(QtWidgets.QWidget): """Composite widget including log text output and control widgets. :param level: initial logging level, defaults to logging.WARNING :param logger: logger object to which to attach, defaults to root logger """ def __init__(self, level = None, logger = None): super().__init__() if logger is None: # if no logger provided, attach to the root logger self._logger = logging.getLogger() else: self._logger = logger self._handler = logging.StreamHandler(self) self._handler.setFormatter(logging.Formatter('%(levelname)s:%(name)s: %(message)s')) self._logger.addHandler(self._handler) if level not in (logging.WARNING, logging.INFO, logging.DEBUG): level = logging.WARNING # set the handler to the requested level, but turn off the logger level # filtering; the logger could be attached to other handlers, so this # will apply level filters at the handlers self._handler.setLevel(level) self._logger.setLevel(logging.NOTSET) self._layout = QtWidgets.QVBoxLayout() self.setLayout(self._layout) controls_box = QtWidgets.QHBoxLayout() self._layout.addLayout(controls_box) level_label = QtWidgets.QLabel() level_label.setText("Global log display level:") controls_box.addWidget(level_label) self.level_selector = QtWidgets.QComboBox() self.level_selector.addItem("Normal") self.level_selector.addItem("Verbose") self.level_selector.addItem("Debug") self.level_selector.setCurrentIndex({logging.WARNING:0, logging.INFO:1, logging.DEBUG:2}[level]) controls_box.addWidget(self.level_selector) self.level_selector.activated['QString'].connect(self._set_logging_level) self.log_output = QtWidgets.QPlainTextEdit() self.log_output.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self._layout.addWidget(self.log_output) return
[docs] def write(self, string): """Write output to the log text area. This enables this object to be used as a stream for printing.""" stripped = string.rstrip() if stripped != "": self.log_output.appendPlainText(stripped) return
def _set_logging_level(self, title): # receive a signal when the user chooses an item, possibly unchanged log.info("Requested logging level: %s", title) levels = {'Normal': logging.WARNING, 'Verbose': logging.INFO, 'Debug': logging.DEBUG} level = levels.get(title, None) if level is not None: self._handler.setLevel(level) log.info("Set logging level to %r", level)
[docs] def set_logging_level(self, title): """Set the current logging level and level display.""" self._set_logging_level(title) self.level_selector.setCurrentText(title)
def get_logging_level(self): return self.level_selector.currentText() def flush_and_remove_memory_handler(self, mem_handler): mem_handler.setTarget(self._handler) mem_handler.flush() self._logger.removeHandler(mem_handler)
################################################################