top_plugin.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Copyright (c) 2013, Oregon State University
00003 # All rights reserved.
00004 
00005 # Redistribution and use in source and binary forms, with or without
00006 # modification, are permitted provided that the following conditions are met:
00007 #     * Redistributions of source code must retain the above copyright
00008 #       notice, this list of conditions and the following disclaimer.
00009 #     * Redistributions in binary form must reproduce the above copyright
00010 #       notice, this list of conditions and the following disclaimer in the
00011 #       documentation and/or other materials provided with the distribution.
00012 #     * Neither the name of the Oregon State University nor the
00013 #       names of its contributors may be used to endorse or promote products
00014 #       derived from this software without specific prior written permission.
00015 
00016 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 # DISCLAIMED. IN NO EVENT SHALL OREGON STATE UNIVERSITY BE LIABLE FOR ANY
00020 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026 
00027 # Author Dan Lazewatsky/lazewatd@engr.orst.edu
00028 
00029 from __future__ import division
00030 
00031 from qt_gui.plugin import Plugin
00032 from python_qt_binding import loadUi
00033 from python_qt_binding.QtGui import QLabel, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QCheckBox, QWidget, QToolBar, QLineEdit, QPushButton
00034 from python_qt_binding.QtCore import Qt, QTimer
00035 
00036 from rqt_top.node_info import NodeInfo
00037 import re
00038 from threading import RLock
00039 import textwrap
00040 
00041 
00042 class TopWidgetItem(QTreeWidgetItem):
00043     def __init__(self, parent=None):
00044         super(TopWidgetItem, self).__init__(parent)
00045         
00046     def __lt__(self, other):
00047         col = self.treeWidget().sortColumn()
00048         dtype = Top.SORT_TYPE[col]
00049         return dtype(self.text(col)) < dtype(other.text(col))
00050 
00051 
00052 class Top(Plugin):
00053 
00054     NODE_FIELDS   = [             'pid', 'get_cpu_percent', 'get_memory_percent', 'get_num_threads']
00055     OUT_FIELDS    = ['node_name', 'pid', 'cpu_percent',     'memory_percent',     'num_threads'    ]
00056     FORMAT_STRS   = ['%s',        '%s',  '%0.2f',           '%0.2f',              '%s'             ]
00057     NODE_LABELS   = ['Node',      'PID', 'CPU %',           'Mem %',              'Num Threads'    ]
00058     SORT_TYPE     = [str,         str,   float,             float,                float            ]
00059     TOOLTIPS      = {
00060         0: ('cmdline', lambda x: '\n'.join(textwrap.wrap(' '.join(x)))),
00061         3: ('memory_info', lambda x: ('Resident: %0.2f MiB, Virtual: %0.2f MiB' % (x[0]/2**20, x[1]/2**20)))
00062     }
00063 
00064     _node_info = NodeInfo()
00065 
00066     name_filter = re.compile('')
00067 
00068     def __init__(self, context):
00069         super(Top, self).__init__(context)
00070         # Give QObjects reasonable names
00071         self.setObjectName('Top')
00072 
00073         # Process standalone plugin command-line arguments
00074         from argparse import ArgumentParser
00075         parser = ArgumentParser()
00076         # Add argument(s) to the parser.
00077         parser.add_argument("-q", "--quiet", action="store_true",
00078                       dest="quiet",
00079                       help="Put plugin in silent mode")
00080         args, unknowns = parser.parse_known_args(context.argv())
00081         # if not args.quiet:
00082         #     print 'arguments: ', args
00083         #     print 'unknowns: ', unknowns
00084 
00085         self._selected_node = ''
00086         self._selected_node_lock = RLock()
00087 
00088         # Setup the toolbar
00089         self._toolbar = QToolBar()
00090         self._filter_box = QLineEdit()
00091         self._regex_box  = QCheckBox()
00092         self._regex_box.setText('regex')
00093         self._toolbar.addWidget(QLabel('Filter'))
00094         self._toolbar.addWidget(self._filter_box)
00095         self._toolbar.addWidget(self._regex_box)
00096 
00097         self._filter_box.returnPressed.connect(self.update_filter)
00098         self._regex_box.stateChanged.connect(self.update_filter)
00099 
00100         # Create a container widget and give it a layout
00101         self._container = QWidget()
00102         self._container.setWindowTitle('Process Monitor')
00103         self._layout    = QVBoxLayout()
00104         self._container.setLayout(self._layout)
00105 
00106         self._layout.addWidget(self._toolbar)
00107 
00108         # Create the table widget
00109         self._table_widget = QTreeWidget()
00110         self._table_widget.setObjectName('TopTable')
00111         self._table_widget.setColumnCount(len(self.NODE_LABELS))
00112         self._table_widget.setHeaderLabels(self.NODE_LABELS)
00113         self._table_widget.itemClicked.connect(self._tableItemClicked)
00114         self._table_widget.setSortingEnabled(True)
00115         self._table_widget.setAlternatingRowColors(True)
00116 
00117         self._layout.addWidget(self._table_widget)
00118         context.add_widget(self._container)
00119 
00120         # Add a button for killing nodes
00121         self._kill_button = QPushButton('Kill Node')
00122         self._layout.addWidget(self._kill_button)
00123         self._kill_button.clicked.connect(self._kill_node)
00124 
00125         # Update twice since the first cpu% lookup will always return 0
00126         self.update_table()
00127         self.update_table()
00128 
00129         self._table_widget.resizeColumnToContents(0)
00130 
00131         # Start a timer to trigger updates
00132         self._update_timer = QTimer()
00133         self._update_timer.setInterval(1000)
00134         self._update_timer.timeout.connect(self.update_table)
00135         self._update_timer.start()
00136 
00137     def _tableItemClicked(self, item, column):
00138         with self._selected_node_lock:
00139             self._selected_node = item.text(0)
00140 
00141     def update_filter(self, *args):
00142         if self._regex_box.isChecked():
00143             expr = self._filter_box.text()
00144         else:
00145             expr = re.escape(self._filter_box.text())
00146         self.name_filter = re.compile(expr)
00147         self.update_table()
00148 
00149     def _kill_node(self):
00150         self._node_info.kill_node(self._selected_node)
00151 
00152     def update_one_item(self, row, info):
00153         twi = TopWidgetItem()
00154         for col, field in enumerate(self.OUT_FIELDS):
00155             val = info[field]
00156             twi.setText(col, self.FORMAT_STRS[col] % val)
00157         self._table_widget.insertTopLevelItem(row, twi)
00158 
00159         for col, (key, func) in self.TOOLTIPS.iteritems():
00160             twi.setToolTip(col, func(info[key]))
00161 
00162         with self._selected_node_lock:
00163             if twi.text(0) == self._selected_node:
00164                 twi.setSelected(True)
00165 
00166         self._table_widget.setItemHidden(twi, len(self.name_filter.findall(info['node_name'])) == 0)
00167 
00168     def update_table(self):
00169         self._table_widget.clear()
00170         infos = self._node_info.get_all_node_fields(self.NODE_FIELDS)
00171         for nx, info in enumerate(infos):
00172             self.update_one_item(nx, info)
00173 
00174     def shutdown_plugin(self):
00175         self._update_timer.stop()
00176 
00177     def save_settings(self, plugin_settings, instance_settings):        
00178         instance_settings.set_value('filter_text', self._filter_box.text())
00179         instance_settings.set_value('is_regex', int(self._regex_box.checkState()))
00180 
00181     def restore_settings(self, plugin_settings, instance_settings):
00182         self._filter_box.setText(instance_settings.value('filter_text'))
00183         is_regex_int = instance_settings.value('is_regex')
00184         if is_regex_int:
00185             self._regex_box.setCheckState(Qt.CheckState(is_regex_int))
00186         else:
00187             self._regex_box.setCheckState(Qt.CheckState(0))
00188         self.update_filter()
00189 
00190     #def trigger_configuration(self):
00191         # Comment in to signal that the plugin has a way to configure it
00192         # Usually used to open a configuration dialog


rqt_top
Author(s): Dan Lazewatsky
autogenerated on Wed Sep 16 2015 06:58:27