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 contextMenuOnItem(self, item, pos): if item is None: return # invoked context menu on whitespace if item.type() == ItemTypes.Setup: menu = QMenu(self) addDeviceAction = menu.addAction('Add device...') addDeviceAction.triggered.connect(self.addDevice) menu.popup(pos) elif item.type() == ItemTypes.Directory: menu = QMenu(self) addSetupAction = menu.addAction('Add setup...') addSetupAction.triggered.connect(self.addSetup) menu.popup(pos) elif item.type() == ItemTypes.Device: menu = QMenu(self) removeDeviceAction = menu.addAction('Remove') removeDeviceAction.triggered.connect(self.removeDevice) menu.popup(pos)
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)
class ConsolePanel(Panel): """Provides a console-like interface. The commands can be entered and the output from the NICOS daemon is displayed. Options: * ``hasinput`` (default True) -- if set to False, the input box is hidden and the console is just an output view. * ``hasmenu`` (default True) -- if set to False, the console does not provide its menu (containing actions for the output view such as Save or Print). * ``fulltime`` (default False) -- if set to True, the console shows the full (date + time) timestamp for every line, instead of only for errors and warnings. * ``watermark`` (default empty) -- the path to an image file that should be used as a watermark in the console window. """ panelName = 'Console' ui = 'panels/console.ui' 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 on_outView_customContextMenuRequested(self, point): self.menu.popup(self.outView.mapToGlobal(point)) def setExpertMode(self, expert): if not self.hasinput: self.inputFrame.setVisible(expert) def setViewOnly(self, viewonly): self.commandInput.setVisible(not viewonly) self.promptLabel.setVisible(not viewonly) def loadSettings(self, settings): self.cmdhistory = settings.value('cmdhistory') or [] def saveSettings(self, settings): # only save 100 entries of the history cmdhistory = self.commandInput.history[-100:] settings.setValue('cmdhistory', cmdhistory) def getMenus(self): if self.hasmenu: return [self.menu] return [] def setCustomStyle(self, font, back): self.commandInput.idle_color = back for widget in (self.outView, self.commandInput): widget.setFont(font) setBackgroundColor(widget, back) self.promptLabel.setFont(font) def updateStatus(self, status, exception=False): self.commandInput.setStatus(status) def completeInput(self, fullstring, lastword): try: return self.client.ask('complete', fullstring, lastword, default=[]) except Exception: return [] def on_client_connected(self): self.actionLabel.hide() self.outView._currentuser = self.client.login def on_client_mode(self, mode): self.promptLabel.setText(modePrompt(mode)) def on_client_initstatus(self, state): self.on_client_mode(state['mode']) self.outView.clear() messages = self.client.ask('getmessages', '10000', default=[]) total = len(messages) // 2500 + 1 for _, batch in enumerateWithProgress(chunks(messages, 2500), text='Synchronizing...', parent=self, total=total): self.outView.addMessages(batch) self.outView.scrollToBottom() def on_client_message(self, message): self.outView.addMessage(message) def on_client_simmessage(self, simmessage): if simmessage[-1] == '0': self.outView.addMessage(simmessage) def on_client_experiment(self, data): (_, proptype) = data if proptype == 'user': # only clear history and output when switching TO a user experiment self.commandInput.history = [] # clear everything except the last command with output self.outView.clearAlmostEverything() def on_outView_anchorClicked(self, url): """Called when the user clicks a link in the out view.""" scheme = url.scheme() if scheme == 'exec': # Direct execution is too dangerous. Just insert it in the editor. if self.inputFrame.isVisible(): self.commandInput.setText(url.path()) self.commandInput.setFocus() elif scheme == 'edit': if self.mainwindow.editor_wintype is None: return win = self.mainwindow.createWindow(self.mainwindow.editor_wintype) panel = win.getPanel('User editor') panel.openFile(url.path()) showPanel(panel) elif scheme == 'trace': TracebackDialog(self, self.outView, url.path()).show() else: self.log.warning('Strange anchor in outView: %s', url) @pyqtSlot() def on_actionPrint_triggered(self): printer = QPrinter() printdlg = QPrintDialog(printer, self) printdlg.setOption(QAbstractPrintDialog.PrintSelection) if printdlg.exec_() == QDialog.Accepted: self.outView.print_(printer) @pyqtSlot() def on_actionSave_triggered(self): fn = QFileDialog.getSaveFileName(self, 'Save', '', 'All files (*.*)')[0] if not fn: return try: fn = fn.encode(sys.getfilesystemencoding()) with open(fn, 'w') as f: f.write(self.outView.getOutputString()) except Exception as err: QMessageBox.warning(self, 'Error', 'Writing file failed: %s' % err) @pyqtSlot() def on_actionCopy_triggered(self): self.outView.copy() @pyqtSlot() def on_actionGrep_triggered(self): self.grepPanel.setVisible(True) self.grepText.setFocus() @pyqtSlot() def on_grepClose_clicked(self): self.grepPanel.setVisible(False) self.commandInput.setFocus() self.outView.scrollToBottom() def on_grepText_returnPressed(self): self.on_grepSearch_clicked() def on_grepText_escapePressed(self): self.on_grepClose_clicked() @pyqtSlot() def on_grepSearch_clicked(self): st = self.grepText.text() if not st: return found = self.outView.findNext(st, self.grepRegex.isChecked()) self.grepNoMatch.setVisible(not found) @pyqtSlot() def on_grepOccur_clicked(self): st = self.grepText.text() if not st: return self.outView.occur(st, self.grepRegex.isChecked()) @pyqtSlot(bool) def on_actionAllowLineWrap_triggered(self, checked): self.mainwindow.allowoutputlinewrap = checked if self.mainwindow.allowoutputlinewrap: self.outView.setLineWrapMode(QTextEdit.WidgetWidth) else: self.outView.setLineWrapMode(QTextEdit.NoWrap) def on_commandInput_execRequested(self, script, action): if action == 'queue': self.client.run(script) else: self.client.tell('exec', script) self.commandInput.setText('')