def addExperiment(self): textLabel = QLabel('Experiment:') textLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) textLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) experimentLabel = QLabel('Unknown') experimentLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.toolBarMain.addWidget(textLabel) self.toolBarMain.addWidget(experimentLabel) # if INSTRUMENT is defined add the logo/name of the instrument experiment = os.getenv('EXPERIMENT') if experiment: experimentLabel.setText(experiment)
def _squeeze(self, text=None): if text is None: text = self._fulltext or self.text() fm = self.fontMetrics() labelwidth = self.size().width() squeezed = False new_lines = [] for line in text.split('\n'): if fm.width(line) > labelwidth: squeezed = True new_lines.append(fm.elidedText(line, Qt.ElideRight, labelwidth)) else: new_lines.append(line) if squeezed: QLabel.setText(self, '\n'.join(map(text_type, new_lines))) self.setToolTip(self._fulltext) else: QLabel.setText(self, self._fulltext) self.setToolTip('')
def __init__(self, parent, client, options): CustomButtonPanel.__init__(self, parent, client, options) # our content is a simple widget ... self._tableWidget = TableWidget(self) self._tableWidget.setColumnCount(1) self._tableWidget.setHorizontalHeaderLabels(['Sample name']) self._tableWidget.horizontalHeaderItem(0).setTextAlignment( Qt.AlignLeft | Qt.AlignVCenter) self._tableWidget.setSortingEnabled(False) self._tableWidget.setCornerLabel('Position') self.vBoxLayout.insertWidget(0, self._tableWidget) client.connected.connect(self.on_client_connected) client.setup.connect(self.on_client_connected) image = options.get('image', None) # insert the optional image at top... if image: l = QLabel(self) l.setText(image) # insert above scrollArea self.vBoxLayout.insertWidget(0, l, alignment=Qt.AlignHCenter) p = QPixmap() if p.load(findResource(image)): l.setPixmap(p) else: msg = 'Loading of Image %r failed:' % image msg += '\n\nCheck GUI config file for %r' % __file__ self.showError(msg) self._numSamples = int(options.get('positions', 11)) self._tableWidget.setRowCount(self._numSamples) # fill in widgets into grid for pos in range(self._numSamples): self._tableWidget.setCellWidget(pos, 0, QLineEdit('')) self._tableWidget.horizontalHeader().setStretchLastSection(100) # now fill in data self._update_sample_info()
def addInstrument(self): textLabel = QLabel('Instrument:') textLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) textLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) instrumentLabel = QLabel('Unknown') instrumentLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.toolBarMain.addWidget(textLabel) self.toolBarMain.addWidget(instrumentLabel) instrument = os.getenv('INSTRUMENT') if instrument: instrument = instrument.split('.')[-1] logo = decolor_logo(QPixmap('resources/%s-logo.svg' % instrument), Qt.white) if logo.isNull(): instrumentLabel.setText(instrument.upper()) return instrumentLabel.setPixmap( logo.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation))
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 MainWindow(DefaultMainWindow): ui = '%s/main.ui' % uipath def __init__(self, log, gui_conf, viewonly=False, tunnel=''): DefaultMainWindow.__init__(self, log, gui_conf, viewonly, tunnel) self.add_logo() self.set_icons() self.style_file = gui_conf.stylefile # Cheeseburger menu dropdown = QMenu('') dropdown.addAction(self.actionConnect) dropdown.addAction(self.actionViewOnly) dropdown.addAction(self.actionPreferences) dropdown.addAction(self.actionExpert) dropdown.addSeparator() dropdown.addAction(self.actionExit) self.actionUser.setMenu(dropdown) self.actionUser.setIconVisibleInMenu(True) self.dropdown = dropdown self.actionExpert.setEnabled(self.client.isconnected) self.actionEmergencyStop.setEnabled(self.client.isconnected) self._init_instrument_name() self._init_experiment_name() def _init_toolbar(self): self.statusLabel = QLabel('', self, pixmap=QPixmap(':/disconnected'), margin=5, minimumSize=QSize(30, 10)) self.statusLabel.setStyleSheet('color: white') self.toolbar = self.toolBarRight self.toolbar.addWidget(self.statusLabel) self.setStatus('disconnected') def _init_experiment_name(self): self.experiment_text = QLabel() self.experiment_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.experiment_text.setStyleSheet( 'font-size: 17pt; font-weight: bold') self.toolBarMain.addWidget(self.experiment_text) self.experiment_label = QLabel() self.experiment_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.experiment_label.setStyleSheet('font-size: 17pt') self.toolBarMain.addWidget(self.experiment_label) def _init_instrument_name(self): self.instrument_text = QLabel() self.instrument_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.instrument_text.setStyleSheet( 'font-size: 17pt; font-weight: bold') self.toolBarMain.addWidget(self.instrument_text) self.instrument_label = QLabel() self.instrument_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.instrument_label.setStyleSheet('font-size: 17pt') self.toolBarMain.addWidget(self.instrument_label) def set_icons(self): self.actionUser.setIcon(get_icon('settings_applications-24px.svg')) self.actionEmergencyStop.setIcon( get_icon('emergency_stop_cross-24px.svg')) self.actionConnect.setIcon(get_icon('power-24px.svg')) self.actionExit.setIcon(get_icon('exit_to_app-24px.svg')) self.actionViewOnly.setIcon(get_icon('lock-24px.svg')) self.actionPreferences.setIcon(get_icon('tune-24px.svg')) self.actionExpert.setIcon(get_icon('fingerprint-24px.svg')) def add_logo(self): logo_label = QLabel() pxr = decolor_logo( QPixmap(path.join(root_path, 'resources', 'logo-icon.png')), Qt.white) logo_label.setPixmap( pxr.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) logo_label.setMargin(5) self.toolBarMain.insertWidget(self.toolBarMain.actions()[0], logo_label) nicos_label = QLabel() pxr = decolor_logo( QPixmap(path.join(root_path, 'resources', 'nicos-logo-high.svg')), Qt.white) nicos_label.setPixmap( pxr.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) self.toolBarMain.insertWidget(self.toolBarMain.actions()[1], nicos_label) def update_instrument_text(self): instrument = self.client.eval('session.instrument', None) self.instrument_text.setText('Instrument:') if instrument: logo = decolor_logo( QPixmap( path.join(root_path, 'resources', f'{instrument}-logo.svg')), Qt.white) if logo.isNull(): self.instrument_label.setText(instrument.upper()) return self.instrument_label.setPixmap( logo.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) else: self.instrument_label.setText('UNKNOWN') def update_experiment_text(self): max_text_length = 50 experiment = self.client.eval('session.experiment.title', None) if experiment is not None: self.experiment_text.setText(" Experiment:") self.experiment_label.setText(experiment[0:max_text_length]) def remove_experiment_and_instrument(self): self.experiment_label.clear() self.experiment_text.clear() self.instrument_label.clear() self.instrument_text.clear() def reloadQSS(self): self.setQSS(self.stylefile) def selectQSS(self): style_file = QFileDialog.getOpenFileName( self, filter="Qt Stylesheet Files (*.qss)")[0] if style_file: self.style_file = style_file self.setQSS(self.style_file) @staticmethod def setQSS(style_file): with open(style_file, 'r', encoding='utf-8') as fd: try: QApplication.instance().setStyleSheet(fd.read()) except Exception as e: print(e) def setStatus(self, status, exception=False): if status == self.current_status: return if self.client.last_action_at and \ self.current_status == 'running' and \ status in ('idle', 'paused') and \ current_time() - self.client.last_action_at > 20: # show a visual indication of what happened if status == 'paused': msg = 'Script is now paused.' elif exception: msg = 'Script has exited with an error.' else: msg = 'Script has finished.' self.trayIcon.showMessage(self.instrument, msg) self.client.last_action_at = 0 self.current_status = status is_connected = status != 'disconnected' if is_connected: self.actionConnect.setText('Disconnect') self.statusLabel.setText('\u2713 Connected') self.update_instrument_text() self.update_experiment_text() else: self.actionConnect.setText('Connect to server...') self.statusLabel.setText('Disconnected') self.setTitlebar(False) # new status icon pixmap = QPixmap(':/' + status + ('exc' if exception else '')) new_icon = QIcon() new_icon.addPixmap(pixmap, QIcon.Disabled) self.trayIcon.setIcon(new_icon) self.trayIcon.setToolTip('%s status: %s' % (self.instrument, status)) if self.showtrayicon: self.trayIcon.show() if self.promptWindow and status != 'paused': self.promptWindow.close() # propagate to panels for panel in self.panels: panel.updateStatus(status, exception) for window in self.windows.values(): for panel in window.panels: panel.updateStatus(status, exception) def on_client_connected(self): DefaultMainWindow.on_client_connected(self) self.actionConnect.setIcon(get_icon("power_off-24px.svg")) self.actionExpert.setEnabled(True) self.actionEmergencyStop.setEnabled(not self.client.viewonly) def on_client_disconnected(self): DefaultMainWindow.on_client_disconnected(self) self.remove_experiment_and_instrument() self.actionConnect.setIcon(get_icon("power-24px.svg")) self.actionExpert.setEnabled(False) self.actionExpert.setChecked(False) self.actionEmergencyStop.setEnabled(False) def on_actionViewOnly_toggled(self, on): DefaultMainWindow.on_actionViewOnly_toggled(self, on) if self.client.isconnected: self.actionEmergencyStop.setEnabled(not self.client.viewonly) else: self.actionEmergencyStop.setEnabled(False) @pyqtSlot(bool) def on_actionConnect_triggered(self, _): # connection or disconnection request? connection_req = self.current_status == "disconnected" super().on_actionConnect_triggered(connection_req) @pyqtSlot() def on_actionUser_triggered(self): w = self.toolBarRight.widgetForAction(self.actionUser) self.dropdown.popup(w.mapToGlobal(QPoint(0, w.height()))) @pyqtSlot() def on_actionEmergencyStop_triggered(self): self.client.tell_action('emergency')
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 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 ToftofProfileWindow(DlgUtils, QMainWindow): 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 _retrieve_detinfo(self): if self._detinfo is None: info = self.panel.client.eval( 'det._detinfo_parsed, ' 'det._anglemap', None) if not info: return self.showError('Cannot retrieve detector info.') self._lambda = self.panel.client.eval('chWL()', None) if not self._lambda: return self.showError('Cannot retrieve wavelength.') self._detinfo, self._anglemap = info self._inverse_anglemap = 0 self._infowindow = QMainWindow(self) self._infolabel = QLabel(self._infowindow) self._infolabel.setTextFormat(Qt.RichText) self._infowindow.setCentralWidget(self._infolabel) self._infowindow.setContentsMargins(10, 10, 10, 10) self._inv_anglemap = [[ entry for entry in self._detinfo[1:] if entry[12] == self._anglemap[detnr] + 1 ][0] for detnr in range(len(self._xs))] def scaleChanged(self, scale): self.update(self._type, self._orig_nbins, self._orig_x, self._orig_y) def update(self, proftype, nbins, x, y): self._orig_x = x self._orig_y = y self._orig_nbins = nbins x.setsize(8 * nbins) y.setsize(8 * nbins) xs = struct.unpack('d' * nbins, x) ys = struct.unpack('d' * nbins, y) if proftype == 0: if self.scale.currentIndex() == 0: xs = xs elif self.scale.currentIndex() == 1: self._retrieve_detinfo() xs = [self._inv_anglemap[int(xi)][5] for xi in xs] else: self._retrieve_detinfo() if self._lambda is None: self.showError('Could not determine wavelength.') self.scale.setCurrentIndex(1) return xs = [ 4 * pi / self._lambda * sin(radians(self._inv_anglemap[int(xi)][5] / 2.)) for xi in xs ] self._xs = xs self._ys = ys self.curve.setData(xs, ys) self.plot.setAxisAutoScale(QwtPlot.xBottom) self.plot.setAxisAutoScale(QwtPlot.yLeft) self.marker.setVisible(False) self.zoomer.setZoomBase(True) self._type = proftype if proftype == 0: self.setWindowTitle( 'Single detector view (time-channel integrated)') self.scaleframe.setVisible(True) elif proftype == 1: self.setWindowTitle('Time channel view (detector integrated)') self.scaleframe.setVisible(False) else: self.scaleframe.setVisible(False) def pickerSelected(self, point): if self._type != 0: return self._retrieve_detinfo() index = self.curve.closestPoint(self.picker.transform(point))[0] detentry = self._inv_anglemap[index][:] detentry.append(self._xs[index]) detentry.append(self._ys[index]) self.marker.setXValue(self._xs[index]) self.marker.setYValue(self._ys[index]) self.marker.setVisible(True) self.plot.replot() self._infowindow.show() entrynames = [ 'EntryNr', 'Rack', 'Plate', 'Pos', 'RPos', '2Theta', 'CableNr', 'CableType', 'CableLen', 'CableEmpty', 'Card', 'Chan', 'Total', 'DetName', 'BoxNr', 'BoxChan', 'XValue', 'Counts' ] formats = [ '%s', '%d', '%d', '%d', '%d', '%.3f', '%d', '%d', '%.2f', '%d', '%d', '%d', '%d', '%r', '%d', '%d', '%s', '%d' ] empties = [1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0] self._infolabel.setText('Detector info:<br><table>' + ''.join( '<tr><td>%s</td><td></td><td>%s</td></tr>%s' % (name, format % value, '<tr></tr>' if empty else '') for (name, format, empty, value) in zip(entrynames, formats, empties, detentry)) + '</table>') def closeEvent(self, event): if self._infowindow: self._infowindow.close()
class Monitor(BaseMonitor): """Qt specific implementation of instrument monitor.""" parameters = { 'noexpired': Param('If true, show expired values as "n/a"', type=bool) } _master = None def mainLoop(self): self._qtapp.exec_() def closeGui(self): if self._master: self._master.close() def _class_import(self, clsname): modname, member = clsname.rsplit('.', 1) mod = __import__(modname, None, None, [member]) return getattr(mod, member) # pylint: disable=too-many-statements 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() def signalKeyChange(self, obj, *args): self._master.keyChange.emit(obj, args) def newWidgetInfo(self, info): self._statuslabel.setText(info) def updateTitle(self, title): self._master.updateTitle.emit(title) def switchWarnPanel(self, on): self._master.switchWarnPanel.emit(on) def _switch_warnpanel(self, on): if on: if self._titlelabel: pal = self._titlelabel.palette() pal.setColor(QPalette.WindowText, self._black) pal.setColor(QPalette.Window, self._red) self._titlelabel.setPalette(pal) self._warnlabel.setText(self._currwarnings) self._warnpanel.setVisible(True) self._master.update() else: self._warnpanel.setVisible(False) if self._titlelabel: pal = self._titlelabel.palette() pal.setColor(QPalette.WindowText, self._gray) pal.setColor(QPalette.Window, self._bgcolor) self._titlelabel.setPalette(pal) # resize to minimum self.reconfigureBoxes() # self._master.update() def reconfigureBoxes(self): emitdict = {} fields = [] for (layout, box) in self._onlyblocks: emitdict[layout, box] = checkSetupSpec(box.setups, self._setups, log=self.log) # check fields inside the block, if the block isn't invisible if emitdict[layout, box]: fields.extend(box._onlyfields) # always check fields not in a setup controlled group fields.extend(self._onlyfields) for field in fields: emitdict[None, field] = checkSetupSpec(field.setups, self._setups, log=self.log) self._master.reconfigure.emit(emitdict)