Beispiel #1
0
    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)
Beispiel #2
0
 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)
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
    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')
Beispiel #8
0
    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')
Beispiel #9
0
    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()
Beispiel #10
0
    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)
Beispiel #11
0
 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)
Beispiel #12
0
 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())
Beispiel #13
0
 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')
Beispiel #14
0
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)
Beispiel #15
0
 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)
Beispiel #16
0
    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))
Beispiel #17
0
    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')
Beispiel #18
0
    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'
Beispiel #19
0
def decolor_logo(pixmap, color):
    ret_pix = QPixmap(pixmap.size())
    ret_pix.fill(color)
    ret_pix.setMask(pixmap.createMaskFromColor(Qt.transparent))
    return ret_pix
Beispiel #20
0
 def setBackgroundImage(self, filepath):
     self._background_image = QPixmap(filepath)
     self.recalculateBackgroundArea()
Beispiel #21
0
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)