class TextSearchFrame(QDockWidget):
    '''
    A frame to find text in the Editor.
    '''
    search_result_signal = Signal(str, bool, str, int)
    ''' @ivar: A signal emitted after search_threaded was started.
        (search text, found or not, file, position in text)
        for each result a signal will be emitted.
    '''
    replace_signal = Signal(str, str, int, str)
    ''' @ivar: A signal emitted to replace string at given position.
        (search text, file, position in text, replaced by text)
    '''
    def __init__(self, tabwidget, parent=None):
        QDockWidget.__init__(self, "Find", parent)
        self.setObjectName('SearchFrame')
        self.setFeatures(QDockWidget.DockWidgetMovable
                         | QDockWidget.DockWidgetFloatable)
        self._dockwidget = QFrame(self)
        self.vbox_layout = QVBoxLayout(self._dockwidget)
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(1)
        # frame with two rows for find and replace
        find_replace_frame = QFrame(self)
        find_replace_vbox_layout = QVBoxLayout(find_replace_frame)
        find_replace_vbox_layout.setContentsMargins(0, 0, 0, 0)
        find_replace_vbox_layout.setSpacing(1)
        #        find_replace_vbox_layout.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Expanding))
        # create frame with find row
        find_frame = self._create_find_frame()
        find_replace_vbox_layout.addWidget(find_frame)
        rplc_frame = self._create_replace_frame()
        find_replace_vbox_layout.addWidget(rplc_frame)
        # frame for find&replace and search results
        self.vbox_layout.addWidget(find_replace_frame)
        self.vbox_layout.addWidget(self._create_found_frame())
        #        self.vbox_layout.addStretch(2024)
        self.setWidget(self._dockwidget)
        # intern search parameters
        self._tabwidget = tabwidget
        self.current_search_text = ''
        self.search_results = []
        self.search_results_fileset = set()
        self._search_result_index = -1
        self._search_recursive = False
        self._search_thread = None

    def _create_find_frame(self):
        find_frame = QFrame(self)
        find_hbox_layout = QHBoxLayout(find_frame)
        find_hbox_layout.setContentsMargins(0, 0, 0, 0)
        find_hbox_layout.setSpacing(1)
        self.search_field = EnchancedLineEdit(find_frame)
        self.search_field.setPlaceholderText('search text')
        self.search_field.textChanged.connect(self.on_search_text_changed)
        self.search_field.returnPressed.connect(self.on_search)
        find_hbox_layout.addWidget(self.search_field)
        self.search_result_label = QLabel(find_frame)
        self.search_result_label.setText(' ')
        find_hbox_layout.addWidget(self.search_result_label)
        self.find_button_back = QPushButton("<")
        self.find_button_back.setFixedWidth(44)
        self.find_button_back.clicked.connect(self.on_search_back)
        find_hbox_layout.addWidget(self.find_button_back)
        self.find_button = QPushButton(">")
        self.find_button.setDefault(True)
        # self.find_button.setFlat(True)
        self.find_button.setFixedWidth(44)
        self.find_button.clicked.connect(self.on_search)
        find_hbox_layout.addWidget(self.find_button)
        return find_frame

    def _create_replace_frame(self):
        # create frame with replace row
        self.rplc_frame = rplc_frame = QFrame(self)
        rplc_hbox_layout = QHBoxLayout(rplc_frame)
        rplc_hbox_layout.setContentsMargins(0, 0, 0, 0)
        rplc_hbox_layout.setSpacing(1)
        self.replace_field = EnchancedLineEdit(rplc_frame)
        self.replace_field.setPlaceholderText('replace text')
        self.replace_field.returnPressed.connect(self.on_replace)
        rplc_hbox_layout.addWidget(self.replace_field)
        self.replace_result_label = QLabel(rplc_frame)
        self.replace_result_label.setText(' ')
        rplc_hbox_layout.addWidget(self.replace_result_label)
        self.replace_button = replace_button = QPushButton("> &Replace >")
        replace_button.setFixedWidth(90)
        replace_button.clicked.connect(self.on_replace_click)
        rplc_hbox_layout.addWidget(replace_button)
        rplc_frame.setVisible(False)
        return rplc_frame

    def _create_found_frame(self):
        ff_frame = QFrame(self)
        self.found_files_vbox_layout = QVBoxLayout(ff_frame)
        self.found_files_vbox_layout.setContentsMargins(0, 0, 0, 0)
        self.recursive_search_box = QCheckBox("recursive search")
        self.found_files_vbox_layout.addWidget(self.recursive_search_box)
        self.found_files_list = QTreeWidget(ff_frame)
        self.found_files_list.setColumnCount(1)
        self.found_files_list.setFrameStyle(QFrame.StyledPanel)
        self.found_files_list.setHeaderHidden(True)
        self.found_files_list.itemActivated.connect(self.on_itemActivated)
        self.found_files_list.setStyleSheet("QTreeWidget {"
                                            "background-color:transparent;"
                                            "}"
                                            "QTreeWidget::item {"
                                            "background-color:transparent;"
                                            "}"
                                            "QTreeWidget::item:selected {"
                                            "background-color: darkgray;"
                                            "}")
        self.found_files_vbox_layout.addWidget(self.found_files_list)
        self.recursive_search_box.setChecked(False)
        return ff_frame

    def keyPressEvent(self, event):
        '''
        Enable the shortcats for search and replace
        '''
        self.parent().keyPressEvent(event)

    def on_search(self):
        '''
        Initiate the new search or request a next search result.
        '''
        if self.current_search_text != self.search_field.text(
        ) or self._search_recursive != self.recursive_search_box.isChecked():
            # clear current search results
            self._reset()
            self.current_search_text = self.search_field.text()
            if self.current_search_text:
                path_text = {}
                self._wait_for_result = True
                for i in range(self._tabwidget.count()):
                    path_text[self._tabwidget.widget(
                        i).filename] = self._tabwidget.widget(
                            i).document().toPlainText()
                self._search_recursive = self.recursive_search_box.isChecked()
                self._search_thread = TextSearchThread(
                    self.current_search_text,
                    self._tabwidget.currentWidget().filename,
                    path_text=path_text,
                    recursive=self._search_recursive)
                self._search_thread.search_result_signal.connect(
                    self.on_search_result)
                self._search_thread.warning_signal.connect(
                    self.on_warning_result)
                self._search_thread.start()
        elif self.search_results:
            self._check_position()
            if self.search_results:
                if self._search_result_index + 1 >= len(self.search_results):
                    self._search_result_index = -1
                self._search_result_index += 1
                (id, search_text, found, path, index, linenr,
                 line) = self.search_results[self._search_result_index]
                self.search_result_signal.emit(search_text, found, path, index)
                self.replace_button.setEnabled(True)
        self._update_label()

    def on_search_back(self):
        '''
        Slot to handle the search back function.
        '''
        self._check_position(False)
        if self.search_results:
            self._search_result_index -= 1
            if self._search_result_index < 0:
                self._search_result_index = len(self.search_results) - 1
            self._update_label()
            (id, search_text, found, path, index, linenr,
             line) = self.search_results[self._search_result_index]
            self.search_result_signal.emit(search_text, found, path, index)
            self.replace_button.setEnabled(True)

    def _check_position(self, forward=True):
        try:
            # if the position of the textCursor was changed by the user, move the search index
            cur_pos = self._tabwidget.currentWidget().textCursor().position()
            id, st, _f, pa, idx, lnr, ltxt = self.search_results[
                self._search_result_index]
            sear_pos = idx + len(st)
            if cur_pos != sear_pos:
                first_idx = self._get_current_index_for_current_file()
                if first_idx != -1:
                    id, st, _f, pa, idx, lnr, ltxt = self.search_results[
                        first_idx]
                    sear_pos = idx + len(st)
                    while cur_pos > sear_pos and self._tabwidget.currentWidget(
                    ).filename == pa:
                        first_idx += 1
                        id, st, _f, pa, idx, lnr, ltxt = self.search_results[
                            first_idx]
                        sear_pos = idx + len(st)
                    self._search_result_index = first_idx
                    if forward:
                        self._search_result_index -= 1
                else:
                    self._reset(True)
        except:
            pass

    def _get_current_index_for_current_file(self):
        for index in range(len(self.search_results)):
            id, _st, _f, pa, _idx = self.search_results[index]
            if self._tabwidget.currentWidget().filename == pa:
                return index
        return -1

    def on_search_result(self, search_text, found, path, index, linenr, line):
        '''
        Slot to handle the signals for search result. This signals are forwarded used
        search_result_signal.
        '''
        if found and search_text == self.current_search_text:
            id = "%d:%s" % (index, path)
            self.search_results_fileset.add(path)
            item = (search_text, found, path, index)
            if item not in self.search_results:
                self.search_results.append(
                    (id, search_text, found, path, index, linenr, line))
            if self._wait_for_result:
                self._search_result_index += 1
                if index >= self._tabwidget.currentWidget().textCursor(
                ).position() or self._tabwidget.currentWidget(
                ).filename != path:
                    self._wait_for_result = False
                    self.search_result_signal.emit(search_text, found, path,
                                                   index)
                    self.replace_button.setEnabled(True)
            pkg, rpath = package_name(os.path.dirname(path))
            itemstr = '%s [%s]' % (os.path.basename(path), pkg)
            if not self.found_files_list.findItems(itemstr, Qt.MatchExactly):
                list_item = QTreeWidgetItem(self.found_files_list)
                list_item.setText(0, itemstr)
                list_item.setToolTip(0, path)
                self.found_files_list.insertTopLevelItem(0, list_item)
                self.found_files_list.expandAll()
            for i in range(self.found_files_list.topLevelItemCount()):
                top_item = self.found_files_list.topLevelItem(i)
                if top_item.text(0) == itemstr:
                    sub_item_str = "%d: %s" % (linenr, line)
                    list_item2 = QTreeWidgetItem()
                    list_item2.setText(0, sub_item_str)
                    list_item2.setWhatsThis(0, id)
                    top_item.addChild(list_item2)
                #self.found_files_list.setVisible(len(self.search_results_fileset) > 0)
        self._update_label()

    def on_warning_result(self, text):
        rospy.logwarn(text)

    def on_replace_click(self):
        self.on_replace()
        self.on_search()

    def on_replace(self):
        '''
        Emits the replace signal, but only if currently selected text is equal to the searched one.
        '''
        if self.search_results:
            try:
                id, search_text, _found, path, index, linenr, line_text = self.search_results[
                    self._search_result_index]
                cursor = self._tabwidget.currentWidget().textCursor()
                if cursor.selectedText() == search_text:
                    rptxt = self.replace_field.text()
                    for rindex in range(self._search_result_index + 1,
                                        len(self.search_results)):
                        iid, st, _f, pa, idx, lnr, ltxt = self.search_results[
                            rindex]
                        if path == pa:
                            self.search_results.pop(rindex)
                            self.search_results.insert(
                                rindex,
                                (iid, st, _f, pa, idx + len(rptxt) - len(st),
                                 lnr, ltxt))
                        else:
                            break
                    self._remove_search_result(self._search_result_index)
                    self._search_result_index -= 1
                    self.replace_signal.emit(search_text, path, index, rptxt)
                else:
                    self.replace_button.setEnabled(False)
            except:
                import traceback
                print traceback.format_exc()
                pass

    def on_itemActivated(self, item):
        '''
        Go to the results for the selected file entry in the list.
        '''
        splits = item.whatsThis(0).split(':')
        if len(splits) == 2:
            item_index = int(splits[0])
            item_path = splits[1]
            new_search_index = -1
            tmp_index = -1
            search_index = -1
            tmp_search_text = ''
            for id, search_text, found, path, index, linenr, line_text in self.search_results:
                new_search_index += 1
                if item_path == path and item_index == index:
                    self._search_result_index = new_search_index
                    self.search_result_signal.emit(search_text, found, path,
                                                   index)
                    self._update_label()

    def on_search_text_changed(self, _text):
        '''
        Clear search result if the text was changed.
        '''
        self._reset()

    def _update_label(self, clear_label=False):
        '''
        Updates the status label for search results. The info is created from search result lists.
        '''
        msg = ' '
        if self.search_results:
            count_files = len(self.search_results_fileset)
            msg = '%d/%d' % (self._search_result_index + 1,
                             len(self.search_results))
            if count_files > 1:
                msg = '%s(%d)' % (msg, count_files)
        if self._search_thread is not None and self._search_thread.is_alive():
            msg = 'searching..%s' % msg
        elif not msg.strip() and self.current_search_text:
            msg = '0 found'
            self.current_search_text = ''
        if clear_label:
            msg = ' '
        self.search_result_label.setText(msg)
        self.find_button_back.setEnabled(len(self.search_results))
        self._select_current_item_in_box(self._search_result_index)

    def file_changed(self, path):
        '''
        Clears search results if for changed file are some search results are available
        :param path: changed file path
        :type path: str
        '''
        if path in self.search_results_fileset:
            self._reset()

    def set_replace_visible(self, value):
        self.rplc_frame.setVisible(value)
        self.raise_()
        self.activateWindow()
        if value:
            self.replace_field.setFocus()
            self.replace_field.selectAll()
            self.setWindowTitle("Find / Replace")
        else:
            self.setWindowTitle("Find")
            self.search_field.setFocus()

    def is_replace_visible(self):
        return self.rplc_frame.isVisible()

    def _reset(self, force_new_search=False):
        # clear current search results
        if self._search_thread is not None:
            self._search_thread.search_result_signal.disconnect()
            self._search_thread.stop()
            self._search_thread = None
        self.current_search_text = ''
        self.search_results = []
        self.search_results_fileset = set()
        self.found_files_list.clear()
        #        self.found_files_list.setVisible(False)
        self._update_label(True)
        self._search_result_index = -1
        self.find_button_back.setEnabled(False)
        if force_new_search:
            self.on_search()

    def enable(self):
        self.setVisible(True)
        #        self.show()
        self.raise_()
        self.activateWindow()
        self.search_field.setFocus()
        self.search_field.selectAll()

    def _select_current_item_in_box(self, index):
        try:
            (id, search_text, found, path, index, linenr,
             line) = self.search_results[index]
            for topidx in range(self.found_files_list.topLevelItemCount()):
                topitem = self.found_files_list.topLevelItem(topidx)
                for childdx in range(topitem.childCount()):
                    child = topitem.child(childdx)
                    if child.whatsThis(0) == id:
                        child.setSelected(True)
                    elif child.isSelected():
                        child.setSelected(False)
        except:
            pass

    def _remove_search_result(self, index):
        try:
            (id, search_text, found, path, index, linenr,
             line) = self.search_results.pop(index)
            pkg, rpath = package_name(os.path.dirname(path))
            itemstr = '%s [%s]' % (os.path.basename(path), pkg)
            found_items = self.found_files_list.findItems(
                itemstr, Qt.MatchExactly)
            for item in found_items:
                for chi in range(item.childCount()):
                    child = item.child(chi)
                    if child.whatsThis(0) == id:
                        item.removeChild(child)
                        break
            # delete top level item if it is now empty
            for topidx in range(self.found_files_list.topLevelItemCount()):
                if self.found_files_list.topLevelItem(
                        topidx).childCount() == 0:
                    self.found_files_list.takeTopLevelItem(topidx)
                    break
            # create new set with files contain the search text
            new_path_set = set(path for _id, _st, _fd, path, _idx, lnr, lntxt
                               in self.search_results)
            self.search_results_fileset = new_path_set
#            self.found_files_list.setVisible(len(self.search_results_fileset) > 0)
        except:
            import traceback
            print traceback.format_exc()
Пример #2
0
class Top(Plugin):

    NODE_FIELDS   = [             'pid', 'get_cpu_percent', 'get_memory_percent', 'get_num_threads']
    OUT_FIELDS    = ['node_name', 'pid', 'cpu_percent',     'memory_percent',     'num_threads'    ]
    FORMAT_STRS   = ['%s',        '%s',  '%0.2f',           '%0.2f',              '%s'             ]
    NODE_LABELS   = ['Node',      'PID', 'CPU %',           'Mem %',              'Num Threads'    ]
    SORT_TYPE     = [str,         str,   float,             float,                float            ]
    TOOLTIPS      = {
        0: ('cmdline', lambda x: '\n'.join(textwrap.wrap(' '.join(x)))),
        3: ('memory_info', lambda x: ('Resident: %0.2f MiB, Virtual: %0.2f MiB' % (x[0]/2**20, x[1]/2**20)))
    }

    _node_info = NodeInfo()

    name_filter = re.compile('')

    def __init__(self, context):
        super(Top, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('Top')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        # if not args.quiet:
        #     print 'arguments: ', args
        #     print 'unknowns: ', unknowns

        self._selected_node = ''
        self._selected_node_lock = RLock()

        # Setup the toolbar
        self._toolbar = QToolBar()
        self._filter_box = QLineEdit()
        self._regex_box  = QCheckBox()
        self._regex_box.setText('regex')
        self._toolbar.addWidget(QLabel('Filter'))
        self._toolbar.addWidget(self._filter_box)
        self._toolbar.addWidget(self._regex_box)

        self._filter_box.returnPressed.connect(self.update_filter)
        self._regex_box.stateChanged.connect(self.update_filter)

        # Create a container widget and give it a layout
        self._container = QWidget()
        self._container.setWindowTitle('Process Monitor')
        self._layout    = QVBoxLayout()
        self._container.setLayout(self._layout)

        self._layout.addWidget(self._toolbar)

        # Create the table widget
        self._table_widget = QTreeWidget()
        self._table_widget.setObjectName('TopTable')
        self._table_widget.setColumnCount(len(self.NODE_LABELS))
        self._table_widget.setHeaderLabels(self.NODE_LABELS)
        self._table_widget.itemClicked.connect(self._tableItemClicked)
        self._table_widget.setSortingEnabled(True)
        self._table_widget.setAlternatingRowColors(True)

        self._layout.addWidget(self._table_widget)
        context.add_widget(self._container)

        # Add a button for killing nodes
        self._kill_button = QPushButton('Kill Node')
        self._layout.addWidget(self._kill_button)
        self._kill_button.clicked.connect(self._kill_node)

        # Update twice since the first cpu% lookup will always return 0
        self.update_table()
        self.update_table()

        self._table_widget.resizeColumnToContents(0)

        # Start a timer to trigger updates
        self._update_timer = QTimer()
        self._update_timer.setInterval(1000)
        self._update_timer.timeout.connect(self.update_table)
        self._update_timer.start()

    def _tableItemClicked(self, item, column):
        with self._selected_node_lock:
            self._selected_node = item.text(0)

    def update_filter(self, *args):
        if self._regex_box.isChecked():
            expr = self._filter_box.text()
        else:
            expr = re.escape(self._filter_box.text())
        self.name_filter = re.compile(expr)
        self.update_table()

    def _kill_node(self):
        self._node_info.kill_node(self._selected_node)

    def update_one_item(self, row, info):
        twi = TopWidgetItem()
        for col, field in enumerate(self.OUT_FIELDS):
            val = info[field]
            twi.setText(col, self.FORMAT_STRS[col] % val)
        self._table_widget.insertTopLevelItem(row, twi)

        for col, (key, func) in self.TOOLTIPS.iteritems():
            twi.setToolTip(col, func(info[key]))

        with self._selected_node_lock:
            if twi.text(0) == self._selected_node:
                twi.setSelected(True)

        self._table_widget.setItemHidden(twi, len(self.name_filter.findall(info['node_name'])) == 0)

    def update_table(self):
        self._table_widget.clear()
        infos = self._node_info.get_all_node_fields(self.NODE_FIELDS)
        for nx, info in enumerate(infos):
            self.update_one_item(nx, info)

    def shutdown_plugin(self):
        self._update_timer.stop()

    def save_settings(self, plugin_settings, instance_settings):        
        instance_settings.set_value('filter_text', self._filter_box.text())
        instance_settings.set_value('is_regex', int(self._regex_box.checkState()))

    def restore_settings(self, plugin_settings, instance_settings):
        self._filter_box.setText(instance_settings.value('filter_text'))
        is_regex_int = instance_settings.value('is_regex')
        if is_regex_int:
            self._regex_box.setCheckState(Qt.CheckState(is_regex_int))
        else:
            self._regex_box.setCheckState(Qt.CheckState(0))
        self.update_filter()
Пример #3
0
class TextSearchFrame(QDockWidget):
    '''
    A frame to find text in the Editor.
    '''
    search_result_signal = Signal(str, bool, str, int)
    ''' @ivar: A signal emitted after search_threaded was started.
        (search text, found or not, file, position in text)
        for each result a signal will be emitted.
    '''
    replace_signal = Signal(str, str, int, str)
    ''' @ivar: A signal emitted to replace string at given position.
        (search text, file, position in text, replaced by text)
    '''

    def __init__(self, tabwidget, parent=None):
        QDockWidget.__init__(self, "Find", parent)
        self.setObjectName('SearchFrame')
        self.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
        self._dockwidget = QFrame(self)
        self.vbox_layout = QVBoxLayout(self._dockwidget)
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(1)
        # frame with two rows for find and replace
        find_replace_frame = QFrame(self)
        find_replace_vbox_layout = QVBoxLayout(find_replace_frame)
        find_replace_vbox_layout.setContentsMargins(0, 0, 0, 0)
        find_replace_vbox_layout.setSpacing(1)
#        find_replace_vbox_layout.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Expanding))
        # create frame with find row
        find_frame = self._create_find_frame()
        find_replace_vbox_layout.addWidget(find_frame)
        rplc_frame = self._create_replace_frame()
        find_replace_vbox_layout.addWidget(rplc_frame)
        # frame for find&replace and search results
        self.vbox_layout.addWidget(find_replace_frame)
        self.vbox_layout.addWidget(self._create_found_frame())
#        self.vbox_layout.addStretch(2024)
        self.setWidget(self._dockwidget)
        # intern search parameters
        self._tabwidget = tabwidget
        self.current_search_text = ''
        self.search_results = []
        self.search_results_fileset = set()
        self._search_result_index = -1
        self._search_recursive = False
        self._search_thread = None

    def _create_find_frame(self):
        find_frame = QFrame(self)
        find_hbox_layout = QHBoxLayout(find_frame)
        find_hbox_layout.setContentsMargins(0, 0, 0, 0)
        find_hbox_layout.setSpacing(1)
        self.search_field = EnchancedLineEdit(find_frame)
        self.search_field.setPlaceholderText('search text')
        self.search_field.textChanged.connect(self.on_search_text_changed)
        self.search_field.returnPressed.connect(self.on_search)
        find_hbox_layout.addWidget(self.search_field)
        self.search_result_label = QLabel(find_frame)
        self.search_result_label.setText(' ')
        find_hbox_layout.addWidget(self.search_result_label)
        self.find_button_back = QPushButton("<")
        self.find_button_back.setFixedWidth(44)
        self.find_button_back.clicked.connect(self.on_search_back)
        find_hbox_layout.addWidget(self.find_button_back)
        self.find_button = QPushButton(">")
        self.find_button.setDefault(True)
        # self.find_button.setFlat(True)
        self.find_button.setFixedWidth(44)
        self.find_button.clicked.connect(self.on_search)
        find_hbox_layout.addWidget(self.find_button)
        return find_frame

    def _create_replace_frame(self):
        # create frame with replace row
        self.rplc_frame = rplc_frame = QFrame(self)
        rplc_hbox_layout = QHBoxLayout(rplc_frame)
        rplc_hbox_layout.setContentsMargins(0, 0, 0, 0)
        rplc_hbox_layout.setSpacing(1)
        self.replace_field = EnchancedLineEdit(rplc_frame)
        self.replace_field.setPlaceholderText('replace text')
        self.replace_field.returnPressed.connect(self.on_replace)
        rplc_hbox_layout.addWidget(self.replace_field)
        self.replace_result_label = QLabel(rplc_frame)
        self.replace_result_label.setText(' ')
        rplc_hbox_layout.addWidget(self.replace_result_label)
        self.replace_button = replace_button = QPushButton("> &Replace >")
        replace_button.setFixedWidth(90)
        replace_button.clicked.connect(self.on_replace_click)
        rplc_hbox_layout.addWidget(replace_button)
        rplc_frame.setVisible(False)
        return rplc_frame

    def _create_found_frame(self):
        ff_frame = QFrame(self)
        self.found_files_vbox_layout = QVBoxLayout(ff_frame)
        self.found_files_vbox_layout.setContentsMargins(0, 0, 0, 0)
        self.recursive_search_box = QCheckBox("recursive search")
        self.found_files_vbox_layout.addWidget(self.recursive_search_box)
        self.found_files_list = QTreeWidget(ff_frame)
        self.found_files_list.setColumnCount(1)
        self.found_files_list.setFrameStyle(QFrame.StyledPanel)
        self.found_files_list.setHeaderHidden(True)
        self.found_files_list.itemActivated.connect(self.on_itemActivated)
        self.found_files_list.setStyleSheet(
            "QTreeWidget {"
            "background-color:transparent;"
            "}"
            "QTreeWidget::item {"
            "background-color:transparent;"
            "}"
            "QTreeWidget::item:selected {"
            "background-color: darkgray;"
            "}")
        self.found_files_vbox_layout.addWidget(self.found_files_list)
        self.recursive_search_box.setChecked(False)
        return ff_frame

    def keyPressEvent(self, event):
        '''
        Enable the shortcats for search and replace
        '''
        self.parent().keyPressEvent(event)

    def on_search(self):
        '''
        Initiate the new search or request a next search result.
        '''
        if self.current_search_text != self.search_field.text() or self._search_recursive != self.recursive_search_box.isChecked():
            # clear current search results
            self._reset()
            self.current_search_text = self.search_field.text()
            if self.current_search_text:
                path_text = {}
                self._wait_for_result = True
                for i in range(self._tabwidget.count()):
                    path_text[self._tabwidget.widget(i).filename] = self._tabwidget.widget(i).document().toPlainText()
                self._search_recursive = self.recursive_search_box.isChecked()
                self._search_thread = TextSearchThread(self.current_search_text, self._tabwidget.currentWidget().filename, path_text=path_text, recursive=self._search_recursive)
                self._search_thread.search_result_signal.connect(self.on_search_result)
                self._search_thread.warning_signal.connect(self.on_warning_result)
                self._search_thread.start()
        elif self.search_results:
            self._check_position()
            if self.search_results:
                if self._search_result_index + 1 >= len(self.search_results):
                    self._search_result_index = -1
                self._search_result_index += 1
                (id, search_text, found, path, index, linenr, line) = self.search_results[self._search_result_index]
                self.search_result_signal.emit(search_text, found, path, index)
                self.replace_button.setEnabled(True)
        self._update_label()

    def on_search_back(self):
        '''
        Slot to handle the search back function.
        '''
        self._check_position(False)
        if self.search_results:
            self._search_result_index -= 1
            if self._search_result_index < 0:
                self._search_result_index = len(self.search_results) - 1
            self._update_label()
            (id, search_text, found, path, index, linenr, line) = self.search_results[self._search_result_index]
            self.search_result_signal.emit(search_text, found, path, index)
            self.replace_button.setEnabled(True)

    def _check_position(self, forward=True):
        try:
            # if the position of the textCursor was changed by the user, move the search index
            cur_pos = self._tabwidget.currentWidget().textCursor().position()
            id, st, _f, pa, idx, lnr, ltxt = self.search_results[self._search_result_index]
            sear_pos = idx + len(st)
            if cur_pos != sear_pos:
                first_idx = self._get_current_index_for_current_file()
                if first_idx != -1:
                    id, st, _f, pa, idx, lnr, ltxt = self.search_results[first_idx]
                    sear_pos = idx + len(st)
                    while cur_pos > sear_pos and self._tabwidget.currentWidget().filename == pa:
                        first_idx += 1
                        id, st, _f, pa, idx, lnr, ltxt = self.search_results[first_idx]
                        sear_pos = idx + len(st)
                    self._search_result_index = first_idx
                    if forward:
                        self._search_result_index -= 1
                else:
                    self._reset(True)
        except:
            pass

    def _get_current_index_for_current_file(self):
        for index in range(len(self.search_results)):
            id, _st, _f, pa, _idx = self.search_results[index]
            if self._tabwidget.currentWidget().filename == pa:
                return index
        return -1

    def on_search_result(self, search_text, found, path, index, linenr, line):
        '''
        Slot to handle the signals for search result. This signals are forwarded used
        search_result_signal.
        '''
        if found and search_text == self.current_search_text:
            id = "%d:%s" % (index, path)
            self.search_results_fileset.add(path)
            item = (search_text, found, path, index)
            if item not in self.search_results:
                self.search_results.append((id, search_text, found, path, index, linenr, line))
            if self._wait_for_result:
                self._search_result_index += 1
                if index >= self._tabwidget.currentWidget().textCursor().position() or self._tabwidget.currentWidget().filename != path:
                    self._wait_for_result = False
                    self.search_result_signal.emit(search_text, found, path, index)
                    self.replace_button.setEnabled(True)
            pkg, rpath = package_name(os.path.dirname(path))
            itemstr = '%s [%s]' % (os.path.basename(path), pkg)
            if not self.found_files_list.findItems(itemstr, Qt.MatchExactly):
                list_item = QTreeWidgetItem(self.found_files_list)
                list_item.setText(0, itemstr)
                list_item.setToolTip(0, path)
                self.found_files_list.insertTopLevelItem(0, list_item)
                self.found_files_list.expandAll()
            for i in range(self.found_files_list.topLevelItemCount()):
                top_item = self.found_files_list.topLevelItem(i)
                if top_item.text(0) == itemstr:
                    sub_item_str = "%d: %s" % (linenr, line)
                    list_item2 = QTreeWidgetItem()
                    list_item2.setText(0, sub_item_str)
                    list_item2.setWhatsThis(0, id)
                    top_item.addChild(list_item2)
                #self.found_files_list.setVisible(len(self.search_results_fileset) > 0)
        self._update_label()

    def on_warning_result(self, text):
        rospy.logwarn(text)

    def on_replace_click(self):
        self.on_replace()
        self.on_search()

    def on_replace(self):
        '''
        Emits the replace signal, but only if currently selected text is equal to the searched one.
        '''
        if self.search_results:
            try:
                id, search_text, _found, path, index, linenr, line_text = self.search_results[self._search_result_index]
                cursor = self._tabwidget.currentWidget().textCursor()
                if cursor.selectedText() == search_text:
                    rptxt = self.replace_field.text()
                    for rindex in range(self._search_result_index + 1, len(self.search_results)):
                        iid, st, _f, pa, idx, lnr, ltxt = self.search_results[rindex]
                        if path == pa:
                            self.search_results.pop(rindex)
                            self.search_results.insert(rindex, (iid, st, _f, pa, idx + len(rptxt) - len(st), lnr, ltxt))
                        else:
                            break
                    self._remove_search_result(self._search_result_index)
                    self._search_result_index -= 1
                    self.replace_signal.emit(search_text, path, index, rptxt)
                else:
                    self.replace_button.setEnabled(False)
            except:
                import traceback
                print traceback.format_exc()
                pass

    def on_itemActivated(self, item):
        '''
        Go to the results for the selected file entry in the list.
        '''
        splits = item.whatsThis(0).split(':')
        if len(splits) == 2:
            item_index = int(splits[0])
            item_path = splits[1]
            new_search_index = -1
            tmp_index = -1
            search_index = -1
            tmp_search_text = ''
            for id, search_text, found, path, index, linenr, line_text in self.search_results:
                new_search_index += 1
                if item_path == path and item_index == index:
                    self._search_result_index = new_search_index
                    self.search_result_signal.emit(search_text, found, path, index)
                    self._update_label()

    def on_search_text_changed(self, _text):
        '''
        Clear search result if the text was changed.
        '''
        self._reset()

    def _update_label(self, clear_label=False):
        '''
        Updates the status label for search results. The info is created from search result lists.
        '''
        msg = ' '
        if self.search_results:
            count_files = len(self.search_results_fileset)
            msg = '%d/%d' % (self._search_result_index + 1, len(self.search_results))
            if count_files > 1:
                msg = '%s(%d)' % (msg, count_files)
        if self._search_thread is not None and self._search_thread.is_alive():
            msg = 'searching..%s' % msg
        elif not msg.strip() and self.current_search_text:
            msg = '0 found'
            self.current_search_text = ''
        if clear_label:
            msg = ' '
        self.search_result_label.setText(msg)
        self.find_button_back.setEnabled(len(self.search_results))
        self._select_current_item_in_box(self._search_result_index)

    def file_changed(self, path):
        '''
        Clears search results if for changed file are some search results are available
        :param path: changed file path
        :type path: str
        '''
        if path in self.search_results_fileset:
            self._reset()

    def set_replace_visible(self, value):
        self.rplc_frame.setVisible(value)
        self.raise_()
        self.activateWindow()
        if value:
            self.replace_field.setFocus()
            self.replace_field.selectAll()
            self.setWindowTitle("Find / Replace")
        else:
            self.setWindowTitle("Find")
            self.search_field.setFocus()

    def is_replace_visible(self):
        return self.rplc_frame.isVisible()

    def _reset(self, force_new_search=False):
        # clear current search results
        if self._search_thread is not None:
            self._search_thread.search_result_signal.disconnect()
            self._search_thread.stop()
            self._search_thread = None
        self.current_search_text = ''
        self.search_results = []
        self.search_results_fileset = set()
        self.found_files_list.clear()
#        self.found_files_list.setVisible(False)
        self._update_label(True)
        self._search_result_index = -1
        self.find_button_back.setEnabled(False)
        if force_new_search:
            self.on_search()

    def enable(self):
        self.setVisible(True)
#        self.show()
        self.raise_()
        self.activateWindow()
        self.search_field.setFocus()
        self.search_field.selectAll()

    def _select_current_item_in_box(self, index):
        try:
            (id, search_text, found, path, index, linenr, line) = self.search_results[index]
            for topidx in range(self.found_files_list.topLevelItemCount()):
                topitem = self.found_files_list.topLevelItem(topidx)
                for childdx in range(topitem.childCount()):
                    child = topitem.child(childdx)
                    if child.whatsThis(0) == id:
                        child.setSelected(True)
                    elif child.isSelected():
                        child.setSelected(False)
        except:
            pass

    def _remove_search_result(self, index):
        try:
            (id, search_text, found, path, index, linenr, line) = self.search_results.pop(index)
            pkg, rpath = package_name(os.path.dirname(path))
            itemstr = '%s [%s]' % (os.path.basename(path), pkg)
            found_items = self.found_files_list.findItems(itemstr, Qt.MatchExactly)
            for item in found_items:
                for chi in range(item.childCount()):
                    child = item.child(chi)
                    if child.whatsThis(0) == id:
                        item.removeChild(child)
                        break
            # delete top level item if it is now empty
            for topidx in range(self.found_files_list.topLevelItemCount()):
                if self.found_files_list.topLevelItem(topidx).childCount() == 0:
                    self.found_files_list.takeTopLevelItem(topidx)
                    break
            # create new set with files contain the search text
            new_path_set = set(path for _id, _st, _fd, path, _idx, lnr, lntxt in self.search_results)
            self.search_results_fileset = new_path_set
#            self.found_files_list.setVisible(len(self.search_results_fileset) > 0)
        except:
            import traceback
            print traceback.format_exc()
Пример #4
0
class RobotMonitor(QWidget):
    sig_err = pyqtSignal(str)
    sig_warn = pyqtSignal(str)
    sig_clear = pyqtSignal()

    def __init__(self, topic):
        super(RobotMonitor, self).__init__()
        self.setObjectName('Robot Monitor')

        self.top_items = []
        layout = QVBoxLayout()

        self.err = QTreeWidget()
        self.err.setHeaderLabel("Errors")
        self.warn = QTreeWidget()
        self.warn.setHeaderLabel("Warnings")

        self.sig_clear.connect(self.clear)
        self.sig_err.connect(self.disp_err)
        self.sig_warn.connect(self.disp_warn)

        self.comp = QTreeWidget()
        self.comp.setHeaderLabel("All")
        self.comp.itemDoubleClicked.connect(self.tree_clicked)

        self.time = TimelineWidget(self)

        layout.addWidget(self.err)
        layout.addWidget(self.warn)
        layout.addWidget(self.comp, 1)
        layout.addWidget(self.time, 0)

        self.setLayout(layout)

        self.paused = False

        self.topic = topic
        self.sub = rospy.Subscriber(self.topic, DiagnosticArray, self.cb)
        self.setWindowTitle('Robot Monitor')

    def cb(self, msg):
        if not self.paused:
            self.sig_clear.emit()
            self.update_tree(msg)
            self.update_we(msg)
            self.time.add_message(msg)

    def tree_clicked(self, item, yes):
        item.on_click()

    def update_tree(self, msg):
        #Update the tree from the bottom

        names = [get_nice_name(k.name) for k in self.top_items]
        add = []
        for i in self._top_level(msg):
            name = get_nice_name(i.name)
            if name in names:
                self.top_items[names.index(name)].update(i, msg)
            else:
                nw = StatusItem(i)
                nw.update(i, msg)
                self.top_items.append(nw)
                add.append(nw)
        
        self.comp.addTopLevelItems(add)
        
    def _top_level(self, msg):
        ret = []
        for i in msg.status:
            if len(i.name.split('/')) == 2:
                ret.append(i)
        
        return ret

    def update_we(self, msg):
        """Update the warning and error boxes"""
        for status in msg.status:
            if status.level == status.WARN:
                txt = "%s : %s"%(status.name, status.message)
                self.sig_warn.emit(txt)
            elif status.level == status.ERROR:
                txt = "%s : %s"%(status.name, status.message)
                self.sig_err.emit(txt)

    def pause(self, msg):
        self.paused = True
        self.sig_clear.emit()
        self.update_we(msg)
        self.update_tree(msg)

    def unpause(self):
        self.paused = False

    def clear(self):
        self.err.clear()
        self.warn.clear()

    def disp_err(self, msg):
        i = QTreeWidgetItem()
        i.setText(0, msg)
        self.err.addTopLevelItem(i)
        
    def disp_warn(self,msg):
        i = QTreeWidgetItem()
        i.setText(0, msg)
        self.warn.addTopLevelItem(i)

    def close(self):
        if self.sub:
            self.sub.unregister()
            self.sub = None
Пример #5
0
class Top(Plugin):

    NODE_FIELDS = [
        'pid', 'get_cpu_percent', 'get_memory_percent', 'get_num_threads'
    ]
    OUT_FIELDS = [
        'node_name', 'pid', 'cpu_percent', 'memory_percent', 'num_threads'
    ]
    FORMAT_STRS = ['%s', '%s', '%0.2f', '%0.2f', '%s']
    NODE_LABELS = ['Node', 'PID', 'CPU %', 'Mem %', 'Num Threads']
    SORT_TYPE = [str, str, float, float, float]
    TOOLTIPS = {
        0: ('cmdline', lambda x: '\n'.join(textwrap.wrap(' '.join(x)))),
        3: ('memory_info', lambda x:
            ('Resident: %0.2f MiB, Virtual: %0.2f MiB' %
             (x[0] / 2**20, x[1] / 2**20)))
    }

    _node_info = NodeInfo()

    name_filter = re.compile('')

    def __init__(self, context):
        super(Top, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('Top')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        # if not args.quiet:
        #     print 'arguments: ', args
        #     print 'unknowns: ', unknowns

        self._selected_node = ''
        self._selected_node_lock = RLock()

        # Setup the toolbar
        self._toolbar = QToolBar()
        self._filter_box = QLineEdit()
        self._regex_box = QCheckBox()
        self._regex_box.setText('regex')
        self._toolbar.addWidget(QLabel('Filter'))
        self._toolbar.addWidget(self._filter_box)
        self._toolbar.addWidget(self._regex_box)

        self._filter_box.returnPressed.connect(self.update_filter)
        self._regex_box.stateChanged.connect(self.update_filter)

        # Create a container widget and give it a layout
        self._container = QWidget()
        self._container.setWindowTitle('Process Monitor')
        self._layout = QVBoxLayout()
        self._container.setLayout(self._layout)

        self._layout.addWidget(self._toolbar)

        # Create the table widget
        self._table_widget = QTreeWidget()
        self._table_widget.setObjectName('TopTable')
        self._table_widget.setColumnCount(len(self.NODE_LABELS))
        self._table_widget.setHeaderLabels(self.NODE_LABELS)
        self._table_widget.itemClicked.connect(self._tableItemClicked)
        self._table_widget.setSortingEnabled(True)
        self._table_widget.setAlternatingRowColors(True)

        self._layout.addWidget(self._table_widget)
        context.add_widget(self._container)

        # Add a button for killing nodes
        self._kill_button = QPushButton('Kill Node')
        self._layout.addWidget(self._kill_button)
        self._kill_button.clicked.connect(self._kill_node)

        # Update twice since the first cpu% lookup will always return 0
        self.update_table()
        self.update_table()

        self._table_widget.resizeColumnToContents(0)

        # Start a timer to trigger updates
        self._update_timer = QTimer()
        self._update_timer.setInterval(1000)
        self._update_timer.timeout.connect(self.update_table)
        self._update_timer.start()

    def _tableItemClicked(self, item, column):
        with self._selected_node_lock:
            self._selected_node = item.text(0)

    def update_filter(self, *args):
        if self._regex_box.isChecked():
            expr = self._filter_box.text()
        else:
            expr = re.escape(self._filter_box.text())
        self.name_filter = re.compile(expr)
        self.update_table()

    def _kill_node(self):
        self._node_info.kill_node(self._selected_node)

    def update_one_item(self, row, info):
        twi = TopWidgetItem()
        for col, field in enumerate(self.OUT_FIELDS):
            val = info[field]
            twi.setText(col, self.FORMAT_STRS[col] % val)
        self._table_widget.insertTopLevelItem(row, twi)

        for col, (key, func) in self.TOOLTIPS.iteritems():
            twi.setToolTip(col, func(info[key]))

        with self._selected_node_lock:
            if twi.text(0) == self._selected_node:
                twi.setSelected(True)

        self._table_widget.setItemHidden(
            twi,
            len(self.name_filter.findall(info['node_name'])) == 0)

    def update_table(self):
        self._table_widget.clear()
        infos = self._node_info.get_all_node_fields(self.NODE_FIELDS)
        for nx, info in enumerate(infos):
            self.update_one_item(nx, info)

    def shutdown_plugin(self):
        self._update_timer.stop()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('filter_text', self._filter_box.text())
        instance_settings.set_value('is_regex',
                                    int(self._regex_box.checkState()))

    def restore_settings(self, plugin_settings, instance_settings):
        self._filter_box.setText(instance_settings.value('filter_text'))
        is_regex_int = instance_settings.value('is_regex')
        if is_regex_int:
            self._regex_box.setCheckState(Qt.CheckState(is_regex_int))
        else:
            self._regex_box.setCheckState(Qt.CheckState(0))
        self.update_filter()