def _refresh_presets(self): pmenu = self.presetmenu pmenu.clear() delmenu = QMenu('Delete', self) try: for preset, info in iteritems(self.presetdict): paction = QAction(preset, self) pdelaction = QAction(preset, self) info = info.copy() def launchpreset(on, info=info): self._createViewFromDialog(info) def delpreset(on, name=preset, act=paction, delact=pdelaction): pmenu.removeAction(act) delmenu.removeAction(delact) self.presetdict.pop(name, None) self._refresh_presets() paction.triggered[bool].connect(launchpreset) pmenu.addAction(paction) pdelaction.triggered[bool].connect(delpreset) delmenu.addAction(pdelaction) except AttributeError: self.presetdict = {} if self.presetdict: pmenu.addSeparator() pmenu.addMenu(delmenu) else: pmenu.addAction('(no presets created)')
def on_tableWidget_customContextMenuRequested(self, point): """Show context menu for adding and deleting echotimes (rows).""" self._stop_edit() menu = QMenu(self) add = menu.addAction('Add echo time') delete = menu.addAction('Delete echo time') row = self.tableWidget.rowAt(point.y()) # Disable the delete action if there is nothing to delete if row == -1: delete.setEnabled(False) sender = self.sender() # The Signal can be sent from the table widget itself or from the # vertical header. In case of the table widget, its viewport hast to be # used for correct placement of the context mneu if hasattr(sender, 'viewport'): sender = sender.viewport() action = menu.exec_(sender.mapToGlobal(point)) if action == add: self._add_row(row) elif action == delete: self._delete_row(row)
def getMenus(self): if not self.menu: menu = QMenu('&Live data', self) menu.addAction(self.actionPrint) menu.addSeparator() menu.addAction(self.actionUnzoom) menu.addAction(self.actionLogScale) self.menu = menu return [self.menu]
def initLiveWidget(self, widgetcls): if isinstance(self.widget, widgetcls): return # delete the old widget if self.widget: self.widgetLayout.removeWidget(self.widget) self.widget.deleteLater() # create a new one self.widget = widgetcls(self) # enable/disable controls and set defaults for new livewidget instances self.setControlsEnabled(True) if isinstance(self.widget, LiveWidget1D): self.set2DControlsEnabled(False) else: self.set2DControlsEnabled(True) # apply current global settings self.widget.setCenterMark(self.actionMarkCenter.isChecked()) self.widget.logscale(self.actionLogScale.isChecked()) if isinstance(self.widget, LiveWidget1D): self.widget.setSymbols(self.actionSymbols.isChecked()) self.widget.setLines(self.actionLines.isChecked()) # liveonly mode does not display a status bar if self._liveOnlyIndex is None: self.widget.gr.cbm.addHandler(MouseEvent.MOUSE_MOVE, self.on_mousemove_gr) # handle menus self.menuColormap = QMenu(self) self.actionsColormap = QActionGroup(self) activeMap = self.widget.getColormap() activeCaption = None for name, value in COLORMAPS.items(): caption = name.title() action = self.menuColormap.addAction(caption) action.setData(caption) action.setCheckable(True) if activeMap == value: action.setChecked(True) # update toolButton text later otherwise this may fail # depending on the setup and qt versions in use activeCaption = caption self.actionsColormap.addAction(action) action.triggered.connect(self.on_colormap_triggered) self.actionColormap.setMenu(self.menuColormap) # finish initiation self.widgetLayout.addWidget(self.widget) if activeCaption: self.toolbar.widgetForAction( self.actionColormap).setText(activeCaption) detectors = self.client.eval('session.experiment.detectors', []) self._register_rois(detectors)
def contextMenuOnItem(self, item, pos): if item is None: return # invoked context menu on whitespace topLevelItems = [] currentIndex = 0 while currentIndex < self.topLevelItemCount(): topLevelItems.append(self.topLevelItem(currentIndex)) currentIndex += 1 if self.currentItem() in topLevelItems: if self.currentItem().text(0) in self.nonListItems: if self.currentItem().childCount() > 0: return # value is already set, can't add multiple values menu = QMenu(self) addValueAction = menu.addAction('Add value...') addValueAction.triggered.connect(self.addValue) menu.popup(pos)
def __init__(self, parent, client, options): LokiPanelBase.__init__(self, parent, client, options) loadUi(self, findResource('nicos_ess/loki/gui/ui_files/scriptbuilder.ui')) self.window = parent self.runBtn.setVisible(False) self.mapping = {} self.expertmode = self.mainwindow.expertmode self._cmdlet = self.sender() self._layout = self.frame.layout() self.index = self._layout.indexOf(self._cmdlet) self.initialise_connection_status_listeners() modules = options.get('modules', []) for module in modules: importString(module) # should register cmdlets for cmdlet in all_cmdlets: def callback(on, cmdlet=cmdlet): inst = cmdlet(self, self.client) inst.cmdletUp.connect(self.on_cmdletUp) inst.cmdletDown.connect(self.on_cmdletDown) inst.cmdletRemove.connect(self.on_cmdletRemove) self.runBtn.setVisible(True) self.frame.layout().insertWidget( self.frame.layout().count() - 2, inst) action = QAction(cmdlet.name, self) action.triggered.connect(callback) self.mapping.setdefault(cmdlet.category, []).append(action) for category in all_categories[::-1]: if category not in self.mapping: return toolbtn = QToolButton(self) toolbtn.setText(category) toolbtn.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) menu.addActions(self.mapping[category]) toolbtn.setMenu(menu) self.btnLayout.insertWidget(1, toolbtn)
def on_roiItemClicked(self, event): if event.getButtons() & MouseEvent.RIGHT_BUTTON: if isinstance(event.roi.reference, FitResult): menu = QMenu(self) actionClipboard = QAction("Copy fit values to clipboard", menu) menu.addAction(actionClipboard) p0dc = event.getDC() selectedItem = menu.exec_( self.mapToGlobal(QPoint(p0dc.x, p0dc.y))) if selectedItem == actionClipboard: res = event.roi.reference text = '\n'.join( (n + '\t' if n else '\t') + (v + '\t' if isinstance(v, string_types) else '%g\t' % v) + (dv if isinstance(dv, string_types) else '%g' % dv) for (n, v, dv) in res.label_contents) QApplication.clipboard().setText(text)
def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, self.ui) self.window = parent self.mapping = {} self.current_cmdlet = None self.expertmode = self.mainwindow.expertmode # collect values of all cmdlets that have been added # so that the common fields carry over to the next cmdlet self.value_collection = {} self.commandInput.history = self.cmdhistory self.commandInput.completion_callback = self.completeInput self.console = None client.initstatus.connect(self.on_client_initstatus) client.mode.connect(self.on_client_mode) client.simresult.connect(self.on_client_simresult) modules = options.get('modules', []) for module in modules: importString(module) # should register cmdlets for cmdlet in all_cmdlets: action = QAction(cmdlet.name, self) def callback(on, cmdlet=cmdlet): self.selectCmdlet(cmdlet) action.triggered.connect(callback) self.mapping.setdefault(cmdlet.category, []).append(action) for category in all_categories[::-1]: if category not in self.mapping: continue toolbtn = QToolButton(self) toolbtn.setText(category) toolbtn.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) menu.addActions(self.mapping[category]) toolbtn.setMenu(menu) self.btnLayout.insertWidget(1, toolbtn)
def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, self.ui) self.commandInput.scrollWidget = self.outView self.grepPanel.hide() self.grepText.scrollWidget = self.outView self.actionLabel.hide() self.outView.setActionLabel(self.actionLabel) self.commandInput.history = self.cmdhistory self.commandInput.completion_callback = self.completeInput self.grepNoMatch.setVisible(False) self.actionAllowLineWrap.setChecked(self.mainwindow.allowoutputlinewrap) client.connected.connect(self.on_client_connected) client.message.connect(self.on_client_message) client.simmessage.connect(self.on_client_simmessage) client.initstatus.connect(self.on_client_initstatus) client.mode.connect(self.on_client_mode) client.experiment.connect(self.on_client_experiment) self.outView.setContextMenuPolicy(Qt.CustomContextMenu) self.menu = QMenu('&Output', self) self.menu.addAction(self.actionCopy) self.menu.addAction(self.actionGrep) self.menu.addSeparator() self.menu.addAction(self.actionSave) self.menu.addAction(self.actionPrint) self.menu.addSeparator() self.menu.addAction(self.actionAllowLineWrap) self.on_actionAllowLineWrap_triggered( self.mainwindow.allowoutputlinewrap) self.hasinput = bool(options.get('hasinput', True)) self.inputFrame.setVisible(self.hasinput) self.hasmenu = bool(options.get('hasmenu', True)) if options.get('fulltime', False): self.outView.setFullTimestamps(True) watermark = options.get('watermark', '') if watermark: watermark = findResource(watermark) if path.isfile(watermark): self.outView.setBackgroundImage(watermark)
def initLiveWidget(self, widgetcls): if isinstance(self.widget, widgetcls): return if self.widget: self.widgetLayout.removeWidget(self.widget) self.widget.deleteLater() self.widget = widgetcls(self) # enable/disable controls and set defaults for new livewidget instances self.setControlsEnabled(True) if isinstance(self.widget, LiveWidget1D): self.set2DControlsEnabled(False) else: self.set2DControlsEnabled(True) # apply current settings self.widget.setCenterMark(self.actionMarkCenter.isChecked()) self.widget.logscale(self.actionLogScale.isChecked()) guiConn = GUIConnector(self.widget.gr) guiConn.connect(MouseEvent.MOUSE_MOVE, self.on_mousemove_gr) self.menuColormap = QMenu(self) self.actionsColormap = QActionGroup(self) activeMap = self.widget.getColormap() activeCaption = None for name, value in iteritems(COLORMAPS): caption = name.title() action = self.menuColormap.addAction(caption) action.setData(caption) action.setCheckable(True) if activeMap == value: action.setChecked(True) # update toolButton text later otherwise this may fail # depending on the setup and qt versions in use activeCaption = caption self.actionsColormap.addAction(action) action.triggered.connect(self.on_colormap_triggered) self.actionColormap.setMenu(self.menuColormap) self.widgetLayout.addWidget(self.widget) if activeCaption: self.toolbar.widgetForAction( self.actionColormap).setText(activeCaption) detectors = self.client.eval('session.experiment.detectors', []) self._register_rois(detectors)
def __init__(self, parent, client, options): LokiPanelBase.__init__(self, parent, client, options) loadUi(self, findResource('nicos_ess/loki/gui/ui_files/sampleconf.ui')) self.sampleGroup.setEnabled(False) self.frame.setLayout(QVBoxLayout()) self.sample_frame = QFrame(self) loadUi( self.sample_frame, findResource('nicos_ess/loki/gui/ui_files/sampleconf_summary.ui')) layout = self.frame.layout() layout.addWidget(self.sample_frame) self.sample_frame.hide() self.sample_frame.posTbl.setEnabled(False) for box in self.sample_frame.findChildren(QLineEdit): box.setEnabled(False) menu = QMenu(self) menu.addAction(self.actionEmpty) menu.addAction(self.actionGenerate) self.createBtn.setMenu(menu) self.configs = [] self.holder_info = options.get('holder_info', []) self.instrument = options.get('instrument', 'loki') self.unapplied_changes = False self.applyBtn.setEnabled(False) self.initialise_connection_status_listeners()
def __init__(self, classesList, parent=None): QDialog.__init__(self, parent) uic.loadUi( path.abspath( path.join(path.dirname(__file__), '..', 'ui', 'dialogs', 'newdevicedialog.ui')), self) self.menu = QMenu('Select class') self.pushButtonSelectClass.setMenu(self.menu) for _class in sorted([str(__class)[14:-2] for __class in classesList]): self.recursiveMenu(_class, self.menu) self.menuCustom = QMenu('Select class') for _class in sorted([ str(__class)[14:-2] for __class in classparser.getDeviceClasses(None) ]): self.recursiveMenu(_class, self.menuCustom) self.checkBoxCustomClasses.stateChanged.connect( self.stateChangedHandler) self.lineEditDeviceName.setValidator( QRegExpValidator(QRegExp('[A-Za-z0-9_]*')))
def getMenus(self): self.menu = menu = QMenu('&Live data', self) menu.addAction(self.actionLoadTOF) menu.addAction(self.actionLoadPAD) menu.addSeparator() menu.addAction(self.actionWriteXml) menu.addAction(self.actionPrint) menu.addSeparator() menu.addAction(self.actionSetAsROI) menu.addAction(self.actionUnzoom) menu.addAction(self.actionLogScale) menu.addAction(self.actionNormalized) menu.addAction(self.actionLegend) return [menu]
def _register_rois(self, detectors): self.rois.clear() self.actionROI.setVisible(False) self.menuROI = QMenu(self) self.actionsROI = QActionGroup(self) self.actionsROI.setExclusive(False) for detname in detectors: self.log.debug('checking rois for detector \'%s\'', detname) for tup in self.client.eval(detname + '.postprocess', ''): roi = tup[0] cachekey = roi + '/roi' # check whether or not this is a roi (cachekey exists). keyval = self.client.getCacheKey(cachekey) if keyval: self.on_roiChange(cachekey, keyval[1]) self.log.debug('register roi: %s', roi) # create roi menu action = self.menuROI.addAction(roi) action.setData(roi) action.setCheckable(True) self.actionsROI.addAction(action) action.triggered.connect(self.on_roi_triggered) self.actionROI.setMenu(self.menuROI) self.actionROI.setVisible(True)
def recursiveMenu(self, partialString, parentMenu): if len(partialString.split('.')) == 1: action = parentMenu.addAction(partialString) action.triggered.connect(self.classSelectedSlot) else: uncombinedNextPartialString = partialString.split('.') menus = parentMenu.findChildren(QMenu) submenuIsPresent = False submenu = None for menu in menus: if menu.title() == uncombinedNextPartialString[0]: submenu = menu uncombinedNextPartialString.pop(0) submenuIsPresent = True if not submenuIsPresent: submenu = QMenu(uncombinedNextPartialString.pop(0), parentMenu) parentMenu.addMenu(submenu) nextPartialString = '.'.join(uncombinedNextPartialString) self.recursiveMenu(nextPartialString, submenu)
def on_paramList_customContextMenuRequested(self, pos): item = self.paramList.itemAt(pos) if not item: return menu = QMenu(self) refreshAction = menu.addAction('Refresh') menu.addAction('Refresh all') # QCursor.pos is more reliable then the given pos action = menu.exec_(QCursor.pos()) if action: cmd = 'session.getDevice(%r).pollParams(volatile_only=False%s)' \ % (self.devname, ', param_list=[%r]' % item.text(0) if action == refreshAction else '') # poll even non volatile parameter as requested explicitely self.client.eval(cmd, None)
def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, findResource('nicos_mlz/kws1/gui/sampleconf.ui')) self.sampleGroup.setEnabled(False) self.frame.setLayout(QVBoxLayout()) menu = QMenu(self) menu.addAction(self.actionCopyAperture) menu.addAction(self.actionCopyDetOffset) menu.addAction(self.actionCopyThickness) menu.addAction(self.actionCopyTimeFactor) menu.addSeparator() menu.addAction(self.actionCopyAll) self.copyBtn.setMenu(menu) menu = QMenu(self) menu.addAction(self.actionEmpty) menu.addAction(self.actionGenerate) self.createBtn.setMenu(menu) self.configs = [] self.dirty = False self.filename = None self.holder_info = options.get('holder_info', []) self.instrument = options.get('instrument', 'kws1')
class LiveDataPanel(Panel): """Provides a generic "detector live view". For most instruments, a specific panel must be implemented that takes care of the individual live display needs. Options: * ``instrument`` -- the instrument name that is passed on to the livewidget module. * ``filetypes`` default[] - List of filename extensions whose content should be displayed. * ``detectors`` (default [] - list of detector devices whose data should be displayed. If not set data from all configured detectors will be shown. * ``cachesize`` (default 20) - Number of entries in the live data cache. The live data cache allows to display of previous taken data. """ panelName = 'Live data view' def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, 'panels/live.ui') self._allowed_tags = set() self._allowed_detectors = set() self._ignore_livedata = False # ignore livedata, e.g. wrong detector self._last_idx = 0 self._last_tag = None self._last_fnames = None self._last_format = None self._runtime = 0 self._range_active = False self._cachesize = 20 self._livewidgets = {} # livewidgets for rois: roi_key -> widget self._fileopen_filter = None self.widget = None self.menu = None self.statusBar = QStatusBar(self, sizeGripEnabled=False) policy = self.statusBar.sizePolicy() policy.setVerticalPolicy(QSizePolicy.Fixed) self.statusBar.setSizePolicy(policy) self.statusBar.setSizeGripEnabled(False) self.layout().addWidget(self.statusBar) self.toolbar = QToolBar('Live data') self.toolbar.addAction(self.actionOpen) self.toolbar.addAction(self.actionPrint) self.toolbar.addSeparator() self.toolbar.addAction(self.actionLogScale) self.toolbar.addSeparator() self.toolbar.addAction(self.actionKeepRatio) self.toolbar.addAction(self.actionUnzoom) self.toolbar.addAction(self.actionColormap) self.toolbar.addAction(self.actionMarkCenter) self.toolbar.addAction(self.actionROI) self._actions2D = [self.actionROI, self.actionColormap] self.setControlsEnabled(False) self.set2DControlsEnabled(False) # self.widget.setControls(Logscale | MinimumMaximum | BrightnessContrast | # Integrate | Histogram) self.liveitems = [] self.setLiveItems(1) self._livechannel = 0 self.splitter.setSizes([20, 80]) self.splitter.restoreState(self.splitterstate) if hasattr(self.window(), 'closed'): self.window().closed.connect(self.on_closed) client.livedata.connect(self.on_client_livedata) client.liveparams.connect(self.on_client_liveparams) client.connected.connect(self.on_client_connected) client.cache.connect(self.on_cache) self.rois = {} self.detectorskey = None # configure instrument specific behavior self._instrument = options.get('instrument', '') # self.widget.setInstrumentOption(self._instrument) # if self._instrument == 'toftof': # self.widget.setAxisLabels('time channels', 'detectors') # elif self._instrument == 'imaging': # self.widget.setControls(ShowGrid | Logscale | Grayscale | # Normalize | Darkfield | Despeckle | # CreateProfile | Histogram | MinimumMaximum) # self.widget.setStandardColorMap(True, False) # configure allowed file types supported_filetypes = ReaderRegistry.filetypes() opt_filetypes = set(options.get('filetypes', supported_filetypes)) self._allowed_tags = opt_filetypes & set(supported_filetypes) # configure allowed detector device names detectors = options.get('detectors') if detectors: self._allowed_detectors = set(detectors) # configure caching self._cachesize = options.get('cachesize', self._cachesize) if self._cachesize < 1: self._cachesize = 1 # always cache the last live image self._datacache = BoundedOrderedDict(maxlen=self._cachesize) def setLiveItems(self, n): nitems = len(self.liveitems) if n < nitems: nfiles = self.fileList.count() for i in range(nitems - 1, n - 1, -1): self.liveitems.pop(i) self.fileList.takeItem(nfiles - nitems + i) if self._livechannel > n: self._livechannel = 0 if n > 0 else None else: for i in range(nitems, n): item = QListWidgetItem('<Live #%d>' % (i + 1)) item.setData(FILENAME, i) item.setData(FILEFORMAT, '') item.setData(FILETAG, 'live') self.fileList.insertItem(self.fileList.count(), item) self.liveitems.append(item) if n == 1: self.liveitems[0].setText('<Live>') else: self.liveitems[0].setText('<Live #1>') def set2DControlsEnabled(self, flag): if flag != self.actionKeepRatio.isChecked(): self.actionKeepRatio.trigger() for action in self._actions2D: action.setVisible(flag) def setControlsEnabled(self, flag): for action in self.toolbar.actions(): action.setEnabled(flag) self.actionOpen.setEnabled(True) # File Open action always available def initLiveWidget(self, widgetcls): if isinstance(self.widget, widgetcls): return if self.widget: self.widgetLayout.removeWidget(self.widget) self.widget.deleteLater() self.widget = widgetcls(self) # enable/disable controls and set defaults for new livewidget instances self.setControlsEnabled(True) if isinstance(self.widget, LiveWidget1D): self.set2DControlsEnabled(False) else: self.set2DControlsEnabled(True) # apply current settings self.widget.setCenterMark(self.actionMarkCenter.isChecked()) self.widget.logscale(self.actionLogScale.isChecked()) guiConn = GUIConnector(self.widget.gr) guiConn.connect(MouseEvent.MOUSE_MOVE, self.on_mousemove_gr) self.menuColormap = QMenu(self) self.actionsColormap = QActionGroup(self) activeMap = self.widget.getColormap() activeCaption = None for name, value in iteritems(COLORMAPS): caption = name.title() action = self.menuColormap.addAction(caption) action.setData(caption) action.setCheckable(True) if activeMap == value: action.setChecked(True) # update toolButton text later otherwise this may fail # depending on the setup and qt versions in use activeCaption = caption self.actionsColormap.addAction(action) action.triggered.connect(self.on_colormap_triggered) self.actionColormap.setMenu(self.menuColormap) self.widgetLayout.addWidget(self.widget) if activeCaption: self.toolbar.widgetForAction( self.actionColormap).setText(activeCaption) detectors = self.client.eval('session.experiment.detectors', []) self._register_rois(detectors) def loadSettings(self, settings): self.splitterstate = settings.value('splitter', '', QByteArray) def saveSettings(self, settings): settings.setValue('splitter', self.splitter.saveState()) settings.setValue('geometry', self.saveGeometry()) def getMenus(self): if not self.menu: menu = QMenu('&Live data', self) menu.addAction(self.actionOpen) menu.addAction(self.actionPrint) menu.addSeparator() menu.addAction(self.actionKeepRatio) menu.addAction(self.actionUnzoom) menu.addAction(self.actionLogScale) menu.addAction(self.actionColormap) menu.addAction(self.actionMarkCenter) menu.addAction(self.actionROI) self.menu = menu return [self.menu] def _get_all_widgets(self): yield self.widget for w in itervalues(self._livewidgets): yield w def getToolbars(self): return [self.toolbar] def on_mousemove_gr(self, event): xyz = None if event.getWindow(): # inside plot xyz = self.widget.getZValue(event) if xyz: fmt = '(%g, %g)' # x, y data 1D integral plots if len(xyz) == 3: fmt += ': %g' # x, y, z data for 2D image plot self.statusBar.showMessage(fmt % xyz) else: self.statusBar.clearMessage() def on_actionColormap_triggered(self): w = self.toolbar.widgetForAction(self.actionColormap) m = self.actionColormap.menu() if m: m.popup(w.mapToGlobal(QPoint(0, w.height()))) def on_colormap_triggered(self): action = self.actionsColormap.checkedAction() name = action.data() for widget in self._get_all_widgets(): widget.setColormap(COLORMAPS[name.upper()]) self.toolbar.widgetForAction(self.actionColormap).setText(name.title()) def _getLiveWidget(self, roi): return self._livewidgets.get(roi + '/roi', None) def showRoiWindow(self, roikey): key = roikey + '/roi' widget = self._getLiveWidget(roikey) region = self.widget._rois[key] if not widget: widget = LiveWidget(None) widget.setWindowTitle(roikey) widget.setColormap(self.widget.getColormap()) widget.setCenterMark(self.actionMarkCenter.isChecked()) widget.logscale(self.actionLogScale.isChecked()) widget.gr.setAdjustSelection(False) # don't use adjust on ROIs for name, roi in iteritems(self.rois): widget.setROI(name, roi) width = max(region.x) - min(region.x) height = max(region.y) - min(region.y) if width > height: dwidth = 500 dheight = 500 * height // width else: dheight = 500 dwidth = 500 * width // height widget.resize(dwidth, dheight) widget.closed.connect(self.on_roiWindowClosed) widget.setWindowForRoi(region) widget.update() widget.show() widget.activateWindow() self._livewidgets[key] = widget def closeRoiWindow(self, roi): widget = self._getLiveWidget(roi) if widget: widget.close() def on_closed(self): for w in self._livewidgets.values(): w.close() def _register_rois(self, detectors): self.rois.clear() self.actionROI.setVisible(False) self.menuROI = QMenu(self) self.actionsROI = QActionGroup(self) self.actionsROI.setExclusive(False) for detname in detectors: self.log.debug('checking rois for detector \'%s\'', detname) for tup in self.client.eval(detname + '.postprocess', ''): roi = tup[0] cachekey = roi + '/roi' # check whether or not this is a roi (cachekey exists). keyval = self.client.getCacheKey(cachekey) if keyval: self.on_roiChange(cachekey, keyval[1]) self.log.debug('register roi: %s', roi) # create roi menu action = self.menuROI.addAction(roi) action.setData(roi) action.setCheckable(True) self.actionsROI.addAction(action) action.triggered.connect(self.on_roi_triggered) self.actionROI.setMenu(self.menuROI) self.actionROI.setVisible(True) def on_actionROI_triggered(self): w = self.toolbar.widgetForAction(self.actionROI) self.actionROI.menu().popup(w.mapToGlobal(QPoint(0, w.height()))) def on_roi_triggered(self): action = self.sender() roi = action.data() if action.isChecked(): self.showRoiWindow(roi) else: self.closeRoiWindow(roi) def on_roiWindowClosed(self): widget = self.sender() if widget: key = None for key, w in iteritems(self._livewidgets): if w == widget: self.log.debug('delete roi: %s', key) del self._livewidgets[key] break if key: roi = key.rsplit('/', 1)[0] for action in self.actionsROI.actions(): if action.data() == roi: action.setChecked(False) self.log.debug('uncheck roi: %s', roi) def on_roiChange(self, key, value): self.log.debug('on_roiChange: %s %s', key, (value, )) self.rois[key] = value for widget in self._get_all_widgets(): widget.setROI(key, value) widget = self._livewidgets.get(key, None) if widget: widget.setWindowForRoi(self.widget._rois[key]) def on_cache(self, data): _time, key, _op, svalue = data try: value = cache_load(svalue) except ValueError: value = None if key in self.rois: self.on_roiChange(key, value) elif key == self.detectorskey and self.widget: self._register_rois(value) def on_client_connected(self): self.client.tell('eventunmask', ['livedata', 'liveparams']) datapath = self.client.eval('session.experiment.datapath', '') if not datapath or not path.isdir(datapath): return if self._instrument == 'imaging': for fn in sorted(os.listdir(datapath)): if fn.endswith('.fits'): self.add_to_flist(path.join(datapath, fn), '', 'fits', False) self.detectorskey = (self.client.eval('session.experiment.name') + '/detlist').lower() def on_client_liveparams(self, params): tag, uid, det, fname, dtype, nx, ny, nz, runtime = params # TODO: remove compatibility code if isinstance(fname, string_types): fname, nx, ny, nz = [fname], [nx], [ny], [nz] if self._allowed_detectors and det not in self._allowed_detectors: self._ignore_livedata = True return self._ignore_livedata = False self._runtime = runtime self._last_uid = uid if dtype: self.setLiveItems(len(fname)) self._last_fnames = None normalized_type = numpy.dtype(dtype).str if normalized_type not in DATATYPES: self._last_format = None self.log.warning('Unsupported live data format: %s', (params, )) return self._last_format = normalized_type elif fname: self._last_fnames = fname self._last_format = None self._last_tag = tag.lower() self._nx = nx self._ny = ny self._nz = nz self._last_idx = 0 def _initLiveWidget(self, array): """Initialize livewidget based on array's shape""" if len(array.shape) == 1: widgetcls = LiveWidget1D else: widgetcls = IntegralLiveWidget self.initLiveWidget(widgetcls) def setData(self, array, uid=None, display=True): """Dispatch data array to corresponding live widgets. Cache array based on uid parameter. No caching if uid is ``None``. """ if uid: if uid not in self._datacache: self.log.debug('add to cache: %s', uid) self._datacache[uid] = array if display: self._initLiveWidget(array) for widget in self._get_all_widgets(): widget.setData(array) def setDataFromFile(self, filename, tag, uid=None, display=True): """Load data array from file and dispatch to live widgets using ``setData``. Do not use caching if uid is ``None``. """ try: array = ReaderRegistry.getReaderCls(tag).fromfile(filename) except KeyError: raise NicosError('Unsupported fileformat %r' % tag) if array is not None: self.setData(array, uid, display=display) else: raise NicosError('Cannot read file %r' % filename) def on_client_livedata(self, data): if self._ignore_livedata: # ignore all live events return idx = self._last_idx # 0 <= array number < n self._last_idx += 1 # check for allowed tags but always allow live data if self._last_tag in self._allowed_tags or self._last_tag == 'live': # pylint: disable=len-as-condition if len(data) and self._last_format: # we got live data with a specified format uid = str(self._last_uid) + '-' + str(idx) array = numpy.frombuffer(data, self._last_format) if self._nz[idx] > 1: array = array.reshape( (self._nz[idx], self._ny[idx], self._nx[idx])) elif self._ny[idx] > 1: array = array.reshape((self._ny[idx], self._nx[idx])) # update display for selected live channel, just cache # otherwise self.setData(array, uid, display=(idx == self._livechannel)) self.liveitems[idx].setData(FILEUID, uid) else: # we got no live data, but a filename with the data # filename corresponds to full qualififed path here for i, filename in enumerate(self._last_fnames): uid = str(self._last_uid) + '-' + str(i) self.add_to_flist(filename, self._last_format, self._last_tag, uid) try: # update display for selected live channel, just cache # otherwise self.setDataFromFile(filename, self._last_tag, uid, display=(i == self._livechannel)) except Exception as e: if uid in self._datacache: # image is already cached # suppress error message for cached image self.log.debug(e) else: # image is not cached and could not be loaded self.log.exception(e) def remove_obsolete_cached_files(self): """Removes outdated cached files from the file list or set cached flag to False if the file is still available on the filesystem. """ cached_item_rows = [] for row in range(self.fileList.count()): item = self.fileList.item(row) if item.data(FILEUID): cached_item_rows.append(row) if len(cached_item_rows) > self._cachesize: for row in cached_item_rows[0:-self._cachesize]: item = self.fileList.item(row) self.log.debug('remove from cache %s %s', item.data(FILEUID), item.data(FILENAME)) if path.isfile(item.data(FILENAME)): item.setData(FILEUID, None) else: self.fileList.takeItem(row) def add_to_flist(self, filename, fformat, ftag, uid=None, scroll=True): shortname = path.basename(filename) item = QListWidgetItem(shortname) item.setData(FILENAME, filename) item.setData(FILEFORMAT, fformat) item.setData(FILETAG, ftag) item.setData(FILEUID, uid) self.fileList.insertItem(self.fileList.count() - len(self.liveitems), item) if uid: self.remove_obsolete_cached_files() if scroll: self.fileList.scrollToBottom() return item def on_fileList_itemClicked(self, item): if item is None: return fname = item.data(FILENAME) ftag = item.data(FILETAG) if item in self.liveitems and ftag == 'live': # show live image self._livechannel = int(fname) fname = None self.log.debug("set livechannel: %d", self._livechannel) else: self._livechannel = None self.log.debug("no direct display") uid = item.data(FILEUID) if uid: # show image from cache array = self._datacache.get(uid, None) if array is not None and array.size: self.setData(array) return if fname: # show image from file self.setDataFromFile(fname, ftag) def on_fileList_currentItemChanged(self, item, previous): self.on_fileList_itemClicked(item) @pyqtSlot() def on_actionOpen_triggered(self): """Open image file using registered reader classes.""" ftypes = { ffilter: ftype for ftype, ffilter in ReaderRegistry.filefilters() } fdialog = FileFilterDialog(self, "Open data files", "", ";;".join(ftypes.keys())) if self._fileopen_filter: fdialog.selectNameFilter(self._fileopen_filter) if fdialog.exec_() == fdialog.Accepted: self._fileopen_filter = fdialog.selectedNameFilter() tag = ftypes[self._fileopen_filter] files = fdialog.selectedFiles() if files: def _cacheFile(fn, tag): uid = uuid4() # setDataFromFile may raise an `NicosException`, e.g. # if the file cannot be opened. self.setDataFromFile(fn, tag, uid, display=False) return self.add_to_flist(fn, None, tag, uid) # load and display first item f = files.pop(0) self.fileList.setCurrentItem(_cacheFile(f, tag)) cachesize = self._cachesize - 1 # add first `cachesize` files to cache for _, f in enumerateWithProgress(files[:cachesize], "Loading data files...", parent=fdialog): _cacheFile(f, tag) # add further files to file list (open on request/itemClicked) for f in files[cachesize:]: self.add_to_flist(f, None, tag) @pyqtSlot() def on_actionUnzoom_triggered(self): self.widget.unzoom() @pyqtSlot() def on_actionPrint_triggered(self): self.widget.printDialog() @pyqtSlot() def on_actionLogScale_triggered(self): for widget in self._get_all_widgets(): widget.logscale(self.actionLogScale.isChecked()) @pyqtSlot() def on_actionMarkCenter_triggered(self): flag = self.actionMarkCenter.isChecked() for widget in self._get_all_widgets(): widget.setCenterMark(flag) @pyqtSlot() def on_actionKeepRatio_triggered(self): self.widget.gr.setAdjustSelection(self.actionKeepRatio.isChecked())
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__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, 'panels/scans.ui') ArbitraryFitter.arby_functions.update(options.get('fit_functions', {})) self.statusBar = QStatusBar(self, sizeGripEnabled=False) policy = self.statusBar.sizePolicy() policy.setVerticalPolicy(QSizePolicy.Fixed) self.statusBar.setSizePolicy(policy) self.layout().addWidget(self.statusBar) self.x_menu = QMenu(self) self.x_menu.aboutToShow.connect(self.on_x_menu_aboutToShow) self.actionXAxis.setMenu(self.x_menu) self.y_menu = QMenu(self) self.y_menu.aboutToShow.connect(self.on_y_menu_aboutToShow) self.actionYAxis.setMenu(self.y_menu) self.actionAutoDisplay.setChecked(True) self.norm_menu = QMenu(self) self.norm_menu.aboutToShow.connect(self.on_norm_menu_aboutToShow) self.actionNormalized.setMenu(self.norm_menu) quickfit = QShortcut(QKeySequence("G"), self) quickfit.activated.connect(self.on_quickfit) self.user_color = Qt.white self.user_font = QFont('Monospace') self.bulk_adding = False self.no_openset = False self.last_norm_selection = None self.fitclass = GaussFitter self.fitfuncmap = {} self.menus = None self.bars = None self.data = self.mainwindow.data # maps set uid -> plot self.setplots = {} # maps set uid -> list item self.setitems = {} # current plot object self.currentPlot = None # stack of set uids self.setUidStack = [] # uids of automatically combined datasets -> uid of combined one self.contSetUids = {} self.splitter.setSizes([20, 80]) self.splitter.restoreState(self.splitterstate) if self.tablecolwidth0 > 0: self.metaTable.setColumnWidth(0, self.tablecolwidth0) self.metaTable.setColumnWidth(1, self.tablecolwidth1) self.data.datasetAdded.connect(self.on_data_datasetAdded) self.data.pointsAdded.connect(self.on_data_pointsAdded) self.data.fitAdded.connect(self.on_data_fitAdded) client.experiment.connect(self.on_client_experiment) self.setCurrentDataset(None) self.updateList()
class ScansPanel(Panel): """Provides a display for the scans of the current experiment. Options: * ``fit_functions`` (default {}) -- dictionary for special fitting functions. The name of the fit function is followed by a set of a list of fit parameters and the string containing the fit function code:: fit_functions = { 'Resonance': (['Vmax = 0.1', 'R = 0.6', 'f = ', 'L = ', 'C = '], 'Vmax / sqrt(R**2 + (f*L-1/(f*C))**2)'), } The function can use the all mathematical functions of the `numpy <http://www.numpy.org/>`_ package. """ panelName = 'Scans' def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, 'panels/scans.ui') ArbitraryFitter.arby_functions.update(options.get('fit_functions', {})) self.statusBar = QStatusBar(self, sizeGripEnabled=False) policy = self.statusBar.sizePolicy() policy.setVerticalPolicy(QSizePolicy.Fixed) self.statusBar.setSizePolicy(policy) self.layout().addWidget(self.statusBar) self.x_menu = QMenu(self) self.x_menu.aboutToShow.connect(self.on_x_menu_aboutToShow) self.actionXAxis.setMenu(self.x_menu) self.y_menu = QMenu(self) self.y_menu.aboutToShow.connect(self.on_y_menu_aboutToShow) self.actionYAxis.setMenu(self.y_menu) self.actionAutoDisplay.setChecked(True) self.norm_menu = QMenu(self) self.norm_menu.aboutToShow.connect(self.on_norm_menu_aboutToShow) self.actionNormalized.setMenu(self.norm_menu) quickfit = QShortcut(QKeySequence("G"), self) quickfit.activated.connect(self.on_quickfit) self.user_color = Qt.white self.user_font = QFont('Monospace') self.bulk_adding = False self.no_openset = False self.last_norm_selection = None self.fitclass = GaussFitter self.fitfuncmap = {} self.menus = None self.bars = None self.data = self.mainwindow.data # maps set uid -> plot self.setplots = {} # maps set uid -> list item self.setitems = {} # current plot object self.currentPlot = None # stack of set uids self.setUidStack = [] # uids of automatically combined datasets -> uid of combined one self.contSetUids = {} self.splitter.setSizes([20, 80]) self.splitter.restoreState(self.splitterstate) if self.tablecolwidth0 > 0: self.metaTable.setColumnWidth(0, self.tablecolwidth0) self.metaTable.setColumnWidth(1, self.tablecolwidth1) self.data.datasetAdded.connect(self.on_data_datasetAdded) self.data.pointsAdded.connect(self.on_data_pointsAdded) self.data.fitAdded.connect(self.on_data_fitAdded) client.experiment.connect(self.on_client_experiment) self.setCurrentDataset(None) self.updateList() def loadSettings(self, settings): self.splitterstate = settings.value('splitter', '', QByteArray) self.tablecolwidth0 = settings.value('tablecolwidth0', 0, int) self.tablecolwidth1 = settings.value('tablecolwidth1', 0, int) def saveSettings(self, settings): settings.setValue('splitter', self.splitter.saveState()) settings.setValue('tablecolwidth0', self.metaTable.columnWidth(0)) settings.setValue('tablecolwidth1', self.metaTable.columnWidth(1)) def setCustomStyle(self, font, back): self.user_font = font self.user_color = back for plot in self.setplots.values(): plot.setBackgroundColor(back) plot.update() bold = QFont(font) bold.setBold(True) larger = scaledFont(font, 1.6) for plot in self.setplots.values(): plot.setFonts(font, bold, larger) def requestClose(self): # Always succeeds, but break up circular references so that the panel # object can be deleted properly. self.currentPlot = None self.setplots.clear() return True def _autoscale(self, x=None, y=None): xflag = x if x is not None else self.actionScaleX.isChecked() yflag = y if y is not None else self.actionScaleY.isChecked() if self.currentPlot: self.currentPlot.setAutoScaleFlags(xflag, yflag) self.actionAutoScale.setChecked(xflag or yflag) self.actionScaleX.setChecked(xflag) self.actionScaleY.setChecked(yflag) self.currentPlot.update() def enablePlotActions(self, on): for action in [ self.actionSavePlot, self.actionPrint, self.actionResetPlot, self.actionAttachElog, self.actionCombine, self.actionClosePlot, self.actionDeletePlot, self.actionLogXScale, self.actionLogScale, self.actionAutoScale, self.actionScaleX, self.actionScaleY, self.actionXAxis, self.actionYAxis, self.actionNormalized, self.actionUnzoom, self.actionLegend, self.actionModifyData, self.actionFitPeak, self.actionFitPeakPV, self.actionFitPeakPVII, self.actionFitTc, self.actionFitCosine, self.actionFitSigmoid, self.actionFitArby, self.actionErrors, ]: action.setEnabled(on) def enableAutoScaleActions(self, on): for action in [self.actionAutoScale, self.actionScaleX, self.actionScaleY]: action.setEnabled(on) def getMenus(self): if not self.menus: menu1 = QMenu('&Data plot', self) menu1.addAction(self.actionSavePlot) menu1.addAction(self.actionPrint) menu1.addAction(self.actionAttachElog) menu1.addSeparator() menu1.addAction(self.actionResetPlot) menu1.addAction(self.actionAutoDisplay) menu1.addAction(self.actionCombine) menu1.addAction(self.actionClosePlot) menu1.addAction(self.actionDeletePlot) menu1.addSeparator() menu1.addAction(self.actionXAxis) menu1.addAction(self.actionYAxis) menu1.addAction(self.actionNormalized) menu1.addSeparator() menu1.addAction(self.actionUnzoom) menu1.addAction(self.actionLogXScale) menu1.addAction(self.actionLogScale) menu1.addAction(self.actionAutoScale) menu1.addAction(self.actionScaleX) menu1.addAction(self.actionScaleY) menu1.addAction(self.actionLegend) menu1.addAction(self.actionErrors) menu1.addSeparator() menu2 = QMenu('Data &manipulation', self) menu2.addAction(self.actionModifyData) menu2.addSeparator() ag = QActionGroup(menu2) ag.addAction(self.actionFitPeakGaussian) ag.addAction(self.actionFitPeakLorentzian) ag.addAction(self.actionFitPeakPV) ag.addAction(self.actionFitPeakPVII) ag.addAction(self.actionFitTc) ag.addAction(self.actionFitCosine) ag.addAction(self.actionFitSigmoid) ag.addAction(self.actionFitLinear) ag.addAction(self.actionFitExponential) menu2.addAction(self.actionFitPeak) menu2.addAction(self.actionPickInitial) menu2.addAction(self.actionFitPeakGaussian) menu2.addAction(self.actionFitPeakLorentzian) menu2.addAction(self.actionFitPeakPV) menu2.addAction(self.actionFitPeakPVII) menu2.addAction(self.actionFitTc) menu2.addAction(self.actionFitCosine) menu2.addAction(self.actionFitSigmoid) menu2.addAction(self.actionFitLinear) menu2.addAction(self.actionFitExponential) menu2.addSeparator() menu2.addAction(self.actionFitArby) self.menus = [menu1, menu2] return self.menus def getToolbars(self): if not self.bars: bar = QToolBar('Scans') bar.addAction(self.actionSavePlot) bar.addAction(self.actionPrint) bar.addSeparator() bar.addAction(self.actionXAxis) bar.addAction(self.actionYAxis) bar.addAction(self.actionNormalized) bar.addSeparator() bar.addAction(self.actionLogXScale) bar.addAction(self.actionLogScale) bar.addAction(self.actionUnzoom) bar.addSeparator() bar.addAction(self.actionAutoScale) bar.addAction(self.actionScaleX) bar.addAction(self.actionScaleY) bar.addAction(self.actionLegend) bar.addAction(self.actionErrors) bar.addAction(self.actionResetPlot) bar.addAction(self.actionDeletePlot) bar.addSeparator() bar.addAction(self.actionAutoDisplay) bar.addAction(self.actionCombine) fitbar = QToolBar('Scan fitting') fitbar.addAction(self.actionFitPeak) wa = QWidgetAction(fitbar) self.fitPickCheckbox = QCheckBox(fitbar) self.fitPickCheckbox.setText('Pick') self.fitPickCheckbox.setChecked(True) self.actionPickInitial.setChecked(True) self.fitPickCheckbox.toggled.connect(self.actionPickInitial.setChecked) self.actionPickInitial.toggled.connect(self.fitPickCheckbox.setChecked) layout = QHBoxLayout() layout.setContentsMargins(10, 0, 10, 0) layout.addWidget(self.fitPickCheckbox) frame = QFrame(fitbar) frame.setLayout(layout) wa.setDefaultWidget(frame) fitbar.addAction(wa) ag = QActionGroup(fitbar) ag.addAction(self.actionFitPeakGaussian) ag.addAction(self.actionFitPeakLorentzian) ag.addAction(self.actionFitPeakPV) ag.addAction(self.actionFitPeakPVII) ag.addAction(self.actionFitTc) ag.addAction(self.actionFitCosine) ag.addAction(self.actionFitSigmoid) ag.addAction(self.actionFitLinear) ag.addAction(self.actionFitExponential) wa = QWidgetAction(fitbar) self.fitComboBox = QComboBox(fitbar) for a in ag.actions(): itemtext = a.text().replace('&', '') self.fitComboBox.addItem(itemtext) self.fitfuncmap[itemtext] = a self.fitComboBox.currentIndexChanged.connect( self.on_fitComboBox_currentIndexChanged) wa.setDefaultWidget(self.fitComboBox) fitbar.addAction(wa) fitbar.addSeparator() fitbar.addAction(self.actionFitArby) self.bars = [bar, fitbar] return self.bars def updateList(self): self.datasetList.clear() for dataset in self.data.sets: if dataset.invisible: continue shortname = '%s - %s' % (dataset.name, dataset.default_xname) item = QListWidgetItem(shortname, self.datasetList) item.setData(32, dataset.uid) self.setitems[dataset.uid] = item def on_logYinDomain(self, flag): if not flag: self.actionLogScale.setChecked(flag) def on_logXinDomain(self, flag): if not flag: self.actionLogXScale.setChecked(flag) def on_datasetList_currentItemChanged(self, item, previous): if self.no_openset or item is None: return self.openDataset(itemuid(item)) def on_datasetList_itemClicked(self, item): # this handler is needed in addition to currentItemChanged # since one can't change the current item if it's the only one if self.no_openset or item is None: return self.openDataset(itemuid(item)) def openDataset(self, uid): dataset = self.data.uid2set[uid] newplot = None if dataset.uid not in self.setplots: newplot = DataSetPlot(self.plotFrame, self, dataset) if self.currentPlot: newplot.enableCurvesFrom(self.currentPlot) self.setplots[dataset.uid] = newplot self.datasetList.setCurrentItem(self.setitems[uid]) plot = self.setplots[dataset.uid] self.enableAutoScaleActions(plot.HAS_AUTOSCALE) if newplot and plot.HAS_AUTOSCALE: from gr.pygr import PlotAxes plot.plot.autoscale = PlotAxes.SCALE_X | PlotAxes.SCALE_Y self.setCurrentDataset(plot) def setCurrentDataset(self, plot): if self.currentPlot: self.plotLayout.removeWidget(self.currentPlot) self.currentPlot.hide() self.metaTable.clearContents() self.currentPlot = plot if plot is None: self.enablePlotActions(False) else: try: self.setUidStack.remove(plot.dataset.uid) except ValueError: pass self.setUidStack.append(plot.dataset.uid) num_items = 0 for catname in INTERESTING_CATS: if catname in plot.dataset.headerinfo: num_items += 2 + len(plot.dataset.headerinfo[catname]) num_items -= 1 # remove last empty row self.metaTable.setRowCount(num_items) i = 0 for catname in INTERESTING_CATS: if catname in plot.dataset.headerinfo: values = plot.dataset.headerinfo[catname] catdesc = catname for name_desc in INFO_CATEGORIES: if name_desc[0] == catname: catdesc = name_desc[1] catitem = QTableWidgetItem(catdesc) font = catitem.font() font.setBold(True) catitem.setFont(font) self.metaTable.setItem(i, 0, catitem) self.metaTable.setSpan(i, 0, 1, 2) i += 1 for dev, name, value in sorted(values): key = '%s_%s' % (dev, name) if name != 'value' else dev self.metaTable.setItem(i, 0, QTableWidgetItem(key)) self.metaTable.setItem(i, 1, QTableWidgetItem(value)) if self.metaTable.columnSpan(i, 0) == 2: self.metaTable.setSpan(i, 0, 1, 1) i += 1 i += 1 self.metaTable.resizeRowsToContents() self.enablePlotActions(True) self.enableAutoScaleActions(self.currentPlot.HAS_AUTOSCALE) self.datasetList.setCurrentItem(self.setitems[plot.dataset.uid]) self.actionXAxis.setText('X axis: %s' % plot.current_xname) self.actionNormalized.setChecked(bool(plot.normalized)) self.actionLogScale.setChecked(plot.isLogScaling()) self.actionLogXScale.setChecked(plot.isLogXScaling()) self.actionLegend.setChecked(plot.isLegendEnabled()) self.actionErrors.setChecked(plot.isErrorBarEnabled()) if plot.HAS_AUTOSCALE: from gr.pygr import PlotAxes mask = plot.plot.autoscale self._autoscale(x=mask & PlotAxes.SCALE_X, y=mask & PlotAxes.SCALE_Y) plot.logYinDomain.connect(self.on_logYinDomain) plot.logXinDomain.connect(self.on_logXinDomain) self.plotLayout.addWidget(plot) plot.show() def on_data_datasetAdded(self, dataset): shortname = '%s - %s' % (dataset.name, dataset.default_xname) if dataset.uid in self.setitems: self.setitems[dataset.uid].setText(shortname) if dataset.uid in self.setplots: self.setplots[dataset.uid].updateDisplay() else: self.no_openset = True item = QListWidgetItem(shortname, self.datasetList) item.setData(32, dataset.uid) self.setitems[dataset.uid] = item if self.actionAutoDisplay.isChecked() and not self.data.bulk_adding: self.openDataset(dataset.uid) self.no_openset = False # If the dataset is a continuation of another dataset, automatically # create a combined dataset. contuids = dataset.continuation if contuids: alluids = tuple(contuids.split(',')) + (dataset.uid,) # Did we already create this set? Then don't create it again. if self.contSetUids.get(alluids) in self.setitems: return allsets = list(map(self.data.uid2set.get, alluids)) newuid = self._combine(COMBINE, allsets) if newuid: self.contSetUids[alluids] = newuid def on_data_pointsAdded(self, dataset): if dataset.uid in self.setplots: self.setplots[dataset.uid].pointsAdded() def on_data_fitAdded(self, dataset, res): if dataset.uid in self.setplots: self.setplots[dataset.uid]._plotFit(res) def on_client_experiment(self, data): self.datasetList.clear() # hide plot self.setCurrentDataset(None) # back to the beginning self.setplots = {} self.setitems = {} self.currentPlot = None self.setUidStack = [] @pyqtSlot() def on_actionClosePlot_triggered(self): current_set = self.setUidStack.pop() if self.setUidStack: self.setCurrentDataset(self.setplots[self.setUidStack[-1]]) else: self.setCurrentDataset(None) del self.setplots[current_set] @pyqtSlot() def on_actionResetPlot_triggered(self): current_set = self.setUidStack.pop() del self.setplots[current_set] self.openDataset(current_set) @pyqtSlot() def on_actionDeletePlot_triggered(self): if self.currentPlot.dataset.scaninfo != 'combined set': if not self.askQuestion('This is not a combined set: still ' 'delete it from the list?'): return current_set = self.setUidStack.pop() self.data.uid2set[current_set].invisible = True if self.setUidStack: self.setCurrentDataset(self.setplots[self.setUidStack[-1]]) else: self.setCurrentDataset(None) del self.setplots[current_set] for i in range(self.datasetList.count()): if itemuid(self.datasetList.item(i)) == current_set: self.datasetList.takeItem(i) break @pyqtSlot() def on_actionSavePlot_triggered(self): filename = self.currentPlot.savePlot() if filename: self.statusBar.showMessage('Plot successfully saved to %s.' % filename) @pyqtSlot() def on_actionPrint_triggered(self): if self.currentPlot.printPlot(): self.statusBar.showMessage('Plot successfully printed.') @pyqtSlot() def on_actionAttachElog_triggered(self): newdlg = dialogFromUi(self, 'panels/plot_attach.ui') suffix = self.currentPlot.SAVE_EXT newdlg.filename.setText( safeName('data_%s' % self.currentPlot.dataset.name + suffix)) ret = newdlg.exec_() if ret != QDialog.Accepted: return descr = newdlg.description.text() fname = newdlg.filename.text() pathname = self.currentPlot.saveQuietly() with open(pathname, 'rb') as fp: remotefn = self.client.ask('transfer', fp.read()) if remotefn is not None: self.client.eval('_LogAttach(%r, [%r], [%r])' % (descr, remotefn, fname)) os.unlink(pathname) @pyqtSlot() def on_actionUnzoom_triggered(self): self.currentPlot.unzoom() @pyqtSlot(bool) def on_actionLogScale_toggled(self, on): self.currentPlot.setLogScale(on) @pyqtSlot(bool) def on_actionLogXScale_toggled(self, on): self.currentPlot.setLogXScale(on) @pyqtSlot(bool) def on_actionAutoScale_toggled(self, on): self._autoscale(on, on) @pyqtSlot(bool) def on_actionScaleX_toggled(self, on): self._autoscale(x=on) @pyqtSlot(bool) def on_actionScaleY_toggled(self, on): self._autoscale(y=on) def on_x_menu_aboutToShow(self): self.x_menu.clear() if not self.currentPlot: return done = set() for name in self.currentPlot.dataset.xnameunits: if name in done: continue done.add(name) action = self.x_menu.addAction(name) action.setData(name) action.setCheckable(True) if name == self.currentPlot.current_xname: action.setChecked(True) action.triggered.connect(self.on_x_action_triggered) @pyqtSlot() def on_x_action_triggered(self, text=None): if text is None: text = self.sender().data() self.actionXAxis.setText('X axis: %s' % text) self.currentPlot.current_xname = text self.currentPlot.updateDisplay() self.on_actionUnzoom_triggered() @pyqtSlot() def on_actionXAxis_triggered(self): self.bars[0].widgetForAction(self.actionXAxis).showMenu() def on_y_menu_aboutToShow(self): self.y_menu.clear() if not self.currentPlot: return for curve in self.currentPlot.dataset.curves: action = self.y_menu.addAction(curve.full_description) action.setData(curve.full_description) action.setCheckable(True) if not curve.hidden: action.setChecked(True) action.triggered.connect(self.on_y_action_triggered) @pyqtSlot() def on_y_action_triggered(self, text=None): if text is None: text = self.sender().data() if not self.currentPlot: return for curve in self.currentPlot.dataset.curves: if curve.full_description == text: curve.hidden = not curve.hidden self.currentPlot.updateDisplay() self.on_actionUnzoom_triggered() @pyqtSlot() def on_actionYAxis_triggered(self): self.bars[0].widgetForAction(self.actionYAxis).showMenu() @pyqtSlot() def on_actionNormalized_triggered(self): if not self.currentPlot: return if self.currentPlot.normalized is not None: self.on_norm_action_triggered('None') else: all_normnames = [name for (_, name) in self.currentPlot.dataset.normindices] if self.last_norm_selection and \ self.last_norm_selection in all_normnames: use = self.last_norm_selection else: use = all_normnames[0] if all_normnames else 'None' self.on_norm_action_triggered(use) def on_norm_menu_aboutToShow(self): self.norm_menu.clear() if self.currentPlot: none_action = self.norm_menu.addAction('None') none_action.setData('None') none_action.setCheckable(True) none_action.setChecked(True) none_action.triggered.connect(self.on_norm_action_triggered) max_action = self.norm_menu.addAction('Maximum') max_action.setData('Maximum') max_action.setCheckable(True) if self.currentPlot.normalized == 'Maximum': max_action.setChecked(True) none_action.setChecked(False) max_action.triggered.connect(self.on_norm_action_triggered) for _, name in self.currentPlot.dataset.normindices: action = self.norm_menu.addAction(name) action.setData(name) action.setCheckable(True) if name == self.currentPlot.normalized: action.setChecked(True) none_action.setChecked(False) action.triggered.connect(self.on_norm_action_triggered) @pyqtSlot() def on_norm_action_triggered(self, text=None): if text is None: text = self.sender().data() if text == 'None': self.currentPlot.normalized = None self.actionNormalized.setChecked(False) else: self.last_norm_selection = text self.currentPlot.normalized = text self.actionNormalized.setChecked(True) self.currentPlot.updateDisplay() self.on_actionUnzoom_triggered() @pyqtSlot(bool) def on_actionLegend_toggled(self, on): self.currentPlot.setLegend(on) @pyqtSlot(bool) def on_actionErrors_toggled(self, on): self.currentPlot.setErrorBarEnabled(on) @pyqtSlot() def on_actionModifyData_triggered(self): self.currentPlot.modifyData() @pyqtSlot() def on_actionFitPeak_triggered(self): self.currentPlot.beginFit(self.fitclass, self.actionFitPeak, pickmode=self.fitPickCheckbox.isChecked()) @pyqtSlot(int) def on_fitComboBox_currentIndexChanged(self, index): self.fitfuncmap[self.fitComboBox.currentText()].trigger() @pyqtSlot() def on_actionFitPeakGaussian_triggered(self): cbi = self.fitComboBox.findText(self.actionFitPeakGaussian.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = GaussFitter @pyqtSlot() def on_actionFitPeakLorentzian_triggered(self): cbi = self.fitComboBox.findText(self.actionFitPeakLorentzian.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = LorentzFitter @pyqtSlot() def on_actionFitPeakPV_triggered(self): cbi = self.fitComboBox.findText(self.actionFitPeakPV.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = PseudoVoigtFitter @pyqtSlot() def on_actionFitPeakPVII_triggered(self): cbi = self.fitComboBox.findText(self.actionFitPeakPVII.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = PearsonVIIFitter @pyqtSlot() def on_actionFitTc_triggered(self): cbi = self.fitComboBox.findText(self.actionFitTc.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = TcFitter @pyqtSlot() def on_actionFitCosine_triggered(self): cbi = self.fitComboBox.findText(self.actionFitCosine.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = CosineFitter @pyqtSlot() def on_actionFitSigmoid_triggered(self): cbi = self.fitComboBox.findText(self.actionFitSigmoid.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = SigmoidFitter @pyqtSlot() def on_actionFitLinear_triggered(self): cbi = self.fitComboBox.findText(self.actionFitLinear.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = LinearFitter @pyqtSlot() def on_actionFitExponential_triggered(self): cbi = self.fitComboBox.findText(self.actionFitExponential.text().replace('&', '')) self.fitComboBox.setCurrentIndex(cbi) self.fitclass = ExponentialFitter @pyqtSlot() def on_actionFitArby_triggered(self): # no second argument: the "arbitrary" action is not checkable self.currentPlot.beginFit(ArbitraryFitter, None, pickmode=self.fitPickCheckbox.isChecked()) @pyqtSlot() def on_quickfit(self): if not self.currentPlot or not self.currentPlot.underMouse(): return self.currentPlot.fitQuick() @pyqtSlot() def on_actionCombine_triggered(self): current = self.currentPlot.dataset.uid dlg = dialogFromUi(self, 'panels/dataops.ui') for i in range(self.datasetList.count()): item = self.datasetList.item(i) newitem = QListWidgetItem(item.text(), dlg.otherList) newitem.setData(32, item.data(32)) if itemuid(item) == current: dlg.otherList.setCurrentItem(newitem) # paint the current set in grey to indicate it's not allowed # to be selected newitem.setBackground(self.palette().brush(QPalette.Mid)) newitem.setFlags(Qt.NoItemFlags) if dlg.exec_() != QDialog.Accepted: return items = dlg.otherList.selectedItems() sets = [self.data.uid2set[current]] for item in items: if itemuid(item) == current: return self.showError('Cannot combine set with itself.') sets.append(self.data.uid2set[itemuid(item)]) for rop, rb in [(TOGETHER, dlg.opTogether), (COMBINE, dlg.opCombine), (ADD, dlg.opAdd), (SUBTRACT, dlg.opSubtract), (DIVIDE, dlg.opDivide)]: if rb.isChecked(): op = rop break self._combine(op, sets) def _combine(self, op, sets): if op == TOGETHER: newset = ScanData() newset.name = combineattr(sets, 'name', sep=', ') newset.invisible = False newset.curves = [] newset.scaninfo = 'combined set' # combine xnameunits from those that are in all sets all_xnu = set(sets[0].xnameunits) for dset in sets[1:]: all_xnu &= set(dset.xnameunits) newset.xnameunits = ['Default'] + [xnu for xnu in sets[0].xnameunits if xnu in all_xnu] newset.default_xname = 'Default' newset.normindices = sets[0].normindices # for together only, the number of curves and their columns # are irrelevant, just put all together for dataset in sets: for curve in dataset.curves: newcurve = curve.copy() if not newcurve.source: newcurve.source = dataset.name newset.curves.append(newcurve) self.data.add_existing_dataset(newset, [dataset.uid for dataset in sets]) return newset.uid # else, need same axes, and same number and types of curves firstset = sets[0] nameprops = [firstset.xnames, firstset.xunits] curveprops = [(curve.description, curve.yindex) for curve in firstset.curves] for dataset in sets[1:]: if [dataset.xnames, dataset.xunits] != nameprops: self.showError('Sets have different axes.') return if [(curve.description, curve.yindex) for curve in dataset.curves] != curveprops: self.showError('Sets have different curves.') return if op == COMBINE: newset = ScanData() newset.name = combineattr(sets, 'name', sep=', ') newset.invisible = False newset.curves = [] newset.scaninfo = 'combined set' newset.xnameunits = firstset.xnameunits newset.default_xname = firstset.default_xname newset.normindices = firstset.normindices for curves in zip(*(dataset.curves for dataset in sets)): newcurve = curves[0].copy() newcurve.datay = DataProxy(c.datay for c in curves) newcurve.datady = DataProxy(c.datady for c in curves) newcurve.datax = {xnu: DataProxy(c.datax[xnu] for c in curves) for xnu in newset.xnameunits} newcurve.datanorm = {nn: DataProxy(c.datanorm[nn] for c in curves) for i, nn in newset.normindices} newset.curves.append(newcurve) self.data.add_existing_dataset(newset, [dataset.uid for dataset in sets]) return newset.uid if op == ADD: sep = ' + ' elif op == SUBTRACT: sep = ' - ' elif op == DIVIDE: sep = ' / ' newset = ScanData() newset.name = combineattr(sets, 'name', sep=sep) newset.invisible = False newset.scaninfo = 'combined set' newset.curves = [] newset.xnameunits = firstset.xnameunits newset.default_xname = firstset.default_xname if op in (SUBTRACT, DIVIDE): # remove information about normalization -- doesn't make sense newset.normindices = [] else: newset.normindices = firstset.normindices for curves in zip(*(dataset.curves for dataset in sets)): newcurve = curves[0].deepcopy() # CRUDE HACK: don't care about the x values, operate by index removepoints = set() for curve in curves[1:]: for i in range(len(newcurve.datay)): y1, y2 = float(newcurve.datay[i]), float(curve.datay[i]) if newcurve.dyindex != -1: dy1 = newcurve.datady[i] dy2 = curve.datady[i] else: dy1 = dy2 = 1. if op == ADD: newcurve.datay[i] = y1 + y2 newcurve.datady[i] = sqrt(dy1**2 + dy2**2) for name in newcurve.datanorm: newcurve.datanorm[name][i] += curve.datanorm[name][i] elif op == SUBTRACT: newcurve.datay[i] = y1 - y2 newcurve.datady[i] = sqrt(dy1**2 + dy2**2) elif op == DIVIDE: if y2 == 0: y2 = 1. # generate a value for now removepoints.add(i) newcurve.datay[i] = y1 / y2 newcurve.datady[i] = sqrt((dy1/y2)**2 + (dy2*y1 / y2**2)**2) # remove points where we would have divided by zero if removepoints: newcurve.datay = [v for (i, v) in enumerate(newcurve.datay) if i not in removepoints] newcurve.datady = [v for (i, v) in enumerate(newcurve.datady) if i not in removepoints] for name in newcurve.datax: newcurve.datax[name] = \ [v for (i, v) in enumerate(newcurve.datax[name]) if i not in removepoints] for name in newcurve.datanorm: newcurve.datanorm[name] = \ [v for (i, v) in enumerate(newcurve.datanorm[name]) if i not in removepoints] newset.curves.append(newcurve) self.data.add_existing_dataset(newset) return newset.uid
def _reinit(self): classes = self.devinfo.classes if sip.isdeleted(self.devitem): # The item we're controlling has been removed from the list (e.g. # due to client reconnect), get it again. self.devitem = self.device_panel._devitems.get( self.devname.lower()) # No such device anymore... if self.devitem is None: self.close() return self.deviceName.setText('Device: %s' % self.devname) self.setWindowTitle('Control %s' % self.devname) self.settingsBtn = self.buttonBox.button( QDialogButtonBox.RestoreDefaults) self.settingsBtn.clicked.connect(self.on_settingsBtn_clicked) # trigger parameter poll self.client.eval('%s.pollParams()' % self.devname, None) # now get all cache keys pertaining to the device and set the # properties we want params = self.client.getDeviceParams(self.devname) self.paraminfo = self.client.getDeviceParamInfo(self.devname) self.paramvalues = dict(params) # put parameter values in the list widget self.paramItems.clear() self.paramList.clear() for key, value in sorted(iteritems(params)): if self.paraminfo.get(key): # normally, show only userparams, except in expert mode is_userparam = self.paraminfo[key]['userparam'] if is_userparam or self.device_panel._show_lowlevel: self.paramItems[key] = item = \ QTreeWidgetItem(self.paramList, [key, str(value)]) # display non-userparams in grey italics, like lowlevel # devices in the device list if not is_userparam: item.setFont(0, lowlevelFont[True]) item.setForeground(0, lowlevelBrush[True]) # set description label if params.get('description'): self.description.setText(params['description']) else: self.description.setVisible(False) # check how to refer to the device in commands: if it is lowlevel, # we need to use quotes self.devrepr = srepr(self.devname) if params.get('lowlevel', True) \ else self.devname # show "Set alias" group box if it is an alias device if 'alias' in params: if params['alias']: self.deviceName.setText(self.deviceName.text() + ' (alias for %s)' % params['alias']) alias_config = self.client.eval('session.alias_config', {}) self.aliasTarget = QComboBox(self) self.aliasTarget.setEditable(True) if self.devname in alias_config: items = [t[0] for t in alias_config[self.devname]] self.aliasTarget.addItems(items) if params['alias'] in items: self.aliasTarget.setCurrentIndex( items.index(params['alias'])) self.targetLayoutAlias.takeAt(1).widget().deleteLater() self.targetLayoutAlias.insertWidget(1, self.aliasTarget) if self.client.viewonly: self.setAliasBtn.setEnabled(False) else: self.aliasGroup.setVisible(False) historyBtn = self.buttonBox.button(QDialogButtonBox.Reset) # show current value/status if it is readable if 'nicos.core.device.Readable' not in classes: self.valueFrame.setVisible(False) self.buttonBox.removeButton(historyBtn) else: self.valuelabel.setText(self.devitem.text(1)) self.statuslabel.setText(self.devitem.text(2)) self.statusimage.setPixmap(self.devitem.icon(0).pixmap(16, 16)) setForegroundBrush(self.statuslabel, self.devitem.foreground(2)) setBackgroundBrush(self.statuslabel, self.devitem.background(2)) # modify history button: add icon and set text historyBtn.setIcon(QIcon(':/find')) historyBtn.setText('Plot history...') historyBtn.clicked.connect(self.on_historyBtn_clicked) if self.client.viewonly: self.limitFrame.setVisible(False) self.targetFrame.setVisible(False) return # add a menu for the "More" button self.moveBtns.clear() menu = QMenu(self) if 'nicos.core.mixins.HasLimits' in classes: menu.addAction(self.actionSetLimits) if 'nicos.core.mixins.HasOffset' in classes: menu.addAction(self.actionAdjustOffset) if 'nicos.devices.abstract.CanReference' in classes: menu.addAction(self.actionReference) if 'nicos.devices.abstract.Coder' in classes: menu.addAction(self.actionSetPosition) if 'nicos.core.device.Moveable' in classes: if not menu.isEmpty(): menu.addSeparator() menu.addAction(self.actionFix) menu.addAction(self.actionRelease) if 'nicos.core.mixins.CanDisable' in classes: if not menu.isEmpty(): menu.addSeparator() menu.addAction(self.actionEnable) menu.addAction(self.actionDisable) if not menu.isEmpty(): menuBtn = QPushButton('More', self) menuBtn.setMenu(menu) self.moveBtns.addButton(menuBtn, QDialogButtonBox.ResetRole) def reset(checked): self.device_panel.exec_command('reset(%s)' % self.devrepr) def stop(checked): self.device_panel.exec_command('stop(%s)' % self.devrepr, immediate=True) self.moveBtns.addButton('Reset', QDialogButtonBox.ResetRole)\ .clicked.connect(reset) if 'nicos.core.device.Moveable' in classes or \ 'nicos.core.device.Measurable' in classes: self.moveBtns.addButton('Stop', QDialogButtonBox.ResetRole)\ .clicked.connect(stop) # show target and limits if the device is Moveable if 'nicos.core.device.Moveable' not in classes: self.limitFrame.setVisible(False) self.targetFrame.setVisible(False) else: if 'nicos.core.mixins.HasLimits' not in classes: self.limitFrame.setVisible(False) else: self.limitMin.setText(str(params['userlimits'][0])) self.limitMax.setText(str(params['userlimits'][1])) # insert a widget to enter a new device value # allowEnter=False because we catch pressing Enter ourselves self.target = DeviceValueEdit(self, dev=self.devname, useButtons=True, allowEnter=False) self.target.setClient(self.client) def btn_callback(target): self.device_panel.exec_command('move(%s, %s)' % (self.devrepr, srepr(target))) self.target.valueChosen.connect(btn_callback) self.targetFrame.layout().takeAt(1).widget().deleteLater() self.targetFrame.layout().insertWidget(1, self.target) def move(checked): try: target = self.target.getValue() except ValueError: return self.device_panel.exec_command('move(%s, %s)' % (self.devrepr, srepr(target))) if self.target.getValue() is not Ellipsis: # (button widget) self.moveBtn = self.moveBtns.addButton( 'Move', QDialogButtonBox.AcceptRole) self.moveBtn.clicked.connect(move) else: self.moveBtn = None if params.get('fixed') and self.moveBtn: self.moveBtn.setEnabled(False) self.moveBtn.setText('(fixed)')
def __init__(self, log, gui_conf, viewonly=False, tunnel=''): DefaultMainWindow.__init__(self, log, gui_conf, viewonly, tunnel) self.addLogo() self.addInstrument() self.addExperiment() self.set_icons() self.stylefile = gui_conf.stylefile # Cheesburger 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
def getMenus(self): menu = QMenu('&History viewer', self) menu.addAction(self.actionNew) menu.addSeparator() menu.addAction(self.actionSavePlot) menu.addAction(self.actionPrint) menu.addAction(self.actionAttachElog) menu.addAction(self.actionSaveData) menu.addSeparator() menu.addAction(self.actionEditView) menu.addAction(self.actionCloseView) menu.addAction(self.actionDeleteView) menu.addAction(self.actionResetView) menu.addSeparator() menu.addAction(self.actionLogScale) menu.addAction(self.actionAutoScale) menu.addAction(self.actionScaleX) menu.addAction(self.actionScaleY) menu.addAction(self.actionUnzoom) menu.addAction(self.actionLegend) menu.addAction(self.actionSymbols) menu.addAction(self.actionLines) ag = QActionGroup(menu) ag.addAction(self.actionFitPeakGaussian) ag.addAction(self.actionFitPeakLorentzian) ag.addAction(self.actionFitPeakPV) ag.addAction(self.actionFitPeakPVII) ag.addAction(self.actionFitTc) ag.addAction(self.actionFitCosine) ag.addAction(self.actionFitSigmoid) ag.addAction(self.actionFitLinear) ag.addAction(self.actionFitExponential) menu.addAction(self.actionFitPeak) menu.addAction(self.actionPickInitial) menu.addAction(self.actionFitPeakGaussian) menu.addAction(self.actionFitPeakLorentzian) menu.addAction(self.actionFitPeakPV) menu.addAction(self.actionFitPeakPVII) menu.addAction(self.actionFitTc) menu.addAction(self.actionFitCosine) menu.addAction(self.actionFitSigmoid) menu.addAction(self.actionFitLinear) menu.addAction(self.actionFitExponential) menu.addSeparator() menu.addAction(self.actionFitArby) menu.addSeparator() menu.addAction(self.actionClose) self._refresh_presets() return [menu, self.presetmenu]
def __init__(self): loadUi(self, 'panels/history.ui') self.user_color = Qt.white self.user_font = QFont('Monospace') self.views = [] # stack of views to display self.viewStack = [] # maps watched keys to their views self.keyviews = {} # current plot object self.currentPlot = None self.fitclass = LinearFitter self.fitfuncmap = {} self.enablePlotActions(False) self.presetmenu = QMenu('&Presets', self) for (name, view) in self.last_views: item = QListWidgetItem(name, self.viewList) item.setForeground(QBrush(QColor('#aaaaaa'))) item.setData(Qt.UserRole, view) self.menus = None self.bar = None # NOTE: for this class, automatic connections don't work on PyQt4 >= # 4.12 since this class is not derived from QObject. But on older PyQt4 # and PyQt5, they do work, so we change use the usual naming scheme # slightly to avoid double connections. self.viewList.currentItemChanged.connect( self.on__viewList_currentItemChanged) self.viewList.itemClicked.connect(self.on__viewList_itemClicked) self.viewList.itemDoubleClicked.connect( self.on__viewList_itemDoubleClicked) self.actionNew.triggered.connect(self.on__actionNew_triggered) self.actionEditView.triggered.connect( self.on__actionEditView_triggered) self.actionCloseView.triggered.connect( self.on__actionCloseView_triggered) self.actionResetView.triggered.connect( self.on__actionResetView_triggered) self.actionDeleteView.triggered.connect( self.on__actionDeleteView_triggered) self.actionSavePlot.triggered.connect( self.on__actionSavePlot_triggered) self.actionPrint.triggered.connect(self.on__actionPrint_triggered) self.actionUnzoom.triggered.connect(self.on__actionUnzoom_triggered) self.actionLogScale.toggled.connect(self.on__actionLogScale_toggled) self.actionAutoScale.toggled.connect(self.on__actionAutoScale_toggled) self.actionScaleX.toggled.connect(self.on__actionScaleX_toggled) self.actionScaleY.toggled.connect(self.on__actionScaleY_toggled) self.actionLegend.toggled.connect(self.on__actionLegend_toggled) self.actionSymbols.toggled.connect(self.on__actionSymbols_toggled) self.actionLines.toggled.connect(self.on__actionLines_toggled) self.actionSaveData.triggered.connect( self.on__actionSaveData_triggered) self.actionFitPeak.triggered.connect(self.on__actionFitPeak_triggered) self.actionFitArby.triggered.connect(self.on__actionFitArby_triggered) self.actionFitPeakGaussian.triggered.connect( self.on__actionFitPeakGaussian_triggered) self.actionFitPeakLorentzian.triggered.connect( self.on__actionFitPeakLorentzian_triggered) self.actionFitPeakPV.triggered.connect( self.on__actionFitPeakPV_triggered) self.actionFitPeakPVII.triggered.connect( self.on__actionFitPeakPVII_triggered) self.actionFitTc.triggered.connect(self.on__actionFitTc_triggered) self.actionFitCosine.triggered.connect( self.on__actionFitCosine_triggered) self.actionFitSigmoid.triggered.connect( self.on__actionFitSigmoid_triggered) self.actionFitLinear.triggered.connect( self.on__actionFitLinear_triggered) self.actionFitExponential.triggered.connect( self.on__actionFitExponential_triggered)
def on_tree_customContextMenuRequested(self, point): item = self.tree.itemAt(point) if item is None: return if item.type() == DEVICE_TYPE: self._menu_dev = item.text(0) ldevname = self._menu_dev.lower() if charmpowersupply in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] i = 0 for menuItem in params['transitions']: self.cps_actions.append(QAction(menuItem)) self.cpsmenu.addAction(self.cps_actions[i]) self.cps_actions[i].triggered.connect( partial(self.on_actionApply_triggered, i)) i = i + 1 self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return if roimanager in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] self.cps_actions.append(QAction('Edit...')) self.cpsmenu.addAction(self.cps_actions[0]) self.cps_actions[0].triggered.connect( partial(self.on_roimanagerEdit, ldevname)) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return if compareimage in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] self.cps_actions.append(QAction('Show &Compare...')) self.cpsmenu.addAction(self.cps_actions[0]) self.cps_actions[0].triggered.connect( partial(self.on_images_compare, ldevname)) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return if playlistmanager in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] self.cps_actions.append(QAction('Edit...')) self.cpsmenu.addAction(self.cps_actions[0]) self.cps_actions[0].triggered.connect( partial(self.on_playlist_edit, ldevname)) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return return super().on_tree_customContextMenuRequested(point)
class DevicesPanel1(DevicesPanel): def on_tree_customContextMenuRequested(self, point): item = self.tree.itemAt(point) if item is None: return if item.type() == DEVICE_TYPE: self._menu_dev = item.text(0) ldevname = self._menu_dev.lower() if charmpowersupply in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] i = 0 for menuItem in params['transitions']: self.cps_actions.append(QAction(menuItem)) self.cpsmenu.addAction(self.cps_actions[i]) self.cps_actions[i].triggered.connect( partial(self.on_actionApply_triggered, i)) i = i + 1 self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return if roimanager in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] self.cps_actions.append(QAction('Edit...')) self.cpsmenu.addAction(self.cps_actions[0]) self.cps_actions[0].triggered.connect( partial(self.on_roimanagerEdit, ldevname)) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return if compareimage in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] self.cps_actions.append(QAction('Show &Compare...')) self.cpsmenu.addAction(self.cps_actions[0]) self.cps_actions[0].triggered.connect( partial(self.on_images_compare, ldevname)) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return if playlistmanager in self._devinfo[ldevname].classes: params = self.client.getDeviceParams(ldevname) self.cpsmenu = QMenu() self.cps_actions = [] self.cps_actions.append(QAction('Edit...')) self.cpsmenu.addAction(self.cps_actions[0]) self.cps_actions[0].triggered.connect( partial(self.on_playlist_edit, ldevname)) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionMove) self.cpsmenu.addAction(self.actionReset) self.cpsmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.cpsmenu.addAction(self.actionPlotHistory) self.cpsmenu.addSeparator() self.cpsmenu.addAction(self.actionShutDown) self.cpsmenu.addAction(self.actionHelp) self.cpsmenu.popup(self.tree.viewport().mapToGlobal(point)) return return super().on_tree_customContextMenuRequested(point) @pyqtSlot() def on_playlist_edit(self, ldevname): if not playlisteditor.win: playlisteditor.win = playlisteditor.Window(self.client, ldevname) playlisteditor.win.show() @pyqtSlot() def on_images_compare(self, ldevname): if not imagecompare.win: imagecompare.win = imagecompare.Window(self.client, ldevname) imagecompare.win.show() @pyqtSlot() def on_roimanagerEdit(self, ldevname): if not roieditor.win: roieditor.win = roieditor.Window(self.client, ldevname) roieditor.win.show() @pyqtSlot() def on_actionApply_triggered(self, index): if self._menu_dev: self.client.eval(self._menu_dev + '.apply(' + str(index) + ')') def on_client_cache(self, data): rv = super().on_client_cache(data) # here we truncate the long status message for charmpowersupply devices (time, key, op, value) = data if '/' not in key: return ldevname, subkey = key.rsplit('/', 1) if ldevname not in self._devinfo: return devitem = self._devitems[ldevname] devinfo = self._devinfo[ldevname] if charmpowersupply in self._devinfo[ldevname].classes: if subkey == 'status': t = devitem.text(2) i = t.rfind('[') if i >= 0: t = t[:i - 1] devitem.setText(2, str(t)) if ldevname in self._control_dialogs: dlg = self._control_dialogs[ldevname] ct = dlg.statuslabel.text() i = ct.rfind('[') if i >= 0: ct = ct[:i - 1] dlg.statuslabel.setText(ct) return rv
class DevicesPanel(Panel): """Provides a graphical list of NICOS devices and their current values. The user can operate basic device functions (move, stop, reset) by selecting an item from the list, which opens a control dialog. Options: * ``useicons`` (default True) -- if set to False, the list widget does not display status icons for the devices. * ``param_display`` (default {}) -- a dictionary containing the device name as key and a parameter name or a list of the parameter names which should be displayed in the device tree as subitems of the device item, for example:: param_display = { 'tas': 'scanmode', 'Exp': ['lastpoint', 'lastscan'] } * ``filters`` (default []) -- a list of tuples containing the name of the filter and the regular expression to filter out the devices. example:: filters = [ ('All', ''), ('Default', 'T|UBahn'), ('Foo', 'bar$'), ] """ panelName = 'Devices' ui = 'panels/devices.ui' @classmethod def _createIcons(cls): # hack to make non-Qt usage as in checksetups work if not hasattr(cls, 'statusIcon'): cls.statusIcon = { OK: QIcon(':/leds/status_green'), WARN: QIcon(':/leds/status_warn'), BUSY: QIcon(':/leds/status_yellow'), NOTREACHED: QIcon(':/leds/status_red'), DISABLED: QIcon(':/leds/status_white'), ERROR: QIcon(':/leds/status_red'), UNKNOWN: QIcon(':/leds/status_unknown'), } @property def groupIcon(self): return QIcon(':/setup') def __init__(self, parent, client, options): DevicesPanel._createIcons() Panel.__init__(self, parent, client, options) loadUi(self, self.ui) self.useicons = bool(options.get('icons', True)) self.param_display = {} param_display = options.get('param_display', {}) for (key, value) in param_display.items(): value = [value] if isinstance(value, string_types) else list(value) self.param_display[key.lower()] = value self.tree.header().restoreState(self._headerstate) self.clear() self.devmenu = QMenu(self) self.devmenu.addAction(self.actionMove) self.devmenu.addAction(self.actionStop) self.devmenu.addAction(self.actionReset) self.devmenu.addSeparator() self.devmenu.addAction(self.actionFix) self.devmenu.addAction(self.actionRelease) self.devmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.devmenu.addAction(self.actionPlotHistory) self.devmenu.addSeparator() self.devmenu.addAction(self.actionShutDown) self.devmenu.addAction(self.actionHelp) self.devmenu_ro = QMenu(self) self.devmenu_ro.addAction(self.actionMove) self.devmenu_ro.addAction(self.actionReset) self.devmenu_ro.addSeparator() if self.mainwindow.history_wintype is not None: self.devmenu_ro.addAction(self.actionPlotHistory) self.devmenu_ro.addSeparator() self.devmenu_ro.addAction(self.actionShutDown) self.devmenu_ro.addAction(self.actionHelp) self._menu_dev = None # device for which context menu is shown self._dev2setup = {} self._setupinfo = {} self._control_dialogs = {} self._show_lowlevel = self.mainwindow.expertmode # daemon request ID of last command executed from this panel # (used to display messages from this command) self._current_status = 'idle' self._exec_reqid = None self._error_window = None client.connected.connect(self.on_client_connected) client.disconnected.connect(self.on_client_disconnected) client.cache.connect(self.on_client_cache) client.device.connect(self.on_client_device) client.setup.connect(self.on_client_setup) client.message.connect(self.on_client_message) self.filters = options.get('filters', []) self.filter.addItem('') for text, rx in self.filters: self.filter.addItem('Filter: %s' % text, rx) self.filter.lineEdit().setPlaceholderText('Enter search expression') def updateStatus(self, status, exception=False): self._current_status = status def saveSettings(self, settings): settings.setValue('headers', self.tree.header().saveState()) def loadSettings(self, settings): self._headerstate = settings.value('headers', '', QByteArray) def _update_view(self): with self.sgroup as settings: for i in range(self.tree.topLevelItemCount()): v = settings.value( '%s/expanded' % self.tree.topLevelItem(i).text(0), True, bool) self.tree.topLevelItem(i).setExpanded(v) def _store_view(self): with self.sgroup as settings: for i in range(self.tree.topLevelItemCount()): settings.setValue( '%s/expanded' % self.tree.topLevelItem(i).text(0), self.tree.topLevelItem(i).isExpanded()) def hideTitle(self): self.titleLbl.setVisible(False) def setExpertMode(self, expert): self._show_lowlevel = expert self.on_client_connected() def clear(self): if self.tree: self._store_view() self._catitems = {} # map lowercased devname -> tree widget item self._devitems = {} self._devparamitems = {} # map lowercased devname -> DevInfo instance self._devinfo = {} self.tree.clear() def on_client_connected(self): self.clear() state = self.client.ask('getstatus') if not state: return devlist = state['devices'] self._read_setup_info(state['setups']) for devname in devlist: self._create_device_item(devname) # close all control dialogs for now nonexisting devices for ldevname in list(self._control_dialogs): if ldevname not in self._devitems: self._control_dialogs[ldevname].close() # add all toplevel items to the tree, sorted for cat in self._catitems: self.tree.addTopLevelItem(self._catitems[cat]) self._catitems[cat].setExpanded(True) for devitem in itervalues(self._devitems): devitem.setExpanded(True) self.tree.sortItems(0, Qt.AscendingOrder) self._update_view() def on_client_disconnected(self): self.clear() def on_client_message(self, message): # show warnings and errors emitted by the current command in a window if message[5] != self._exec_reqid or message[2] < WARNING: return msg = '%s: %s' % (message[0], message[3].strip()) if self._error_window is None: def reset_errorwindow(): self._error_window = None self._error_window = ErrorDialog(self) self._error_window.accepted.connect(reset_errorwindow) self._error_window.addMessage(msg) self._error_window.show() else: self._error_window.addMessage(msg) self._error_window.activateWindow() def _read_setup_info(self, setuplists=None): if setuplists is None: allstatus = self.client.ask('getstatus') if allstatus is None: return setuplists = allstatus['setups'] loaded_setups = set(setuplists[0]) self._dev2setup = {} self._setupinfo = self.client.eval('session.getSetupInfo()', {}) if self._setupinfo is None: self.log.warning('session.getSetupInfo() returned None instead ' 'of {}') return for setupname, info in iteritems(self._setupinfo): if info is None: continue if setupname not in loaded_setups: continue for devname in info['devices']: self._dev2setup[devname] = setupname def _create_device_item(self, devname, add_cat=False): ldevname = devname.lower() # get all cache keys pertaining to the device params = self.client.getDeviceParams(devname) if not params: return lowlevel_device = params.get('lowlevel') or False if lowlevel_device and not self._show_lowlevel: return if 'nicos.core.data.sink.DataSink' in params.get('classes', []) and \ not self._show_lowlevel: return # remove still-existing previous item for the same device name if ldevname in self._devitems: self.on_client_device(('destroy', [devname])) cat = self._dev2setup.get(devname) if cat is None: # device is not in any setup? reread setup info self._read_setup_info() cat = self._dev2setup.get(devname) if cat is None: # still not there -> give up return if cat not in self._catitems: display_order = self._setupinfo[cat].get('display_order', 50) representative = self._setupinfo[cat].get('extended', {}).get( 'representative', '').lower() catitem = SetupTreeWidgetItem(cat, display_order, representative) catitem.setToolTip(0, self._setupinfo[cat].get('description', '')) f = catitem.font(0) f.setBold(True) catitem.setFont(0, f) catitem.setIcon(0, self.groupIcon) self._catitems[cat] = catitem if add_cat: self.tree.addTopLevelItem(catitem) catitem.setExpanded(True) else: catitem = self._catitems[cat] # create a tree node for the device devitem = QTreeWidgetItem(catitem, [devname, '', ''], DEVICE_TYPE) devitem.setForeground(0, lowlevelBrush[lowlevel_device]) devitem.setFont(0, lowlevelFont[lowlevel_device]) if self.useicons: devitem.setIcon(0, self.statusIcon[OK]) devitem.setToolTip(0, params.get('description', '')) self._devitems[ldevname] = devitem # fill the device info with dummy values, will be populated below self._devinfo[ldevname] = DevInfo(devname) # let the cache handler process all properties for key, value in iteritems(params): self.on_client_cache( (0, ldevname + '/' + key, OP_TELL, cache_dump(value))) def on_client_setup(self, setuplists): # update setup tooltips self._read_setup_info(setuplists) for i in range(self.tree.topLevelItemCount()): catitem = self.tree.topLevelItem(i) cat = catitem.text(0) catitem.setToolTip(0, self._setupinfo[cat].get('description', '')) def on_client_device(self, data): (action, devlist) = data if not devlist: return if action == 'create': for devname in devlist: self._create_device_item(devname, add_cat=True) self.tree.sortItems(0, Qt.AscendingOrder) self._update_view() elif action == 'destroy': self._store_view() for devname in devlist: ldevname = devname.lower() if ldevname in self._devitems: # remove device item and cached info... item = self._devitems[ldevname] del self._devitems[ldevname] del self._devinfo[ldevname] self._devparamitems.pop(ldevname, None) try: catitem = item.parent() except RuntimeError: # Qt object has already been destroyed pass else: catitem.removeChild(item) # remove category item if it has no further children if catitem.childCount() == 0: self.tree.takeTopLevelItem( self.tree.indexOfTopLevelItem(catitem)) del self._catitems[catitem.text(0)] self._update_view() def on_client_cache(self, data): (time, key, op, value) = data if '/' not in key: return ldevname, subkey = key.rsplit('/', 1) if ldevname not in self._devinfo: return if ldevname in self._control_dialogs: self._control_dialogs[ldevname].on_cache(subkey, value) devitem = self._devitems[ldevname] devinfo = self._devinfo[ldevname] if subkey == 'value': if time < devinfo.valtime: return if not value: fvalue = '' else: fvalue = cache_load(value) if isinstance(fvalue, list): fvalue = tuple(fvalue) devinfo.value = fvalue devinfo.expired = op != OP_TELL devinfo.valtime = time fmted = devinfo.fmtValUnit() devitem.setText(1, fmted) if ldevname in self._control_dialogs: self._control_dialogs[ldevname].valuelabel.setText(fmted) devitem.setForeground(1, valueBrush[devinfo.expired, devinfo.fixed]) if not devitem.parent().isExpanded(): if ldevname == devitem.parent().representative: devitem.parent().setText(1, fmted) elif subkey == 'status': if time < devinfo.stattime: return if not value: status = (UNKNOWN, '?') else: status = cache_load(value) devinfo.status = status devinfo.stattime = time devitem.setText(2, str(status[1])) if status[0] not in self.statusIcon: # old or wrong status constant return if self.useicons: devitem.setIcon(0, self.statusIcon[status[0]]) devitem.setForeground(2, foregroundBrush[status[0]]) devitem.setBackground(2, backgroundBrush[status[0]]) else: devitem.setForeground(0, foregroundBrush[BUSY]) devitem.setBackground(0, backgroundBrush[status[0]]) if not devitem.parent().isExpanded(): item = devitem.parent() item.setBackground( 0, backgroundBrush[self._getHighestStatus(item)]) else: devitem.parent().setBackground(0, backgroundBrush[OK]) if ldevname in self._control_dialogs: dlg = self._control_dialogs[ldevname] dlg.statuslabel.setText(status[1]) dlg.statusimage.setPixmap(self.statusIcon[status[0]].pixmap( 16, 16)) setForegroundBrush(dlg.statuslabel, foregroundBrush[status[0]]) setBackgroundBrush(dlg.statuslabel, backgroundBrush[status[0]]) elif subkey == 'fmtstr': if not value: return devinfo.fmtstr = cache_load(value) devitem.setText(1, devinfo.fmtValUnit()) elif subkey == 'unit': if not value: value = "''" devinfo.unit = cache_load(value) devitem.setText(1, devinfo.fmtValUnit()) elif subkey == 'fixed': if not value: value = "''" devinfo.fixed = bool(cache_load(value)) devitem.setForeground(1, valueBrush[devinfo.expired, devinfo.fixed]) if ldevname in self._control_dialogs: dlg = self._control_dialogs[ldevname] if dlg.moveBtn: dlg.moveBtn.setEnabled(not devinfo.fixed) dlg.moveBtn.setText(devinfo.fixed and '(fixed)' or 'Move') elif subkey == 'userlimits': if not value: return value = cache_load(value) if ldevname in self._control_dialogs: dlg = self._control_dialogs[ldevname] dlg.limitMin.setText(str(value[0])) dlg.limitMax.setText(str(value[1])) elif subkey == 'classes': if not value: value = "[]" devinfo.classes = set(cache_load(value)) elif subkey == 'alias': if not value: return if ldevname in self._control_dialogs: dlg = self._control_dialogs[ldevname] dlg._reinit() elif subkey == 'description': devitem.setToolTip(0, cache_load(value or "''")) if subkey in self.param_display.get(ldevname, ()): if not devinfo.params: devinfo.params = self.client.getDeviceParamInfo(devinfo.name) value = devinfo.fmtParam(subkey, cache_load(value)) if subkey not in self._devparamitems.setdefault(ldevname, {}): devitem = self._devitems[ldevname] self._devparamitems[ldevname][subkey] = \ QTreeWidgetItem(devitem, [subkey, value, ''], PARAM_TYPE) devitem.setExpanded(True) else: self._devparamitems[ldevname][subkey].setText(1, value) def on_tree_itemExpanded(self, item): if item.type() == SETUP_TYPE: item.setText(1, '') item.setBackground(0, backgroundBrush[OK]) def _getHighestStatus(self, item): retval = OK for i in range(item.childCount()): lstatus = self._devinfo[item.child(i).text(0).lower()].status[0] if retval < lstatus: retval = lstatus return retval def on_tree_itemCollapsed(self, item): if item.type() == SETUP_TYPE: item.setBackground(0, backgroundBrush[self._getHighestStatus(item)]) if item.representative: item.setText(1, self._devitems[item.representative].text(1)) def on_tree_customContextMenuRequested(self, point): item = self.tree.itemAt(point) if item is None: return if item.type() == DEVICE_TYPE: self._menu_dev = item.text(0) ldevname = self._menu_dev.lower() if 'nicos.core.device.Moveable' in self._devinfo[ldevname].classes and \ not self.client.viewonly: self.devmenu.popup(self.tree.viewport().mapToGlobal(point)) elif 'nicos.core.device.Readable' in self._devinfo[ ldevname].classes: self.devmenu_ro.popup(self.tree.viewport().mapToGlobal(point)) def on_filter_editTextChanged(self, text): for i in range(self.filter.count()): if text == self.filter.itemText(i): rx = QRegExp(self.filter.itemData(i)) break else: rx = QRegExp(text) for i in range(self.tree.topLevelItemCount()): setupitem = self.tree.topLevelItem(i) all_children_hidden = True for j in range(setupitem.childCount()): devitem = setupitem.child(j) if rx.indexIn(devitem.text(0)) == -1: devitem.setHidden(True) else: devitem.setHidden(False) all_children_hidden = False setupitem.setHidden(all_children_hidden) @pyqtSlot() def on_actionShutDown_triggered(self): if self._menu_dev: if self.askQuestion('This will unload the device until the setup ' 'is loaded again. Proceed?'): self.exec_command('RemoveDevice(%s)' % srepr(self._menu_dev), ask_queue=False) @pyqtSlot() def on_actionReset_triggered(self): if self._menu_dev: self.exec_command('reset(%s)' % srepr(self._menu_dev)) @pyqtSlot() def on_actionFix_triggered(self): if self._menu_dev: reason, ok = QInputDialog.getText( self, 'Fix', 'Please enter the reason for fixing %s:' % self._menu_dev) if not ok: return self.exec_command('fix(%s, %r)' % (srepr(self._menu_dev), reason)) @pyqtSlot() def on_actionRelease_triggered(self): if self._menu_dev: self.exec_command('release(%s)' % srepr(self._menu_dev)) @pyqtSlot() def on_actionStop_triggered(self): if self._menu_dev: self.exec_command('stop(%s)' % srepr(self._menu_dev), immediate=True) @pyqtSlot() def on_actionMove_triggered(self): if self._menu_dev: self._open_control_dialog(self._menu_dev) @pyqtSlot() def on_actionHelp_triggered(self): if self._menu_dev: self.client.eval('session.showHelp(session.devices[%r])' % self._menu_dev) @pyqtSlot() def on_actionPlotHistory_triggered(self): if self._menu_dev: self.plot_history(self._menu_dev) def on_tree_itemActivated(self, item, column): if item.type() == DEVICE_TYPE: devname = item.text(0) self._open_control_dialog(devname) elif item.type() == PARAM_TYPE: devname = item.parent().text(0) dlg = self._open_control_dialog(devname) dlg.editParam(item.text(0)) def _open_control_dialog(self, devname): ldevname = devname.lower() if ldevname in self._control_dialogs: dlg = self._control_dialogs[ldevname] if dlg.isVisible(): dlg.activateWindow() return dlg devinfo = self._devinfo[ldevname] item = self._devitems[ldevname] dlg = ControlDialog(self, devname, devinfo, item, self.log, self._show_lowlevel) dlg.closed.connect(self._control_dialog_closed) dlg.rejected.connect(dlg.close) self._control_dialogs[ldevname] = dlg dlg.show() return dlg def _control_dialog_closed(self, ldevname): dlg = self._control_dialogs.pop(ldevname, None) if dlg: dlg.deleteLater() # API shared with ControlDialog def exec_command(self, command, ask_queue=True, immediate=False): if ask_queue and not immediate and self._current_status != 'idle': qwindow = ScriptExecQuestion() result = qwindow.exec_() if result == QMessageBox.Cancel: return elif result == QMessageBox.Apply: immediate = True if immediate: self.client.tell('exec', command) self._exec_reqid = None # no request assigned to this command else: self._exec_reqid = self.client.run(command) def plot_history(self, dev): if self.mainwindow.history_wintype is not None: win = self.mainwindow.createWindow(self.mainwindow.history_wintype) if win: panel = win.getPanel('History viewer') panel.newView(dev) showPanel(panel)
def getMenus(self): if not self.menus: menu1 = QMenu('&Data plot', self) menu1.addAction(self.actionSavePlot) menu1.addAction(self.actionPrint) menu1.addAction(self.actionAttachElog) menu1.addSeparator() menu1.addAction(self.actionResetPlot) menu1.addAction(self.actionAutoDisplay) menu1.addAction(self.actionCombine) menu1.addAction(self.actionClosePlot) menu1.addAction(self.actionDeletePlot) menu1.addSeparator() menu1.addAction(self.actionXAxis) menu1.addAction(self.actionYAxis) menu1.addAction(self.actionNormalized) menu1.addSeparator() menu1.addAction(self.actionUnzoom) menu1.addAction(self.actionLogXScale) menu1.addAction(self.actionLogScale) menu1.addAction(self.actionAutoScale) menu1.addAction(self.actionScaleX) menu1.addAction(self.actionScaleY) menu1.addAction(self.actionLegend) menu1.addAction(self.actionErrors) menu1.addSeparator() menu2 = QMenu('Data &manipulation', self) menu2.addAction(self.actionModifyData) menu2.addSeparator() ag = QActionGroup(menu2) ag.addAction(self.actionFitPeakGaussian) ag.addAction(self.actionFitPeakLorentzian) ag.addAction(self.actionFitPeakPV) ag.addAction(self.actionFitPeakPVII) ag.addAction(self.actionFitTc) ag.addAction(self.actionFitCosine) ag.addAction(self.actionFitSigmoid) ag.addAction(self.actionFitLinear) ag.addAction(self.actionFitExponential) menu2.addAction(self.actionFitPeak) menu2.addAction(self.actionPickInitial) menu2.addAction(self.actionFitPeakGaussian) menu2.addAction(self.actionFitPeakLorentzian) menu2.addAction(self.actionFitPeakPV) menu2.addAction(self.actionFitPeakPVII) menu2.addAction(self.actionFitTc) menu2.addAction(self.actionFitCosine) menu2.addAction(self.actionFitSigmoid) menu2.addAction(self.actionFitLinear) menu2.addAction(self.actionFitExponential) menu2.addSeparator() menu2.addAction(self.actionFitArby) self.menus = [menu1, menu2] return self.menus
def __init__(self, parent, client, options): DevicesPanel._createIcons() Panel.__init__(self, parent, client, options) loadUi(self, self.ui) self.useicons = bool(options.get('icons', True)) self.param_display = {} param_display = options.get('param_display', {}) for (key, value) in param_display.items(): value = [value] if isinstance(value, string_types) else list(value) self.param_display[key.lower()] = value self.tree.header().restoreState(self._headerstate) self.clear() self.devmenu = QMenu(self) self.devmenu.addAction(self.actionMove) self.devmenu.addAction(self.actionStop) self.devmenu.addAction(self.actionReset) self.devmenu.addSeparator() self.devmenu.addAction(self.actionFix) self.devmenu.addAction(self.actionRelease) self.devmenu.addSeparator() if self.mainwindow.history_wintype is not None: self.devmenu.addAction(self.actionPlotHistory) self.devmenu.addSeparator() self.devmenu.addAction(self.actionShutDown) self.devmenu.addAction(self.actionHelp) self.devmenu_ro = QMenu(self) self.devmenu_ro.addAction(self.actionMove) self.devmenu_ro.addAction(self.actionReset) self.devmenu_ro.addSeparator() if self.mainwindow.history_wintype is not None: self.devmenu_ro.addAction(self.actionPlotHistory) self.devmenu_ro.addSeparator() self.devmenu_ro.addAction(self.actionShutDown) self.devmenu_ro.addAction(self.actionHelp) self._menu_dev = None # device for which context menu is shown self._dev2setup = {} self._setupinfo = {} self._control_dialogs = {} self._show_lowlevel = self.mainwindow.expertmode # daemon request ID of last command executed from this panel # (used to display messages from this command) self._current_status = 'idle' self._exec_reqid = None self._error_window = None client.connected.connect(self.on_client_connected) client.disconnected.connect(self.on_client_disconnected) client.cache.connect(self.on_client_cache) client.device.connect(self.on_client_device) client.setup.connect(self.on_client_setup) client.message.connect(self.on_client_message) self.filters = options.get('filters', []) self.filter.addItem('') for text, rx in self.filters: self.filter.addItem('Filter: %s' % text, rx) self.filter.lineEdit().setPlaceholderText('Enter search expression')