Source code for qudi.core.gui.main_gui.logwidget

# -*- coding: utf-8 -*-
"""
This file contains the qudi log widget class.

Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
distribution and on <https://github.com/Ulm-IQO/qudi-core/>

This file is part of qudi.

Qudi is free software: you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with qudi.
If not, see <https://www.gnu.org/licenses/>.
"""

from PySide2 import QtCore, QtGui, QtWidgets
from qudi.core.logger import get_record_table_model


[docs] class LogFilterProxy(QtCore.QSortFilterProxyModel): """ A subclass of QProxyFilterModel that determines which log entries contained in the log model are shown in the view. """
[docs] def __init__(self, parent=None): """ Create the LogFilterProxy. Parameters ---------- parent : QObject Parent object of the filter. """ super().__init__(parent) self._show_levels = frozenset({'debug', 'info', 'warning', 'error', 'critical'})
def filterAcceptsRow(self, source_row, source_parent): """ Determine whether a row (log entry) should be shown. Parameters ---------- source_row : QModelIndex The row in the source model that needs to be filtered. source_parent : QModelIndex The parent model index. Returns ------- bool `True` if the row (log entry) should be shown, `False` otherwise. """ model = self.sourceModel() level = model.data(model.index(source_row, 1), QtCore.Qt.DisplayRole) if level is None: return False return level in self._show_levels def set_levels(self, levels): """ Set which types of messages are shown through the filter. Parameters ---------- levels : set of str Set of all message levels that should be shown. """ self._show_levels = frozenset(levels) self.invalidateFilter()
[docs] class SelectableTextDelegate(QtWidgets.QStyledItemDelegate): """A subclass of QStyledItemDelegate to display a text editor for copying text fragments. """ def createEditor(self, parent, option, index): """ Overwrite method from base class QStyledItemDelegate to show a read-only QLabel widget. This is necessary to disable editing by the user but still allow text to be marked and copied. Parameters ---------- parent : QObject The parent object for the editor to be created. option : QStyleOptionViewItem Display options for the editor widget. index : QModelIndex Data model index. Returns ------- QLabel Instance of QLabel. """ editor = QtWidgets.QLabel(parent) editor.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) editor.setAlignment(option.displayAlignment) return editor def setEditorData(self, editor, index): """ Overwrite method from the base class QStyledItemDelegate to fill the QLineEdit widget with data. Parameters ---------- editor : QLineEdit Editor widget to be populated with data. index : QModelIndex Data model index. """ data = index.data(QtCore.Qt.EditRole) editor.setText(f' {data}')
[docs] class LogTableWidget(QtWidgets.QTableView): """Customized QTableView including the model for display of logging entries. """
[docs] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) record_model = get_record_table_model() if record_model is None: raise RuntimeError('No record table model set up in qudi.core.logger') self.filter_model = LogFilterProxy() self.filter_model.setSourceModel(record_model) self.setModel(self.filter_model) self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) self.setEditTriggers(QtWidgets.QTableView.DoubleClicked) self.setAlternatingRowColors(True) self.setSelectionMode(QtWidgets.QTableView.NoSelection) self.setHorizontalScrollMode(QtWidgets.QTableView.ScrollPerPixel) self.setVerticalScrollMode(QtWidgets.QTableView.ScrollPerPixel) self.setShowGrid(False) self.setCornerButtonEnabled(False) self.horizontalHeader().setCascadingSectionResizes(True) self.horizontalHeader().setStretchLastSection(True) self.verticalHeader().hide() self.setItemDelegate(SelectableTextDelegate()) self.horizontalHeader().setMinimumSectionSize(50) self.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed) self.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed) # Set fixed with for "Time" and "Level" columns since they contain fixed width strings metrics = QtGui.QFontMetrics(self.font()) self.setColumnWidth(0, metrics.horizontalAdvance(' 5555-55-55 55:55:55 ')) self.setColumnWidth(1, metrics.horizontalAdvance(' warning ')) # Estimate starting width of "Source" column self.setColumnWidth(2, metrics.horizontalAdvance(__name__ * 2)) self.filter_model.rowsInserted.connect(self._entry_added)
@QtCore.Slot(QtCore.QModelIndex, int, int) def _entry_added(self, parent, first, last): while first <= last: self.resizeRowToContents(first) first += 1 self.scrollToBottom() def set_level_filter(self, show_levels): self.filter_model.set_levels(show_levels) self.scrollToBottom()
[docs] class LogWidget(QtWidgets.QSplitter): """A widget to show log entries and filter them. """
[docs] def __init__(self, parent=None, debug_mode=False, **kwargs): """ Creates the log widget. Parameters ---------- parent : QObject Qt parent object for the log widget. manager : Manager Manager instance this widget belongs to. """ super().__init__(QtCore.Qt.Horizontal, parent, **kwargs) # Build GUI elements # Set up QTableView to display log entries self.log_tablewidget = LogTableWidget() self.log_tablewidget.setObjectName('log_table_widget') # Set up QTreeWidget for log filter ui self.filter_treewidget = QtWidgets.QTreeWidget() self.filter_treewidget.setObjectName('filter_treewidget') self.filter_treewidget.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) self.filter_treewidget.setMinimumSize(210, 0) self.filter_treewidget.setEditTriggers(QtWidgets.QTreeWidget.NoEditTriggers) self.filter_treewidget.setDropIndicatorShown(False) self.filter_treewidget.setDragEnabled(False) self.filter_treewidget.setSelectionMode(QtWidgets.QTreeWidget.NoSelection) self.filter_treewidget.setSelectionBehavior(QtWidgets.QTreeWidget.SelectItems) self.filter_treewidget.setColumnCount(1) self.filter_treewidget.setHeaderLabels(('Display:',)) item = QtWidgets.QTreeWidgetItem() item.setText(0, 'All message types:') item.setCheckState(0, QtCore.Qt.Checked) log_levels = ('debug', 'info', 'warning', 'error', 'critical')[int(not debug_mode):] for text in log_levels: child_item = QtWidgets.QTreeWidgetItem() child_item.setText(0, text) child_item.setCheckState(0, QtCore.Qt.Checked) item.addChild(child_item) self.filter_treewidget.addTopLevelItem(item) self.filter_treewidget.expandItem(item) self.log_tablewidget.set_level_filter(log_levels) # embed log view and filter tree into QSplitter widget self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) self.addWidget(self.log_tablewidget) self.addWidget(self.filter_treewidget) self.setStretchFactor(0, 1) # connect signals self.filter_treewidget.itemChanged.connect(self.update_filter_state)
@QtCore.Slot(object, int) def update_filter_state(self, item, column): """ Update the log view based on the filter widget check states and synchronize check box states. Parameters ---------- item : int Item number. column : int Column number. """ # check all / uncheck all state show_all_item = self.filter_treewidget.topLevelItem(0) level_items = [show_all_item.child(ii) for ii in range(show_all_item.childCount())] if item is show_all_item: self.filter_treewidget.expandItem(item) if show_all_item.checkState(0): self.filter_treewidget.blockSignals(True) for it in level_items: it.setCheckState(0, QtCore.Qt.Checked) self.filter_treewidget.blockSignals(False) else: # Prevent user from unchecking "show all" self.filter_treewidget.blockSignals(True) show_all_item.setCheckState(0, QtCore.Qt.Checked) self.filter_treewidget.blockSignals(False) else: show_all = all(it.checkState(0) for it in level_items) self.filter_treewidget.blockSignals(True) show_all_item.setCheckState(0, QtCore.Qt.Checked if show_all else QtCore.Qt.Unchecked) self.filter_treewidget.blockSignals(False) # set level filters level_filter = {str(it.text(0)) for it in level_items if it.checkState(0)} self.log_tablewidget.set_level_filter(level_filter)