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

Source Code for Module node_manager_fkie.topic_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 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
62 - def __init__(self, name, parent=None):
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 # def __del__(self): 77 # print "delete TOPIC", self._topic.name 78 79 @property
80 - def topic(self):
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 # update the tooltip and icon 106 # if pubs_changed or subs_changed: 107 # self._topic = topic_info.copy() 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
116 - def updatePublisherView(self):
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
131 - def updateSubscriberView(self):
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
146 - def updateTypeView(self):
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 # removed tooltip for clarity !!! 157 # tooltip = '' 158 try: 159 mclass = roslib.message.get_message_class(self.topic.type) 160 # tooltip = str(mclass) 161 if not mclass is None: 162 # tooltip = str(mclass.__slots__) 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 # tooltip = ''.join([tooltip, '\n\t', str(f), ': ', str(idtype), ' (', str(primitive),')']) 177 except ValueError: 178 pass
179 # cfg_col.setToolTip(tooltip) 180
181 - def _on_wait_for_publishing(self):
182 self.updateIconView(QtGui.QIcon(':/icons/state_off.png'))
183
184 - def _on_partial_publishing(self):
185 self.updateIconView(QtGui.QIcon(':/icons/state_part.png'))
186
187 - def _on_publishing(self):
188 self.updateIconView(QtGui.QIcon(':/icons/state_run.png'))
189
190 - def _publish_finished(self):
191 self._publish_thread = None 192 self.setIcon(QtGui.QIcon())
193
194 - def show_error_msg(self, msg):
195 WarningMessageBox(QtGui.QMessageBox.Warning, "Publish error", 196 'Error while publish to %s'%self.topic.name, 197 str(msg)).exec_()
198 199 @classmethod
200 - def toHTML(cls, topic_name):
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
216 - def type(self):
217 return TopicItem.ITEM_TYPE
218 219 @classmethod
220 - def getItemList(self, name, root):
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 # TopicItem.updatePublisherView(topic, pubItem) 236 items.append(pubItem) 237 subItem = QtGui.QStandardItem() 238 # TopicItem.updateSubscriberView(topic, subItem) 239 items.append(subItem) 240 typeItem = QtGui.QStandardItem() 241 # TopicItem.updateTypeView(topic, typeItem) 242 items.append(typeItem) 243 return items
244
245 246 # def __eq__(self, item): 247 # ''' 248 # Compares the name of topic. 249 # ''' 250 # if isinstance(item, str) or isinstance(item, unicode): 251 # return self.topic.name.lower() == item.lower() 252 # elif not (item is None): 253 # return self.topic.name.lower() == item.topic.name.lower() 254 # return False 255 # 256 # def __gt__(self, item): 257 # ''' 258 # Compares the name of topic. 259 # ''' 260 # if isinstance(item, str) or isinstance(item, unicode): 261 # return self.topic.name.lower() > item.lower() 262 # elif not (item is None): 263 # return self.topic.name.lower() > item.topic.name.lower() 264 # return False 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
277 - def __init__(self):
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
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 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
297 - def updateModelData(self, topics):
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 #remove or update the existing items 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 # insert other items in sorted order 316 # cputimes = os.times() 317 # cputime_init = cputimes[0] + cputimes[1] 318 for (name, topic) in topics.items(): 319 doAddItem = True 320 self._update_topic(name, topic, updated)
321 # cputimes = os.times() 322 # cputime = cputimes[0] + cputimes[1] - cputime_init 323 # print " update topic ", cputime, ", topic count:", len(topics) 324
325 - def _update_topic(self, name, topic=None, updated=None):
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