Package node_manager_fkie :: Module echo_dialog
[frames] | no frames]

Source Code for Module node_manager_fkie.echo_dialog

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33   
 34  from PySide import QtCore, QtGui 
 35   
 36  import time 
 37  import math 
 38   
 39  import roslib 
 40  import roslib.message 
 41  import rospy 
 42  import threading 
 43   
44 -class EchoDialog(QtGui.QDialog):
45 46 MESSAGE_HZ_LIMIT = 100 47 48 ''' 49 This dialog shows the output of a topic. 50 ''' 51 52 finished_signal = QtCore.Signal(str) 53 ''' 54 finished_signal has as parameter the name of the topic and is emitted, if this 55 dialog was closed. 56 ''' 57 58 msg_signal = QtCore.Signal(str) 59 ''' 60 msg_signal is a signal, which is emitted, if a new message was received. 61 ''' 62
63 - def __init__(self, topic, type, show_only_rate=False, parent=None):
64 ''' 65 Creates an input dialog. 66 @param topic: the name of the topic 67 @type topic: C{str} 68 @param type: the type of the topic 69 @type type: C{str} 70 @raise Exception: if no topic class was found for the given type 71 ''' 72 QtGui.QDialog.__init__(self, parent=parent) 73 self.setObjectName(' - '.join(['EchoDialog', topic])) 74 self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) 75 self.setWindowFlags(QtCore.Qt.Window) 76 self.setWindowTitle(''.join(['Echo of ' if not show_only_rate else 'Hz of ', topic])) 77 self.resize(728,512) 78 self.verticalLayout = QtGui.QVBoxLayout(self) 79 self.verticalLayout.setObjectName("verticalLayout") 80 self.verticalLayout.setContentsMargins(1, 1, 1, 1) 81 82 self.topic = topic 83 self.show_only_rate = show_only_rate 84 self.lock = threading.Lock() 85 self.last_printed_tn = 0 86 self.msg_t0 = -1. 87 self.msg_tn = 0 88 self.times =[] 89 90 self.message_count = 0 91 92 self._rate_message = '' 93 94 self.message_ignored_count = 0 95 self.ts_first_msg = 0 96 self.message_interval_count = 0 97 self.message_interval_count_last = 0 98 99 self.field_filter_fn = None 100 101 options = QtGui.QWidget(self) 102 if not show_only_rate: 103 hLayout = QtGui.QHBoxLayout(options) 104 hLayout.setContentsMargins(1, 1, 1, 1) 105 self.no_str_checkbox = no_str_checkbox = QtGui.QCheckBox('Hide strings') 106 no_str_checkbox.toggled.connect(self.on_no_str_checkbox_toggled) 107 hLayout.addWidget(no_str_checkbox) 108 self.no_arr_checkbox = no_arr_checkbox = QtGui.QCheckBox('Hide arrays') 109 no_arr_checkbox.toggled.connect(self.on_no_arr_checkbox_toggled) 110 hLayout.addWidget(no_arr_checkbox) 111 # add spacer 112 spacerItem = QtGui.QSpacerItem(515, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 113 hLayout.addItem(spacerItem) 114 # add clear button 115 cancelButton = QtGui.QToolButton(self) 116 cancelButton.setText('clear') 117 cancelButton.clicked.connect(self.on_clear_btn_clicked) 118 hLayout.addWidget(cancelButton) 119 self.verticalLayout.addWidget(options) 120 121 self.display = QtGui.QTextEdit(self) 122 self.display.setReadOnly(True) 123 self.verticalLayout.addWidget(self.display); 124 125 self.status_label = QtGui.QLabel('0 messages', self) 126 self.verticalLayout.addWidget(self.status_label) 127 128 # subscribe to the topic 129 msg_class = roslib.message.get_message_class(type) 130 if not msg_class: 131 raise Exception("Cannot load message class for [%s]. Are your messages built?"%type) 132 self.sub = rospy.Subscriber(topic, msg_class, self._msg_handle) 133 self.msg_signal.connect(self._append_message) 134 135 self.print_hz_timer = QtCore.QTimer() 136 self.print_hz_timer.timeout.connect(self._on_calc_hz) 137 self.print_hz_timer.start(1000)
138 139 # print "======== create", self.objectName() 140 # 141 # def __del__(self): 142 # print "******* destroy", self.objectName() 143 144 # def hideEvent(self, event): 145 # self.close() 146
147 - def closeEvent (self, event):
148 self.sub.unregister() 149 del self.sub 150 self.finished_signal.emit(self.topic) 151 if self.parent() is None: 152 QtGui.QApplication.quit()
153 # else: 154 # self.setParent(None) 155
156 - def create_field_filter(self, echo_nostr, echo_noarr):
157 def field_filter(val): 158 try: 159 fields = val.__slots__ 160 field_types = val._slot_types 161 for f, t in zip(val.__slots__, val._slot_types): 162 if echo_noarr and '[' in t: 163 continue 164 elif echo_nostr and 'string' in t: 165 continue 166 yield f 167 except: 168 pass
169 return field_filter
170
171 - def on_no_str_checkbox_toggled(self, state):
172 self.field_filter_fn = self.create_field_filter(state, self.no_arr_checkbox.isChecked())
173
174 - def on_no_arr_checkbox_toggled(self, state):
175 self.field_filter_fn = self.create_field_filter(self.no_str_checkbox.isChecked(), state)
176
177 - def on_clear_btn_clicked(self):
178 self.display.clear()
179
180 - def _msg_handle(self, data):
181 self.msg_signal.emit(roslib.message.strify_message(data, field_filter=self.field_filter_fn))
182
183 - def _append_message(self, msg):
184 ''' 185 Adds a label to the dialog's layout and shows the given text. 186 @param msg: the text to add to the dialog 187 @type msg: C{str} 188 ''' 189 current_time = time.time() 190 with self.lock: 191 current_time = time.time() 192 # time reset 193 if self.msg_t0 < 0 or self.msg_t0 > current_time: 194 self.msg_t0 = current_time 195 self.msg_tn = current_time 196 self.times = [] 197 else: 198 self.times.append(current_time - self.msg_tn) 199 self.msg_tn = current_time 200 201 #only keep statistics for the last 5000 messages so as not to run out of memory 202 if len(self.times) > 5000: 203 self.times.pop(0) 204 205 206 207 self.message_count += 1 208 # if not self.ts_first_msg: 209 # self.ts_first_msg = time.time() 210 # elif int(current_time) - int(self.ts_first_msg) > 0: 211 # self.message_interval_count_last = self.message_interval_count 212 # self.message_interval_count = 0 213 # self.ts_first_msg = current_time 214 # self.message_interval_count += 1 215 # if self.message_interval_count > self.MESSAGE_HZ_LIMIT: 216 # self.message_ignored_count += 1 217 # status_text = ' '.join([str(self.message_count), 'messages']) 218 # # show rate of last second 219 # if self.message_interval_count_last > 0: 220 # status_text = ' '.join([status_text, ', rate: ', str(self.message_interval_count_last),'Hz']) 221 # # show the info, what no more then the limit messages is displayed 222 # if self.message_ignored_count > 0: 223 # status_text = ' '.join([status_text, ', skipped: ', str(self.message_ignored_count), ' (maximum displayed: ', str(self.MESSAGE_HZ_LIMIT),'Hz)']) 224 # self.status_label.setText(status_text) 225 # # append message only if the limit is not reached 226 if not self.show_only_rate: 227 self.display.append(''.join(['<pre style="background-color:#FFFCCC; font-family:Fixedsys,Courier,monospace; padding:10px;">\n', msg,'\n</pre><hr>'])) 228 self._print_status()
229
230 - def _on_calc_hz(self):
231 if rospy.is_shutdown(): 232 self.close() 233 return 234 if self.msg_tn == self.last_printed_tn: 235 self._rate_message = 'no new messages' 236 return 237 with self.lock: 238 # the code from ROS rostopic 239 n = len(self.times) 240 if n == 0: 241 return 242 mean = sum(self.times) / n 243 rate = 1./mean if mean > 0. else 0 244 245 #std dev 246 std_dev = math.sqrt(sum((x - mean)**2 for x in self.times) /n) 247 # min and max 248 max_delta = max(self.times) 249 min_delta = min(self.times) 250 251 self.last_printed_tn = self.msg_tn 252 self._rate_message = "average rate: %.3f\tmin: %.3fs max: %.3fs std dev: %.5fs window: %s"%(rate, min_delta, max_delta, std_dev, n+1) 253 self._print_status() 254 if self.show_only_rate: 255 self.display.append(self._rate_message)
256 257
258 - def _print_status(self):
259 status_text = ' '.join([str(self.message_count), 'messages', ', ' if self._rate_message else '', self._rate_message]) 260 self.status_label.setText(status_text)
261