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 python_qt_binding import QtCore
34 from python_qt_binding import QtGui
35
36 import threading
37
38 from urlparse import urlparse
39 from socket import getaddrinfo, AF_INET, AF_INET6
40
41 import node_manager_fkie as nm
44 ITEM_TYPE = QtGui.QStandardItem.UserType + 35
45
46 NOT_SYNC = 0
47 START_SYNC = 1
48 SYNC = 2
49
50 ICON_PREFIX = 'irondevil'
51
52
53 - def __init__(self, master, parent=None):
63
64 @property
67
68 @property
70 return self._syncronized
71
72 @synchronized.setter
74 if self._syncronized != value:
75 self._syncronized = value
76 if value in self.ICONS:
77 self.setIcon(self.ICONS[value])
78
80 if isinstance(item, str) or isinstance(item, unicode):
81 return self.master.name.lower() == item.lower()
82 elif not (item is None):
83 return self.master.name.lower() == item.master.name.lower()
84 return False
85
87 if isinstance(item, str) or isinstance(item, unicode):
88 return self.master.name.lower() > item.lower()
89 elif not (item is None):
90 return self.master.name.lower() > item.master.name.lower()
91 return False
92
95 '''
96 The master item stored in the master model. This class stores the master as
97 master_discovery_fkie.ROSMaster.
98 '''
99
100 ITEM_TYPE = QtGui.QStandardItem.UserType + 34
101
102 - def __init__(self, master, local=False, quality=None, parent=None):
103 self.name = ''.join([master.name, ' (localhost)']) if local else master.name
104 QtGui.QStandardItem.__init__(self, self.name)
105 self.parent_item = None
106 self._master = master
107 self.__quality = quality
108 self.descr = ''
109 self.ICONS = {'green' : QtGui.QIcon(":/icons/stock_connect_green.png"),
110 'yellow': QtGui.QIcon(":/icons/stock_connect_yellow.png"),
111 'red' : QtGui.QIcon(":/icons/stock_connect_red.png"),
112 'grey' : QtGui.QIcon(":/icons/stock_connect.png"),
113 'disconnected' : QtGui.QIcon(":/icons/stock_disconnect.png"),
114 'warning' : QtGui.QIcon(':/icons/crystal_clear_warning.png') }
115 self.master_ip = None
116 self._master_errors = []
117 self._threaded_get_ip()
118 self.updateNameView(master, quality, self)
119
121 thread = threading.Thread(target=self.__get_ip)
122 thread.daemon = True
123 thread.start()
124
126 try:
127
128 o = urlparse(self.master.uri)
129 result = getaddrinfo(o.hostname, None)
130 ips = []
131 for r in result:
132 if r[0] == AF_INET6:
133 (family, socktype, proto, canonname, (ip, port, flow, scope)) = r
134 else:
135 (family, socktype, proto, canonname, (ip, port)) = r
136 if self.master_ip is None and ip:
137 self.master_ip = ''
138 if ip and not ip in ips:
139 self.master_ip = ' '.join([self.master_ip, ip])
140 ips.append(ip)
141
142 except:
143 import traceback
144 print traceback.format_exc(1)
145
146 @property
149
150 @master.setter
153
154 @property
156 return self.__quality
157
158 @quality.setter
163
167
169 '''
170 This method is called after the master state is changed to update the
171 representation of the master. The name will not be changed, but all other
172 data.
173 @param parent: Item which contains this master item. This is needed to update
174 other columns of this master.
175 @type parent: L{PySide.QtGui.QStandardItem}
176 '''
177 if not parent is None:
178
179 child = parent.child(self.row(), MasterModel.COL_NAME)
180 if not child is None:
181 self.updateNameView(self.master, self.quality, child)
182
184 '''
185 Updates the representation of the column contains the name state.
186 @param master: the topic data
187 @type master: master_discovery_fkie.TopicInfo
188 @param item: corresponding item in the model
189 @type item: L{TopicItem}
190 '''
191 tooltip = ''.join(['<html><body>'])
192 tooltip = ''.join([tooltip, '<h4>', master.uri, '</h4>'])
193 tooltip = ''.join([tooltip, '<dl>'])
194 tooltip = ''.join([tooltip, '<dt>', 'IP: ', str(self.master_ip), '</dt>'])
195 if master.online:
196 if not quality is None and quality != -1.:
197 tooltip = ''.join([tooltip, '<dt>', 'Quality: ', str(quality),' %', '</dt>'])
198 else:
199 tooltip = ''.join([tooltip, '<dt>', 'Quality: not available</dt>'])
200
201
202 else:
203 tooltip = ''.join([tooltip, '<dt>', 'offline', '</dt>'])
204 tooltip = ''.join([tooltip, '</dl>'])
205 if item.descr:
206
207 tooltip = ''.join([tooltip, item.descr])
208
209 if master.online:
210 if self._master_errors or self.master_ip is None:
211 item.setIcon(self.ICONS['warning'])
212 if self.master_ip is None:
213 tooltip = ''.join([tooltip, '<h4>', '<font color="#CC0000">Host not reachable by name!!! The ROS topics may not by connected!!!</font>', '</h4>'])
214 if self._master_errors:
215 tooltip = ''.join([tooltip, '<h4>Errors reported by master_discovery:</h4>'])
216 for err in self._master_errors:
217 tooltip = ''.join([tooltip, '<dt><font color="#CC0000">%s</font></dt>'%err])
218 elif not quality is None and quality != -1.:
219 if quality > 30:
220 item.setIcon(self.ICONS['green'])
221 elif quality > 5:
222 item.setIcon(self.ICONS['yellow'])
223 else:
224 item.setIcon(self.ICONS['red'])
225 else:
226 item.setIcon(self.ICONS['grey'])
227 else:
228 item.setIcon(self.ICONS['disconnected'])
229
230 tooltip = ''.join([tooltip, '</body></html>'])
231 item.setToolTip(tooltip)
232
236
237 @classmethod
239 '''
240 @param text: the text
241 @type text: C{str}
242 @return: the HTML representation of the name of the text
243 @rtype: C{str}
244 '''
245 ns, sep, name = text.rpartition('/')
246 result = ''
247 if sep:
248 result = ''.join(['<html><body>', '<span style="color:gray;">', str(ns), sep, '</span><b>', name, '</b></body></html>'])
249 else:
250 result = name
251 return result
252
255
256 @classmethod
258 '''
259 Creates the list of the items from master. This list is used for the
260 visualization of master data as a table row.
261 @param master the master data
262 @type master master_discovery_fkie.ROSMaster
263 @param local: whether the master is local or not
264 @type local: bool
265 @return: the list for the representation as a row
266 @rtype: C{[L{MasterItem} or L{PySide.QtGui.QStandardItem}, ...]}
267 '''
268 items = []
269 item = MasterSyncItem(master)
270 items.append(item)
271 item = MasterItem(master, local)
272 items.append(item)
273 return items
274
276 if isinstance(item, str) or isinstance(item, unicode):
277 return self.master.name.lower() == item.lower()
278 elif not (item is None):
279 return self.master.name.lower() == item.master.name.lower()
280 return False
281
283 if isinstance(item, str) or isinstance(item, unicode):
284 return self.master.name.lower() > item.lower()
285 elif not (item is None):
286 return self.master.name.lower() > item.master.name.lower()
287 return False
288
292 '''
293 The model to manage the list with masters in ROS network.
294 '''
295 header = [('Sync', 22), ('Name', -1)]
296 '''@ivar: the list with columns C{[(name, width), ...]}'''
297 COL_SYNC = 0
298 COL_NAME = 1
299
300 - def __init__(self, local_masteruri=None):
301 '''
302 Creates a new list model.
303 '''
304 QtGui.QStandardItemModel.__init__(self)
305 self.setColumnCount(len(MasterModel.header))
306 self._masteruri = local_masteruri
307 self.pyqt_workaround = dict()
308
310 '''
311 @param index: parent of the list
312 @type index: L{PySide.QtCore.QModelIndex}
313 @return: Flag or the requestet item
314 @rtype: L{PySide.QtCore.Qt.ItemFlag}
315 @see: U{http://www.pyside.org/docs/pyside-1.0.1/PySide/QtCore/Qt.html}
316 '''
317 if not index.isValid():
318 return QtCore.Qt.NoItemFlags
319
320
321 return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
322
323
325 '''
326 Updates the information of the ros master. If the ROS master not exists, it
327 will be added.
328
329 @param master: the ROS master to update
330 @type master: L{master_discovery_fkie.msg.ROSMaster}
331 '''
332
333 root = self.invisibleRootItem()
334 for i in reversed(range(root.rowCount())):
335 masterItem = root.child(i)
336 if masterItem.master.uri == master.uri and masterItem.master.name != master.name:
337 root.removeRow(i)
338 try:
339 del self.pyqt_workaround[masterItem.master.name]
340 except:
341 pass
342 break
343
344
345 root = self.invisibleRootItem()
346 doAddItem = True
347 for i in range(root.rowCount()):
348 masterItem = root.child(i, self.COL_NAME)
349 if (masterItem == master.name):
350
351 masterItem.master = master
352 masterItem.updateMasterView(root)
353 doAddItem = False
354 break
355 elif (masterItem > master.name):
356 mitem = MasterItem.getItemList(master, (nm.is_local(nm.nameres().getHostname(master.uri))))
357 self.pyqt_workaround[master.name] = mitem
358 root.insertRow(i, mitem)
359 mitem[self.COL_NAME].parent_item = root
360 doAddItem = False
361 break
362 if doAddItem:
363 mitem = MasterItem.getItemList(master, (nm.is_local(nm.nameres().getHostname(master.uri))))
364 self.pyqt_workaround[master.name] = mitem
365 root.appendRow(mitem)
366 mitem[self.COL_NAME].parent_item = root
367
369 '''
370 Updates the information of the ros master.
371
372 @param master: the ROS master to update
373 @type master: C{str}
374 @param quality: the quality of the connection to master
375 @type quality: C{float}
376 '''
377 root = self.invisibleRootItem()
378 for i in reversed(range(root.rowCount())):
379 masterItem = root.child(i, self.COL_NAME)
380 if masterItem.master.name in master:
381 masterItem.quality = quality
382 break
383
385 '''
386 Set the master to checked state
387
388 @param master: the ROS master to update
389 @type master: C{str}
390 @param state: new state
391 @type state: C{bool}
392 '''
393 root = self.invisibleRootItem()
394 for i in reversed(range(root.rowCount())):
395 masterItem = root.child(i, self.COL_SYNC)
396 if masterItem.master.name == master:
397 masterItem.synchronized = MasterSyncItem.SYNC if state else MasterSyncItem.NOT_SYNC
398 break
399
401 '''
402 Remove the master with given name.
403
404 @param master: the ROS master to add
405 @type master: C{str}
406 '''
407 root = self.invisibleRootItem()
408 for i in reversed(range(root.rowCount())):
409 masterItem = root.child(i, self.COL_NAME)
410 if masterItem.master.name == master:
411 root.removeRow(i)
412 try:
413 del self.pyqt_workaround_sync[masterItem.master.name]
414 del self.pyqt_workaround_info[masterItem.master.name]
415 except:
416 pass
417 break
418
420 '''
421 Updates the errors reported by master_discovery.
422
423 @param master: the ROS master to update
424 @type master: C{str}
425 @param errors: the list with errors
426 @type errors: C{[str]}
427 '''
428 root = self.invisibleRootItem()
429 for i in reversed(range(root.rowCount())):
430 masterItem = root.child(i, self.COL_NAME)
431 if masterItem.master.name in master:
432 masterItem.updateMasterErrors(errors)
433 break
434
436 '''
437 Updates the description of the master with given name.
438
439 @param master: the ROS master to add
440 @type master: C{str}
441 @param descr: the description of the master coded as HTML
442 @type descr: C{str}
443 '''
444 root = self.invisibleRootItem()
445 for i in range(root.rowCount()):
446 masterItem = root.child(i, self.COL_NAME)
447 if masterItem and masterItem.master.name == master:
448 masterItem.updateDescription(descr)
449