1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
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
112 spacerItem = QtGui.QSpacerItem(515, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
113 hLayout.addItem(spacerItem)
114
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
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
140
141
142
143
144
145
146
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
154
155
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
173
176
179
181 self.msg_signal.emit(roslib.message.strify_message(data, field_filter=self.field_filter_fn))
182
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
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
202 if len(self.times) > 5000:
203 self.times.pop(0)
204
205
206
207 self.message_count += 1
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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
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
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
246 std_dev = math.sqrt(sum((x - mean)**2 for x in self.times) /n)
247
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
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