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

Source Code for Module node_manager_fkie.master_list_model

  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  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 
42 43 -class MasterSyncItem(QtGui.QStandardItem):
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
57 - def master(self):
58 return self._master
59 60 @property
61 - def synchronized(self):
62 return self._syncronized
63 64 @synchronized.setter
65 - def synchronized(self, value):
66 if self._syncronized != value: 67 self._syncronized = value 68 self.setIcon(self.ICONS['sync'] if value else self.ICONS['not_sync'])
69
70 - def __eq__(self, item):
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
77 - def __gt__(self, item):
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
84 85 -class MasterItem(QtGui.QStandardItem):
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
110 - def _threaded_get_ip(self):
111 thread = threading.Thread(target=self.__get_ip) 112 thread.daemon = True 113 thread.start()
114
115 - def __get_ip(self):
116 try: 117 # get the IP of the master uri 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 # self.updateNameView(self.master, self.quality, self) 129 except: 130 import traceback 131 print traceback.format_exc()
132 133 @property
134 - def master(self):
135 return self._master
136 137 @master.setter
138 - def master(self, value):
139 self._master = value
140 141 @property
142 - def quality(self):
143 return self.__quality
144 145 @quality.setter
146 - def quality(self, value):
147 if self.__quality != value: 148 self.__quality = value 149 self.updateMasterView(self.parent_item)
150
151 - def updateMasterView(self, parent):
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 #update the name decoration 162 child = parent.child(self.row(), MasterModel.COL_NAME) 163 if not child is None: 164 self.updateNameView(self.master, self.quality, child)
165
166 - def updateNameView(self, master, quality, item):
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 # if item.checkState() == QtCore.Qt.Checked: 182 # tooltip = ''.join([tooltip, '<dt>', 'synchronized', '</dt>']) 183 else: 184 tooltip = ''.join([tooltip, '<dt>', 'offline', '</dt>']) 185 tooltip = ''.join([tooltip, '</dl>']) 186 if item.descr: 187 # tooltip = ''.join([tooltip, '<b><u>Description:</u></b>']) 188 tooltip = ''.join([tooltip, item.descr]) 189 # update the icon 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
209 - def updateDescription(self, descr):
210 self.descr = descr 211 self.updateNameView(self.master, self.quality, self)
212 213 @classmethod
214 - def toHTML(cls, text):
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
229 - def type(self):
230 return MasterItem.ITEM_TYPE
231 232 @classmethod
233 - def getItemList(self, master, local):
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
251 - def __eq__(self, item):
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
258 - def __gt__(self, item):
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
265 266 267 -class MasterModel(QtGui.QStandardItemModel):
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() # workaround for using with PyQt: store the python object to keep the defined attributes in the MasterItem subclass
284
285 - def flags(self, index):
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 # item = self.itemFromIndex(index) 296 # if item and item.master.online: 297 return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
298 # return QtCore.Qt.NoItemFlags 299
300 - def updateMaster(self, master):
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 # remove master, if his name was changed but not the ROS master URI 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 # update or add a the item 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 # update item 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 # workaround for using with PyQt: store the python object to keep the defined attributes in the MasterItem subclass 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 # workaround for using with PyQt: store the python object to keep the defined attributes in the MasterItem subclass 341 root.appendRow(mitem) 342 mitem[self.COL_NAME].parent_item = root
343
344 - def updateMasterStat(self, master, quality):
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
360 - def setChecked(self, master, state):
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
376 - def removeMaster(self, master):
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
395 - def updateDescription(self, master, descr):
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