From: Jens Steinhauser Date: Wed, 14 Oct 2015 22:09:47 +0000 (+0200) Subject: Show log messages in the GUI. X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;ds=sidebyside;h=a6fe45e1c6c5bc2659f670768bcdcf5f6f483966;hp=ac584e86982b0251970cef07d01d3646a5df4d0a;p=sigrok-meter.git Show log messages in the GUI. --- diff --git a/mainwindow.py b/mainwindow.py index 64caa2a..a28e15c 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -21,11 +21,14 @@ import acquisition import datamodel +import datetime import icons import multiplotwidget import os.path import qtcompat import settings +import sigrok.core as sr +import sys import textwrap import time import util @@ -67,6 +70,9 @@ class MainWindow(QtGui.QMainWindow): self.context = context self.drivers = drivers + self.logModel = QtGui.QStringListModel(self) + self.context.set_log_callback(self._log_callback) + self.delegate = datamodel.MultimeterDelegate(self, self.font()) self.model = datamodel.MeasurementDataModel(self) @@ -100,6 +106,31 @@ class MainWindow(QtGui.QMainWindow): self.start_stop_acquisition() + def _log_callback(self, level, message): + if level.id > settings.logging.level.value().id: + return + + t = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + message = '[{}] sr: {}'.format(t, message) + + sys.stderr.write(message + '\n') + + scrollBar = self.logView.verticalScrollBar() + bottom = scrollBar.value() == scrollBar.maximum() + + rows = self.logModel.rowCount() + maxrows = settings.logging.lines.value() + while rows > maxrows: + self.logModel.removeRows(0, 1) + rows -= 1 + + if self.logModel.insertRow(rows): + index = self.logModel.index(rows) + self.logModel.setData(index, message, QtCore.Qt.DisplayRole) + + if bottom: + self.logView.scrollToBottom() + def _setup_ui(self): self.setWindowTitle('sigrok-meter') # Resizing the listView below will increase this again. @@ -152,10 +183,10 @@ class MainWindow(QtGui.QMainWindow): #actionAdd.setIcon(icons.add) #actionAdd.triggered.connect(self.showAddDevicePage) - #actionLog = self.sideBar.addAction('Logs') - #actionLog.setCheckable(True) - #actionLog.setIcon(icons.log) - #actionLog.triggered.connect(self.showLogPage) + actionLog = self.sideBar.addAction('Logs') + actionLog.setCheckable(True) + actionLog.setIcon(icons.log) + actionLog.triggered.connect(self.showLogPage) actionPreferences = self.sideBar.addAction('Preferences') actionPreferences.setCheckable(True) @@ -166,7 +197,7 @@ class MainWindow(QtGui.QMainWindow): self.actionGroup = QtGui.QActionGroup(self) self.actionGroup.addAction(actionGraph) #self.actionGroup.addAction(actionAdd) - #self.actionGroup.addAction(actionLog) + self.actionGroup.addAction(actionLog) self.actionGroup.addAction(actionPreferences) # show graph at startup @@ -242,10 +273,14 @@ class MainWindow(QtGui.QMainWindow): layout.addWidget(label) def _setup_logPage(self): + self.logView = QtGui.QListView(self) + self.logView.setModel(self.logModel) + self.logView.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.logView.setSelectionMode(QtGui.QAbstractItemView.NoSelection) + self.logPage = QtGui.QWidget(self) layout = QtGui.QVBoxLayout(self.logPage) - label = QtGui.QLabel('log page') - layout.addWidget(label) + layout.addWidget(self.logView) def _setup_preferencesPage(self): self.preferencesPage = QtGui.QWidget(self) @@ -262,6 +297,41 @@ class MainWindow(QtGui.QMainWindow): spin.valueChanged[int].connect(settings.graph.backlog.setValue) layout.addWidget(spin, 1, 1) + layout.addWidget(QtGui.QLabel('Logging'), 2, 0) + layout.addWidget(QtGui.QLabel('Log level:'), 3, 0) + + cbox = QtGui.QComboBox() + descriptions = [ + 'no messages at all', + 'error messages', + 'warnings', + 'informational messages', + 'debug messages', + 'very noisy debug messages' + ] + for i, desc in enumerate(descriptions): + level = sr.LogLevel.get(i) + text = '{} ({})'.format(level.name, desc) + # The numeric log level corresponds to the index of the text in the + # combo box. Should this ever change, we could use the 'userData' + # that can also be stored in the item. + cbox.addItem(text) + + cbox.setCurrentIndex(settings.logging.level.value().id) + cbox.currentIndexChanged[int].connect( + (lambda i: settings.logging.level.setValue(sr.LogLevel.get(i)))) + layout.addWidget(cbox, 3, 1) + + layout.addWidget(QtGui.QLabel('Number of lines to log:'), 4, 0) + + spin = QtGui.QSpinBox(self) + spin.setMinimum(100) + spin.setMaximum(10 * 1000 * 1000) + spin.setSingleStep(100) + spin.setValue(settings.logging.lines.value()) + spin.valueChanged[int].connect(settings.logging.lines.setValue) + layout.addWidget(spin, 4, 1) + layout.setRowStretch(layout.rowCount(), 100) def showPage(self, page): diff --git a/settings.py b/settings.py index 1c5523d..c779ce4 100644 --- a/settings.py +++ b/settings.py @@ -19,6 +19,7 @@ ## import qtcompat +import sigrok.core as sr QtCore = qtcompat.QtCore QtGui = qtcompat.QtGui @@ -30,32 +31,34 @@ class Setting(QtCore.QObject): '''Signal emitted when the setting has changed.''' changed = QtCore.Signal(object) - def __init__(self, key, default=None, conv=None): + def __init__(self, key, default=None, s=None, d=None): '''Initializes the Settings object. :param key: The key of the used 'QSettings' object. :param default: Value returned if the setting doesn't already exist. - :param conv: Function used to convert the setting to the correct type. + :param s: Function used to serialize the value into a string. + :param d: Function used to convert a string into a value. ''' super(self.__class__, self).__init__() self._key = key self._default = default - self._conv = conv + self._serialize = s if s else (lambda x: x) + self._deserialize = d if d else (lambda x: x) self._value = None def value(self): s = QtCore.QSettings() v = s.value(self._key, self._default) - self._value = self._conv(v) if self._conv else v + self._value = self._deserialize(v) return self._value @QtCore.Slot(object) def setValue(self, value): if value != self._value: s = QtCore.QSettings() - s.setValue(self._key, value) + s.setValue(self._key, self._serialize(value)) s.sync() self._value = value self.changed.emit(self._value) @@ -64,6 +67,28 @@ class _SettingsGroup(object): '''Dummy class to group multiple 'Setting' objects together.''' pass +_default_loglevel = 'WARN' + +def _d_loglevel(s): + '''Converts a string into a sr.LogLevel.''' + d = { + 'NONE': sr.LogLevel.NONE, + 'ERR': sr.LogLevel.ERR, + 'WARN': sr.LogLevel.WARN, + 'INFO': sr.LogLevel.INFO, + 'DBG': sr.LogLevel.DBG, + 'SPEW': sr.LogLevel.SPEW + } + + if not (s in d): + s = _default_loglevel + + return d[s] + +def _s_loglevel(l): + '''Converts a sr.LogLevel into a string.''' + return l.name + def init(): '''Creates the 'Settings' objects for all known settings and places them into the module's namespace. @@ -82,5 +107,11 @@ def init(): globals()['mainwindow'] = mainwindow graph = _SettingsGroup() - graph.backlog = Setting('graph/backlog', 30, conv=int) + graph.backlog = Setting('graph/backlog', 30, d=int) globals()['graph'] = graph + + logging = _SettingsGroup() + logging.level = Setting('logging/level', _default_loglevel, + s=_s_loglevel, d=_d_loglevel) + logging.lines = Setting('logging/lines', 1000, d=int) + globals()['logging'] = logging diff --git a/sigrok-meter b/sigrok-meter index a56cf68..c991fad 100755 --- a/sigrok-meter +++ b/sigrok-meter @@ -27,7 +27,6 @@ import textwrap import signal default_drivers = [('demo:analog_channels=4', 'samplerate=4')] -default_loglevel = 2 def parse_cli(): parser = argparse.ArgumentParser( @@ -63,7 +62,7 @@ def parse_cli(): help='Specify device configuration options') parser.add_argument('-l', '--loglevel', type=int, - default=default_loglevel, + default=None, help='Set loglevel (5 is most verbose)') parser.add_argument('--pyside', action='store_true', @@ -98,13 +97,6 @@ if __name__ == '__main__': QtGui = qtcompat.QtGui import mainwindow - context = sr.Context_create() - try: - loglevel = sr.LogLevel.get(args.loglevel) - context.log_level = loglevel - except: - sys.exit('Error: invalid log level.') - app = QtGui.QApplication([]) # Initialize modules that need a QApplication to exist. @@ -113,6 +105,15 @@ if __name__ == '__main__': import icons icons.load_icons() + context = sr.Context_create() + + if args.loglevel != None: + try: + loglevel = sr.LogLevel.get(args.loglevel) + settings.logging.level.setValue(loglevel) + except: + sys.exit('Error: invalid log level.') + s = mainwindow.MainWindow(context, args.drivers) s.show()