Sun, 03 Feb 2008 23:39:27 +0100
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))