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