def focusInEvent(self, event):
     # check for file changes
     try:
         if self.filename and self.file_info:
             if self.file_info.lastModified() != QFileInfo(
                     self.filename).lastModified():
                 self.file_info = QFileInfo(self.filename)
                 result = MessageBox.question(self,
                                              "File changed",
                                              "File was changed, reload?",
                                              buttons=MessageBox.Yes
                                              | MessageBox.No)
                 if result == MessageBox.Yes:
                     f = QFile(self.filename)
                     if f.open(QIODevice.ReadOnly | QIODevice.Text):
                         self.setText(unicode(f.readAll(), "utf-8"))
                         self.document().setModified(False)
                         self.textChanged.emit()
                     else:
                         MessageBox.critical(
                             self, "Error",
                             "Cannot open launch file%s" % self.filename)
     except:
         pass
     QTextEdit.focusInEvent(self, event)
Example #2
0
    def __init__(self, filename, parent=None):
        self.parent = parent
        QTextEdit.__init__(self, parent)
        self.setObjectName(' - '.join(['Editor', filename]))
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_custom_context_menu)
        #        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.setAcceptRichText(False)
        font = QFont()
        font.setFamily("Fixed".decode("utf-8"))
        font.setPointSize(12)
        self.setFont(font)
        self.setLineWrapMode(QTextEdit.NoWrap)
        self.setTabStopWidth(25)
        self.setAcceptRichText(False)
        self.setCursorWidth(2)
        self.setFontFamily("courier new")
        self.setProperty("backgroundVisible", True)
        self.regexp_list = [
            QRegExp("\\binclude\\b"),
            QRegExp("\\btextfile\\b"),
            QRegExp("\\bfile\\b"),
            QRegExp("\\bvalue=.*pkg:\/\/\\b"),
            QRegExp("\\bvalue=.*package:\/\/\\b"),
            QRegExp("\\bvalue=.*\$\(find\\b"),
            QRegExp("\\bargs=.*\$\(find\\b"),
            QRegExp("\\bdefault=.*\$\(find\\b")
        ]
        self.filename = filename
        self.file_info = None
        if self.filename:
            f = QFile(filename)
            if f.open(QIODevice.ReadOnly | QIODevice.Text):
                self.file_info = QFileInfo(filename)
                self.setText(unicode(f.readAll(), "utf-8"))

        self.path = '.'
        # enables drop events
        self.setAcceptDrops(True)
        if filename.endswith('.launch'):
            self.hl = XmlHighlighter(self.document())
            self.cursorPositionChanged.connect(self._document_position_changed)
        else:
            self.hl = YamlHighlighter(self.document())
        # variables for threaded search
        self._search_thread = None
        self._stop = False
 def focusInEvent(self, event):
     # check for file changes
     try:
         if self.filename and self.file_info:
             if self.file_info.lastModified() != QFileInfo(self.filename).lastModified():
                 self.file_info = QFileInfo(self.filename)
                 result = MessageBox.question(self, "File changed", "File was changed, reload?", buttons=MessageBox.Yes | MessageBox.No)
                 if result == MessageBox.Yes:
                     f = QFile(self.filename)
                     if f.open(QIODevice.ReadOnly | QIODevice.Text):
                         self.setText(unicode(f.readAll(), "utf-8"))
                         self.document().setModified(False)
                         self.textChanged.emit()
                     else:
                         MessageBox.critical(self, "Error", "Cannot open launch file%s" % self.filename)
     except:
         pass
     QTextEdit.focusInEvent(self, event)
    def __init__(self, filename, parent=None):
        self.parent = parent
        QTextEdit.__init__(self, parent)
        self.setObjectName(' - '.join(['Editor', filename]))
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_custom_context_menu)
#        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.setAcceptRichText(False)
        font = QFont()
        font.setFamily("Fixed".decode("utf-8"))
        font.setPointSize(12)
        self.setFont(font)
        self.setLineWrapMode(QTextEdit.NoWrap)
        self.setTabStopWidth(25)
        self.setAcceptRichText(False)
        self.setCursorWidth(2)
        self.setFontFamily("courier new")
        self.setProperty("backgroundVisible", True)
        self.regexp_list = [QRegExp("\\binclude\\b"), QRegExp("\\btextfile\\b"),
                            QRegExp("\\bfile\\b"), QRegExp("\\bvalue=.*pkg:\/\/\\b"),
                            QRegExp("\\bvalue=.*package:\/\/\\b"),
                            QRegExp("\\bvalue=.*\$\(find\\b"),
                            QRegExp("\\bargs=.*\$\(find\\b"),
                            QRegExp("\\bdefault=.*\$\(find\\b")]
        self.filename = filename
        self.file_info = None
        if self.filename:
            f = QFile(filename)
            if f.open(QIODevice.ReadOnly | QIODevice.Text):
                self.file_info = QFileInfo(filename)
                self.setText(unicode(f.readAll(), "utf-8"))

        self.path = '.'
        # enables drop events
        self.setAcceptDrops(True)
        if filename.endswith('.launch'):
            self.hl = XmlHighlighter(self.document())
            self.cursorPositionChanged.connect(self._document_position_changed)
        else:
            self.hl = YamlHighlighter(self.document())
        # variables for threaded search
        self._search_thread = None
        self._stop = False
class ScreenWidget(QWidget):
    '''
    Shows the output of a screen.
    '''

    clear_signal = Signal()
    cleared_signal = Signal()

    output = Signal(str)
    output_prefix = Signal(str)
    error_signal = Signal(str)
    auth_signal = Signal(str, str, str)  # host, nodename, user

    def __init__(self,
                 masteruri,
                 screen_name,
                 nodename,
                 user=None,
                 parent=None):
        '''
        Creates the window, connects the signals and init the class.
        '''
        QWidget.__init__(self, parent)
        # load the UI file
        screen_dock_file = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), '..', 'ui',
            'logscreen', 'ScreenWidget.ui')
        loadUi(screen_dock_file, self)
        self.setObjectName("ScreenWidget")
        self.setWindowIcon(nm.settings().icon('crystal_clear_show_io.png'))
        # self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
        self.pauseButton.setIcon(nm.settings().icon('sekkyumu_pause.png'))
        self._valid = True
        self._lock = threading.RLock()
        self.finished = False
        self.qfile = None
        self.thread = None
        self._info = ''
        self._masteruri = ''
        self._nodename = ''
        self._first_fill = True
        self._seek_start = -1
        self._seek_end = -1
        self._pause_read_end = False
        self._ssh_output_file = None
        self._ssh_error_file = None
        self._ssh_input_file = None
        self._on_pause = False
        self._char_format_end = None
        self.loggers.setVisible(False)
        self.loglevelButton.toggled.connect(self.on_toggle_loggers)
        self.logger_handler = None
        # connect to the button signals
        self.output.connect(self._on_output)
        self.output_prefix.connect(self._on_output_prefix)
        self.error_signal.connect(self._on_error)
        self.auth_signal.connect(self.on_request_pw)
        self.clearCloseButton.clicked.connect(self.clear)
        # self.pauseButton.clicked.connect(self.stop)
        self.pauseButton.toggled.connect(self.pause)
        self.clear_signal.connect(self.clear)
        self.textBrowser.verticalScrollBar().valueChanged.connect(
            self.on_scrollbar_position_changed)
        self.textBrowser.verticalScrollBar().rangeChanged.connect(
            self.on_scrollbar_range_changed)
        self.textBrowser.set_reader(self)
        self.tf = TerminalFormats()
        self.hl = ScreenHighlighter(self.textBrowser.document())
        self.searchFrame.setVisible(False)
        self.grepFrame.setVisible(False)
        self.grepLineEdit.textChanged.connect(self.on_grep_changed)
        self._shortcut_search = QShortcut(
            QKeySequence(self.tr("Ctrl+F", "Activate search")), self)
        self._shortcut_search.activated.connect(self.on_search)
        self._shortcut_grep = QShortcut(
            QKeySequence(self.tr("Ctrl+G", "Activate grep")), self)
        self._shortcut_grep.activated.connect(self.on_grep)
        self.searchLineEdit.editingFinished.connect(self.on_search_prev)
        self.searchNextButton.clicked.connect(self.on_search_next)
        self.searchPrevButton.clicked.connect(self.on_search_prev)
        # self.visibilityChanged.connect(self.stop)
        self._connect(masteruri, screen_name, nodename, user)

    def masteruri(self):
        return self._masteruri

    def name(self):
        return self._nodename

    def clear(self):
        '''
        Removes all messages and emit the `cleared_signal`.
        '''
        self.textBrowser.clear()
        self.infoLabel.setText('')
        self.cleared_signal.emit()

    def finish(self):
        self.finished = True
        self.output.disconnect()
        self.output_prefix.disconnect()
        self.close()

    def closeEvent(self, event):
        self.stop()
        QWidget.closeEvent(self, event)

    def hide(self):
        self.stop()
        QWidget.hide(self)

    def close(self):
        self.stop()
        QWidget.close(self)

    def on_search(self):
        self.searchFrame.setVisible(not self.searchFrame.isVisible())
        if self.searchFrame.isVisible():
            self.searchLineEdit.setFocus()
            self.searchLineEdit.selectAll()
        else:
            cursor = self.textBrowser.textCursor()
            cursor.clearSelection()
            self.textBrowser.setTextCursor(cursor)
            self.textBrowser.setFocus()

    def on_search_next(self):
        self._perform_search(forward=True)

    def on_search_prev(self):
        self._perform_search(forward=False)

    def _perform_search(self, forward=False):
        search_str = self.searchLineEdit.text()
        if search_str:
            cursor = self.textBrowser.textCursor()
            if forward:
                search_result = self.textBrowser.document().find(
                    search_str, cursor)
            else:
                search_result = self.textBrowser.document().find(
                    search_str, cursor, QTextDocument.FindBackward)
            if search_result.position() > -1:
                self.textBrowser.setTextCursor(search_result)
                self.searchLabel.setText('')
                # self.searchLabel.setText('%d' % search_result.position())
            else:
                self.searchLabel.setText('no results')
        else:
            self.searchLabel.setText('')

    def on_grep(self):
        self.grepFrame.setVisible(not self.grepFrame.isVisible())
        if self.grepFrame.isVisible():
            self.grepLineEdit.setFocus()
            self.on_grep_changed(self.grepLineEdit.text())
            self.hl.set_grep_text('')
            self.grepLineEdit.selectAll()
        else:
            self.on_grep_changed('')
            self.textBrowser.setFocus()

    def on_grep_changed(self, text):
        self.hl.set_grep_text(text)

    def stop(self):
        '''
        '''
        if self.qfile is not None and self.qfile.isOpen():
            self.qfile.close()
            self.qfile = None
            self._seek_start = -1
            self._seek_end = -1
            self._pause_read_end = False
            # self.clear()
        try:
            self._ssh_output_file.close()
            self._ssh_error_file.close()
            # send Ctrl+C to remote process
            self._ssh_input_file.write('%s\n' % chr(3))
            self._ssh_input_file.close()
        except Exception:
            pass

    def pause(self, state):
        self._on_pause = state

    def valid(self):
        return self._valid

    def _connect(self, masteruri, screen_name, nodename, user=None):
        self._masteruri = masteruri
        if self.qfile is not None and self.qfile.isOpen():
            self.qfile.close()
            self.clear_signal.emit()
        host = get_hostname(masteruri)
        if nm.is_local(host):
            self._nodename = nodename
            if screen_name:
                screen_log = screen.get_logfile(node=nodename)
            else:
                screen_log = screen.get_ros_logfile(node=nodename)
            self.qfile = QFile(screen_log)
            self.setWindowTitle(nodename)
            if self.qfile.open(QIODevice.ReadOnly):
                self._first_fill = True
                self.qfile.seek(self.qfile.size() - 1)
                # self.lread()
                self._info = "END"
                self.thread = threading.Thread(target=self._read_log,
                                               kwargs={"filename": screen_log})
                self.thread.setDaemon(True)
                self.thread.start()
            else:
                self._valid = False
        else:
            self._connect_ssh(host, nodename, user)
        self.logger_handler = LoggerHandler(
            nodename,
            masteruri=masteruri,
            layout=self.scrollAreaWidgetContents.layout())
        self.logger_handler.update()
        return False

    def _read_log(self, filename, lines=80):
        while self.qfile is not None and self.qfile.isOpen():
            with self._lock:
                if self._first_fill:
                    chars_count = self._seek_count_lines(lines)
                    self._seek_start = self.qfile.pos()
                    data = self.qfile.read(chars_count)
                    if sys.version_info > (3, 0):
                        data = data.decode('utf-8')
                    self.output.emit(data)
                    self._seek_end = self.qfile.pos()
                    self._first_fill = False
                else:
                    if self._seek_end != -1:
                        self.qfile.seek(self._seek_end)
                    if (not self._pause_read_end
                            and self.qfile.bytesAvailable()):
                        start = self.qfile.pos()
                        data = self.qfile.readAll().data()
                        if sys.version_info > (3, 0):
                            data = data.decode('utf-8')
                        self.output.emit(data)
                        self._seek_end = self.qfile.pos()
                        self._info = "NEW: %d" % (self._seek_end - start)
            time.sleep(0.25)

    def reverse_read(self, lines=20):
        with self._lock:
            if self.qfile is not None and self.qfile.isOpen():
                if lines == -1:
                    self.qfile.seek(0)
                    chars_count = self._seek_start
                else:
                    self.qfile.seek(self._seek_start)
                    chars_count = self._seek_count_lines(lines)
                self._seek_start = self.qfile.pos()
                data = self.qfile.read(chars_count)
                if sys.version_info > (3, 0):
                    data = data.decode('utf-8')
                self.output_prefix.emit(data)

    def _seek_count_lines(self, lines=20):
        if self.qfile.pos() < 2:
            self.qfile.seek(0)
            return self.qfile.pos()
        count = 0
        chars_count = 2
        line_size = 0
        count_reached = False
        self.qfile.seek(self.qfile.pos() - 2)
        while (not count_reached) and (self.qfile.pos() > 0):
            ch = self.qfile.read(1)
            self.qfile.seek(self.qfile.pos() - 2)
            chars_count += 1
            line_size += 1
            if line_size > 120:
                count += 1
                line_size = 0
            if ch == '\n':
                count += 1
                line_size = 0
                if count >= lines:
                    count_reached = True
        return chars_count + 1

    def _on_output_prefix(self, msg):
        '''
        This text will be prepended
        '''
        if self.finished or self._on_pause:
            return
        if msg:
            cursor = QTextCursor(self.textBrowser.document())
            self.tf.insert_formated(cursor, msg.rstrip())
            self.textBrowser.setTextCursor(cursor)
            self.textBrowser.moveCursor(QTextCursor.Start)
            self._update_info_label()

    def _on_output(self, msg):
        '''
        This text will be appended.
        '''
        if self.finished or self._on_pause:
            return
        if msg:
            at_end = self.textBrowser.verticalScrollBar().value(
            ) > self.textBrowser.verticalScrollBar().maximum() - 20
            cursor_select = self.textBrowser.textCursor()
            # store selection and do not scroll to the appended text
            if not cursor_select.hasSelection():
                cursor_select = None
            cursor = self.textBrowser.textCursor()
            cursor.movePosition(QTextCursor.End)
            if self.hl.has_grep_text():
                # grep new text
                lines = msg.splitlines(True)
                for line in lines:
                    if self.hl.contains_grep_text(line):
                        self._char_format_end = self.tf.insert_formated(
                            cursor, line, char_format=None)
            else:
                self._char_format_end = self.tf.insert_formated(
                    cursor, msg, char_format=self._char_format_end)
            if cursor_select is not None:
                # restore selection
                self.textBrowser.setTextCursor(cursor_select)
            elif at_end:
                self.textBrowser.moveCursor(QTextCursor.End)
            self._update_info_label()
            if not self.finished:
                self.show()

    def on_scrollbar_position_changed(self, value):
        self._update_info_label()

    def on_scrollbar_range_changed(self, min, max):
        self._update_info_label()

    def _on_error(self, msg):
        self.textBrowser.append(msg)
        self._update_info_label('SSH ERROR')

    def _update_info_label(self, info=''):
        info_text = info
        vbar_value = self.textBrowser.verticalScrollBar().value()
        if not info_text:
            if vbar_value == 0:
                if self._seek_start == 0:
                    info_text = 'START'
                else:
                    info_text += "%d %%" % (self._seek_start * 100 /
                                            self._seek_end)
            elif vbar_value == self.textBrowser.verticalScrollBar().maximum():
                info_text = 'END'
            else:
                info_text = "%d / %d" % (
                    vbar_value / 20,
                    self.textBrowser.verticalScrollBar().maximum() / 20)
        self.infoLabel.setText(info_text + '\t%s / %s' %
                               (sizeof_fmt(self._seek_end - self._seek_start),
                                sizeof_fmt(self._seek_end)))

    def _connect_ssh(self, host, nodename, user=None, pw=None):
        try:
            if user is not None:
                self.infoLabel.setText('connecting to %s@%s' % (user, host))
            else:
                self.infoLabel.setText('connecting to %s' % host)
            ok = False
            self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh(
            ).ssh_exec(host, [
                nm.settings().start_remote_script, '--tail_screen_log',
                nodename
            ],
                       user,
                       pw,
                       auto_pw_request=False,
                       get_pty=True)
            if ok:
                thread = threading.Thread(target=self._read_ssh_output,
                                          args=((self.ssh_output_file, )))
                thread.setDaemon(True)
                thread.start()
                thread = threading.Thread(target=self._read_ssh_error,
                                          args=((self.ssh_error_file, )))
                thread.setDaemon(True)
                thread.start()
            elif self.ssh_output_file:
                self.ssh_output_file.close()
                self.ssh_error_file.close()
        except nm.AuthenticationRequest as e:
            self.auth_signal.emit(host, nodename, user)
        except Exception as e:
            self.error_signal.emit('%s\n' % e)

    def on_request_pw(self, host, nodename, user):
        res, user, pw = nm.ssh()._requestPW(user, host)
        if res:
            self._connect_ssh(host, nodename, user, pw)

    def _read_ssh_output(self, output_file):
        while not output_file.closed:
            text = output_file.readline()
            if text:
                self.output.emit(text)

    def _read_ssh_error(self, error_file):
        try:
            while not error_file.closed:
                text = error_file.readline()
                if text:
                    self.error_signal.emit(text)
        except Exception:
            pass

    def on_toggle_loggers(self, state):
        self.loggers.setVisible(state)
        if state:
            self.logger_handler.update()