class StatusLed(BaseLed): designer_description = 'LED showing the status of the device' designer_icon = ':/leds/yellow_on' dev = PropDef('dev', str, '', 'Device name') key = PropDef('key', str, '', 'Key name of the device') colors = { OK: 'green', BUSY: 'yellow', WARN: 'orange', NOTREACHED: 'red', DISABLED: 'gray', ERROR: 'red', UNKNOWN: 'gray', } def propertyUpdated(self, pname, value): if pname == 'dev': if value: self.key = value + '.status' BaseLed.propertyUpdated(self, pname, value) def on_keyChange(self, key, value, time, expired): if value is None: expired = True value = (OK, '') if expired: self.ledStatus = False else: self.ledStatus = True self.ledColor = self.colors[value[0]]
class ValueLed(BaseLed): designer_description = 'LED showing if the selected value is true' dev = PropDef('dev', str, '', 'NICOS device to use the value') key = PropDef('key', str, '', 'Key to use as the value') goal = PropDef( 'goal', str, '', 'Comparison value (by default the LED is ' 'green if value is true/nonzero)') _goalval = None def propertyUpdated(self, pname, value): if pname == 'dev': if value: self.key = value + '.value' elif pname == 'goal': self._goalval = ast.literal_eval(value) if value else None BaseLed.propertyUpdated(self, pname, value) def on_keyChange(self, key, value, time, expired): if expired: self.ledStatus = False else: self.ledStatus = True if self._goalval is not None: green = value == self._goalval else: green = value if green: self.ledColor = 'green' else: self.ledColor = 'red'
class RefsansWidget(NicosWidget, RefsansView): tubeangle = PropDef('tubeangle', str, '', 'Inclination of the tube') pivot = PropDef('pivot', str, '', 'Mounting point of the tube (pivot)') detpos = PropDef('detpos', str, '', 'Detector position inside tube') def __init__(self, parent): RefsansView.__init__(self, parent=parent) NicosWidget.__init__(self) self._keymap = {} self._statuskeymap = {} self._targetkeymap = {} def registerKeys(self): for dev in ['tubeangle', 'pivot', 'detpos']: devname = self.props.get(dev) if devname: k = self._source.register(self, devname + '/value') self._keymap[k] = dev k = self._source.register(self, devname + '/status') self._statuskeymap[k] = dev k = self._source.register(self, devname + '/target') self._targetkeymap[k] = dev def on_keyChange(self, key, value, time, expired): if key in self._keymap and not expired: self.values[self._keymap[key]] = value self.update() elif key in self._statuskeymap and not expired: self.status[self._statuskeymap[key]] = value[0] self.update() elif key in self._targetkeymap and not expired: self.targets[self._targetkeymap[key]] = value self.update()
class BeamOption(NicosWidget, QWidget): designer_description = 'SANS-1 beam option' def __init__(self, parent, designMode=False): self._curstr = '' self._curstatus = OK self._fixed = '' QWidget.__init__(self, parent) NicosWidget.__init__(self) dev = PropDef('dev', str, '', 'NICOS device name') height = PropDef('height', int, 4, 'Widget height in characters') width = PropDef('width', int, 10, 'Widget width in characters') name = PropDef('name', str, '', 'Display name') def sizeHint(self): return QSize( self.props['width'] * self._scale, self.props['height'] * self._scale + (self.props['name'] and self._scale * 2.5 or 0)) def registerKeys(self): self.registerDevice(self.props['dev']) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): self._curstr = unitvalue self.update() def on_devMetaChange(self, dev, fmtstr, unit, fixed): self._fixed = fixed self.update() def on_devStatusChange(self, dev, code, status, expired): self._curstatus = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setBrush(_magenta) painter.setRenderHint(QPainter.Antialiasing) w = self.props['width'] * self._scale h = self.props['height'] * self._scale if self.props['name']: painter.setFont(self.font()) painter.drawText(0, 0, w, self._scale * 2.5, Qt.AlignCenter, self.props['name']) yoff = self._scale * 2.5 else: yoff = 0 painter.setBrush(statusbrush[self._curstatus]) painter.drawRect(2, 2 + yoff, w - 4, h - 4) painter.setFont(self.valueFont) painter.drawText(2, 2 + yoff, w - 4, h - 4, Qt.AlignCenter, self._curstr)
class BaseLed(QLabel, NicosWidget): designer_icon = ':/leds/green_on' _ledPatternName = ':/leds/{color}_{status}' ledStatus = PropDef('ledStatus', bool, True, 'Status to display the ' '"On color" (bright)') ledInverted = PropDef('ledInverted', bool, False, 'Status to display the ' '"Off color" (dark)') ledColor = PropDef('ledColor', str, 'green', 'Color of the LED (default ' 'is green)') def __init__(self, parent=None, designMode=False): QLabel.__init__(self, parent) NicosWidget.__init__(self) self._refresh() def sizeHint(self): if self.layout() is None: return QSize(24, 24) return QLabel.sizeHint(self) def minimumSizeHint(self): return QSize(8, 8) def _refresh(self): status = self.props['ledStatus'] inverted = self.props['ledInverted'] color = self.props['ledColor'] if inverted: status = not status status = status and "on" or "off" ledName = self._ledPatternName.format(color=color, status=status) pixmap = QPixmap(ledName).scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) self.setPixmap(pixmap) self.setAlignment(Qt.AlignCenter) def resizeEvent(self, event): self._refresh() return QWidget.resizeEvent(self, event) def registerKeys(self): self.registerKey(self.props['key']) def propertyUpdated(self, pname, value): self._refresh() NicosWidget.propertyUpdated(self, pname, value)
class SinglePushButton(PushButton): designer_description = 'Simulation of a push button with a light and only'\ ' one state to swith On or Off' designer_icon = ':/leds/yellow_on' toState = PropDef('toState', str, '1', 'Target for action') def __init__(self, parent=None, designMode=False): self._stateTo = 1 PushButton.__init__(self, parent, designMode) def propertyUpdated(self, pname, value): PushButton.propertyUpdated(self, pname, value) if pname == 'toState': if isinstance(value, str): self._stateTo = value else: self._stateTo = ast.literal_eval(value) if value else 1 def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self._client.run('move(%s, %r)' % (self.dev, self._stateTo)) event.accept()
class ClickableOutputLed(ValueLed): designer_description = 'Digital Output Led that changes device state on click' designer_icon = ':/leds/orange_on' ledColor = PropDef('ledColor', str, 'orange', 'Default led color') stateActive = PropDef('stateActive', str, '1', 'Target for active LED ' 'state (green)') stateInactive = PropDef('stateInactive', str, '0', 'Target for inactive ' 'LED state (red)') def __init__(self, parent=None, designMode=False): self.current = None self._stateActive = 1 self._stateInactive = 0 ValueLed.__init__(self, parent, designMode) def on_keyChange(self, key, value, time, expired): ValueLed.on_keyChange(self, key, value, time, expired) self.current = value def propertyUpdated(self, pname, value): ValueLed.propertyUpdated(self, pname, value) if pname == 'stateInactive': if isinstance(value, string_types): self._stateInactive = value else: self._stateInactive = ast.literal_eval(value) if value else 0 if pname == 'stateActive': if isinstance(value, string_types): self._stateActive = value else: self._stateActive = ast.literal_eval(value) if value else 1 def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.ledColor = 'orange' if self.current == self._stateActive: self._client.run('move(%s, %r)' % (self.dev, self._stateInactive)) else: self._client.run('move(%s, %r)' % (self.dev, self._stateActive)) event.accept() else: event.ignore()
class ValueLabel(SqueezedLabel): """Label that just displays a single value.""" designer_description = 'A label that just displays a single value' # designer_icon = ':/' # XXX add appropriate icons dev = PropDef('dev', str, '', 'NICOS device name, if set, display ' 'value of this device') key = PropDef('key', str, '', 'Cache key to display') format = PropDef( 'format', str, '', 'Python format string to use for the ' 'value; if "dev" is given this defaults to the ' '"fmtstr" set in NICOS') def __init__(self, parent, designMode=False, **kwds): self._designMode = designMode SqueezedLabel.__init__(self, parent, designMode, **kwds) if designMode: self.setText('(value display)') self._callback = lambda value, strvalue: from_maybe_utf8(strvalue) def setFormatCallback(self, callback): self._callback = callback def propertyUpdated(self, pname, value): if pname == 'dev': if value: self.key = value + '.value' elif pname == 'key' and self._designMode: self.setText('(%s)' % value) NicosWidget.propertyUpdated(self, pname, value) def registerKeys(self): if self.props['dev']: self.registerDevice(self.props['dev'], fmtstr=self.props['format']) else: self.registerKey(self.props['key'], fmtstr=self.props['format']) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): if expired: setForegroundColor(self, QColor('grey')) else: setForegroundColor(self, QColor('black')) self.setText(self._callback(value, strvalue))
class DeviceParamEdit(DeviceValueEdit): designer_description = 'Editor for a device parameter with the right ' \ 'kind of widget' param = PropDef('param', str, '', 'Parameter name') def propertyUpdated(self, pname, value): if pname in ('dev', 'param'): self._reinit() NicosWidget.propertyUpdated(self, pname, value) def registerKeys(self): self.registerKey(self.props['dev'] + '.' + self.props['param']) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): if self.props['updateValue']: self._reinit(value) def _reinit(self, curvalue=None): if not self._client: return devname = str(self.props['dev']) parname = str(self.props['param']) if devname and parname: pvals = self._client.getDeviceParams(devname) pinfo = self._client.getDeviceParamInfo(devname) or {} mainunit = pvals.get('unit', 'main') if parname not in pinfo: punit = '' valuetype = str else: punit = (pinfo[parname]['unit'] or '').replace('main', mainunit) valuetype = pinfo[parname]['type'] if curvalue is None: curvalue = pvals.get(parname) if curvalue is None and valuetype is not None: curvalue = valuetype() else: valuetype = str curvalue = '' punit = '' self._inner = create(self, valuetype, curvalue, unit=punit, client=self._client) last = self._layout.takeAt(0) if last: last.widget().deleteLater() self._layout.insertWidget(0, self._inner) self._inner.valueModified.connect(self.valueModified)
class BeamPosition(CollimatorTable): designer_description = 'REFSANS NOK and slit table' key = PropDef('key', str, '', 'Cache key to display') def __init__(self, parent, designMode=False): CollimatorTable.__init__(self, parent, designMode) def registerKeys(self): self.registerKey(self.props['key']) CollimatorTable.registerKeys(self)
class DeviceParamTree(NicosWidget, BaseDeviceParamTree): designer_description = 'Displays devices and their parameters' showparams = PropDef('showparams', bool, True, 'Show parameters as subitems') def __init__(self, parent, designMode=False, **kwds): BaseDeviceParamTree.__init__(self, parent, **kwds) NicosWidget.__init__(self) def setClient(self, client): NicosWidget.setClient(self, client) BaseDeviceParamTree.setClient(self, client) def propertyUpdated(self, pname, value): if pname == 'showparams': self._showparams = value NicosWidget.propertyUpdated(self, pname, value)
class TrendPlot(NicosWidget, QWidget): designer_description = 'A trend plotter for one or more devices' designer_icon = ':/plotter' widgetInfo = pyqtSignal(str) timeSeriesUpdate = pyqtSignal(object) # colors = [Qt.red, Qt.darkGreen, Qt.blue, Qt.black, Qt.magenta, Qt.cyan, # Qt.darkGray] devices = PropDef( 'devices', 'QStringList', [], ''' List of devices or cache keys that the plot should display. For devices use device name. For keys use cache key with "." or "/" separator, e.g. T.heaterpower. To access items of a sequence, use subscript notation, e.g. T.userlimits[0] ''') names = PropDef( 'names', 'QStringList', [], 'Names for the plot curves, ' 'defaults to the device names/keys.') legend = PropDef('legend', bool, False, 'If a legend should be shown.') plotwindow = PropDef( 'plotwindow', int, 3600, 'The range of time in ' 'seconds that should be represented by the plot.') plotinterval = PropDef( 'plotinterval', float, 2, 'The minimum time in ' 'seconds between two points that should be ' 'plotted.') height = PropDef( 'height', int, 10, 'Height of the plot widget in units ' 'of app font character width.') width = PropDef( 'width', int, 30, 'Width of the plot widget in units ' 'of app font character width.') # pylint: disable=W0231 def __init__(self, parent, designMode=False): self.ncurves = 0 self.ctimers = {} self.keyindices = {} self.plotcurves = {} self.series = {} self.legendobj = None # X label settings, default values for default window of 3600s self._showdate = False self._showsecs = False QWidget.__init__(self, parent) NicosWidget.__init__(self) def initUi(self): # axes setup self.widget = InteractiveGRWidget(self) self.plot = Plot(viewport=(.1, .95, .25, .95)) self.axes = NicosTimePlotAxes(self.plot._viewport) self.axes.setWindow(0, 1, 0, 1) self.plot.addAxes(self.axes) self.plot.setLegend(True) self.plot.setLegendWidth(0.07) self.plot.offsetXLabel = -.2 self.axes.setXtickCallback(self.xtickCallBack) self.widget.addPlot(self.plot) layout = QHBoxLayout(self) layout.addWidget(self.widget) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.curves = [] # event support self.widget.cbm.addHandler(LegendEvent.ROI_CLICKED, self.on_legendItemClicked, LegendEvent) self.widget.cbm.addHandler(MouseEvent.MOUSE_MOVE, self.on_mouseMove) self.timeSeriesUpdate.connect(self.on_timeSeriesUpdate) def xtickCallBack(self, x, y, _svalue, value): gr.setcharup(-1., 1.) gr.settextalign(gr.TEXT_HALIGN_RIGHT, gr.TEXT_VALIGN_TOP) dx = .02 timeVal = localtime(value) if self._showdate: gr.text(x + dx, y - 0.01, strftime(DATEFMT, timeVal)) if self._showsecs: gr.text(x - dx, y - 0.01, strftime(TIMEFMT, timeVal)) else: gr.text(x - dx, y - 0.01, strftime(SHORTTIMEFMT, timeVal)) gr.setcharup(0., 1.) def propertyUpdated(self, pname, value): if pname == 'plotwindow': self._showdate = value > 24 * 3600 self._showsecs = value < 300 elif pname in ('width', 'height'): self.setMinimumSize( QSize(self._scale * (self.props['width'] + .5), self._scale * (self.props['height'] + .5))) elif pname == 'legend': self.plot.setLegend(value) NicosWidget.propertyUpdated(self, pname, value) def setFont(self, font): pass # TODO: can't set font for GR right now def on_mouseMove(self, event): wc = event.getWC(self.plot.viewport) ts = strftime(DATEFMT + ' ' + TIMEFMT, localtime(wc.x)) msg = 't = %s, y = %g' % (ts, wc.y) self.widgetInfo.emit(msg) def on_legendItemClicked(self, event): if event.getButtons() & MouseEvent.LEFT_BUTTON: event.curve.visible = not event.curve.visible self.update() def on_timeSeriesUpdate(self, series): curve = self.plotcurves[series] curve.x = series.x curve.y = series.y c = self.axes.getCurves() dy = abs(c.ymin - c.ymax) * 0.05 self.axes.setWindow(c.xmin, c.xmax, c.ymin - dy, c.ymax + dy) self.widget.update() self.ctimers[curve].start(5000) def on_keyChange(self, key, value, time, expired): if key not in self.keyindices or value is None: return for index in self.keyindices[key]: series = self.series[key, index] # restrict time of value to 1 minute past at # maximum, so that it doesn't get culled by the windowing time = max(time, currenttime() - 60) if index: try: fvalue = functools.reduce(operator.getitem, index, value) series.add_value(time, fvalue) except Exception: pass else: series.add_value(time, value) def addcurve(self, key, index, title, scale, offset): series = TimeSeries(key, self.props['plotinterval'], scale, offset, self.props['plotwindow'], self) series.init_empty() curve = PlotCurve([currenttime()], [0], legend=title) self.plotcurves[series] = curve self.ncurves += 1 self.curves.append(curve) self.axes.addCurves(curve) self.series[key, index] = series self.widget.update() # record the current value at least every 5 seconds, to avoid curves # not updating if the value doesn't change def update(): series.synthesize_value() self.ctimers[curve] = QTimer(singleShot=True) self.ctimers[curve].timeout.connect(update) def registerKeys(self): for key, name in zip_longest(self.props['devices'], self.props['names']): if name is None: name = key key, index, scale, offset = extractKeyAndIndex(key) keyid = self._source.register(self, key) self.keyindices.setdefault(keyid, []).append(index) self.addcurve(keyid, index, name, scale, offset)
class VRefsans(NicosWidget, QWidget): designer_description = 'Display of the REFSANS NOK configuration' nok0dev = PropDef('nok0dev', str, '', 'NOK 0 device') shutter_gammadev = PropDef('shutter_gammadev', str, '', 'NOK 1 device') nok2dev = PropDef('nok2dev', str, '', 'NOK 2 device') nok3dev = PropDef('nok3dev', str, '', 'NOK 3 device') nok4dev = PropDef('nok4dev', str, '', 'NOK 4 device') nok5adev = PropDef('nok5adev', str, '', 'NOK 5a device') nok5bdev = PropDef('nok5bdev', str, '', 'NOK 5b device') nok6dev = PropDef('nok6dev', str, '', 'NOK 6 device') nok7dev = PropDef('nok7dev', str, '', 'NOK 7 device') nok8dev = PropDef('nok8dev', str, '', 'NOK 8 device') height = PropDef('height', int, 30, 'Widget height in characters') width = PropDef('width', int, 40, 'Widget width in characters') def __init__(self, parent, designMode=False): QWidget.__init__(self, parent) NicosWidget.__init__(self) # default values (used when no such devices are configured) self.values = { 'nok0': 0, 'shutter_gamma': 0, 'nok2': (0, 0), 'nok3': (0, 0), 'nok4': (0, 0), 'nok5a': (0, 0), 'nok5b': (0, 0), 'nok6': (0, 0), 'nok7': (0, 0), 'nok8': (0, 0), } self.targets = self.values.copy() self.status = { 'nok0': OK, 'shutter_gamma': OK, 'nok2': OK, 'nok3': OK, 'nok4': OK, 'nok5a': OK, 'nok5b': OK, 'nok6': OK, 'nok7': OK, 'nok8': OK, } self._keymap = {} self._statuskeymap = {} self._targetkeymap = {} self._lengthkeymap = {} self._length = [0, 100, 300, 600, 1000, 1720, 1720, 1720, 1190, 880] self._fulllength = sum(self._length) def registerKeys(self): for dev in [ 'nok0', 'shutter_gamma', 'nok2', 'nok3', 'nok4', 'nok5a', 'nok5b', 'nok6', 'nok7', 'nok8' ]: devname = str(self.props[dev + 'dev']) if devname: k1 = self._source.register(self, devname + '/value') self._keymap[k1] = dev k2 = self._source.register(self, devname + '/status') self._statuskeymap[k2] = dev k3 = self._source.register(self, devname + '/target') self._targetkeymap[k3] = dev k4 = self._source.register(self, devname + '/length') self._lengthkeymap[k4] = dev def on_keyChange(self, key, value, time, expired): if key in self._keymap and not expired: self.values[self._keymap[key]] = value self.update() elif key in self._statuskeymap and not expired: self.status[self._statuskeymap[key]] = value[0] self.update() elif key in self._targetkeymap and not expired: self.targets[self._targetkeymap[key]] = value self.update() elif key in self._lengthkeymap and not expired: self.targets[self._lengthkeymap[key]] = value self.update() def sizeHint(self): return QSize(self.props['width'] * self._scale + 2, self.props['height'] * self._scale + 2) def _calculatePoint(self, ind, x, y): w, h = self.width * self._scale, self.height * self._scale x = sum(self._length[0:ind]) mx = 4 + w * x / self._fulllength my = 5 + h * y / 400 return (mx, my) def paintEvent(self, event): w, h = self.width * self._scale, self.height * self._scale painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QColor('black')) painter.setBrush(_white) painter.drawRect(1, 1, w, h) # determine positions beam = QPolygonF([QPointF(4, 5)]) i = 0 for k in [ 'nok0', 'shutter_gamma', 'nok2', 'nok3', 'nok4', 'nok5a', 'nok5b', 'nok6', 'nok7', 'nok8' ]: v = self.values[k] if isinstance(v, (tuple, readonlylist)): x, y = v # pylint: disable=W0633 elif isinstance(v, int): x, y = 0, v else: raise Exception('%r' % type(v)) p = self._calculatePoint(i, x, y) beam.append(QPointF(p[0], p[1])) i += 1 x, y = self.values['nok8'] p = self._calculatePoint(i + 1, x, y) painter.setPen(beambackgroundpen) painter.drawPolyline(beam) # draw beam painter.setPen(beampen) painter.drawPolyline(beam)
class TimeDistance(NicosWidget, TimeDistanceWidget): designer_description = 'REFSANS time distance diagram display' chopper1 = PropDef('chopper1', str, 'chopper1', 'Chopper 1 device') chopper2 = PropDef('chopper2', str, 'chopper2', 'Chopper 2 device') chopper3 = PropDef('chopper3', str, 'chopper3', 'Chopper 3 device') chopper4 = PropDef('chopper4', str, 'chopper4', 'Chopper 4 device') chopper5 = PropDef('chopper5', str, 'chopper5', 'Chopper 5 device') chopper6 = PropDef('chopper6', str, 'chopper6', 'Chopper 6 device') disc2_pos = PropDef('disc2_pos', str, 'disc2_pos', 'Position of disc2 translation') periods = PropDef('periods', int, 2, 'Number of periods to display') D = PropDef('D', float, 22.8, 'Beamline length') fp = PropDef('flightpath', str, 'real_flight_path', 'Flight path device') def __init__(self, parent, designMode=False): TimeDistanceWidget.__init__(self, parent) NicosWidget.__init__(self) self._speed = 0 self._phases = [0] * 6 self._disk2_pos = 5 self._fp = 0 def registerKeys(self): # chopperspeed, chopper phases,a for dev in ['chopper1', 'disc2_pos']: devname = self.props.get(dev) if devname: self._source.register(self, devname + '/value') for dev in [ 'chopper1', 'chopper2', 'chopper3', 'chopper4', 'chopper5', 'chopper6' ]: devname = self.props.get(dev) if devname: self._source.register(self, devname + '/phase') devname = self.props.get('chopper2') if devname: self._source.register(self, devname + '/pos') devname = self.props.get('fp') if devname: self._source.register(self, devname + '/value') def on_keyChange(self, key, value, time, expired): _, dev, param = key.split('/') devs = [key for key, d in self.props.items() if d == dev] if devs: devname = devs[0] if param == 'value': if value is None: return if devname == 'chopper1': self._speed = int(value) elif devname == 'disc2_pos': self._disk2_pos = int(value) elif devname == 'fp': self._fp = int(value) elif param == 'phase': if devname.startswith('chopper'): if value is not None: index = int(devname[-1]) - 1 self._phases[index] = float(value) elif param == 'pos' and devname == 'chopper2': if value is not None: self._disk2_pos = int(value) self.plot(self._speed, self._phases, self.props['periods'], self._disk2_pos, self.props['D'], self._fp)
class TasWidget(NicosWidget, QWidget): """Display of the TAS table configuration.""" designer_description = __doc__ mthdev = PropDef('mthdev', str, '', 'Monochromator rocking angle device') mttdev = PropDef('mttdev', str, '', 'Monochromator scattering angle device') sthdev = PropDef('sthdev', str, '', 'Sample rotation device') sttdev = PropDef('sttdev', str, '', 'Sample scattering angle device') athdev = PropDef('athdev', str, '', 'Analyzer rocking angle device') attdev = PropDef('attdev', str, '', 'Analyzer scattering angle device') Lmsdev = PropDef('Lmsdev', str, '', 'Distance monochromator->sample device') Lsadev = PropDef('Lsadev', str, '', 'Distance sample->analyzer device') Laddev = PropDef('Laddev', str, '', 'Distance analyzer->detector device') height = PropDef('height', int, 30, 'Widget height in characters') width = PropDef('width', int, 40, 'Widget width in characters') def __init__(self, parent, designMode=False): QWidget.__init__(self, parent) NicosWidget.__init__(self) # default values (used when no such devices are configured) self.values = { 'mth': -45, 'mtt': -90, 'sth': 30, 'stt': 60, 'ath': -45, 'att': -90, 'Lms': 1000, 'Lsa': 580, 'Lad': 400, } self.targets = self.values.copy() self.status = { 'mth': OK, 'mtt': OK, 'sth': OK, 'stt': OK, 'ath': OK, 'att': OK, } self._keymap = {} self._statuskeymap = {} self._targetkeymap = {} def registerKeys(self): for dev in [ 'mth', 'mtt', 'sth', 'stt', 'ath', 'att', 'Lms', 'Lsa', 'Lad' ]: devname = str(self.props[dev + 'dev']) if devname: k1 = self._source.register(self, devname + '/value') self._keymap[k1] = dev k2 = self._source.register(self, devname + '/status') self._statuskeymap[k2] = dev k3 = self._source.register(self, devname + '/target') self._targetkeymap[k3] = dev def on_keyChange(self, key, value, time, expired): if key in self._keymap and not expired: self.values[self._keymap[key]] = value self.update() elif key in self._statuskeymap and not expired: self.status[self._statuskeymap[key]] = value[0] self.update() elif key in self._targetkeymap and not expired: self.targets[self._targetkeymap[key]] = value self.update() def sizeHint(self): return QSize(self.props['width'] * self._scale + 2, self.props['height'] * self._scale + 2) def paintEvent(self, event): s = self.size() w, h = s.width(), s.height() # calculate the maximum length if all elements in a line maxL = self.values['Lms'] + self.values['Lsa'] + self.values['Lad'] # add the size of the Monochromator and detector scale = min((w / 2 - anaradius) / float(maxL), (h - monoradius - anaradius) / float(maxL)) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QColor('black')) painter.setBrush(_white) painter.drawRect(0, 0, w, h) # determine positions # incoming beam if self.values['mth'] < 0: bx, by = 3, h - (2 + monoradius) else: bx, by = 3, 2 + monoradius # monochromator mx, my = w / 2., by # sample L = self.values['Lms'] * scale # length is in mm -- scale down a bit mttangle = self.values['mtt'] * deg2rad mttangle_t = self.targets['mtt'] * deg2rad if self.values['mth'] < 0: mttangle = -mttangle mttangle_t = -mttangle_t sx, sy = mx + L * cos(mttangle), my - L * sin(mttangle) sx_t, sy_t = mx + L * cos(mttangle_t), my - L * sin(mttangle_t) # analyzer L = self.values['Lsa'] * scale # length is in mm -- scale down a bit sttangle = self.values['stt'] * deg2rad sttangle_t = self.targets['stt'] * deg2rad if self.values['sth'] < 0: sttangle = mttangle - sttangle sttangle_t = mttangle_t - sttangle_t else: sttangle = mttangle + sttangle sttangle_t = mttangle_t + sttangle_t ax, ay = sx + L * cos(sttangle), sy - L * sin(sttangle) ax_t, ay_t = sx_t + L * cos(sttangle_t), sy_t - L * sin(sttangle_t) # detector L = self.values['Lad'] * scale # length is in mm -- scale down a bit attangle = self.values['att'] * deg2rad attangle_t = self.targets['att'] * deg2rad if self.values['ath'] < 0: attangle = sttangle - attangle attangle_t = sttangle_t - attangle_t else: attangle = sttangle + attangle attangle_t = sttangle_t + attangle_t dx, dy = ax + L * cos(attangle), ay - L * sin(attangle) dx_t, dy_t = ax_t + L * cos(attangle_t), ay_t - L * sin(attangle_t) # draw table "halos" painter.setPen(nopen) if self.status['mth'] != OK: painter.setBrush(statusbrush[self.status['mth']]) painter.drawEllipse(QPoint(mx, my), monoradius + halowidth, monoradius + halowidth) elif self.status['mtt'] != OK: painter.setBrush(statusbrush[self.status['mtt']]) painter.drawEllipse(QPoint(mx, my), monoradius + halowidth, monoradius + halowidth) if self.status['sth'] != OK: painter.setBrush(statusbrush[self.status['sth']]) painter.drawEllipse(QPoint(sx, sy), sampleradius + halowidth, sampleradius + halowidth) elif self.status['stt'] != OK: painter.setBrush(statusbrush[self.status['stt']]) painter.drawEllipse(QPoint(sx, sy), sampleradius + halowidth, sampleradius + halowidth) if self.status['ath'] != OK: painter.setBrush(statusbrush[self.status['ath']]) painter.drawEllipse(QPoint(ax, ay), anaradius + halowidth, anaradius + halowidth) elif self.status['att'] != OK: painter.setBrush(statusbrush[self.status['att']]) painter.drawEllipse(QPoint(ax, ay), anaradius + halowidth, anaradius + halowidth) # draw table targets painter.setPen(targetpen) painter.setBrush(_nobrush) painter.drawEllipse(QPoint(sx_t, sy_t), sampleradius - .5, sampleradius - .5) painter.drawEllipse(QPoint(ax_t, ay_t), anaradius - .5, anaradius - .5) painter.drawEllipse(QPoint(dx_t, dy_t), detradius - .5, detradius - .5) # draw the tables painter.setPen(defaultpen) painter.setBrush(monotablebrush) painter.drawEllipse(QPoint(mx, my), monoradius, monoradius) painter.setBrush(sampletablebrush) painter.drawEllipse(QPoint(sx, sy), sampleradius, sampleradius) painter.setBrush(anatablebrush) painter.drawEllipse(QPoint(ax, ay), anaradius, anaradius) painter.setBrush(dettablebrush) painter.drawEllipse(QPoint(dx, dy), detradius, detradius) painter.setBrush(_white) painter.setPen(nopen) painter.drawEllipse(QPoint(mx, my), monoradius / 2, monoradius / 2) # painter.drawEllipse(QPoint(sx, sy), 20, 20) painter.drawEllipse(QPoint(ax, ay), anaradius / 2, anaradius / 2) # painter.drawEllipse(QPoint(dx, dy), 20, 20) beam = QPolygonF([ QPointF(bx, by), QPointF(mx, my), QPointF(sx, sy), QPointF(ax, ay), QPointF(dx, dy) ]) painter.setPen(beambackgroundpen) painter.drawPolyline(beam) # draw mono crystals painter.setPen(monopen) mthangle = -self.values['mth'] * deg2rad painter.drawLine(mx + 10 * cos(mthangle), my - 10 * sin(mthangle), mx - 10 * cos(mthangle), my + 10 * sin(mthangle)) # draw ana crystals athangle = -self.values['ath'] * deg2rad alpha = athangle + sttangle # TODO if the angle is too small then it could be that the ath value # must be turned by 90 deg (PANDA: chair setup) ?? if attangle < 0 and alpha < attangle: alpha += pi_2 painter.drawLine(ax + 10 * cos(alpha), ay - 10 * sin(alpha), ax - 10 * cos(alpha), ay + 10 * sin(alpha)) # draw sample painter.setPen(samplepen) painter.setBrush(samplebrush) sthangle = self.values['sth'] * deg2rad alpha = sthangle + mttangle + pi_4 # painter.drawRect(sx - 5, sy - 5, 10, 10) sz = 10 painter.drawPolygon( QPolygonF([ QPointF(sx + sz * cos(alpha), sy - sz * sin(alpha)), QPointF(sx + sz * cos(alpha + pi_2), sy - sz * sin(alpha + pi_2)), QPointF(sx - sz * cos(alpha), sy + sz * sin(alpha)), QPointF(sx - sz * cos(alpha + pi_2), sy + sz * sin(alpha + pi_2)), QPointF(sx + sz * cos(alpha), sy - sz * sin(alpha)) ])) painter.setPen(samplecoordpen) sr = sampleradius for angle in [alpha - pi_4, alpha - 3 * pi_4]: painter.drawLine(sx - sr * cos(angle), sy + sr * sin(angle), sx + sr * cos(angle), sy - sr * sin(angle)) # draw detector painter.setPen(monopen) painter.setBrush(_white) painter.drawEllipse(QPoint(dx, dy), 4, 4) # draw beam painter.setPen(beampen) painter.drawPolyline(beam)
class Tube(NicosWidget, QWidget): designer_description = 'KWS tube' devices = PropDef('devices', 'QStringList', []) height = PropDef('height', int, 10, 'Widget height in characters') width = PropDef('width', int, 30, 'Widget width in characters') name = PropDef('name', str, '') posscale = PropDef('posscale', float, 20) color = PropDef('color', 'QColor', _grey.color()) beamstop = PropDef('beamstop', bool, False, 'Beamstop instead of detector Y/Z') smalldet = PropDef( 'smalldet', float, 0, 'Positioning of small detector, ' 'or zero for no small detector') def __init__(self, parent, designMode=False): # z, x, y, small_x, small_y self._curval = [0, 0, 0, 0, 0] self._curstr = ['', '', '', '', ''] self._curstatus = [OK, OK, OK, OK, OK] QWidget.__init__(self, parent) NicosWidget.__init__(self) def sizeHint(self): return QSize( self.props['width'] * self._scale + 10, self.props['height'] * self._scale + (self.props['smalldet'] and 50 or 0) + (self.props['name'] and self._scale * 2.5 or 0) + 40) def registerKeys(self): for dev in self.props['devices']: self.registerDevice(str(dev)) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curval[idx] = value self._curstr[idx] = unitvalue self.update() def on_devStatusChange(self, dev, code, status, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curstatus[idx] = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setBrush(QBrush(self.color)) painter.setRenderHint(QPainter.Antialiasing) fontscale = float(self._scale) h = self.props['height'] * fontscale w = self.props['width'] * fontscale posscale = (w - 100) / self.props['posscale'] # Draw name above tube if self.props['name']: painter.setFont(self.font()) painter.drawText(5, 0, w, fontscale * 2.5, Qt.AlignCenter, self.props['name']) yoff = fontscale * 2.5 elif self.props['smalldet']: yoff = 50 else: yoff = 0 # Draw tube painter.setPen(self.color) painter.drawEllipse(5, 5 + yoff, 50, h) painter.drawRect(30, 5 + yoff, w - 50, h) painter.setPen(QColor('black')) painter.drawArc(5, 5 + yoff, 50, h, 1440, 2880) painter.drawLine(30, 5 + yoff, w - 25, 5 + yoff) painter.drawLine(30, 5 + yoff + h, w - 25, 5 + yoff + h) painter.drawEllipse(w - 45, 5 + yoff, 50, h) if self.props['smalldet']: sw = 20 sx = 30 + self.props['smalldet'] * posscale painter.setPen(self.color) painter.drawRect(sx - sw, 2, 2 * sw, yoff + 10) painter.setPen(QColor('black')) painter.drawLine(sx - sw, 5 + yoff, sx - sw, 2) painter.drawLine(sx - sw, 2, sx + sw, 2) painter.drawLine(sx + sw, 2, sx + sw, 5 + yoff) # draw detector pos_val = self._curval[0] if pos_val is not None: pos_status = self._curstatus[0] pos_str = self._curstr[0] x_val = self._curval[1] x_status = self._curstatus[1] x_str = '%.1f x' % x_val y_val = self._curval[2] y_status = self._curstatus[2] y_str = '%.1f y' % y_val stat = max(pos_status, x_status, y_status) painter.setBrush(statusbrush[stat]) painter.setFont(self.valueFont) painter.resetTransform() # Translate to detector position xp = 30 + pos_val * posscale painter.translate(xp + fontscale / 2., 15 + yoff + (h - 20) / 2.) painter.drawRect(-fontscale / 2., -(h - 20) / 2., fontscale, h - 20) painter.resetTransform() # Put X/Y values left or right of detector depending on position if pos_val < 14: xoff = 2 * fontscale else: xoff = -8.5 * fontscale # X translation painter.drawText(xp + xoff, yoff + 2 * fontscale, 7 * fontscale, 2 * fontscale, Qt.AlignRight, x_str) # Y translation painter.drawText(xp + xoff, yoff + 3.5 * fontscale, 7 * fontscale, 2 * fontscale, Qt.AlignRight, y_str) # Z position minx = max(0, xp + 5 - 4 * fontscale) painter.drawText(minx, h + 10 + yoff, 8 * fontscale, 30, Qt.AlignCenter, pos_str) # draw beamstop if self.props['beamstop']: painter.setPen(QPen(_blue.color())) painter.drawRect(xp - 8, yoff + 15 + posscale / 350 * (1100 - y_val), 2, 10) # draw small detector if self.props['smalldet'] and self._curval[4] is not None: x_status = self._curstatus[3] x_str = '%4.1f x' % self._curval[3] y_status = self._curstatus[4] y_val = self._curval[4] y_str = '%4.0f y' % y_val stat = max(x_status, y_status) painter.setBrush(statusbrush[stat]) painter.setPen(QPen(_black.color())) painter.setFont(self.valueFont) sy = 10 + y_val * posscale / 250 painter.drawRect(sx - fontscale / 2., sy, fontscale, 30) painter.drawText(sx - 10.5 * fontscale, sy, 8 * fontscale, 2 * fontscale, Qt.AlignRight, x_str) painter.drawText(sx - 10.5 * fontscale, sy + 1.5 * fontscale, 8 * fontscale, 2 * fontscale, Qt.AlignRight, y_str)
class Lenses(NicosWidget, QWidget): designer_description = 'KWS lenses' def __init__(self, parent, designMode=False): # lens_in, lens_out self._curval = [0, 0] self._curstr = ['', ''] self._curstatus = [OK, OK] QWidget.__init__(self, parent) NicosWidget.__init__(self) devices = PropDef('devices', 'QStringList', []) height = PropDef('height', int, 4) width = PropDef('width', int, 10) def registerKeys(self): for dev in self.props['devices']: self.registerDevice(str(dev)) def sizeHint(self): return QSize(self._scale * self.props['width'], self._scale * self.props['height']) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curval[idx] = value self._curstr[idx] = unitvalue self.update() def on_devStatusChange(self, dev, code, status, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curstatus[idx] = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(_black.color())) painter.setBrush(_grey) fontscale = float(self._scale) h = self.props['height'] * fontscale w = self.props['width'] * fontscale painter.drawRect(2, 10, w - 4, h / 2) is_in = int(self._curval[0]) is_out = int(self._curval[1]) lensheight = h / 2 - 25 lensw = w / 32 for (i, n, x) in [(0, 4, 0), (1, 6, 6), (2, 16, 14)]: if is_in & (1 << i): lensy = 22 elif is_out & (1 << i): lensy = h / 2 + 20 else: lensy = h / 4 + 22 for j in range(n): painter.drawRect((1 + x + j) * lensw, lensy, lensw + 1, lensheight)
class PictureDisplay(NicosWidget, QWidget): """A display widget to show a picture.""" designer_description = 'Widget to display a picture file' filepath = PropDef('filepath', str, '', 'Path to the picture that should ' 'be displayed') name = PropDef('name', str, '', 'Name (caption) to be displayed above ' 'the picture') refresh = PropDef('refresh', int, 0, 'Interval to check for updates ' 'in seconds') height = PropDef('height', int, 0) width = PropDef('width', int, 0) def __init__(self, parent=None, designMode=False, **kwds): QWidget.__init__(self, parent, **kwds) NicosWidget.__init__(self) self._last_mtime = None self.namelabel = QLabel(self) self.namelabel.setAlignment(Qt.AlignHCenter) self.piclabel = QLabel(self) self.piclabel.setScaledContents(True) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.piclabel, 1) self.setLayout(layout) def registerKeys(self): pass def setPicture(self): size = QSize(self.props['width'] * self._scale, self.props['height'] * self._scale) if isfile(self._filePath): pixmap = QPixmap(self._filePath) else: pixmap = QPixmap(size) pixmap.fill() if size.isEmpty(): self.piclabel.setPixmap(pixmap) else: self.piclabel.setPixmap(pixmap.scaled(size)) self.piclabel.resize(self.piclabel.sizeHint()) def updatePicture(self): if not isfile(self._filePath): return # on first iteration self._last_mtime is None -> always setPicture() mtime = getmtime(self._filePath) if self._last_mtime != mtime: self._last_mtime = mtime self.setPicture() def propertyUpdated(self, pname, value): NicosWidget.propertyUpdated(self, pname, value) if pname == 'filepath': self._filePath = findResource(value) self.setPicture() elif pname == 'name': layout = QVBoxLayout() if value: layout.addWidget(self.namelabel) layout.addSpacing(5) layout.addWidget(self.piclabel, 1) sip.delete(self.layout()) self.setLayout(layout) self.namelabel.setText(value) elif pname in ('width', 'height'): self.setPicture() elif pname == 'refresh': if value: self._refreshTimer = QTimer() self._refreshTimer.setInterval(value * 1000) self._refreshTimer.timeout.connect(self.updatePicture) self._refreshTimer.start()
class DeviceValueEdit(NicosWidget, QWidget): designer_description = 'Editor for a device value with the right kind ' \ 'of widget' valueModified = pyqtSignal() valueChosen = pyqtSignal(object) dev = PropDef('dev', str, '', 'Device name') useButtons = PropDef('useButtons', bool, False, 'Use buttons for values with few choices?') allowEnter = PropDef('allowEnter', bool, True, 'Emit valueChosen signal on pressing Enter?') updateValue = PropDef('updateValue', bool, False, 'Update the editor when the device value changes?') showUnit = PropDef('showUnit', bool, True, 'Show the unit next to the input') def __init__(self, parent, designMode=False, **kwds): self._inner = None QWidget.__init__(self, parent, **kwds) NicosWidget.__init__(self) self._layout = QHBoxLayout() self._layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self._layout) if designMode: self._layout.insertWidget(0, QLabel('(Value Editor)', self)) def setFocus(self): if self._inner is not None: self._inner.setFocus() else: QWidget.setFocus(self) def propertyUpdated(self, pname, value): if pname == 'dev': self._reinit() NicosWidget.propertyUpdated(self, pname, value) def setClient(self, client): NicosWidget.setClient(self, client) self._reinit() def on_client_connected(self): self._reinit() def registerKeys(self): if self.props['updateValue']: self.registerDevice(self.props['dev']) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): self._reinit(value) def on_devMetaChange(self, dev, fmtstr, unit, fixed): self._reinit() def _reinit(self, curvalue=None): if not self._client or not self._client.isconnected: return devname = str(self.props['dev']) if devname: params = self._client.getDeviceParams(devname) valuetype = self._client.getDeviceValuetype(devname) valueinfo = self._client.eval( 'session.getDevice(%r).valueInfo()' % devname, None) unit = params.get('unit', '') if self.props['showUnit'] else '' fmtstr = params.get('fmtstr', '%s') if curvalue is None: curvalue = params.get('target') if curvalue is None and valuetype is not None: curvalue = valuetype() else: valuetype = str curvalue = '' fmtstr = '%s' unit = '' valueinfo = None self._inner = create(self, valuetype, curvalue, fmtstr, unit, allow_buttons=self.props['useButtons'], allow_enter=self.props['allowEnter'], client=self._client, valinfo=valueinfo) last = self._layout.takeAt(0) if last: last.widget().deleteLater() self._layout.insertWidget(0, self._inner) self._inner.valueModified.connect(self.valueModified) self._inner.valueChosen.connect(self.valueChosen) def getValue(self): if self._inner: return self._inner.getValue() def setValue(self, value): self._reinit(value)
class ValueDisplay(NicosWidget, QWidget): """Value display widget with two labels.""" designer_description = 'A widget with name/value labels' designer_icon = ':/table' widgetInfo = pyqtSignal(str) dev = PropDef('dev', str, '', 'NICOS device name, if set, display ' 'value of this device') key = PropDef('key', str, '', 'Cache key to display (without "nicos/"' ' prefix), set either "dev" or this') statuskey = PropDef('statuskey', str, '', 'Cache key to extract status ' 'information for coloring value, if "dev" is ' 'given this is set automatically') name = PropDef('name', str, '', 'Name of the value to display above/' 'left of the value; if "dev" is given this ' 'defaults to the device name') unit = PropDef('unit', str, '', 'Unit of the value to display next to ' 'the name; if "dev" is given this defaults to ' 'the unit set in NICOS') format = PropDef('format', str, '', 'Python format string to use for the ' 'value; if "dev" is given this defaults to the ' '"fmtstr" set in NICOS') maxlen = PropDef('maxlen', int, -1, 'Maximum length of the value string to ' 'allow; defaults to no limit') width = PropDef('width', int, 8, 'Width of the widget in units of the ' 'width of one character') istext = PropDef('istext', bool, False, 'If given, a "text" font will be ' 'used for the value instead of the monospaced ' 'font used for numeric values') showName = PropDef('showName', bool, True, 'If false, do not display the ' 'label for the value name') showStatus = PropDef('showStatus', bool, True, 'If false, do not display ' 'the device status as a color of the value text') showExpiration = PropDef('showExpiration', bool, True, 'If true, display ' 'expired cache values as "n/a"') horizontal = PropDef('horizontal', bool, False, 'If true, display name ' 'label left of the value instead of above it') def __init__(self, parent, designMode=False, colorScheme=None, **kwds): # keys being watched self._mainkeyid = None self._statuskeyid = None # other current values self._isfixed = '' # XXX could be taken from devinfo self._lastvalue = designMode and '1.4' or None self._laststatus = (OK, '') self._lastchange = 0 self._mouseover = False self._mousetimer = None self._expired = True self._colorscheme = colorScheme or defaultColorScheme QWidget.__init__(self, parent, **kwds) NicosWidget.__init__(self) self._statuscolors = self._colorscheme['fore'][UNKNOWN], \ self._colorscheme['back'][UNKNOWN] self._labelcolor = None def propertyUpdated(self, pname, value): if pname == 'dev': if value: self.key = value + '.value' self.statuskey = value + '.status' elif pname == 'width': if value < 0: self.reinitLayout() else: onechar = QFontMetrics(self.valueFont).width('0') self.valuelabel.setMinimumSize(QSize(onechar * (value + .5), 0)) elif pname == 'istext': self.valuelabel.setFont(value and self.font() or self.valueFont) self.width = self.width elif pname == 'valueFont': self.valuelabel.setFont(self.valueFont) self.width = self.width # update char width calculation elif pname == 'showName': self.namelabel.setVisible(value) elif pname == 'showStatus': if not value: setBothColors(self.valuelabel, (self._colorscheme['fore'][UNKNOWN], self._colorscheme['back'][UNKNOWN])) elif pname == 'horizontal': self.reinitLayout() if pname in ('dev', 'name', 'unit'): self.update_namelabel() NicosWidget.propertyUpdated(self, pname, value) def initUi(self): self.namelabel = QLabel(' ', self, textFormat=Qt.RichText) self.update_namelabel() valuelabel = SensitiveSMLabel('----', self, self._label_entered, self._label_left) valuelabel.setFrameShape(QFrame.Panel) valuelabel.setAlignment(Qt.AlignHCenter) valuelabel.setFrameShadow(QFrame.Sunken) valuelabel.setAutoFillBackground(True) setBothColors(valuelabel, (self._colorscheme['fore'][UNKNOWN], self._colorscheme['back'][UNKNOWN])) valuelabel.setLineWidth(2) self.valuelabel = valuelabel self.width = 8 self.reinitLayout() def reinitLayout(self): # reinitialize UI after switching horizontal/vertical layout if self.props['horizontal']: new_layout = QHBoxLayout() new_layout.addWidget(self.namelabel) new_layout.addStretch() new_layout.addWidget(self.valuelabel) self.namelabel.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) else: new_layout = QVBoxLayout() new_layout.addWidget(self.namelabel) tmplayout = QHBoxLayout() if self.width >= 0: tmplayout.addStretch() tmplayout.addWidget(self.valuelabel) if self.width >= 0: tmplayout.addStretch() new_layout.addLayout(tmplayout) self.namelabel.setAlignment(Qt.AlignHCenter) if self.layout(): sip.delete(self.layout()) new_layout.setContentsMargins(1, 1, 1, 1) # save space self.setLayout(new_layout) def registerKeys(self): if self.props['dev']: self.registerDevice(self.props['dev'], self.props['unit'], self.props['format']) else: self.registerKey(self.props['key'], self.props['statuskey'], self.props['unit'], self.props['format']) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): # check expired values self._expired = expired self._lastvalue = value self._lastchange = currenttime() if self.props['maxlen'] > -1: self.valuelabel.setText(strvalue[:self.props['maxlen']]) else: self.valuelabel.setText(strvalue) if self._expired: setBothColors(self.valuelabel, (self._colorscheme['fore'][UNKNOWN], self._colorscheme['expired'])) if self.props['showExpiration']: self.valuelabel.setText(NOT_AVAILABLE) elif not self.props['istext']: setBothColors(self.valuelabel, (self._colorscheme['fore'][BUSY], self._colorscheme['back'][BUSY])) QTimer.singleShot(1000, self._applystatuscolor) else: self._applystatuscolor() def _applystatuscolor(self): if self._expired: setBothColors(self.valuelabel, (self._colorscheme['fore'][UNKNOWN], self._colorscheme['expired'])) else: setBothColors(self.valuelabel, self._statuscolors) if self._labelcolor: self.namelabel.setAutoFillBackground(True) setBackgroundColor(self.namelabel, self._labelcolor) else: self.namelabel.setAutoFillBackground(False) def on_devStatusChange(self, dev, code, status, expired): if self.props['showStatus']: self._statuscolors = self._colorscheme['fore'][code], \ self._colorscheme['back'][code] self._labelcolor = self._colorscheme['label'][code] self._laststatus = code, status self._applystatuscolor() def on_devMetaChange(self, dev, fmtstr, unit, fixed): self._isfixed = fixed and ' (F)' self.format = fmtstr self.unit = unit or '' def update_namelabel(self): name = self.props['name'] or self.props['dev'] or self.props['key'] self.namelabel.setText( html.escape(str(name)) + ' <font color="#888888">%s</font><font color="#0000ff">%s</font> ' % (html.escape(self.props['unit'].strip()), self._isfixed)) def _label_entered(self, widget, event, from_mouse=True): infotext = '%s = %s' % (self.props['name'] or self.props['dev'] or self.props['key'], self.valuelabel.text()) if self.props['unit'].strip(): infotext += ' %s' % self.props['unit'] if self.props['statuskey']: try: const, msg = self._laststatus except ValueError: const, msg = self._laststatus, '' infotext += ', status is %s: %s' % (statuses.get(const, '?'), msg) infotext += ', changed %s ago' % ( nicedelta(currenttime() - self._lastchange)) self.widgetInfo.emit(infotext) if from_mouse: self._mousetimer = QTimer(self, timeout=lambda: self._label_entered(widget, event, False) ) self._mousetimer.start(1000) def _label_left(self, widget, event): if self._mousetimer: self._mousetimer.stop() self._mousetimer = None self.widgetInfo.emit('')
class TasWidget(NicosWidget, TasView): """Display of the TAS table configuration.""" designer_description = __doc__ mthdev = PropDef('mthdev', str, '', 'Monochromator rocking angle device') mttdev = PropDef('mttdev', str, '', 'Monochromator scattering angle device') sthdev = PropDef('sthdev', str, '', 'Sample rotation device') sttdev = PropDef('sttdev', str, '', 'Sample scattering angle device') athdev = PropDef('athdev', str, '', 'Analyzer rocking angle device') attdev = PropDef('attdev', str, '', 'Analyzer scattering angle device') Lmsdev = PropDef('Lmsdev', str, '', 'Distance monochromator->sample device') Lsadev = PropDef('Lsadev', str, '', 'Distance sample->analyzer device') Laddev = PropDef('Laddev', str, '', 'Distance analyzer->detector device') height = PropDef('height', int, 30, 'Widget height in characters') width = PropDef('width', int, 40, 'Widget width in characters') def __init__(self, parent, designMode=False): TasView.__init__(self, parent) NicosWidget.__init__(self) self._keymap = {} self._statuskeymap = {} self._targetkeymap = {} def registerKeys(self): for dev in self.values: devname = str(self.props[dev + 'dev']) if devname: self._keymap[self._source.register(self, devname + '/value')] = dev self._statuskeymap[self._source.register( self, devname + '/status')] = dev self._targetkeymap[self._source.register( self, devname + '/target')] = dev def on_keyChange(self, key, value, time, expired): if not expired: if key in self._keymap: # Scale the distances if self._keymap[key] in ('Lms', 'Lsa', 'Lad'): value /= 10 self.values[self._keymap[key]] = value elif key in self._statuskeymap: self.status[self._statuskeymap[key]] = value[0] elif key in self._targetkeymap: self.targets[self._targetkeymap[key]] = value else: return self.update() def sizeHint(self): return QSize(self.props['width'] * self._scale + 2, self.props['height'] * self._scale + 2)
class Collimation(NicosWidget, QWidget): designer_description = 'KWS collimation' devices = PropDef('devices', 'QStringList', []) height = PropDef('height', int, 4) width = PropDef('width', int, 10) polarizer = PropDef('polarizer', int, 0, 'Number of bits for the polarizer') def __init__(self, parent, designMode=False): # coll_in, coll_out, ap_20, ap_14, ap_8, ap_4, ap_2, pol_in, pol_out self._curval = [0, 0, (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), 0, 0] self._curstr = ['', '', '', '', '', '', '', '', ''] self._curstatus = [OK, OK, OK, OK, OK, OK, OK, OK, OK] QWidget.__init__(self, parent) NicosWidget.__init__(self) def registerKeys(self): for dev in self.props['devices']: self.registerDevice(str(dev)) def sizeHint(self): return QSize(self._scale * self.props['width'], self._scale * self.props['height'] * 1.2) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curval[idx] = value self._curstr[idx] = unitvalue self.update() def on_devStatusChange(self, dev, code, status, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curstatus[idx] = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) pen = painter.pen() fontscale = float(self._scale) painter.setFont(scaledFont(self.valueFont, 0.9)) h = self.props['height'] * fontscale w = self.props['width'] * fontscale elwidth = w / 20. elheight = h / 3 pol_bits = self.props['polarizer'] is_in = int(self._curval[0] << pol_bits | self._curval[7]) is_out = int(self._curval[1] << pol_bits | self._curval[8]) x = elwidth y = 2.5 * fontscale for i in range(18): painter.setPen(QPen(_black.color())) painter.setBrush(_grey) painter.drawRect(x, y, elwidth - 2, elheight) painter.setBrush(_blue) if is_in & (1 << (17 - i)): ely = 3 elif is_out & (1 << (17 - i)): ely = 2 + elheight / 2 else: ely = 2 + elheight / 4 painter.drawRect(x + 3, y + ely, elwidth - 8, elheight / 3) if i >= 18 - pol_bits: painter.setPen(QPen(_white.color())) painter.drawText(x + 3, y + ely - 2, elwidth - 8, elheight / 3 + 2, Qt.AlignHCenter, 'POL') painter.setPen(QPen(_black.color())) painter.drawText(x, 3, elwidth, 2 * fontscale, Qt.AlignRight | Qt.AlignTop, str(19 - i)) x += elwidth painter.fillRect(0, y + elheight / 3 - 5, w, 3, _yellow) painter.setPen(pen) x = elwidth + 1 y += elheight + 4 slhw = 1.6 * elwidth for i, slitpos in enumerate([20, 14, 8, 4, 2]): slitw, slith = self._curval[2 + i] xmiddle = x + ((20 - slitpos) * elwidth) painter.drawLine(xmiddle, y, xmiddle, y + 15) painter.setBrush(_white) painter.drawRect(xmiddle - 0.5 * slhw, y + 15, slhw, slhw) painter.setBrush(collstatusbrush[self._curstatus[2 + i]]) w = (50 - slitw) * slhw / 100 h = (50 - slith) * slhw / 100 painter.drawRect(xmiddle - 0.5 * slhw + w, y + 15 + h, slhw - 2 * w, slhw - 2 * h) painter.drawText(xmiddle - 0.8 * elwidth, y + 15, slhw, slhw, Qt.AlignCenter, '%.1f\n%.1f' % (slitw, slith))
class CollimatorTable(NicosWidget, QWidget): """Displays a list of 'beam options' as a vertical stack. Options are displayed as vertical stack of named elements drawn on top of a centered blue line ('the beam'). If the device value is in 'options', the correspondig element is drawn on top of 'the beam' by moving the whole stack vertically. If the device value is in 'disabled_options', the whole stack of options is vertically shifted 'out of beam'. Other values are ignored as they are considered temporary (while moving an option). If the device state happens to be in error, the name label is displayed in red to indicate the error. """ designer_description = 'SANS-1 collimator table' def __init__(self, parent, designMode=False): self._curstr = '' self._curstatus = OK self._fixed = '' self.shift = -1 QWidget.__init__(self, parent) NicosWidget.__init__(self) dev = PropDef('dev', str, '', 'NICOS device name of a switcher') options = PropDef( 'options', 'QStringList', [], 'list of valid switcher-' 'values to display in top-down order (first element ' 'will be displayed on top location)') disabled_options = PropDef( 'disabled_options', 'QStringList', [], 'list of valid switcher values for which ' 'all options are display out-of-beam') height = PropDef('height', int, 4, 'Widget height in characters') width = PropDef('width', int, 10, 'Widget width in characters') name = PropDef('name', str, '', 'Display name') def registerKeys(self): self.registerDevice(self.props['dev']) def sizeHint(self): return QSize( self._scale * self.props['width'], self._scale * 2.5 * self.props['height'] + (self.props['name'] and 2.5 * self._scale or 0)) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): self._curstr = strvalue self.update() def on_devMetaChange(self, dev, fmtstr, unit, fixed): self._fixed = fixed self.update() def on_devStatusChange(self, dev, code, status, expired): self._curstatus = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) h = self._scale * 2.5 * self.props['height'] w = self._scale * self.props['width'] # cache pen pen = painter.pen() if self.props['name']: painter.setFont(self.font()) if self._curstatus != OK: painter.fillRect(0, 0, w, self._scale * 2.5, statusbrush[self._curstatus]) if self._fixed: painter.setPen(QPen(_blue.color())) else: painter.setPen(QPen(_black.color())) painter.drawText(0, 0, w, self._scale * 2.5, Qt.AlignCenter, self.props['name']) painter.setPen(pen) yoff = self._scale * 2.5 else: yoff = 0 painter.setPen(QPen(_blue.color())) y = h * 0.5 + yoff painter.drawLine(0, y, w, y) painter.drawLine(0, y + 1, w, y + 1) painter.drawLine(0, y + 2, w, y + 2) # reset pen painter.setPen(pen) painter.setBrush(statusbrush[self._curstatus]) if self._curstr in self.props['options']: self.shift = self.props['options'].index(self._curstr) if self._curstr in self.props['disabled_options']: self.shift = len(self.props['options']) painter.setFont(self.valueFont) h0 = max(2 * self._scale, 2 * self._scale + 4) painter.setClipRect(0, yoff, w, h) for i, t in enumerate(self.props['options']): y = h * 0.5 + yoff + h0 * (self.shift - i - 0.45) b = statusbrush[self._curstatus] if t == self._curstr: painter.setBrush(b) else: painter.setBrush(_grey if b == statusbrush[OK] else b) painter.drawRect(5, y + 2, w - 10, h0 - 4) painter.drawText(5, y + 2, w - 10, h0 - 4, Qt.AlignCenter, t)
class SampleSlit(NicosWidget, QWidget): designer_description = 'KWS sample slit' def __init__(self, parent, designMode=False): self._curval = [0, 0, 0, 0] self._curstatus = OK self._opmode = 'offcentered' QWidget.__init__(self, parent) NicosWidget.__init__(self) device = PropDef('device', str, '') maxh = PropDef('maxh', float, 30) maxw = PropDef('maxw', float, 30) height = PropDef('height', int, 4) width = PropDef('width', int, 10) def registerKeys(self): self.registerDevice(str(self.props['device'])) self.registerKey('%s/opmode' % self.props['device']) def sizeHint(self): return QSize(self._scale * self.props['width'], self._scale * self.props['height']) def on_keyChange(self, key, value, time, expired): if key.endswith('/opmode'): self._opmode = value return NicosWidget.on_keyChange(self, key, value, time, expired) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): self._curval = value self.update() def on_devStatusChange(self, dev, code, status, expired): self._curstatus = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) fontscale = float(self._scale) ww = self.props['width'] * fontscale - 4 wh = self.props['height'] * fontscale - 4 sx, sy = self.props['maxw'], self.props['maxh'] if self._opmode == 'offcentered': dx, dy, w, h = self._curval x0, x1, y0, y1 = dx - w / 2., dx + w / 2., dy - h / 2., dy + h / 2. l1, l2, l3, l4 = '(%.1f, %.1f)' % (dx, dy), '%.1f x %.1f' % ( w, h), '', '' elif self._opmode == 'centered': w, h = self._curval x0, x1, y0, y1 = -w / 2., w / 2., -h / 2., h / 2. l1, l2, l3, l4 = '', '%.1f x %.1f' % (w, h), '', '' elif self._opmode.startswith('4blades'): x0, x1, y0, y1 = self._curval l1, l2, l3, l4 = '%.1f' % y1, '%.1f' % y0, '%.1f' % x0, '%.1f' % x1 if self._opmode.endswith('opposite'): x0 *= -1 y0 *= -1 x0 = (x0 + sx / 2) / sx * ww x1 = (x1 + sx / 2) / sx * ww y0 = wh - (y0 + sy / 2) / sy * wh y1 = wh - (y1 + sy / 2) / sy * wh painter.setPen(QPen(_black.color())) painter.setBrush(_white) painter.drawRect(2, 2, ww, wh) painter.setBrush(collstatusbrush[self._curstatus]) painter.drawRect(2 + x0, 2 + y1, x1 - x0, y0 - y1) painter.setFont(scaledFont(self.valueFont, 0.8)) painter.drawText(2, 2, ww, wh, Qt.AlignTop | Qt.AlignHCenter, l1) painter.drawText(2, 2, ww, wh, Qt.AlignBottom | Qt.AlignHCenter, l2) painter.drawText(2, 2, ww, wh, Qt.AlignVCenter | Qt.AlignLeft, l3) painter.drawText(2, 2, ww, wh, Qt.AlignVCenter | Qt.AlignRight, l4)
class Tube2(NicosWidget, QWidget): """Sans1Tube with two detectors...""" designer_description = 'SANS-1 tube with two detectors' def __init__(self, parent, designMode=False): # det1pos, det1shift, det1tilt, det2pos self._curval = [0, 0, 0, 0] self._curstr = ['', '', '', ''] self._curstatus = [OK, OK, OK, OK] QWidget.__init__(self, parent) NicosWidget.__init__(self) devices = PropDef('devices', 'QStringList', [], 'position, shift and ' 'tilt of det1, position of det2') height = PropDef('height', int, 10, 'Widget height in characters') width = PropDef('width', int, 30, 'Widget width in characters') name = PropDef('name', str, '', 'Display name') posscale = PropDef('posscale', float, 20000, 'Length of the tube') color = PropDef('color', 'QColor', _magenta.color(), 'Color of the tube') def sizeHint(self): return QSize( self.props['width'] * self._scale + 10, self.props['height'] * self._scale + (self.props['name'] and self._scale * 2.5 or 0) + 40) def registerKeys(self): for dev in self.props['devices']: self.registerDevice(str(dev)) def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curval[idx] = value self._curstr[idx] = unitvalue self.update() def on_devStatusChange(self, dev, code, status, expired): try: idx = self.props['devices'].index(dev) except ValueError: return self._curstatus[idx] = code self.update() def paintEvent(self, event): painter = QPainter(self) painter.setBrush(QBrush(self.color)) painter.setRenderHint(QPainter.Antialiasing) fontscale = float(self._scale) h = self.props['height'] * fontscale w = self.props['width'] * fontscale posscale = (w - 120) / self.props['posscale'] if self.props['name']: painter.setFont(self.font()) painter.drawText(5, 0, w, fontscale * 2.5, Qt.AlignCenter, self.props['name']) yoff = fontscale * 2.5 else: yoff = 0 painter.setPen(self.color) painter.drawEllipse(5, 5 + yoff, 50, h) painter.drawRect(30, 5 + yoff, w - 50, h) painter.setPen(QColor('black')) painter.drawArc(5, 5 + yoff, 50, h, 1440, 2880) painter.drawLine(30, 5 + yoff, w - 25, 5 + yoff) painter.drawLine(30, 5 + yoff + h, w - 25, 5 + yoff + h) painter.drawEllipse(w - 45, 5 + yoff, 50, h) # draw Detector 1 minx = 0 pos_val = self._curval[0] if pos_val is not None: pos_status = self._curstatus[0] pos_str = self._curstr[0] shift_val = self._curval[1] shift_status = self._curstatus[1] shift_str = self._curstr[1] if shift_val > 0: shift_str += ' ↓' elif shift_val < 0: shift_str += ' ↑' # Not used at the moment, prepared for later use tilt_val = self._curval[2] tilt_status = self._curstatus[2] tilt_str = self._curstr[2] if tilt_str.endswith('deg'): tilt_str = tilt_str[:-3] + '°' stat = max(pos_status, shift_status, tilt_status) painter.setBrush(statusbrush[stat]) # tf = QTransform() # tf.rotate(tilt_val) painter.resetTransform() painter.translate(60 + pos_val * posscale + fontscale / 2., 15 + yoff + shift_val * posscale + (h - 20) / 2.) painter.rotate(-tilt_val) painter.drawRect(-fontscale / 2., -(h - 20) / 2., fontscale, h - 20) # XXX tilt ??? painter.resetTransform() painter.setFont(self.valueFont) painter.drawText( 60 + pos_val * posscale - 10.5 * fontscale, -5 + yoff + h - fontscale, # + (shift_val - 4) * posscale, 9.5 * fontscale, 2 * fontscale, Qt.AlignRight, tilt_str) painter.drawText( 60 + pos_val * posscale - 6.5 * fontscale, yoff + fontscale, # + (shift_val - 4) * posscale, 9.5 * fontscale, 2 * fontscale, Qt.AlignLeft, shift_str) minx = max(minx, 60 + pos_val * posscale + 5 - 4 * fontscale) painter.drawText(minx, h + 10 + yoff, 8 * fontscale, 30, Qt.AlignCenter, pos_str) minx = minx + 8 * fontscale
class MultiList(NicosWidget, QWidget): """A list of entries, where each entry is a frame loaded from a UI file.""" designer_description = 'A list (with add/remove controls) of .ui entries' uifile = PropDef('uifile', str, '', 'UI file to use for the entries') entryAdded = pyqtSignal(object) entryRemoved = pyqtSignal(object) def __init__(self, parent, designMode=False, **kwds): QWidget.__init__(self, parent, **kwds) NicosWidget.__init__(self) self._entries = [] self._vlayout = QVBoxLayout() self._vlayout.setContentsMargins(0, 0, 0, 0) self.setLayout(self._vlayout) self._add() self._designer = designMode def registerKeys(self): pass def propertyUpdated(self, pname, value): if pname == 'uifile': self.clear() NicosWidget.propertyUpdated(self, pname, value) def entry(self, i): return self._entries[i].subwidget def entries(self): return [e.subwidget for e in self._entries] def count(self): return len(self._entries) def clear(self): for entry in self._entries[::-1]: self._remove(entry) self._add() def _remove(self, entry): self._entries.remove(entry) self._vlayout.removeWidget(entry) self.entryRemoved.emit(entry.subwidget) entry.deleteLater() if self._entries: self._entries[-1].setButton('+') def _add(self): new_frame = MultiEntry(self, self._client, self.props['uifile']) new_frame.addOrRemove.connect(self._addRemove) if self._entries: self._entries[-1].setButton('-') self._entries.append(new_frame) self._vlayout.addWidget(new_frame) self.entryAdded.emit(self._entries[-1].subwidget) def _addRemove(self): if not self._entries or self.sender() is self._entries[-1]: self._add() else: self._remove(self.sender())
class MultiAnalyzerWidget(NicosWidget, MultiAnalyzerView): rd1 = PropDef('rd1', str, 'rd1', 'Detector 1 position') rd2 = PropDef('rd2', str, 'rd2', 'Detector 2 position') rd3 = PropDef('rd3', str, 'rd3', 'Detector 3 position') rd4 = PropDef('rd4', str, 'rd4', 'Detector 4 position') rd5 = PropDef('rd5', str, 'rd5', 'Detector 5 position') rd6 = PropDef('rd6', str, 'rd6', 'Detector 6 position') rd7 = PropDef('rd7', str, 'rd7', 'Detector 7 position') rd8 = PropDef('rd8', str, 'rd8', 'Detector 8 position') rd9 = PropDef('rd9', str, 'rd9', 'Detector 9 position') rd10 = PropDef('rd10', str, 'rd10', 'Detector 10 position') rd11 = PropDef('rd11', str, 'rd11', 'Detector 11 position') rg1 = PropDef('rg1', str, 'rg1', 'Guide 1 rotation') rg2 = PropDef('rg2', str, 'rg2', 'Guide 2 rotation') rg3 = PropDef('rg3', str, 'rg3', 'Guide 3 rotation') rg4 = PropDef('rg4', str, 'rg4', 'Guide 4 rotation') rg5 = PropDef('rg5', str, 'rg5', 'Guide 5 rotation') rg6 = PropDef('rg6', str, 'rg6', 'Guide 6 rotation') rg7 = PropDef('rg7', str, 'rg7', 'Guide 7 rotation') rg8 = PropDef('rg8', str, 'rg8', 'Guide 8 rotation') rg9 = PropDef('rg9', str, 'rg9', 'Guide 9 rotation') rg10 = PropDef('rg10', str, 'rg10', 'Guide 10 rotation') rg11 = PropDef('rg11', str, 'rg11', 'Guide 11 rotation') ra1 = PropDef('ra1', str, 'ra1', 'Monochromator crystal 1 rotation') ra2 = PropDef('ra2', str, 'ra2', 'Monochromator crystal 2 rotation') ra3 = PropDef('ra3', str, 'ra3', 'Monochromator crystal 3 rotation') ra4 = PropDef('ra4', str, 'ra4', 'Monochromator crystal 4 rotation') ra5 = PropDef('ra5', str, 'ra5', 'Monochromator crystal 5 rotation') ra6 = PropDef('ra6', str, 'ra6', 'Monochromator crystal 6 rotation') ra7 = PropDef('ra7', str, 'ra7', 'Monochromator crystal 7 rotation') ra8 = PropDef('ra8', str, 'ra8', 'Monochromator crystal 8 rotation') ra9 = PropDef('ra9', str, 'ra9', 'Monochromator crystal 9 rotation') ra10 = PropDef('ra10', str, 'ra10', 'Monochromator crystal 10 rotation') ra11 = PropDef('ra11', str, 'ra11', 'Monochromator crystal 11 rotation') ta1 = PropDef('ta1', str, 'ta1', 'Monochromator crystal 1 translation') ta2 = PropDef('ta2', str, 'ta2', 'Monochromator crystal 2 translation') ta3 = PropDef('ta3', str, 'ta3', 'Monochromator crystal 3 translation') ta4 = PropDef('ta4', str, 'ta4', 'Monochromator crystal 4 translation') ta5 = PropDef('ta5', str, 'ta5', 'Monochromator crystal 5 translation') ta6 = PropDef('ta6', str, 'ta6', 'Monochromator crystal 6 translation') ta7 = PropDef('ta7', str, 'ta7', 'Monochromator crystal 7 translation') ta8 = PropDef('ta8', str, 'ta8', 'Monochromator crystal 8 translation') ta9 = PropDef('ta9', str, 'ta9', 'Monochromator crystal 9 translation') ta10 = PropDef('ta10', str, 'ta10', 'Monochromator crystal 10 translation') ta11 = PropDef('ta11', str, 'ta11', 'Monochromator crystal 11 translation') cad = PropDef('cad', str, 'cad', 'CAD device') lsa = PropDef('lsa', str, 'lsa', 'Distance sample analyser center') height = PropDef('height', int, 30, 'Widget height in characters') width = PropDef('width', int, 40, 'Widget width in characters') def __init__(self, parent): MultiAnalyzerView.__init__(self) NicosWidget.__init__(self) self._keymap = {} self._statuskeymap = {} self._targetkeymap = {} def registerKeys(self): for dev in [ 'ta1', 'ta2', 'ta3', 'ta4', 'ta5', 'ta6', 'ta7', 'ta8', 'ta9', 'ta10', 'ta11', 'ra1', 'ra2', 'ra3', 'ra4', 'ra5', 'ra6', 'ra7', 'ra8', 'ra9', 'ra10', 'ra11', 'rd1', 'rd2', 'rd3', 'rd4', 'rd5', 'rd6', 'rd7', 'rd8', 'rd9', 'rd10', 'rd11', 'rg1', 'rg2', 'rg3', 'rg4', 'rg5', 'rg6', 'rg7', 'rg8', 'rg9', 'rg10', 'rg11', 'cad', 'lsa' ]: devname = self.props.get(dev) if devname: k = self._source.register(self, devname + '/value') self._keymap[k] = dev k = self._source.register(self, devname + '/status') self._statuskeymap[k] = dev k = self._source.register(self, devname + '/target') self._targetkeymap[k] = dev def on_keyChange(self, key, value, time, expired): if key in self._keymap and not expired: self.values[self._keymap[key]] = value self.update() elif key in self._statuskeymap and not expired: self.status[self._statuskeymap[key]] = value[0] self.update() elif key in self._targetkeymap and not expired: self.targets[self._targetkeymap[key]] = value self.update() def sizeHint(self): return QSize(self.props['width'] * self._scale + 2, self.props['height'] * self._scale + 2)