Refactored the preference system for the qt gpib plotter app. One can now set the colors for plotting pens.

Sun, 03 Feb 2008 23:39:27 +0100

author
David Douard <david.douard@logilab.fr>
date
Sun, 03 Feb 2008 23:39:27 +0100
changeset 35
3b7a38af5c42
parent 34
022e881b758e
child 36
cb4124e3e75b

Refactored the preference system for the qt gpib plotter app. One can now set the colors for plotting pens.

bin/qgpibplotter file | annotate | diff | comparison | revisions
gpib.py file | annotate | diff | comparison | revisions
plotter/hpgl_qt.py file | annotate | diff | comparison | revisions
plotter/qgpib_plotter.py file | annotate | diff | comparison | revisions
plotter/qpreferences.py file | annotate | diff | comparison | revisions
plotter/qpreferences_dialog.ui file | annotate | diff | comparison | revisions
prologix.py file | annotate | diff | comparison | revisions
pygpib.py file | annotate | diff | comparison | revisions
tools.py file | annotate | diff | comparison | revisions
--- a/bin/qgpibplotter	Sat Jan 26 11:39:30 2008 +0100
+++ b/bin/qgpibplotter	Sun Feb 03 23:39:27 2008 +0100
@@ -1,4 +1,8 @@
 #!/usr/bin/python
-
-from plotter.qgpib_plotter import main
+import sys, os
+try:
+    from plotter.qgpib_plotter import main
+except ImportError:
+    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
+    from plotter.qgpib_plotter import main
 main()
--- a/gpib.py	Sat Jan 26 11:39:30 2008 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-"""
-gpib: create serial connection to GPIB-USB device (ProLogix is the
-only supported device for now).
-"""
-import serial
-from serial.serialutil import SerialException
-import time
-
-class ConnectionError(Exception):
-    pass
-
-class Constants(object):
-    def __init__(self):
-        self.constants = {}
-        self.descriptions = {}
-        self.rev_constants = {}
-        for v, k, m in self._constants:
-            self.k = v
-            self.constants[v] = k
-            self.rev_constants[k] = v
-            self.descriptions[v] = m
-            
-    def __getitem__(self, k):
-        if isinstance(k, basestring):
-            return self.rev_constants[k]
-        else:
-            return self.constants[k]
-
-    def get_description(self, k):
-        if isinstance(k, basestring):
-            k = self.rev_constants[k]
-        return self.descriptions[k]
-    
-
-class MODE(Constants):    
-    _constants = [(1, "CONTROLLER", "Set device as Controller in Charge"),
-                  (0, "DEVICE", "Set device as simple listener"),
-                  ]
-# TODO
-# class STATUS_BYTE(Constants):
-#     # IEEE 488.2 Status Byte constants
-#     MAV = 0x10 # Message AVailable: bit 4 of the Status Byte
-#     ESB = 0x20 # Event Status Bit: bit 5 of the Status Byte
-#     MSS = 0x40 # Master Summary Status bit: bit 6 of the Status Byte (NOT
-#                # sent in response to a serial poll)
-#     RQS = 0x40 # Request Service: bit 6 of the Status Byte (when sent in
-#                # response to a serial poll)
-# class SESR(Constants):
-#     # SESR constants (Standard Event Status Register)
-#     PON = 0x80 # Power On: Power has been turned On since last register
-#                # read access
-#     URQ = 0x40 # User Request: the user has activated some device control
-#                # (whatever the Remote Local state is)
-#     CME = 0x20 # Command Error
-#     EXE = 0x10 # Execution Error
-#     DDE = 0x08 # Device Dependant Error
-#     QYE = 0x04 # QuerY Error (attempt to read data while Output Queue is
-#                # empty, or data in the OQ was lost)
-#     RQC = 0x02 # Request Control: tell the CiC that the device wants to
-#                # become the CiC
-#     OPC = 0x01 # Operation Complete: device has completed any pending
-#                # operation (ready to accept new commands). This bit is
-#                # generated in response to a OPC command.
-    
--- a/plotter/hpgl_qt.py	Sat Jan 26 11:39:30 2008 +0100
+++ b/plotter/hpgl_qt.py	Sun Feb 03 23:39:27 2008 +0100
@@ -24,6 +24,7 @@
         l = QtGui.QVBoxLayout(self)
         l.setMargin(1)
         self.qview = QtGui.QGraphicsView(self)
+        self.qview.setRenderHints(QtGui.QPainter.Antialiasing)
         self.qview.scale(0.5,-0.5)
         l = self.layout()
         l.addWidget(self.qview)
@@ -72,7 +73,12 @@
             
     def SP(self):
         HPGLParser.SP(self)
-        self.qpen.setColor(QtGui.QColor(self.pen_colors[self.pen]))
+        color = self.pen_colors[self.pen]
+        if isinstance(color, tuple):
+            color = QtGui.QColor(*color)
+        else:
+            color = QtGui.QColor(color)
+        self.qpen.setColor(color)
         
     def parse(self, data):
         HPGLParser.parse(self, data)
--- a/plotter/qgpib_plotter.py	Sat Jan 26 11:39:30 2008 +0100
+++ b/plotter/qgpib_plotter.py	Sun Feb 03 23:39:27 2008 +0100
@@ -17,18 +17,54 @@
 sys.path.append(ldir)
 form_class, base_class = uic.loadUiType(os.path.join(ldir, "qhpgl_plotter.ui"))
 
-from qpreferences import PreferenceItem, AbstractPreferences, PreferencesEditor
+from qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem
+from qpreferences import PointItem, SizeItem, ByteArrayItem
+from qpreferences import AbstractPreferences
+from qpreferenceseditor import PreferencesEditor
 
 class Preferences(AbstractPreferences):
     ORGANISATION="Logilab"
     APPLICATION="qgpib_plotter"
 
-    device = PreferenceItem('/dev/ttyUSB0', name=u'device', description=u'GPIB device')
-    address = PreferenceItem(5, name=u'GPIB address')
-    _pos = PreferenceItem(basetype=QtCore.QPoint)
-    _size = PreferenceItem(basetype=QtCore.QSize)
-    _appState = PreferenceItem(basetype=QtCore.QByteArray)
+    _pos = PointItem()
+    _size = SizeItem()
+    _appState = ByteArrayItem()
 
+    device = UnicodeItem(default='/dev/ttyUSB0',
+                         name=u'device',
+                         description=u'GPIB device',
+                         group="GPIB settings")
+    address = IntItem(default=5, min=0, max=16,
+                      name=u'GPIB address',
+                      group="GPIB settings")
+    
+    background = ColorItem(default=QtGui.QColor("white"),
+                           name="Background",
+                           group="Colors")
+    color0 = ColorItem(default=QtGui.QColor("black"),
+                       name="Pen #0",
+                       group="Colors")
+    color1 = ColorItem(default=QtGui.QColor("green"),
+                       name="Pen #1",
+                       group="Colors")
+    color2 = ColorItem(default=QtGui.QColor("red"),
+                       name="Pen #2",
+                       group="Colors")
+    color3 = ColorItem(default=QtGui.QColor("blue"),
+                       name="Pen #3",
+                       group="Colors")
+    color4 = ColorItem(default=QtGui.QColor("yellow"),
+                       name="Pen #4",
+                       group="Colors")
+    color5 = ColorItem(default=QtGui.QColor("cyan"),
+                       name="Pen #5",
+                       group="Colors")
+    color6 = ColorItem(default=QtGui.QColor("magenta"),
+                       name="Pen #6",
+                       group="Colors")
+    color7 = ColorItem(default=QtGui.QColor("darkred"),
+                       name="Pen #7",
+                       group="Colors")
     
 class QtHPGLPlotter(QtGui.QMainWindow, form_class):
     def __init__(self, parent=None):
@@ -43,7 +79,19 @@
             self.resize(self._prefs._size)
         if self._prefs._appState:
             self.restoreState(self._prefs._appState)
+        self.readPreferences()
 
+    def readPreferences(self):
+        bg = self._prefs.background
+        if bg and bg.isValid():
+            self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg))
+        pen_colors = [self._prefs["color%d"%i] for i in range(8)]
+        print 
+        self.plotterWidget.pen_colors = pen_colors
+        
+    def replotCurrent(self):
+        self.currentPlotChanged(self.plotsView.currentIndex())
+        
     def setupUi(self):
         form_class.setupUi(self, self) # call qtdesigner generated form creation        
         # actions defined in designer
@@ -84,6 +132,8 @@
             
     def preferencesTriggered(self, checked=False):
         PreferencesEditor(self._prefs, self).exec_()
+        self.readPreferences()
+        self.replotCurrent()
 
     def quitTriggered(self, checked=False):
         self.close()
--- a/plotter/qpreferences.py	Sat Jan 26 11:39:30 2008 +0100
+++ b/plotter/qpreferences.py	Sun Feb 03 23:39:27 2008 +0100
@@ -1,5 +1,6 @@
 import os
 from PyQt4 import QtCore, QtGui, uic
+from tools import AbstractRegister 
 
 def fromVariant(v):
     _cvrts = {0: lambda x:None,
@@ -12,46 +13,114 @@
               22: lambda x:x.toSizeF(),
               25: lambda x:x.toPoint(),
               26: lambda x:x.toPointF(),
-              
+              64: lambda x:QtGui.QFont(x),
+              67: lambda x:QtGui.QColor(x),
               }
     t = v.userType()
     return _cvrts[t](v)
-    
-class PreferenceItem(object):
+
+class PreferenceMetaclass(type):
+    _widgets = {}
+    def __init__(cls, name, bases, dct):
+        # called at class creation
+        super(type, cls).__init__(name, bases, dct)
+        if name != "BaseItem":
+            ItemRegister().add(cls)
+
+class BaseItem(object):
+    #__metaclass__ = PreferenceMetaclass
     _id = 0
-    def __init__(self, default=None, basetype=None, name=None, description=None):
+    def __init__(self, default=None, name=None, description=None, group=None):
         self._default = default
-        self._basetype = basetype
-        if self._basetype is None:
-            self._basetype = self._default.__class__
-        self._id = "_pref%X"%self.__class__._id
+        self._id = BaseItem._id
         self._name = name
         self._description = description
-        self.__class__._id += 1
+        self._group = group
+        BaseItem._id += 1
 
+    def validate(self, value):
+        return True
+    
     def __get__(self, obj, cls):
-        if obj is None:
+        if obj is None: #when called from the class, return the Item itself
             return self
         try:
             return obj.getPref(self._id)
         except Exception, e:
-            #print "humm", e
             return None
 
     def __set__(self, obj, value):
         obj.setPref(self._id, value)
+
+class ItemRegister(AbstractRegister):
+    _registered_type = BaseItem
+    getItem = AbstractRegister.get_class
+
+class PointItem(BaseItem):
+    _type = QtCore.QPoint
+
+class SizeItem(BaseItem):
+    _type = QtCore.QSize
+
+class ByteArrayItem(BaseItem):
+    _type = QtCore.QByteArray
+    
+class UnicodeItem(BaseItem):
+    _type = unicode
+    def validate(self, value):
+        return isinstance(value, basestring)
         
+class IntItem(BaseItem):
+    _type = int
+    def __init__(self, default=None, name=None, description=None, group=None, min=None, max=None):
+        BaseItem.__init__(self, default, name, description, group)
+        self._min = min
+        self._max = max
+
+    def validate(self, value):
+        try:
+            value = self._type(value)
+        except:
+            return False
+        if self._min is not None and value<self._min:
+            return False
+        if self._max is not None and value>self._max:
+            return False
+        return True
+
+class ColorItem(BaseItem):
+    _type = QtGui.QColor
+
+    def validate(self, value):
+        try:
+            self._type(value)
+            return True
+        except:
+            return False
+    
 class AbstractPreferences(QtCore.QObject):
     def __init__(self):
         QtCore.QObject.__init__(self)
         self._settings = QtCore.QSettings(QtCore.QSettings.UserScope,
                                           self.ORGANISATION, self.APPLICATION)
         self._prefs = {}
+        self.groups = []
+        keys = []
         for k in dir(self.__class__):
-            item = getattr(self.__class__, k)
-            if isinstance(item, PreferenceItem):
-                self._prefs[item._id] = k            
-        
+            item = self.getItem(k)
+            if isinstance(item, BaseItem):
+                keys.append((k,item))
+        keys.sort(key=lambda x: x[1]._id)
+        print [x[1]._id for x in keys]
+        for k, item in keys:
+            self._prefs[item._id] = k
+            if item._group not in self.groups:
+                self.groups.append(item._group)
+
+    def getItem(self, key):
+        return getattr(self.__class__, key)
+        #return self._prefs.get(key, None)
+    
     def getPref(self, key):
         key = self._prefs.get(key, key)
         default = getattr(self.__class__, key)._default
@@ -59,15 +128,15 @@
             default = QtCore.QVariant(default)
         else:
             default = QtCore.QVariant()
-        val = self._settings.value(key, default)
-        return fromVariant(val)
+        val = fromVariant(self._settings.value(key, default))
+        return val
     
     def setPref(self, key, value):
         key = self._prefs.get(key, key)
         self._settings.setValue(key, QtCore.QVariant(value))
 
-    def keys(self):
-        return [k for k in self._prefs.values() if not k.startswith('_')]
+    def keys(self, group=None):
+        return [k for k in self._prefs.values() if not k.startswith('_') and self.getItem(k)._group == group]
 
     def getName(self, key):
         item = getattr(self.__class__, key)
@@ -76,66 +145,7 @@
     def getDescription(self, key):
         item = getattr(self.__class__, key)
         return item._description
+
+    def __getitem__(self, key):
+        return getattr(self, key)
     
-form_class, base_class = uic.loadUiType(os.path.join(os.path.dirname(__file__), "qpreferences_dialog.ui"))
-
-class PreferencesEditor(QtGui.QDialog, form_class):
-    def __init__(self, preferences, parent=None):
-        QtGui.QDialog.__init__(self, parent)
-        self.setupUi(self)
-        self._prefs = preferences
-        self.buildUI()
-
-    def buildUI(self):
-        w = self.centralFrame
-        g = QtGui.QGridLayout(w)
-        p = self._prefs
-        eds = {}
-        self._editors = eds
-        for i, k in enumerate(p.keys()):
-            name = p.getName(k)
-            if not name:
-                name = k
-            l = QtGui.QLabel(name, w)
-            g.addWidget(l, i, 0)
-            if p.getDescription(k):
-                l.setToolTip(p.getDescription(k))
-            
-            e = QtGui.QLineEdit(w)
-            eds[k] = e
-            g.addWidget(e, i, 1)
-            val = p.getPref(k)
-            if val is None:
-                val = ''
-            if not isinstance(val, basestring):
-                val = unicode(val)
-            e.setText(val)
-            
-        g.addWidget(QtGui.QWidget(w), i+1, 0)
-        g.setRowStretch(i+1,1)
-        g.setColumnStretch(1,1)
-
-    def accept(self):
-        p=self._prefs
-        for k in p.keys():
-            newval = unicode(self._editors[k].text())
-            p.setPref(k, newval)            
-        return QtGui.QDialog.accept(self)
-            
-if __name__ == '__main__':
-    class TestPreferences(AbstractPreferences):
-        ORGANISATION="Logilab"
-        APPLICATION="test_qpref_editor"
-
-        device = PreferenceItem('/dev/ttyUSB0', name="the device")
-        address = PreferenceItem(5, description="GPIB address of the plotter")
-        _pos = PreferenceItem(None)
-        _size = PreferenceItem(None)
-        _appState = PreferenceItem(None)
-        
-    a = QtGui.QApplication([])
-
-    prefs = TestPreferences()
-    w = PreferencesEditor(prefs)
-    w.show()
-    a.exec_()
--- a/plotter/qpreferences_dialog.ui	Sat Jan 26 11:39:30 2008 +0100
+++ b/plotter/qpreferences_dialog.ui	Sun Feb 03 23:39:27 2008 +0100
@@ -14,13 +14,15 @@
   </property>
   <layout class="QVBoxLayout" >
    <item>
-    <widget class="QFrame" name="centralFrame" >
-     <property name="frameShape" >
-      <enum>QFrame::StyledPanel</enum>
+    <widget class="QTabWidget" name="centralTab" >
+     <property name="currentIndex" >
+      <number>0</number>
      </property>
-     <property name="frameShadow" >
-      <enum>QFrame::Raised</enum>
-     </property>
+     <widget class="QWidget" name="tab" >
+      <attribute name="title" >
+       <string>Tab 1</string>
+      </attribute>
+     </widget>
     </widget>
    </item>
    <item>
--- a/prologix.py	Sat Jan 26 11:39:30 2008 +0100
+++ b/prologix.py	Sun Feb 03 23:39:27 2008 +0100
@@ -8,8 +8,8 @@
 import serial
 import time
 
-import gpib
-from gpib import ConnectionError
+import pygpib
+from pygpib import ConnectionError
 
 GPIB_CONTROLLER = 1
 GPIB_DEVICE = 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpib.py	Sun Feb 03 23:39:27 2008 +0100
@@ -0,0 +1,64 @@
+"""
+gpib: create serial connection to GPIB-USB device (ProLogix is the
+only supported device for now).
+"""
+import serial
+from serial.serialutil import SerialException
+import time
+
+class ConnectionError(Exception):
+    pass
+
+class Constants(object):
+    def __init__(self):
+        self.constants = {}
+        self.descriptions = {}
+        self.rev_constants = {}
+        for v, k, m in self._constants:
+            self.k = v
+            self.constants[v] = k
+            self.rev_constants[k] = v
+            self.descriptions[v] = m
+            
+    def __getitem__(self, k):
+        if isinstance(k, basestring):
+            return self.rev_constants[k]
+        else:
+            return self.constants[k]
+
+    def get_description(self, k):
+        if isinstance(k, basestring):
+            k = self.rev_constants[k]
+        return self.descriptions[k]
+    
+
+class MODE(Constants):    
+    _constants = [(1, "CONTROLLER", "Set device as Controller in Charge"),
+                  (0, "DEVICE", "Set device as simple listener"),
+                  ]
+# TODO
+# class STATUS_BYTE(Constants):
+#     # IEEE 488.2 Status Byte constants
+#     MAV = 0x10 # Message AVailable: bit 4 of the Status Byte
+#     ESB = 0x20 # Event Status Bit: bit 5 of the Status Byte
+#     MSS = 0x40 # Master Summary Status bit: bit 6 of the Status Byte (NOT
+#                # sent in response to a serial poll)
+#     RQS = 0x40 # Request Service: bit 6 of the Status Byte (when sent in
+#                # response to a serial poll)
+# class SESR(Constants):
+#     # SESR constants (Standard Event Status Register)
+#     PON = 0x80 # Power On: Power has been turned On since last register
+#                # read access
+#     URQ = 0x40 # User Request: the user has activated some device control
+#                # (whatever the Remote Local state is)
+#     CME = 0x20 # Command Error
+#     EXE = 0x10 # Execution Error
+#     DDE = 0x08 # Device Dependant Error
+#     QYE = 0x04 # QuerY Error (attempt to read data while Output Queue is
+#                # empty, or data in the OQ was lost)
+#     RQC = 0x02 # Request Control: tell the CiC that the device wants to
+#                # become the CiC
+#     OPC = 0x01 # Operation Complete: device has completed any pending
+#                # operation (ready to accept new commands). This bit is
+#                # generated in response to a OPC command.
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools.py	Sun Feb 03 23:39:27 2008 +0100
@@ -0,0 +1,62 @@
+class AbstractRegister(object):
+    _instance = None
+    _registered_type = None
+    def __new__(cls):
+        # implements a singleton
+        if cls._instance is None:
+            #debug('Instanciating %s', cls.__name__)
+            cls._instance = super(AbstractRegister, cls).__new__(cls)
+            cls._instance.registered = {}
+            cls._instance.accepts = set()
+        return cls._instance
+    
+    def add(self, cls):
+        assert issubclass(cls, self._registered_type)
+        if cls is self._registered_type:
+            return
+        if cls._accepts is None:
+            return
+        #assert isinstance(cls._accepts, (basestring, tuple))
+        
+        #debug("Registerered %s for %s", cls.__name__)
+        if not isinstance(cls._accepts, tuple):
+            cls._accepts = (cls._accepts,)
+        for key in cls._accepts:
+            key = self._get_typ(key)
+            #debug("  new key = %s", key)            
+            self.registered.setdefault(key, []).append(cls)
+            self.accepts.add(key)
+
+    def _get_typ(self, typ):
+        if not isinstance(typ, basestring):
+            if not isinstance(typ, type):
+                return None
+            typ = typ.__name__
+        return typ
+    
+    def __contains__(self, val):
+        val = self._get_typ(val)
+        return val in self.accepts
+
+    def get_class(self, typ):
+        item = typ
+        if not isinstance(typ, type):
+            typ = typ.__class__
+        name = typ.__name__
+        #debug("Looking a widget for %s", typ.__name__)
+        orig_typ = typ
+        while typ is not None:
+            if typ.__name__ in self.registered:
+                for w in self.registered[typ.__name__]:
+                    if w._filter is None or w._filter(item):
+                        #debug("Widget for %s is %s", typ.__name__, w)
+                        return  w#self.registered[typ.__name__]
+            if typ.__bases__:
+                typ = typ.__bases__[0]
+                if typ == object:
+                    typ = None
+                elif typ.__name__ in ("ObjectItem_", "AbstractListItem_"):
+                    typ = orig_typ._type
+            else:
+                typ = None
+        raise ValueError('No registered class for %s' % (name))

mercurial