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 from PySide import QtCore
34 from PySide import QtGui
35
36 import os
37 import time
38 import threading
39
40 import rospy
41 import roslib
42 import roslib.message
43 import roslib.msgs
44 import genpy
45 from master_discovery_fkie.master_info import TopicInfo
46 from detailed_msg_box import WarningMessageBox
47 from parameter_dialog import ParameterDialog, ServiceDialog
48
49
50 -class TopicItem(QtCore.QObject, QtGui.QStandardItem):
51 '''
52 The topic item stored in the topic model. This class stores the topic as
53 L{master_discovery_fkie.TopicInfo}. The name of the topic represented in HTML.
54 '''
55
56 ITEM_TYPE = QtGui.QStandardItem.UserType + 36
57 COL_PUB = 1
58 COL_SUB = 2
59 COL_TYPE = 3
60
61
63 '''
64 Initialize the topic item.
65 @param name: the topic name
66 @type name: C{str}
67 '''
68 QtCore.QObject.__init__(self)
69 QtGui.QStandardItem.__init__(self, self.toHTML(name))
70 self.parent_item = parent
71 self._topic = TopicInfo(name)
72 self._first_call = True
73 '''@ivar: service info as L{master_discovery_fkie.ServiceInfo}.'''
74 self._publish_thread = None
75
76
77
78
79 @property
81 '''
82 Returns the TopicInfo instance of this topic.
83 @rtype: L{master_discovery_fkie.TopicInfo}
84 '''
85 return self._topic
86
87 @topic.setter
88 - def topic(self, topic_info):
89 '''
90 Sets the TopicInfo and updates the view, if needed.
91 @type topic_info: L{master_discovery_fkie.TopicInfo}
92 '''
93 pubs_changed = False
94 subs_changed = False
95 type_changed = False
96 if self._topic.publisherNodes != topic_info.publisherNodes:
97 pubs_changed = True
98 self._topic.publisherNodes = topic_info.publisherNodes
99 if self._topic.subscriberNodes != topic_info.subscriberNodes:
100 subs_changed = True
101 self._topic.subscriberNodes = topic_info.subscriberNodes
102 if self._topic.type != topic_info.type:
103 self._topic.type = topic_info.type
104 type_changed = True
105
106
107
108 if pubs_changed or self._first_call:
109 self.updatePublisherView()
110 if subs_changed or self._first_call:
111 self.updateSubscriberView()
112 if type_changed or self._first_call:
113 self.updateTypeView()
114 self._first_call = False
115
117 '''
118 Updates the representation of the column contains the publisher state.
119 '''
120 if not self.parent_item is None:
121 cfg_col = self.parent_item.child(self.row(), TopicItem.COL_PUB)
122 if not cfg_col is None and isinstance(cfg_col, QtGui.QStandardItem):
123 cfg_col.setText(str(len(self.topic.publisherNodes)))
124 tooltip = ''.join(['<h4>', 'Publisher [', self.topic.name, ']:</h4><dl>'])
125 for p in self.topic.publisherNodes:
126 tooltip = ''.join([tooltip, '<dt>', p, '</dt>'])
127 tooltip = ''.join([tooltip, '</dl>'])
128 if len(self.topic.publisherNodes) > 0:
129 cfg_col.setToolTip(''.join(['<div>', tooltip, '</div>']))
130
132 '''
133 Updates the representation of the column contains the subscriber state.
134 '''
135 if not self.parent_item is None:
136 cfg_col = self.parent_item.child(self.row(), TopicItem.COL_SUB)
137 if not cfg_col is None and isinstance(cfg_col, QtGui.QStandardItem):
138 cfg_col.setText(str(len(self.topic.subscriberNodes)))
139 tooltip = ''.join(['<h4>', 'Subscriber [', self.topic.name, ']:</h4><dl>'])
140 for p in self.topic.subscriberNodes:
141 tooltip = ''.join([tooltip, '<dt>', p, '</dt>'])
142 tooltip = ''.join([tooltip, '</dl>'])
143 if len(self.topic.subscriberNodes) > 0:
144 cfg_col.setToolTip(''.join(['<div>', tooltip, '</div>']))
145
147 '''
148 Updates the representation of the column contains the type of the topic.
149 '''
150 if not self.parent_item is None:
151 cfg_col = self.parent_item.child(self.row(), TopicItem.COL_TYPE)
152 if not cfg_col is None and isinstance(cfg_col, QtGui.QStandardItem):
153 cfg_col.setText(str(self.topic.type))
154 if not self.topic.type is None and not cfg_col.toolTip():
155 return
156
157
158 try:
159 mclass = roslib.message.get_message_class(self.topic.type)
160
161 if not mclass is None:
162
163 for f in mclass.__slots__:
164 idx = mclass.__slots__.index(f)
165 idtype = mclass._slot_types[idx]
166 base_type = roslib.msgs.base_msg_type(idtype)
167 primitive = "unknown"
168 if base_type in roslib.msgs.PRIMITIVE_TYPES:
169 primitive = "primitive"
170 else:
171 try:
172 list_msg_class = roslib.message.get_message_class(base_type)
173 primitive = "class", list_msg_class.__slots__
174 except ValueError:
175 pass
176
177 except ValueError:
178 pass
179
180
182 self.updateIconView(QtGui.QIcon(':/icons/state_off.png'))
183
185 self.updateIconView(QtGui.QIcon(':/icons/state_part.png'))
186
188 self.updateIconView(QtGui.QIcon(':/icons/state_run.png'))
189
191 self._publish_thread = None
192 self.setIcon(QtGui.QIcon())
193
195 WarningMessageBox(QtGui.QMessageBox.Warning, "Publish error",
196 'Error while publish to %s'%self.topic.name,
197 str(msg)).exec_()
198
199 @classmethod
201 '''
202 Creates a HTML representation of the topic name.
203 @param topic_name: the topic name
204 @type topic_name: C{str}
205 @return: the HTML representation of the topic name
206 @rtype: C{str}
207 '''
208 ns, sep, name = topic_name.rpartition('/')
209 result = ''
210 if sep:
211 result = ''.join(['<div>', '<span style="color:gray;">', str(ns), sep, '</span><b>', name, '</b></div>'])
212 else:
213 result = name
214 return result
215
218
219 @classmethod
221 '''
222 Creates the list of the items from topic. This list is used for the
223 visualization of topic data as a table row.
224 @param name: the topic name
225 @type name: C{str}
226 @param root: The parent QStandardItem
227 @type root: L{PySide.QtGui.QStandardItem}
228 @return: the list for the representation as a row
229 @rtype: C{[L{TopicItem} or L{PySide.QtGui.QStandardItem}, ...]}
230 '''
231 items = []
232 item = TopicItem(name, parent=root)
233 items.append(item)
234 pubItem = QtGui.QStandardItem()
235
236 items.append(pubItem)
237 subItem = QtGui.QStandardItem()
238
239 items.append(subItem)
240 typeItem = QtGui.QStandardItem()
241
242 items.append(typeItem)
243 return items
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 -class TopicModel(QtGui.QStandardItemModel):
268 '''
269 The model to manage the list with topics in ROS network.
270 '''
271 header = [('Name', 300),
272 ('Publisher', 50),
273 ('Subscriber', 50),
274 ('Type', -1)]
275 '''@ivar: the list with columns C{[(name, width), ...]}'''
276
278 '''
279 Creates a new list model.
280 '''
281 QtGui.QStandardItemModel.__init__(self)
282 self.setColumnCount(len(TopicModel.header))
283 self.setHorizontalHeaderLabels([label for label, width in TopicModel.header])
284
286 '''
287 @param index: parent of the list
288 @type index: L{PySide.QtCore.QModelIndex}
289 @return: Flag or the requested item
290 @rtype: L{PySide.QtCore.Qt.ItemFlag}
291 @see: U{http://www.pyside.org/docs/pyside-1.0.1/PySide/QtCore/Qt.html}
292 '''
293 if not index.isValid():
294 return QtCore.Qt.NoItemFlags
295 return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
296
298 '''
299 Updates the topics model. New topic will be inserted in sorting order. Not
300 available topics removed from the model.
301 @param topics: The dictionary with topics
302 @type topics: C{dict(topic name : L{master_discovery_fkie.TopicInfo}, ...)}
303 '''
304 topic_names = topics.keys()
305 root = self.invisibleRootItem()
306 updated = []
307
308 for i in reversed(range(root.rowCount())):
309 topicItem = root.child(i)
310 if not topicItem.topic.name in topic_names:
311 root.removeRow(i)
312 else:
313 topicItem.topic = topics[topicItem.topic.name]
314 updated.append(topicItem.topic.name)
315
316
317
318 for (name, topic) in topics.items():
319 doAddItem = True
320 self._update_topic(name, topic, updated)
321
322
323
324
326 new_item_row = None
327 root = self.invisibleRootItem()
328 doAddItem = True
329 for i in range(root.rowCount()):
330 topicItem = root.child(i)
331 if updated is None or not name in updated:
332 res = cmp(topicItem.topic.name.lower(), name.lower())
333 if res > 0:
334 new_item_row = TopicItem.getItemList(name, root)
335 root.insertRow(i, new_item_row)
336 if not topic is None:
337 new_item_row[0].topic = topic
338 doAddItem = False
339 break
340 else:
341 doAddItem = False
342 break
343 if doAddItem:
344 new_item_row = TopicItem.getItemList(name, root)
345 root.appendRow(new_item_row)
346 if not topic is None:
347 new_item_row[0].topic = topic
348 return new_item_row
349