def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, self.ui) pxr = decolor_logo(QPixmap("resources/nicos-logo-high.svg"), Qt.white) self.nicosLabel.setPixmap(pxr) self.runningCmdLabel.setIndent(10) self.idle_style = """QLabel { color: black; background: white; border-radius: 10px;};""" self.runningCmdLabel.setStyleSheet(self.idle_style) # if INSTRUMENT is defined add the logo/name of the instrument instrument = os.getenv('INSTRUMENT') if instrument: instrument = instrument.split('.')[-1] logo = decolor_logo(QPixmap('resources/%s-logo.svg' % instrument), Qt.white) if logo.isNull(): self.instrumentLabel.setText(instrument.upper()) else: self.instrumentLabel.setPixmap( logo.scaledToHeight(self.instrumentLabel.height(), Qt.SmoothTransformation)) else: self.instrumentLabel.setText('') self.current_script = [] client.processing.connect(self.on_client_processing) client.status.connect(self.on_client_status)
def __init__(self, controller, parent=None, state=None): CellItem.__init__(self, controller, parent, state) pixmap1 = QPixmap(path.join(widgetpath, 'focus.png')) pixmap2 = QPixmap(path.join(widgetpath, 'collimator.png')) self.picbtn = PicButton(pixmap1, pixmap2, parent=self) self.widgets.append(self.picbtn) self.set_layout() self.picbtn.clicked.connect(self.on_clicked) self.setValue(state)
def __init__(self, client, parent=None): QAbstractButton.__init__(self, parent) self.flag = False self.pixmap = QPixmap(path.join(widgetpath, 'focus.png')) self.pixmap2 = QPixmap(path.join(widgetpath, 'collimator.png')) NicosWidget.__init__(self) self.setClient(client) self.current = self._client.getDeviceValue('ellcol') self.currmap = self.pixmap if self.current == 'Ell' else self.pixmap2 self.repaint() self.clicked.connect(self.on_clicked)
def __init__(self, parent, client, **settings): QDialog.__init__(self, parent) loadUi(self, 'tools/calculator.ui') self.closeBtn.clicked.connect(self.doclose) self.braggfmlLabel.setPixmap(QPixmap( path.join(path.dirname(__file__), 'calculator_images', 'braggfml.png'))) for fld in bragg_fields: getattr(self, 'chk' + fld).toggled.connect(self.gen_checked(fld)) self._miezesettings = settings.get('mieze', []) if not self._miezesettings: self.tabWidget.removeTab(1) else: self.mzwavelengthInput.textChanged.connect(self.mzcalc) self.mzdistanceInput.textChanged.connect(self.mzcalc) self.mzformulaLabel.setPixmap(QPixmap( path.join(path.dirname(__file__), 'calculator_images', 'miezefml.png'))) self.mztimeTable.setHeaderLabels(['Setting', u'MIEZE time τ']) for setting in self._miezesettings: self.mztimeTable.addTopLevelItem(QTreeWidgetItem([setting, ''])) for fld in neutron_fields: getattr(self, 'prop' + fld).textEdited.connect(self.n_calc) self.presets = DlgPresets('nicoscalctool', [ (self.tabWidget, 0), (self.mzwavelengthInput, '10'), (self.mzdistanceInput, '100'), (self.inputLambda, '4.5'), (self.input2Theta, '0'), (self.inputD, '10'), (self.inputN, '1'), (self.inputQ, '0'), (self.chkLambda, 1), (self.chk2Theta, 0), (self.chkN, 1), (self.chkD, 1), (self.chkQ, 0), (self.chkSampleDet, 1), (self.inputSampleDet, '0'), (self.propL, '1.8'), (self.propK, '3.4907'), (self.propE, '25.2482'), (self.propNy, '6.1050'), (self.propT, '292.9934'), (self.propV, '2197.80'), ]) self.presets.load() self.braggcalc() self.n_calc('') dblval = DoubleValidator(self) for fld in bragg_fields: inputbox = getattr(self, 'input'+fld) inputbox.textChanged.connect(self.braggcalc) inputbox.setValidator(dblval)
def addLogo(self): logoLabel = QLabel() pxr = decolor_logo(QPixmap("resources/logo-icon.png"), Qt.white) logoLabel.setPixmap( pxr.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) self.toolBarMain.insertWidget(self.toolBarMain.actions()[0], logoLabel) nicosLabel = QLabel() pxr = decolor_logo(QPixmap("resources/nicos-logo-high.svg"), Qt.white) nicosLabel.setPixmap( pxr.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) self.toolBarMain.insertWidget(self.toolBarMain.actions()[1], nicosLabel)
def mouseMoveEvent(self, event): if not (event.buttons() & Qt.LeftButton): return if not self._dragStartPos.isNull() and \ self.tabAt(self._dragStartPos) != -1 and \ (event.pos() - self._dragStartPos).manhattanLength() \ < QApplication.startDragDistance(): self._dragInitiated = True if (event.buttons() == Qt.LeftButton) and self._dragInitiated and \ not self.geometry().contains(event.pos()): finishMoveEvent = QMouseEvent(QEvent.MouseMove, event.pos(), Qt.NoButton, Qt.NoButton, Qt.NoModifier) QTabBar.mouseMoveEvent(self, finishMoveEvent) drag = QDrag(self) mimedata = QMimeData() mimedata.setData('action', b'application/tab-detach') drag.setMimeData(mimedata) if QT_VER == 4: pixmap = QPixmap.grabWidget(self.parentWidget().currentWidget()) else: pixmap = self.parentWidget().currentWidget().grab() pixmap = pixmap.scaled(640, 480, Qt.KeepAspectRatio) drag.setPixmap(pixmap) drag.setDragCursor(QPixmap(), Qt.LinkAction) dragged = drag.exec_(Qt.MoveAction) if dragged == Qt.IgnoreAction: # moved outside of tab widget event.accept() self.tabDetached.emit(self.tabAt(self._dragStartPos), QCursor.pos()) elif dragged == Qt.MoveAction: # moved inside of tab widget if not self._dragDroppedPos.isNull(): event.accept() self.tabMoved.emit(self.tabAt(self._dragStartPos), self.tabAt(self._dragDroppedPos)) self._dragDroppedPos = QPoint() else: QTabBar.mouseMoveEvent(self, event)
def _init_toolbar(self): self.statusLabel = QLabel('', self, pixmap=QPixmap(':/disconnected'), margin=5, minimumSize=QSize(30, 10)) self.toolbar = self.toolBarMain self.toolbar.addWidget(self.statusLabel) self.setStatus('disconnected')
def _init_toolbar(self): self.statusLabel = QLabel('', self, pixmap=QPixmap(':/disconnected'), margin=5, minimumSize=QSize(30, 10)) self.statusLabel.setStyleSheet('color: white') self.toolbar = self.toolBarRight self.toolbar.addWidget(self.statusLabel) self.setStatus('disconnected')
def __init__(self, parent, client, options): CustomButtonPanel.__init__(self, parent, client, options) # our content is a simple widget ... self._tableWidget = TableWidget(self) self._tableWidget.setColumnCount(1) self._tableWidget.setHorizontalHeaderLabels(['Sample name']) self._tableWidget.horizontalHeaderItem(0).setTextAlignment( Qt.AlignLeft | Qt.AlignVCenter) self._tableWidget.setSortingEnabled(False) self._tableWidget.setCornerLabel('Position') self.vBoxLayout.insertWidget(0, self._tableWidget) client.connected.connect(self.on_client_connected) client.setup.connect(self.on_client_connected) image = options.get('image', None) # insert the optional image at top... if image: l = QLabel(self) l.setText(image) # insert above scrollArea self.vBoxLayout.insertWidget(0, l, alignment=Qt.AlignHCenter) p = QPixmap() if p.load(findResource(image)): l.setPixmap(p) else: msg = 'Loading of Image %r failed:' % image msg += '\n\nCheck GUI config file for %r' % __file__ self.showError(msg) self._numSamples = int(options.get('positions', 11)) self._tableWidget.setRowCount(self._numSamples) # fill in widgets into grid for pos in range(self._numSamples): self._tableWidget.setCellWidget(pos, 0, QLineEdit('')) self._tableWidget.horizontalHeader().setStretchLastSection(100) # now fill in data self._update_sample_info()
def add_logo(self): logo_label = QLabel() pxr = decolor_logo( QPixmap(path.join(root_path, 'resources', 'logo-icon.png')), Qt.white) logo_label.setPixmap( pxr.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) logo_label.setMargin(5) self.toolBarMain.insertWidget(self.toolBarMain.actions()[0], logo_label) nicos_label = QLabel() pxr = decolor_logo( QPixmap(path.join(root_path, 'resources', 'nicos-logo-high.svg')), Qt.white) nicos_label.setPixmap( pxr.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) self.toolBarMain.insertWidget(self.toolBarMain.actions()[1], nicos_label)
def _refresh(self): status = self.props['ledStatus'] inverted = self.props['ledInverted'] color = self.props['ledColor'] if inverted: status = not status status = status and "on" or "off" ledName = self._ledPatternName.format(color=color, status=status) pixmap = QPixmap(ledName).scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) self.setPixmap(pixmap) self.setAlignment(Qt.AlignCenter)
def setPicture(self): size = QSize(self.props['width'] * self._scale, self.props['height'] * self._scale) if isfile(self._filePath): pixmap = QPixmap(self._filePath) else: pixmap = QPixmap(size) pixmap.fill() if size.isEmpty(): self.piclabel.setPixmap(pixmap) else: self.piclabel.setPixmap(pixmap.scaled(size)) self.piclabel.resize(self.piclabel.sizeHint())
def update_instrument_text(self): instrument = self.client.eval('session.instrument', None) self.instrument_text.setText('Instrument:') if instrument: logo = decolor_logo( QPixmap( path.join(root_path, 'resources', f'{instrument}-logo.svg')), Qt.white) if logo.isNull(): self.instrument_label.setText(instrument.upper()) return self.instrument_label.setPixmap( logo.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation)) else: self.instrument_label.setText('UNKNOWN')
class ElCol(NicosWidget, QAbstractButton): def __init__(self, client, parent=None): QAbstractButton.__init__(self, parent) self.flag = False self.pixmap = QPixmap(path.join(widgetpath, 'focus.png')) self.pixmap2 = QPixmap(path.join(widgetpath, 'collimator.png')) NicosWidget.__init__(self) self.setClient(client) self.current = self._client.getDeviceValue('ellcol') self.currmap = self.pixmap if self.current == 'Ell' else self.pixmap2 self.repaint() self.clicked.connect(self.on_clicked) def paintEvent(self, event): painter = QPainter(self) if self.flag is False: painter.drawPixmap(event.rect(), self.currmap) self.rect = event.rect() else: pixmap = self.pixmap if self.current == 'Ell' else self.pixmap2 painter.drawPixmap(event.rect(), pixmap) self.rect = event.rect() self.currmap = pixmap self.flag = False def registerKeys(self): self.registerDevice('ellcol') def on_devValueChange(self, dev, value, strvalue, unitvalue, expired): self.current = value self.flag = True self.repaint() def sizeHint(self): return self.pixmap.size() def enterEvent(self, event): pass def leaveEvent(self, event): pass def on_clicked(self): data = 'Ell' if self.current == 'Col' else 'Col' self._client.tell('exec', "ellcol.move('%s')" % data)
def setStatus(self, status, exception=False): if status == self.current_status: return if self.client.last_action_at and \ self.current_status == 'running' and \ status in ('idle', 'paused') and \ current_time() - self.client.last_action_at > 20: # show a visual indication of what happened if status == 'paused': msg = 'Script is now paused.' elif exception: msg = 'Script has exited with an error.' else: msg = 'Script has finished.' self.trayIcon.showMessage(self.instrument, msg) self.client.last_action_at = 0 self.current_status = status is_connected = status != 'disconnected' if is_connected: self.actionConnect.setText('Disconnect') self.statusLabel.setText('\u2713 Connected') self.update_instrument_text() self.update_experiment_text() else: self.actionConnect.setText('Connect to server...') self.statusLabel.setText('Disconnected') self.setTitlebar(False) # new status icon pixmap = QPixmap(':/' + status + ('exc' if exception else '')) new_icon = QIcon() new_icon.addPixmap(pixmap, QIcon.Disabled) self.trayIcon.setIcon(new_icon) self.trayIcon.setToolTip('%s status: %s' % (self.instrument, status)) if self.showtrayicon: self.trayIcon.show() if self.promptWindow and status != 'paused': self.promptWindow.close() # propagate to panels for panel in self.panels: panel.updateStatus(status, exception) for window in self.windows.values(): for panel in window.panels: panel.updateStatus(status, exception)
def addInstrument(self): textLabel = QLabel('Instrument:') textLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) textLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) instrumentLabel = QLabel('Unknown') instrumentLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.toolBarMain.addWidget(textLabel) self.toolBarMain.addWidget(instrumentLabel) instrument = os.getenv('INSTRUMENT') if instrument: instrument = instrument.split('.')[-1] logo = decolor_logo(QPixmap('resources/%s-logo.svg' % instrument), Qt.white) if logo.isNull(): instrumentLabel.setText(instrument.upper()) return instrumentLabel.setPixmap( logo.scaledToHeight(self.toolBarMain.height(), Qt.SmoothTransformation))
def __init__(self, log, gui_conf, viewonly=False, tunnel=''): QMainWindow.__init__(self) DlgUtils.__init__(self, 'NICOS') loadUi(self, self.ui) # set app icon in multiple sizes icon = QIcon() icon.addFile(':/appicon') icon.addFile(':/appicon-16') icon.addFile(':/appicon-48') self.setWindowIcon(icon) if tunnel and SSHTunnelForwarder is None: self.showError('You want to establish a connection to NICOS via ' "a SSH tunnel, but the 'sshtunnel' module is not " 'installed. The tunneling feature will disabled.') self.tunnel = tunnel if SSHTunnelForwarder is not None else '' self.tunnelServer = None # hide admin label until we are connected as admin self.adminLabel.hide() # our logger instance self.log = log # window for displaying errors self.errorWindow = None # window for "prompt" event confirmation self.promptWindow = None # debug console window, if opened self.debugConsole = None # log messages sent by the server self.messages = [] # are we in expert mode? (always false on startup) self.expertmode = False # no wrapping at startup self.allowoutputlinewrap = False # set-up the initial connection data self.conndata = ConnectionData(host='localhost', port=1301, user='******', password=None, viewonly=viewonly) # state members self.current_status = None # connect the client's events self.client = NicosGuiClient(self, self.log) self.client.error.connect(self.on_client_error) self.client.broken.connect(self.on_client_broken) self.client.failed.connect(self.on_client_failed) self.client.connected.connect(self.on_client_connected) self.client.disconnected.connect(self.on_client_disconnected) self.client.status.connect(self.on_client_status) self.client.showhelp.connect(self.on_client_showhelp) self.client.clientexec.connect(self.on_client_clientexec) self.client.plugplay.connect(self.on_client_plugplay) self.client.watchdog.connect(self.on_client_watchdog) self.client.prompt.connect(self.on_client_prompt) # data handling setup self.data = DataHandler(self.client) # panel configuration self.gui_conf = gui_conf self.initDataReaders() self.mainwindow = self # determine if there is an editor window type, because we would like to # have a way to open files from a console panel later self.editor_wintype = self.gui_conf.find_panel( ('editor.EditorPanel', 'nicos.clients.gui.panels.editor.EditorPanel')) self.history_wintype = self.gui_conf.find_panel( ('history.HistoryPanel', 'nicos.clients.gui.panels.history.HistoryPanel')) # additional panels self.panels = [] self.splitters = [] self.windowtypes = [] self.windows = {} # setting presets self.instrument = self.gui_conf.name self.createWindowContent() # timer for reconnecting self.reconnectTimer = QTimer(singleShot=True, timeout=self._reconnect) self._reconnect_count = 0 self._reconnect_time = 0 # setup tray icon self.trayIcon = QSystemTrayIcon(self) self.trayIcon.activated.connect(self.on_trayIcon_activated) self.trayMenu = QMenu(self) nameAction = self.trayMenu.addAction(self.instrument) nameAction.setEnabled(False) self.trayMenu.addSeparator() toggleAction = self.trayMenu.addAction('Hide main window') toggleAction.setCheckable(True) toggleAction.triggered[bool].connect( lambda hide: self.setVisible(not hide)) self.trayIcon.setContextMenu(self.trayMenu) self.statusLabel = QLabel('', self, pixmap=QPixmap(':/disconnected'), margin=5, minimumSize=QSize(30, 10)) self.toolBarMain.addWidget(self.statusLabel) # help window self.helpWindow = None # watchdog window self.watchdogWindow = None # plug-n-play notification windows self.pnpWindows = {} # create initial state self.setStatus('disconnected')
def __init__(self, parent, client, options): Panel.__init__(self, parent, client, options) loadUi(self, 'panels/status.ui') self.stopcounting = False self.menus = None self.bar = None self.queueFrame.hide() self.statusLabel.hide() self.pause_color = QColor('#ffdddd') self.idle_color = parent.user_color self.script_queue = ScriptQueue(self.queueFrame, self.queueView) self.current_line = -1 self.current_request = {} self.curlineicon = QIcon(':/currentline') self.errlineicon = QIcon(':/errorline') empty = QPixmap(16, 16) empty.fill(Qt.transparent) self.otherlineicon = QIcon(empty) self.traceView.setItemDelegate(LineDelegate(24, self.traceView)) self.stopcounting = bool(options.get('stopcounting', False)) if self.stopcounting: tooltip = 'Aborts the current executed script' self.actionStop.setToolTip(tooltip) self.actionStop.setText('Abort current script') self.actionStop2.setToolTip(tooltip) self.showETA = bool(options.get('eta', False)) self.etaWidget.hide() client.request.connect(self.on_client_request) client.processing.connect(self.on_client_processing) client.blocked.connect(self.on_client_blocked) client.status.connect(self.on_client_status) client.initstatus.connect(self.on_client_initstatus) client.disconnected.connect(self.on_client_disconnected) client.rearranged.connect(self.on_client_rearranged) client.updated.connect(self.on_client_updated) client.eta.connect(self.on_client_eta) bar = QToolBar('Script control') bar.setObjectName(bar.windowTitle()) # unfortunately it is not wise to put a menu in its own dropdown menu, # so we have to duplicate the actionBreak and actionStop... dropdown1 = QMenu('', self) dropdown1.addAction(self.actionBreak) dropdown1.addAction(self.actionBreakCount) dropdown1.addAction(self.actionFinishEarly) self.actionBreak2.setMenu(dropdown1) dropdown2 = QMenu('', self) dropdown2.addAction(self.actionStop) dropdown2.addAction(self.actionFinish) dropdown2.addAction(self.actionFinishEarlyAndStop) self.actionStop2.setMenu(dropdown2) bar.addAction(self.actionBreak2) bar.addAction(self.actionContinue) bar.addAction(self.actionStop2) bar.addAction(self.actionEmergencyStop) self.bar = bar # self.mainwindow.addToolBar(bar) menu = QMenu('&Script control', self) menu.addAction(self.actionBreak) menu.addAction(self.actionBreakCount) menu.addAction(self.actionContinue) menu.addAction(self.actionFinishEarly) menu.addSeparator() menu.addAction(self.actionStop) menu.addAction(self.actionFinish) menu.addAction(self.actionFinishEarlyAndStop) menu.addSeparator() menu.addAction(self.actionEmergencyStop) self.mainwindow.menuBar().insertMenu( self.mainwindow.menuWindows.menuAction(), menu) self.activeGroup = QActionGroup(self) self.activeGroup.addAction(self.actionBreak) self.activeGroup.addAction(self.actionBreak2) self.activeGroup.addAction(self.actionBreakCount) self.activeGroup.addAction(self.actionContinue) self.activeGroup.addAction(self.actionStop) self.activeGroup.addAction(self.actionStop2) self.activeGroup.addAction(self.actionFinish) self.activeGroup.addAction(self.actionFinishEarly) self.activeGroup.addAction(self.actionFinishEarlyAndStop) self._status = 'idle'
def decolor_logo(pixmap, color): ret_pix = QPixmap(pixmap.size()) ret_pix.fill(color) ret_pix.setMask(pixmap.createMaskFromColor(Qt.transparent)) return ret_pix
def setBackgroundImage(self, filepath): self._background_image = QPixmap(filepath) self.recalculateBackgroundArea()
class MessageView(QTextBrowser): def __init__(self, parent): QTextBrowser.__init__(self, parent) self._messages = [] self._actionlabel = None self._currentuser = None self.setFullTimestamps(False) self._background_image = None self._background_image_area = None self.text_curson_position = QTextCursor.End def setFullTimestamps(self, on): if on: self.formatTime = format_time_full self.formatImportantTime = lambda timeval: ': ' else: self.formatTime = format_time self.formatImportantTime = \ lambda timeval: ' ' + format_time_full(timeval) def setActionLabel(self, label): self._actionlabel = label def clear(self): QTextBrowser.clear(self) self._messages = [] def clearAlmostEverything(self): # Clears all messages, except the last input command with its output. # This is used for clearing output on NewExperiment, because the event # that clears the messages arrives *after* the command has run. msgs = self._messages[:] self.clear() i = 0 for i in range(len(msgs) - 1, -1, -1): if msgs[i][2] == INPUT: break self.addMessages(msgs[i:]) def scrollToBottom(self): bar = self.verticalScrollBar() bar.setValue(bar.maximum()) def getLatest(self, n=5): # Return latest n commands together with warning/error output. inputcount = 0 retmsgs = [] for i in range(len(self._messages) - 1, -1, -1): if self._messages[i][2] == INPUT: retmsgs.append(self._messages[i]) inputcount += 1 if inputcount == n: break elif self._messages[i][2] >= WARNING: retmsgs.append(self._messages[i]) return retmsgs[::-1] def formatMessage(self, message, actions=True): # message is a sequence: # (logger, time, levelno, message, exc_text, reqid) fmt = None levelno = message[2] if message[0] == 'nicos': name = '' else: name = '%-10s: ' % message[0] if message[5] == '0': # simulation result started by console name = '(sim) ' + name if levelno == ACTION: if actions and self._actionlabel: action = message[3].strip() if action: self._actionlabel.setText('Status: ' + action) self._actionlabel.show() else: self._actionlabel.hide() return '', None elif levelno <= DEBUG: text = name + message[3] fmt = grey elif levelno <= INFO: if message[3].startswith(' > '): fmt = QTextCharFormat(bold) fmt.setAnchor(True) command = to_utf8(message[3][4:].strip()) fmt.setAnchorHref('exec:' + urllib.parse.quote(command)) return name + message[3], fmt text = name + message[3] elif levelno == INPUT: m = command_re.match(message[3]) if m: fmt = QTextCharFormat(bold) fmt.setAnchor(True) command = to_utf8(m.group(2)) fmt.setAnchorHref('exec:' + urllib.parse.quote(command)) if m.group(1) != self._currentuser: fmt.setForeground(QBrush(QColor('#0000C0'))) return message[3], fmt m = script_re.match(message[3]) if m: fmt = QTextCharFormat(bold) if m.group(2): command = to_utf8(m.group(2)) fmt.setAnchor(True) fmt.setAnchorHref('edit:' + urllib.parse.quote(command)) if m.group(1) != self._currentuser: fmt.setForeground(QBrush(QColor('#0000C0'))) return message[3], fmt m = update_re.match(message[3]) if m: fmt = QTextCharFormat(bold) if m.group(2): command = to_utf8(m.group(2)) fmt.setAnchor(True) fmt.setAnchorHref('edit:' + urllib.parse.quote(command)) if m.group(1) != self._currentuser: fmt.setForeground(QBrush(QColor('#006090'))) else: fmt.setForeground(QBrush(QColor('#00A000'))) return message[3], fmt return message[3], bold elif levelno <= WARNING: text = levels[levelno] + ': ' + name + message[3] fmt = magenta else: text = levels[levelno] + self.formatImportantTime(message[1]) + \ name + message[3] fmt = redbold if message[4] and fmt: # need to construct a new unique object for this fmt = QTextCharFormat(fmt) # show traceback info on click fmt.setAnchor(True) tbinfo = to_utf8(message[4]) fmt.setAnchorHref('trace:' + urllib.parse.quote(tbinfo)) return text, fmt def addText(self, text, fmt=None): textcursor = self.textCursor() textcursor.movePosition(self.text_curson_position) textcursor.setCharFormat(fmt or std) textcursor.insertText(from_maybe_utf8(text)) def addMessage(self, message): bar = self.verticalScrollBar() prevmax = bar.maximum() prevval = bar.value() text, fmt = self.formatMessage(message) if text: # not for ACTIONs self.addText(self.formatTime(message[1]), grey) self.addText(text, fmt) self._messages.append(message) # only scroll to bottom if we were there already if prevval >= prevmax - 5: self.scrollToBottom() def addMessages(self, messages): textcursor = self.textCursor() textcursor.movePosition(self.text_curson_position) formatter = self.formatMessage for message in messages: text, fmt = formatter(message, actions=False) if text: textcursor.setCharFormat(grey) textcursor.insertText(self.formatTime(message[1])) textcursor.setCharFormat(fmt or std) textcursor.insertText(text) self._messages.append(message) def getOutputString(self): return self.toPlainText() def findNext(self, what, regex=False): cursor = self.textCursor() if regex: rx = QRegExp(what, Qt.CaseInsensitive) newcurs = self.document().find(rx, cursor) else: newcurs = self.document().find(what, cursor) self.setTextCursor(newcurs) return not newcurs.isNull() def occur(self, what, regex=False): content = self.toPlainText().split('\n') if regex: regexp = QRegExp(what, Qt.CaseInsensitive) content = [line for line in content if regexp.indexIn(line) >= 0] else: what = what.lower() content = [line for line in content if what in line.lower()] content = '\n'.join(content) window = QMainWindow(self) window.resize(600, 800) window.setWindowTitle('Lines matching %r' % what) widget = QTextEdit(window) widget.setFont(self.font()) window.setCentralWidget(widget) widget.setText(content) window.show() def setBackgroundImage(self, filepath): self._background_image = QPixmap(filepath) self.recalculateBackgroundArea() def recalculateBackgroundArea(self): if self._background_image is None: return # recalculate the rect to draw the background image into size = self._background_image.size() # scale to viewport size and add some margin size.scale(self.viewport().size() - QSize(30, 30), Qt.KeepAspectRatio) # center background image p = (self.viewport().size() - size) / 2 self._background_image_area = QRect(p.width(), p.height(), size.width(), size.height()) def scrollContentsBy(self, x, y): QTextBrowser.scrollContentsBy(self, x, y) if self._background_image: # repaint viewport on scoll to preserve the background image. # Using 'update' to let qt optimize the process (speed/flickering) self.viewport().update() def enableReverseScrolling(self, value): if value: self.text_curson_position = QTextCursor.Start else: self.text_curson_position = QTextCursor.End def resizeEvent(self, ev): # recalculate the background area only if necessary self.recalculateBackgroundArea() QTextBrowser.resizeEvent(self, ev) def paintEvent(self, ev): if self._background_image: # draw background image if any (should be mostly transparent!) painter = QPainter() painter.begin(self.viewport()) painter.drawPixmap(self._background_image_area, self._background_image) painter.end() QTextBrowser.paintEvent(self, ev)