def __init__(self, parent, client, instrument, configs, config=None): QDialog.__init__(self, parent) self.instrument = instrument self.configs = configs self.client = client self.setWindowTitle('Sample configuration') layout = QVBoxLayout() self.frm = QFrame(self) loadUi(self.frm, findResource('nicos_ess/loki/gui/ui_files/sampleconf_one.ui')) self.frm.addDevBtn.clicked.connect(self.on_addDevBtn_clicked) self.frm.delDevBtn.clicked.connect(self.on_delDevBtn_clicked) self.frm.readDevsBtn.clicked.connect(self.on_readDevsBtn_clicked) box = QDialogButtonBox(self) box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) box.accepted.connect(self.maybeAccept) box.rejected.connect(self.reject) layout.addWidget(self.frm) layout.addWidget(box) self.setLayout(layout) self.frm.thickBox.setValidator(DoubleValidator(self)) if config is not None: configToFrame(self.frm, config) # Apply local customisations to the stylesheet self.setStyleSheet(ConfigEditDialog_QSS) if not config: self.frm.whatLbl.setText('New sample configuration')
def __init__(self, parent, client, instrument, configs, config=None): QDialog.__init__(self, parent) self.instrument = instrument self.configs = configs self.client = client self.setWindowTitle('Sample configuration') layout = QVBoxLayout() self.frm = QFrame(self) loadUi(self.frm, findResource('nicos_mlz/kws1/gui/sampleconf_one.ui')) self.frm.addDevBtn.clicked.connect(self.on_addDevBtn_clicked) self.frm.delDevBtn.clicked.connect(self.on_delDevBtn_clicked) self.frm.readDevsBtn.clicked.connect(self.on_readDevsBtn_clicked) self.frm.readApBtn.clicked.connect(self.on_readApBtn_clicked) box = QDialogButtonBox(self) box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) box.accepted.connect(self.maybeAccept) box.rejected.connect(self.reject) layout.addWidget(self.frm) layout.addWidget(box) self.setLayout(layout) for box in [self.frm.offsetBox, self.frm.thickBox, self.frm.factorBox, self.frm.apXBox, self.frm.apYBox, self.frm.apWBox, self.frm.apHBox]: box.setValidator(DoubleValidator(self)) if config is not None: configToFrame(self.frm, config)
def __init__(self, item, window, menuwindow, parent): from nicos.clients.gui.panels.utils import createWindowItem QMainWindow.__init__(self, parent) self.user_color = window.user_color self.mainwindow = window.mainwindow self.log = NicosLogger('AuxiliarySubWindow') self.log.parent = self.mainwindow.log self.panels = [] # we have to nest one step to get consistent layout spacing # around the central widget central = QWidget(self) layout = QVBoxLayout() # only keep margin at the top (below the tabs) layout.setContentsMargins(0, 6, 0, 0) if len(item) == 1: (subitem, setupSpec) = item + (None, ) else: (subitem, setupSpec) = item it = createWindowItem(subitem, window, menuwindow, self, self.log) if it: if isinstance(it, ( Panel, QSplitter, )): if isinstance(it, Panel): it.hideTitle() # if tab has its own setups overwrite panels setups if setupSpec: it.setSetups(setupSpec) it.setWidgetVisible.connect(parent.setWidgetVisibleSlot) layout.addWidget(it) central.setLayout(layout) self.setCentralWidget(central)
def __init__(self, parent=None): QWidget.__init__(self, parent) self.canvas = FigureCanvas(Figure(figsize=(20, 16))) self._static_ax = self.canvas.figure.subplots() layout = QVBoxLayout() layout.addWidget(self.canvas) self.setLayout(layout)
class CustomButtonPanel(Panel): """Base class for custom instrument specific panels with a QDialogButtonBox at the lower right and some glue magic for fancy stuff... """ buttons = QDialogButtonBox.Close | QDialogButtonBox.Apply def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) # make a vertical layout for 'ourself' self.vBoxLayout = QVBoxLayout(self) # make a buttonBox self.buttonBox = QDialogButtonBox(self.buttons, parent=self) self.buttonBox.setObjectName('buttonBox') # put buttonBox below main content self.vBoxLayout.addWidget(self.buttonBox) allButtons = 'Ok Open Save Cancel Close Discard Apply Reset '\ 'RestoreDefaults Help SaveAll Yes YesToAll No NoToAll '\ 'Abort Retry Ignore'.split() for btn_name in allButtons: btn = self.buttonBox.button(getattr(QDialogButtonBox, btn_name)) if btn: handler = getattr(self, 'on_buttonBox_%s_clicked' % btn_name, None) if not handler: # pylint: disable=function-redefined def handler(self=self, n=btn_name): self.showError('on_buttonBox_%s_clicked not ' 'implemented!' % n) btn.clicked.connect(handler) def panelState(self): """returns current window state as obtained from the stack of parents""" obj = self while hasattr(obj, 'parent'): if isinstance(obj, AuxiliaryWindow): return "tab" elif isinstance(obj, DetachedWindow): return "detached" obj = obj.parent() return "main" def on_buttonBox_Close_clicked(self): self.closeWindow() def on_buttonBox_Ok_clicked(self): """OK = Apply + Close""" if hasattr(self, 'on_buttonBox_Apply_clicked'): self.on_buttonBox_Apply_clicked() self.on_buttonBox_Close_clicked()
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 __init__(self, parent): QMainWindow.__init__(self, parent) DlgUtils.__init__(self, 'Live data') self.panel = parent layout1 = QVBoxLayout() self.plot = QwtPlot(self) layout1.addWidget(self.plot) self.curve = QwtPlotCurve() self.curve.setRenderHint(QwtPlotCurve.RenderAntialiased) self.curve.attach(self.plot) self.marker = QwtPlotMarker() self.marker.attach(self.plot) self.markerpen = QPen(Qt.red) self.marker.setSymbol( QwtSymbol(QwtSymbol.Ellipse, QBrush(), self.markerpen, QSize(7, 7))) self.zoomer = QwtPlotZoomer(self.plot.canvas()) self.zoomer.setMousePattern(QwtPlotZoomer.MouseSelect3, Qt.NoButton) self.picker = QwtPlotPicker(self.plot.canvas()) self.picker.setSelectionFlags(QwtPlotPicker.PointSelection | QwtPlotPicker.ClickSelection) self.picker.setMousePattern(QwtPlotPicker.MouseSelect1, Qt.MidButton) self.picker.selected.connect(self.pickerSelected) layout2 = QHBoxLayout() layout2.addWidget(QLabel('Scale:', self)) self.scale = QComboBox(self) self.scale.addItems([ 'Single detectors, sorted by angle', 'Scattering angle 2theta (deg)', 'Q value (A-1)' ]) self.scale.currentIndexChanged[int].connect(self.scaleChanged) layout2.addWidget(self.scale) layout2.addStretch() self.scaleframe = QFrame(self) self.scaleframe.setLayout(layout2) self.scaleframe.setVisible(False) layout1.addWidget(self.scaleframe) mainframe = QFrame(self) mainframe.setLayout(layout1) self.setCentralWidget(mainframe) self.setContentsMargins(6, 6, 6, 6) plotfont = scaledFont(self.font(), 0.7) self.plot.setAxisFont(QwtPlot.xBottom, plotfont) self.plot.setAxisFont(QwtPlot.yLeft, plotfont) self.plot.setCanvasBackground(Qt.white) self.resize(800, 200) self._detinfo = None self._anglemap = None self._infowindow = None self._infolabel = None self._xs = self._ys = None self._type = None
def __init__(self, parent, measdef, client): self._edit = None self.measdef = measdef self.client = client QDialog.__init__(self, parent) loadUi(self, findResource('nicos_ess/loki/gui/ui_files/detsets.ui')) self.table.setColumnCount(len(measdef.getElements())) # apply current settings self._rows = [] if measdef.detsets: for row in measdef.detsets[0]: self.addRow(row) # create widgets for new setting self._new_elements = {} headers = [] for i, (eltype, cls) in enumerate(measdef.getElements()): element = self._new_elements[eltype] = cls(eltype, self.client) w = element.createWidget(self, self.client) for other in self._new_elements.values(): if other is not element: element.otherChanged(other.eltype, other.getValue()) def handler(new_value, eltype=eltype): for other in self._new_elements.values(): other.otherChanged(eltype, new_value) element.changed.connect(handler) headers.append(element.getLabel()) layout = QVBoxLayout() layout.addWidget(QLabel(headers[-1], self)) layout.addWidget(w) self.widgetFrame.layout().insertLayout(i, layout) # update table widget self.table.setHorizontalHeaderLabels(headers) self.table.resizeColumnsToContents() for i in range(len(measdef.getElements())): self.table.setColumnWidth(i, max(50, 1.5 * self.table.columnWidth(i))) self.table.resizeRowsToContents()
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()
def viewTextFile(self, fname): with open(fname) as f: contents = f.read() qd = QDialog(self, 'PreviewDlg', True) qd.setCaption('File preview') qd.resize(QSize(500, 500)) lay = QVBoxLayout(qd, 11, 6, 'playout') lb = QLabel(qd, 'label') lb.setText('Viewing %s:' % fname) lay.addWidget(lb) tx = QTextEdit(qd, 'preview') tx.setReadOnly(1) tx.setText(contents) font = QFont(tx.font()) font.setFamily('monospace') tx.setFont(font) lay.addWidget(tx) btn = QPushButton(qd, 'ok') btn.setAutoDefault(1) btn.setDefault(1) btn.setText('Close') btn.clicked.connect(qd.accept) lay.addWidget(btn, 0, QWidget.AlignRight) qd.show()
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 ItemsWidget(QScrollArea): valueModified = pyqtSignal() valueChosen = pyqtSignal(object) allow_reorder = True def __init__(self, parent, nmin, allow_enter=False): QScrollArea.__init__(self, parent) self.setWidgetResizable(True) self.frame = QFrame(self) self.layout = QVBoxLayout() self.layout.setContentsMargins(2, 2, 2, 2) self.addBtn = QPushButton(QIcon(':/add'), '', self.frame) self.addBtn.clicked.connect(self.on_addBtn_clicked) self.addBtn.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)) self.layout.addWidget(self.addBtn) self.layout.addStretch() self.frame.setLayout(self.layout) self.setWidget(self.frame) self.items = [] self.nmin = nmin self.allow_enter = allow_enter def insertItem(self, *widgets): item = QWidget(self.frame) item._widgets = widgets layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) for widget in widgets: layout.addWidget(widget) if self.allow_reorder: btn = QPushButton(QIcon(':/up'), '', item) btn._item = item btn.clicked.connect(self.on_upBtn_clicked) layout.addWidget(btn) btn = QPushButton(QIcon(':/down'), '', item) btn._item = item btn.clicked.connect(self.on_downBtn_clicked) layout.addWidget(btn) btn = QPushButton(QIcon(':/remove'), '', item) btn._item = item btn.clicked.connect(self.on_removeBtn_clicked) layout.addWidget(btn) item.setLayout(layout) self.layout.insertWidget(self.layout.count() - 2, item) self.items.append(item) def on_addBtn_clicked(self): self.insertItem(*self.createItem()) self.valueModified.emit() def on_removeBtn_clicked(self): if len(self.items) <= self.nmin: return item = self.sender()._item index = self.items.index(item) del self.items[index] self.layout.takeAt(index).widget().deleteLater() self.valueModified.emit() def on_upBtn_clicked(self): item = self.sender()._item index = self.items.index(item) if index <= 0: return self._swapItems(index - 1) def on_downBtn_clicked(self): item = self.sender()._item index = self.items.index(item) if index >= len(self.items) - 1: return self._swapItems(index) def _swapItems(self, firstindex): item1 = self.items[firstindex] item2 = self.items[firstindex + 1] self.layout.takeAt(firstindex) self.layout.takeAt(firstindex) # moved up one self.items[firstindex:firstindex + 2] = [item2, item1] self.layout.insertWidget(firstindex, item2) self.layout.insertWidget(firstindex + 1, item1) self.valueModified.emit()
class MainWindow(QMainWindow): i = 0 def __init__(self, parent=None): QMainWindow.__init__(self, parent) # scroll area Widget contents - layout self.scrollLayout = QFormLayout() self.scrollLayout.setContentsMargins(0, 0, 0, 0) # scroll area Widget contents self.scrollWidget = QWidget() self.scrollWidget.setLayout(self.scrollLayout) # scroll area self.scrollArea = QScrollArea() self.scrollArea.setWidgetResizable(True) self.scrollArea.setWidget(self.scrollWidget) # main layout self.mainLayout = QVBoxLayout() self.mainLayout.setSpacing(0) # add all main to the main vLayout self.mainLayout.addWidget(self.scrollArea) # central Widget self.centralWidget = QWidget() self.centralWidget.setLayout(self.mainLayout) # set central Widget self.setCentralWidget(self.centralWidget) try: self._bus = ModbusTcpClient('drum.panda.frm2') self._bus.connect() self._sync() print("Modbus synced!") print(self.ReadWord(0x20), self.ReadWord(0x21)) except Exception as err: print("Modbus failed: %r, using demo mode!" % err) self._bus = None self._sync() widgets = [] widgets.append( BaseDev(self, 'mtt motor inputs', 0, has_status=True, addr=34)) widgets.append( BaseDev(self, 'spare inputs', 1, has_status=True, addr=44)) widgets.append( BaseDev(self, 'spare outputs', 2, has_status=True, target='0x%04x' % self.ReadWord(60), addr=59)) widgets.append( BaseDev(self, 'enable_word', 3, target='0x%04x' % self.ReadWord(73), addr=73)) widgets.append(BaseDev(self, 'cycle_counter', 4, addr=74)) widgets.append( BaseDev(self, 'handle_cw', 5, has_status=True, target='%d' % self.ReadWord(51), addr=50)) widgets.append( BaseDev(self, 'handle_ccw', 6, has_status=True, target='%d' % self.ReadWord(54), addr=53)) widgets.append(BaseDev(self, 'enc1', 7, has_status=True, addr=46)) widgets.append(BaseDev(self, 'enc2', 8, has_status=True, addr=48)) widgets.append( BaseDev(self, 'arm_switch', 9, has_status=True, target='%d' % self.ReadWord(63), addr=62)) widgets.append(BaseDev(self, 'encoder1', 10, has_status=False, addr=65)) widgets.append( BaseDev(self, 'arm', 11, has_status=True, target='%f' % self.ReadFloat(70), value_offset=2, addr=68)) widgets.append( BaseDev(self, 'magnet', 12, has_status=True, target='%d' % self.ReadWord(57), addr=56)) widgets.append( BaseDev(self, 'air', 13, has_status=True, target='%d' % self.ReadWord(60), addr=59)) for w in widgets: self.addWidget(w) widgets.sort(key=lambda w: w.index) self.widgets = widgets self.startTimer(225) # in ms ! def resetter(self, index): w = self.widgets[index] if w.has_status: addr = w.base_address + w.offset if w.has_target: addr += w.offset print(addr) self.reset(addr) else: print("resetter: device %d has no status" % index) def stopper(self, index): w = self.widgets[index] if w.has_target: addr = w.base_address if w.name == 'enable_word': print("stopper: DISABLING %d" % (addr)) self.WriteWord(addr, 0) else: addr += w.offset if w.has_target: addr += w.offset print("stopper: stopping on addr %d" % (addr)) self.stop(addr) else: print("stopper: cannot stop - no target %d" % index) def targeter(self, index, valuestr): w = self.widgets[index] if w.has_target: v = str(valuestr).strip() if not v: return # ignore empty values (no value entered into the box?) addr = w.base_address if w.offset == 2: v = float(v) addr += w.offset print("targeter: setting addr %d to %f" % (addr, v)) self.WriteFloat(addr, v) else: if v.startswith('0x') or v.startswith('0X'): v = int(v[2:], 16) elif v.startswith(('x', 'X', '$')): v = int(v[1:], 16) else: v = int(v) if w.name != 'enable_word': addr += w.offset print("targeter: setting addr %d to %r" % (addr, valuestr)) self.WriteWord(addr, v) else: print("targeter: device fas no target %d:%r" % (index, valuestr)) def ReadWord(self, addr): return self._registers[int(addr)] def WriteWord(self, addr, value): self._bus.write_register(int(addr | 0x4000), int(value)) self._sync() def ReadDWord(self, addr): return unpack( '<I', pack('<HH', self._registers[int(addr)], self._registers[int(addr) + 1])) def WriteDWord(self, addr, value): low, high = unpack('<HH', pack('<I', int(value))) self._bus.write_registers(int(addr | 0x4000), [low, high]) self._sync() def ReadFloat(self, addr): return unpack( '<f', pack('<HH', self._registers[int(addr) + 1], self._registers[int(addr)])) def WriteFloat(self, addr, value): low, high = unpack('<HH', pack('<f', float(value))) self._bus.write_registers(int(addr | 0x4000), [high, low]) self._sync() def _sync(self): if self._bus: self._registers = self._bus.read_holding_registers(0x4000, 75).registers[:] # print(self._registers) else: self._registers = [self.i] * 75 self.i += 1 def reset(self, addr): self.WriteWord(addr, 0x0fff & self.ReadWord(addr)) def stop(self, addr): self.WriteWord(addr, 0x1000 | (0x0fff & self.ReadWord(addr))) def timerEvent(self, event): self._sync() w = self.widgets # 1: %MB68: cycle counter val = self.ReadWord(34) stat = self.ReadWord(35) w[0].valueWidget.setText(bin(65536 | val)[3:]) w[0].statvalueWidget.setText('0x%04x' % stat) w[0].statusWidget.setText('mtt motor inputs') # 10: %MB96: spare inputs val = self.ReadWord(44) stat = self.ReadWord(45) w[1].valueWidget.setText(bin(65536 | val)[3:]) w[1].statvalueWidget.setText('0x%04x' % stat) w[1].statusWidget.setText('spare inputs') # 17: %MB136: spare outputs val = self.ReadWord(59) target = self.ReadWord(60) stat = self.ReadWord(61) stati = Stati(stat) w[2].valueWidget.setText('%d' % val) w[2].statvalueWidget.setText('0x%04x' % stat) w[2].statusWidget.setText(', '.join(stati)) w[2].targetWidget.setPlaceholderText('%d' % target) # 19: %MB146: enable code word val = self.ReadWord(73) w[3].valueWidget.setText('0x%04x' % val) w[3].targetWidget.setPlaceholderText('0x%04x' % val) # 20: %MB148: cycle counter val = self.ReadWord(74) w[4].valueWidget.setText('0x%04x' % val) # 13: %MB112: liftclamp val = self.ReadWord(50) target = self.ReadWord(51) stat = self.ReadWord(52) stati = Stati(stat) if (stat & 0x9000) == 0x9000: stati.append('ERR:Movement timed out') if stat & 0x0800: stati.append('ERR:liftclamp switches in Error') if stat & 0x0004: stati.append('No Air pressure') if stat & 0x0002: stati.append('ERR:Actuator Wire shorted!') if stat & 0x0001: stati.append('ERR:Actuator Wire open!') w[5].valueWidget.setText('%d' % val) w[5].statvalueWidget.setText('0x%04x' % stat) w[5].statusWidget.setText(', '.join(stati)) w[5].targetWidget.setPlaceholderText('%d' % target) # 13: %MB112: liftclamp val = self.ReadWord(53) target = self.ReadWord(54) stat = self.ReadWord(55) stati = Stati(stat) if (stat & 0x9000) == 0x9000: stati.append('ERR:Movement timed out') if stat & 0x0800: stati.append('ERR:liftclamp switches in Error') if stat & 0x0004: stati.append('No Air pressure') if stat & 0x0002: stati.append('ERR:Actuator Wire shorted!') if stat & 0x0001: stati.append('ERR:Actuator Wire open!') w[6].valueWidget.setText('%d' % val) w[6].statvalueWidget.setText('0x%04x' % stat) w[6].statusWidget.setText(', '.join(stati)) w[6].targetWidget.setPlaceholderText('%d' % target) # 7: %MB192: enc1 val = self.ReadWord(46) stat = self.ReadWord(47) stati = Stati(stat) if stat & 0x0002: stati.append('ERR:Underflow!') if stat & 0x0001: stati.append('ERR:Overflow!') w[7].valueWidget.setText(str(val)) w[7].statvalueWidget.setText('0x%04x' % stat) w[7].statusWidget.setText(', '.join(stati)) # 7: %MB192: arm val = self.ReadWord(48) stat = self.ReadWord(49) stati = Stati(stat) if stat & 0x0002: stati.append('ERR:Underflow!') if stat & 0x0001: stati.append('ERR:Overflow!') w[8].valueWidget.setText(str(val)) w[8].statvalueWidget.setText('0x%04x' % stat) w[8].statusWidget.setText(', '.join(stati)) # 17: %MB136: spare outputs val = self.ReadWord(62) target = self.ReadWord(63) stat = self.ReadWord(64) stati = Stati(stat) w[9].valueWidget.setText('%d' % val) w[9].statvalueWidget.setText('0x%04x' % stat) w[9].statusWidget.setText(', '.join(stati)) w[9].targetWidget.setPlaceholderText('%d' % target) # encoder val = self.ReadFloat(65) w[10].valueWidget.setText('%f' % val) # motro val = self.ReadFloat(68) target = self.ReadFloat(70) stat = self.ReadWord(72) stati = Stati(stat) w[11].valueWidget.setText('%f' % val) w[11].statvalueWidget.setText('0x%04x' % stat) w[11].statusWidget.setText(', '.join(stati)) w[11].targetWidget.setPlaceholderText('%f' % target) # 13: %MB112: liftclamp val = self.ReadWord(56) target = self.ReadWord(57) stat = self.ReadWord(58) stati = Stati(stat) if (stat & 0x9000) == 0x9000: stati.append('ERR:Movement timed out') if stat & 0x0800: stati.append('ERR:liftclamp switches in Error') if stat & 0x0004: stati.append('No Air pressure') if stat & 0x0002: stati.append('ERR:Actuator Wire shorted!') if stat & 0x0001: stati.append('ERR:Actuator Wire open!') w[12].valueWidget.setText('%d' % val) w[12].statvalueWidget.setText('0x%04x' % stat) w[12].statusWidget.setText(', '.join(stati)) w[12].targetWidget.setPlaceholderText('%d' % target) # 13: %MB112: air val = self.ReadWord(59) target = self.ReadWord(60) stat = self.ReadWord(61) stati = Stati(stat) if (stat & 0x9000) == 0x9000: stati.append('ERR:Movement timed out') if stat & 0x0800: stati.append('ERR:liftclamp switches in Error') if stat & 0x0004: stati.append('No Air pressure') if stat & 0x0002: stati.append('ERR:Actuator Wire shorted!') if stat & 0x0001: stati.append('ERR:Actuator Wire open!') w[13].valueWidget.setText('%d' % val) w[13].statvalueWidget.setText('0x%04x' % stat) w[13].statusWidget.setText(', '.join(stati)) w[13].targetWidget.setPlaceholderText('%d' % target) def addWidget(self, which): which.setContentsMargins(10, 0, 0, 0) self.scrollLayout.addRow(which) l = QFrame() l.setLineWidth(1) # l.setMidLineWidth(4) l.setFrameShape(QFrame.HLine) l.setContentsMargins(10, 0, 10, 0) self.scrollLayout.addRow(l)
class MainWindow(QMainWindow): i = 0 def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # scroll area Widget contents - layout self.scrollLayout = QFormLayout() self.scrollLayout.setContentsMargins(0, 0, 0, 0) # scroll area Widget contents self.scrollWidget = QWidget() self.scrollWidget.setLayout(self.scrollLayout) # scroll area self.scrollArea = QScrollArea() self.scrollArea.setWidgetResizable(True) self.scrollArea.setWidget(self.scrollWidget) # main layout self.mainLayout = QVBoxLayout() self.mainLayout.setSpacing(0) # add all main to the main vLayout self.mainLayout.addWidget(self.scrollArea) # central Widget self.centralWidget = QWidget() self.centralWidget.setLayout(self.mainLayout) # set central Widget self.setCentralWidget(self.centralWidget) try: self._bus = ModbusTcpClient('wechsler.panda.frm2') self._bus.connect() self._sync() print("PLC conforms to spec %.4f" % self.ReadFloat(0)) except Exception: print("Modbus failed, using demo mode!") self._bus = None self._sync() widgets = [] widgets.append(WriteWord(self, 'last_liftpos', addr=58 / 2)) widgets.append(ReadWord(self, 'analog1', addr=92 / 2)) widgets.append(ReadWord(self, 'analog2', addr=96 / 2)) widgets.append(AnalogInput(self, 'liftpos_analog', addr=146 / 2)) widgets.append(DiscreteInput(self, 'lift_sw', addr=68 / 2)) widgets.append(LIFT(self, 'lift', 104 / 2)) widgets.append(WriteWord(self, 'last_magpos', addr=60 / 2)) widgets.append(DiscreteInput(self, 'magazin_sw', addr=72 / 2)) widgets.append(MAGAZIN(self, 'magazin', addr=110 / 2)) widgets.append(DiscreteInput(self, 'magazin_occ_sw', addr=84 / 2)) widgets.append(DiscreteInput(self, 'magazin_occ', addr=88 / 2)) widgets.append(DiscreteInput(self, 'liftclamp_sw', addr=76 / 2)) widgets.append(CLAMP(self, 'liftclamp', addr=116 / 2)) widgets.append(DiscreteInput(self, 'magazinclamp_sw', addr=80 / 2)) widgets.append(CLAMP(self, 'magazinclamp', addr=122 / 2)) widgets.append(CLAMP(self, 'tableclamp', addr=128 / 2)) widgets.append(CLAMP(self, 'inhibit_relay', addr=134 / 2)) widgets.append(WriteWord(self, 'enable_word', addr=150 / 2)) widgets.append(DiscreteInput(self, 'spare inputs', addr=100 / 2)) widgets.append(DiscreteOutput(self, 'spare outputs', addr=140 / 2)) widgets.append(ReadWord(self, 'cycle_counter', addr=152 / 2)) for w in widgets: self.addWidget(w) self.widgets = widgets self.startTimer(225) # in ms ! def ReadWord(self, addr): return self._registers[int(addr)] def WriteWord(self, addr, value): if self._bus: self._bus.write_register(int(addr | 0x4000), int(value)) self._sync() def ReadDWord(self, addr): return unpack( '<I', pack('<HH', self._registers[int(addr)], self._registers[int(addr) + 1])) def WriteDWord(self, addr, value): if self._bus: low, high = unpack('<HH', pack('<I', int(value))) self._bus.write_registers(int(addr | 0x4000), [low, high]) self._sync() def ReadFloat(self, addr): return unpack( '<f', pack('<HH', self._registers[int(addr) + 1], self._registers[int(addr)])) def WriteFloat(self, addr, value): if self._bus: low, high = unpack('<HH', pack('<f', float(value))) self._bus.write_registers(int(addr | 0x4000), [high, low]) self._sync() def _sync(self): if self._bus: self._registers = self._bus.read_holding_registers(0x4000, 77).registers[:] else: self._registers = [self.i + i for i in range(77)] self.i += 1 def timerEvent(self, event): # pylint: disable=R0915 self._sync() for w in self.widgets: w._update() return def addWidget(self, which): which.setContentsMargins(10, 0, 0, 0) self.scrollLayout.addRow(which) l = QFrame() l.setLineWidth(1) # l.setMidLineWidth(4) l.setFrameShape(QFrame.HLine) l.setContentsMargins(10, 0, 10, 0) self.scrollLayout.addRow(l)
def initGui(self): def log_unhandled(*exc_info): traceback.print_exception(*exc_info) self.log.exception('unhandled exception in QT callback', exc_info=exc_info) sys.excepthook = log_unhandled self._qtapp = QApplication(['qtapp'], organizationName='nicos', applicationName='gui') self._master = master = MonitorWindow() if self._geometry == 'fullscreen': master.showFullScreen() master._wantFullScreen = True # In some Qt5 versions, showFullScreen is buggy and doesn't # actually resize the window (but hides decoration etc). # So, explicitly set the geometry of the first screen. master.setGeometry(QApplication.screens()[0].geometry()) QCursor.setPos(master.geometry().bottomRight()) elif isinstance(self._geometry, tuple): w, h, x, y = self._geometry master.setGeometry(x, y, w, h) # colors used for the display of watchdog warnings, not for the # individual value displays self._bgcolor = QColor('gray') self._black = QColor('black') self._red = QColor('red') self._gray = QColor('gray') master.setWindowTitle(self.title) self._bgcolor = master.palette().color(QPalette.Window) timefont = QFont(self.font, self._timefontsize) blockfont = QFont(self.font, self._fontsizebig) warnfont = QFont(self.font, self._fontsizebig) warnfont.setBold(True) labelfont = QFont(self.font, self._fontsize) stbarfont = QFont(self.font, int(self._fontsize * 0.8)) valuefont = QFont(self.valuefont or self.font, self._fontsize) blheight = QFontMetrics(blockfont).height() tiheight = QFontMetrics(timefont).height() # split window into to panels/frames below each other: # one displays time, the other is divided further to display blocks. # first the timeframe: masterframe = QFrame(master) masterlayout = QVBoxLayout() if self.title: self._titlelabel = QLabel( '', master, font=timefont, autoFillBackground=True, alignment=Qt.AlignHCenter) pal = self._titlelabel.palette() pal.setColor(QPalette.WindowText, self._gray) self._titlelabel.setPalette(pal) self._titlelabel.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) self._master.updateTitle.connect(self._titlelabel.setText) masterlayout.addWidget(self._titlelabel) masterlayout.addSpacing(0.2 * tiheight) else: self._titlelabel = None self._warnpanel = QFrame(master) self._warnpanel.setVisible(False) warningslayout = QVBoxLayout() lbl = QLabel('Warnings', self._warnpanel, font=warnfont) lbl.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) warningslayout.addWidget(lbl) self._warnlabel = SqueezedLabel('', self._warnpanel, font=blockfont) warningslayout.addWidget(self._warnlabel) self._warnpanel.setLayout(warningslayout) masterlayout.addWidget(self._warnpanel) master.switchWarnPanel.connect(self._switch_warnpanel) displayframe = QFrame(master) self._plots = {} colorScheme = lightColorScheme if self.colors == 'light' else None fontCache = {1.0: valuefont} def _create_field(groupframe, field): def _setup(widget): fontscale = field.get('fontscale', 1.0) if fontscale not in fontCache: fontCache[fontscale] = scaledFont(valuefont, fontscale) widget.valueFont = fontCache[fontscale] widget.setFont(labelfont) for key in field: if key in widget.properties: setattr(widget, key, field[key]) widget.setSource(self) if hasattr(widget, 'widgetInfo'): widget.widgetInfo.connect(self.newWidgetInfo) return widget if isinstance(field, str): field = {'dev': field} if 'min' in field: field['min'] = repr(field['min']) if 'max' in field: field['max'] = repr(field['max']) setups = field.get('setups', None) if 'gui' in field: resource = findResource(field.pop('gui')) try: instance = uic.loadUi(resource) except Exception as err: self.log.exception('could not load .ui file %r, ignoring', resource) instance = QLabel('%r could not be loaded:\n%s' % (resource, err)) else: for child in instance.findChildren(NicosWidget): _setup(child) instance.setups = setups return instance elif 'widget' in field: widget_class = self._class_import(field.pop('widget')) widget = widget_class(groupframe) if isinstance(widget, NicosWidget): _setup(widget) for child in widget.findChildren(NicosWidget): _setup(child) widget.setups = setups return widget elif 'plot' in field and plot_available: # XXX make this more standard plotwidget = self._plots.get(field['plot']) if plotwidget: plotwidget.devices += [field.get('dev', field.get('key', ''))] plotwidget.names += [field.get('name', field.get('dev', field.get('key', '')))] return None plotwidget = TrendPlot(groupframe) _setup(plotwidget) plotwidget.legend = field.get('legend', True) plotwidget.plotwindow = field.get('plotwindow', 3600) plotwidget.plotinterval = field.get('plotinterval', 2) self._plots[field['plot']] = plotwidget plotwidget.devices = [field.get('dev', field.get('key', ''))] plotwidget.names = [field.get('name', field.get('dev', field.get('key', '')))] plotwidget.setups = setups return plotwidget elif 'picture' in field: picwidget = PictureDisplay(groupframe) picwidget.filepath = field['picture'] picwidget.setups = setups return _setup(picwidget) else: display = ValueDisplay(groupframe, colorScheme=colorScheme, showExpiration=self.noexpired) display.setups = setups return _setup(display) # now iterate through the layout and create the widgets to display it displaylayout = QVBoxLayout(spacing=20) for superrow in self.layout: boxlayout = QHBoxLayout(spacing=20) boxlayout.setContentsMargins(10, 10, 10, 10) for column in superrow: columnlayout = QVBoxLayout(spacing=0.8*blheight) for block in column: block = self._resolve_block(block) blocklayout_outer = QHBoxLayout() blocklayout_outer.addStretch() blocklayout = QVBoxLayout() blocklayout.addSpacing(0.5 * blheight) blockbox = BlockBox(displayframe, block._title, blockfont, block._options) for row in block: if row in (None, '---'): blocklayout.addSpacing(12) else: rowlayout = QHBoxLayout() rowlayout.addStretch() rowlayout.addSpacing(self._padding) for field in row: fieldwidget = _create_field(blockbox, field) if fieldwidget: rowlayout.addWidget(fieldwidget) rowlayout.addSpacing(self._padding) if fieldwidget.setups: if blockbox.setups: blockbox._onlyfields.append(fieldwidget) else: self._onlyfields.append(fieldwidget) # start hidden fieldwidget.setHidden(True) rowlayout.addStretch() blocklayout.addLayout(rowlayout) if blockbox.setups: self._onlyblocks.append((blocklayout_outer, blockbox)) blockbox.setHidden(True) # start hidden blocklayout.addSpacing(0.3 * blheight) blockbox.setLayout(blocklayout) blocklayout_outer.addWidget(blockbox) blocklayout_outer.addStretch() columnlayout.addLayout(blocklayout_outer) columnlayout.addStretch() boxlayout.addLayout(columnlayout) displaylayout.addLayout(boxlayout) displayframe.setLayout(displaylayout) for plot in self._plots.values(): plot.setSource(self) masterlayout.addWidget(displayframe) masterframe.setLayout(masterlayout) master.setCentralWidget(masterframe) # initialize status bar self._statuslabel = QLabel(font=stbarfont) master.statusBar().addWidget(self._statuslabel) self._statustimer = None master.show()