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
40
41 import node_manager_fkie as nm
44 ITEM_TYPE = QtGui.QStandardItem.UserType + 35
45
46 - def __init__(self, master, parent=None):
47 self.name = master.name
48 QtGui.QStandardItem.__init__(self, self.name)
49 self.parent_item = None
50 self._master = master
51 self._syncronized = False
52 self.ICONS = {'sync' : QtGui.QIcon(":/icons/crystal_clear_sync.png"),
53 'not_sync': QtGui.QIcon(":/icons/crystal_clear_not_sync.png") }
54 self.setIcon(self.ICONS['not_sync'])
55
56 @property
59
60 @property
62 return self._syncronized
63
64 @synchronized.setter
66 if self._syncronized != value:
67 self._syncronized = value
68 self.setIcon(self.ICONS['sync'] if value else self.ICONS['not_sync'])
69
71 if isinstance(item, str) or isinstance(item, unicode):
72 return self.master.name.lower() == item.lower()
73 elif not (item is None):
74 return self.master.name.lower() == item.master.name.lower()
75 return False
76
78 if isinstance(item, str) or isinstance(item, unicode):
79 return self.master.name.lower() > item.lower()
80 elif not (item is None):
81 return self.master.name.lower() > item.master.name.lower()
82 return False
83
86 '''
87 The master item stored in the master model. This class stores the master as
88 master_discovery_fkie.ROSMaster.
89 '''
90
91 ITEM_TYPE = QtGui.QStandardItem.UserType + 34
92
93 - def __init__(self, master, local=False, quality=None, parent=None):
94 self.name = ''.join([master.name, ' (localhost)']) if local else master.name
95 QtGui.QStandardItem.__init__(self, self.name)
96 self.parent_item = None
97 self._master = master
98 self.__quality = quality
99 self.descr = ''
100 self.ICONS = {'green' : QtGui.QIcon(":/icons/stock_connect_green.png"),
101 'yellow': QtGui.QIcon(":/icons/stock_connect_yellow.png"),
102 'red' : QtGui.QIcon(":/icons/stock_connect_red.png"),
103 'grey' : QtGui.QIcon(":/icons/stock_connect.png"),
104 'disconnected' : QtGui.QIcon(":/icons/stock_disconnect.png"),
105 'warning' : QtGui.QIcon(':/icons/crystal_clear_warning.png') }
106 self.master_ip = None
107 self._threaded_get_ip()
108 self.updateNameView(master, quality, self)
109
111 thread = threading.Thread(target=self.__get_ip)
112 thread.daemon = True
113 thread.start()
114
116 try:
117
118 o = urlparse(self.master.uri)
119 result = getaddrinfo(o.hostname, None)
120 ips = []
121 for r in result:
122 (family, socktype, proto, canonname, (ip, port)) = r
123 if self.master_ip is None and ip:
124 self.master_ip = ''
125 if ip and not ip in ips:
126 self.master_ip = ' '.join([self.master_ip, ip])
127 ips.append(ip)
128
129 except:
130 import traceback
131 print traceback.format_exc()
132
133 @property
136
137 @master.setter
140
141 @property
143 return self.__quality
144
145 @quality.setter
150
152 '''
153 This method is called after the master state is changed to update the
154 representation of the master. The name will not be changed, but all other
155 data.
156 @param parent: Item which contains this master item. This is needed to update
157 other columns of this master.
158 @type parent: L{PySide.QtGui.QStandardItem}
159 '''
160 if not parent is None:
161
162 child = parent.child(self.row(), MasterModel.COL_NAME)
163 if not child is None:
164 self.updateNameView(self.master, self.quality, child)
165
167 '''
168 Updates the representation of the column contains the name state.
169 @param master: the topic data
170 @type master: master_discovery_fkie.TopicInfo
171 @param item: corresponding item in the model
172 @type item: L{TopicItem}
173 '''
174 tooltip = ''.join(['<html><body>'])
175 tooltip = ''.join([tooltip, '<h4>', master.uri, '</h4>'])
176 tooltip = ''.join([tooltip, '<dl>'])
177 tooltip = ''.join([tooltip, '<dt>', 'IP: ', str(self.master_ip), '</dt>'])
178 if master.online:
179 if not quality is None:
180 tooltip = ''.join([tooltip, '<dt>', 'Quality: ', str(quality),' %', '</dt>'])
181
182
183 else:
184 tooltip = ''.join([tooltip, '<dt>', 'offline', '</dt>'])
185 tooltip = ''.join([tooltip, '</dl>'])
186 if item.descr:
187
188 tooltip = ''.join([tooltip, item.descr])
189
190 if master.online:
191 if self.master_ip is None:
192 item.setIcon(self.ICONS['warning'])
193 tooltip = ''.join([tooltip, '<h4>', 'Host not reachable by name!!! The ROS topics may not by connected!!!', '</h4>'])
194 elif not quality is None:
195 if quality > 30:
196 item.setIcon(self.ICONS['green'])
197 elif quality > 5:
198 item.setIcon(self.ICONS['yellow'])
199 else:
200 item.setIcon(self.ICONS['red'])
201 else:
202 item.setIcon(self.ICONS['grey'])
203 else:
204 item.setIcon(self.ICONS['disconnected'])
205
206 tooltip = ''.join([tooltip, '</body></html>'])
207 item.setToolTip(tooltip)
208
212
213 @classmethod
215 '''
216 @param text: the text
217 @type text: C{str}
218 @return: the HTML representation of the name of the text
219 @rtype: C{str}
220 '''
221 ns, sep, name = text.rpartition('/')
222 result = ''
223 if sep:
224 result = ''.join(['<html><body>', '<span style="color:gray;">', str(ns), sep, '</span><b>', name, '</b></body></html>'])
225 else:
226 result = name
227 return result
228
231
232 @classmethod
234 '''
235 Creates the list of the items from master. This list is used for the
236 visualization of master data as a table row.
237 @param master the master data
238 @type master master_discovery_fkie.ROSMaster
239 @param local: whether the master is local or not
240 @type local: bool
241 @return: the list for the representation as a row
242 @rtype: C{[L{MasterItem} or L{PySide.QtGui.QStandardItem}, ...]}
243 '''
244 items = []
245 item = MasterSyncItem(master)
246 items.append(item)
247 item = MasterItem(master, local)
248 items.append(item)
249 return items
250
252 if isinstance(item, str) or isinstance(item, unicode):
253 return self.master.name.lower() == item.lower()
254 elif not (item is None):
255 return self.master.name.lower() == item.master.name.lower()
256 return False
257
259 if isinstance(item, str) or isinstance(item, unicode):
260 return self.master.name.lower() > item.lower()
261 elif not (item is None):
262 return self.master.name.lower() > item.master.name.lower()
263 return False
264
268 '''
269 The model to manage the list with masters in ROS network.
270 '''
271 header = [('Sync', 22), ('Name', -1)]
272 '''@ivar: the list with columns C{[(name, width), ...]}'''
273 COL_SYNC = 0
274 COL_NAME = 1
275
276 - def __init__(self, local_masteruri=None):
277 '''
278 Creates a new list model.
279 '''
280 QtGui.QStandardItemModel.__init__(self)
281 self.setColumnCount(len(MasterModel.header))
282 self._masteruri = local_masteruri
283 self.pyqt_workaround = dict()
284
286 '''
287 @param index: parent of the list
288 @type index: L{PySide.QtCore.QModelIndex}
289 @return: Flag or the requestet 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
296
297 return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
298
299
301 '''
302 Updates the information of the ros master. If the ROS master not exists, it
303 will be added.
304
305 @param master: the ROS master to update
306 @type master: L{master_discovery_fkie.msg.ROSMaster}
307 '''
308
309 root = self.invisibleRootItem()
310 for i in reversed(range(root.rowCount())):
311 masterItem = root.child(i)
312 if masterItem.master.uri == master.uri and masterItem.master.name != master.name:
313 root.removeRow(i)
314 try:
315 del self.pyqt_workaround[masterItem.master.name]
316 except:
317 pass
318 break
319
320
321 root = self.invisibleRootItem()
322 doAddItem = True
323 for i in range(root.rowCount()):
324 masterItem = root.child(i, self.COL_NAME)
325 if (masterItem == master.name):
326
327 masterItem.master = master
328 masterItem.updateMasterView(root)
329 doAddItem = False
330 break
331 elif (masterItem > master.name):
332 mitem = MasterItem.getItemList(master, (nm.is_local(nm.nameres().getHostname(master.uri))))
333 self.pyqt_workaround[master.name] = mitem
334 root.insertRow(i, mitem)
335 mitem[self.COL_NAME].parent_item = root
336 doAddItem = False
337 break
338 if doAddItem:
339 mitem = MasterItem.getItemList(master, (nm.is_local(nm.nameres().getHostname(master.uri))))
340 self.pyqt_workaround[master.name] = mitem
341 root.appendRow(mitem)
342 mitem[self.COL_NAME].parent_item = root
343
345 '''
346 Updates the information of the ros master.
347
348 @param master: the ROS master to update
349 @type master: C{str}
350 @param quality: the quality of the connection to master
351 @type quality: C{float}
352 '''
353 root = self.invisibleRootItem()
354 for i in reversed(range(root.rowCount())):
355 masterItem = root.child(i, self.COL_NAME)
356 if masterItem.master.name in master:
357 masterItem.quality = quality
358 break
359
361 '''
362 Set the master to checked state
363
364 @param master: the ROS master to update
365 @type master: C{str}
366 @param state: new state
367 @type state: C{bool}
368 '''
369 root = self.invisibleRootItem()
370 for i in reversed(range(root.rowCount())):
371 masterItem = root.child(i, self.COL_SYNC)
372 if masterItem.master.name == master:
373 masterItem.synchronized = state
374 break
375
377 '''
378 Remove the master with given name.
379
380 @param master: the ROS master to add
381 @type master: C{str}
382 '''
383 root = self.invisibleRootItem()
384 for i in reversed(range(root.rowCount())):
385 masterItem = root.child(i, self.COL_NAME)
386 if masterItem.master.name == master:
387 root.removeRow(i)
388 try:
389 del self.pyqt_workaround_sync[masterItem.master.name]
390 del self.pyqt_workaround_info[masterItem.master.name]
391 except:
392 pass
393 break
394
396 '''
397 Updates the description of the master with given name.
398
399 @param master: the ROS master to add
400 @type master: C{str}
401 @param descr: the description of the master coded as HTML
402 @type descr: C{str}
403 '''
404 root = self.invisibleRootItem()
405 for i in range(root.rowCount()):
406 masterItem = root.child(i, self.COL_NAME)
407 if masterItem and masterItem.master.name == master:
408 masterItem.updateDescription(descr)
409