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