rostop_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 import roslib; roslib.load_manifest('rostop_gui')
00031 
00032 from qt_gui.plugin import Plugin
00033 from python_qt_binding import loadUi
00034 from python_qt_binding.QtGui import QLabel, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QCheckBox, QWidget, QToolBar, QLineEdit, QPushButton
00035 from python_qt_binding.QtCore import Qt, QTimer
00036 
00037 from rostop_gui.node_info import NodeInfo
00038 import re
00039 from threading import RLock
00040 import textwrap
00041 
00042 class RosTop(Plugin):
00043 
00044     node_fields   = [             'pid', 'get_cpu_percent', 'get_memory_percent', 'get_num_threads']
00045     out_fields    = ['node_name', 'pid', 'cpu_percent',     'memory_percent',     'num_threads'    ]
00046     format_strs   = ['%s',        '%s',  '%0.2f',           '%0.2f',              '%s'             ]
00047     node_labels   = ['Node',      'PID', 'CPU %',           'Mem %',              'Num Threads'    ]
00048     tooltips      = {
00049         0: ('cmdline', lambda x: '\n'.join(textwrap.wrap(' '.join(x)))),
00050         3: ('memory_info', lambda x: ('Resident: %0.2f MiB, Virtual: %0.2f MiB' % (x[0]/2**20, x[1]/2**20)))
00051     }
00052 
00053     _node_info = NodeInfo()
00054 
00055     name_filter = re.compile('')
00056 
00057     def __init__(self, context):
00058         super(RosTop, self).__init__(context)
00059         # Give QObjects reasonable names
00060         self.setObjectName('RosTop')
00061 
00062         # Process standalone plugin command-line arguments
00063         from argparse import ArgumentParser
00064         parser = ArgumentParser()
00065         # Add argument(s) to the parser.
00066         parser.add_argument("-q", "--quiet", action="store_true",
00067                       dest="quiet",
00068                       help="Put plugin in silent mode")
00069         args, unknowns = parser.parse_known_args(context.argv())
00070         # if not args.quiet:
00071         #     print 'arguments: ', args
00072         #     print 'unknowns: ', unknowns
00073 
00074         self._selected_node = ''
00075         self._selected_node_lock = RLock()
00076 
00077         # Setup the toolbar
00078         self._toolbar = QToolBar()
00079         self._filter_box = QLineEdit()
00080         self._regex_box  = QCheckBox()
00081         self._regex_box.setText('regex')
00082         self._toolbar.addWidget(QLabel('Filter'))
00083         self._toolbar.addWidget(self._filter_box)
00084         self._toolbar.addWidget(self._regex_box)
00085 
00086         self._filter_box.returnPressed.connect(self.update_filter)
00087         self._regex_box.stateChanged.connect(self.update_filter)
00088 
00089         # Create a container widget and give it a layout
00090         self._container = QWidget()
00091         self._layout    = QVBoxLayout()
00092         self._container.setLayout(self._layout)
00093 
00094         self._layout.addWidget(self._toolbar)
00095 
00096         # Create the table widget
00097         self._table_widget = QTreeWidget()
00098         self._table_widget.setObjectName('TopTable')
00099         self._table_widget.setColumnCount(len(self.node_labels))
00100         self._table_widget.setHeaderLabels(self.node_labels)
00101         self._table_widget.itemClicked.connect(self._tableItemClicked)
00102         self._table_widget.setSortingEnabled(True)
00103         self._table_widget.setAlternatingRowColors(True)
00104 
00105         self._layout.addWidget(self._table_widget)
00106         context.add_widget(self._container)
00107 
00108         # Add a button for killing nodes
00109         self._kill_button = QPushButton('Kill Node')
00110         self._layout.addWidget(self._kill_button)
00111         self._kill_button.clicked.connect(self._kill_node)
00112 
00113         # Update twice since the first cpu% lookup will always return 0
00114         self.update_table()
00115         self.update_table()
00116 
00117         self._table_widget.resizeColumnToContents(0)
00118 
00119         # Start a timer to trigger updates
00120         self._update_timer = QTimer()
00121         self._update_timer.setInterval(1000)
00122         self._update_timer.timeout.connect(self.update_table)
00123         self._update_timer.start()
00124 
00125     def _tableItemClicked(self, item, column):
00126         with self._selected_node_lock:
00127             self._selected_node = item.text(0)
00128 
00129     def update_filter(self, *args):
00130         if self._regex_box.isChecked():
00131             expr = self._filter_box.text()
00132         else:
00133             expr = re.escape(self._filter_box.text())
00134         self.name_filter = re.compile(expr)
00135         self.update_table()
00136 
00137     def _kill_node(self):
00138         self._node_info.kill_node(self._selected_node)
00139 
00140     def update_one_item(self, row, info):
00141         twi = QTreeWidgetItem()
00142         for col, field in enumerate(self.out_fields):
00143             val = info[field]
00144             twi.setText(col, self.format_strs[col] % val)
00145         self._table_widget.insertTopLevelItem(row, twi)
00146 
00147         for col, (key, func) in self.tooltips.iteritems():
00148             twi.setToolTip(col, func(info[key]))
00149 
00150         with self._selected_node_lock:
00151             if twi.text(0) == self._selected_node:
00152                 twi.setSelected(True)
00153 
00154         self._table_widget.setItemHidden(twi, len(self.name_filter.findall(info['node_name'])) == 0)
00155 
00156     def update_table(self):
00157         self._table_widget.clear()
00158         infos = self._node_info.get_all_node_fields(self.node_fields)
00159         for nx, info in enumerate(infos):
00160             self.update_one_item(nx, info)
00161 
00162     def shutdown_plugin(self):
00163         self._update_timer.stop()
00164 
00165     def save_settings(self, plugin_settings, instance_settings):        
00166         instance_settings.set_value('filter_text', self._filter_box.text())
00167         instance_settings.set_value('is_regex', int(self._regex_box.checkState()))
00168 
00169     def restore_settings(self, plugin_settings, instance_settings):
00170         self._filter_box.setText(instance_settings.value('filter_text'))
00171         self._regex_box.setCheckState(Qt.CheckState(instance_settings.value('is_regex')))
00172         self.update_filter()
00173 
00174     #def trigger_configuration(self):
00175         # Comment in to signal that the plugin has a way to configure it
00176         # Usually used to open a configuration dialog


rostop_gui
Author(s): Dan Lazewatsky
autogenerated on Mon Oct 6 2014 07:12:09