]> sigrok.org Git - sigrok-meter.git/commitdiff
Show log messages in the GUI.
authorJens Steinhauser <redacted>
Wed, 14 Oct 2015 22:09:47 +0000 (00:09 +0200)
committerJens Steinhauser <redacted>
Wed, 14 Oct 2015 22:09:47 +0000 (00:09 +0200)
mainwindow.py
settings.py
sigrok-meter

index 64caa2a7e9b72b03afef01d0ba4978b06aba2cc8..a28e15c5990e53e1893d7f7a25ae37db5250c03f 100644 (file)
 
 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('<b>Logging</b>'), 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):
index 1c5523df4575a319e06e8cf9baa3c3e1ae837b71..c779ce460e5035a3dba743165274df6d86bb8c14 100644 (file)
@@ -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
index a56cf68f59caa8743854884afa343ae102ecf190..c991fad0e5fdf3dfddac710d0e557ead8b641a4b 100755 (executable)
@@ -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()