WUT Velma robot API
system_widget.py
Go to the documentation of this file.
1 # Copyright (c) 2014, Robot Control and Pattern Recognition Group, Warsaw University of Technology
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # * Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
11 # * Neither the name of the Warsaw University of Technology nor the
12 # names of its contributors may be used to endorse or promote products
13 # derived from this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 # DISCLAIMED. IN NO EVENT SHALL <COPYright HOLDER> BE LIABLE FOR ANY
19 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 from __future__ import division
27 import os
28 import subprocess
29 import tempfile
30 
31 from python_qt_binding import loadUi
32 from python_qt_binding.QtCore import Qt, QTimer, Signal, Slot, QRectF
33 from python_qt_binding.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout,\
34  QScrollArea, QGraphicsScene
35 from python_qt_binding.QtGui import QPixmap
36 import roslib
37 import rospkg
38 import rospy
39 from rospy.exceptions import ROSException
40 
41 from .topic_info import TopicInfo
42 from .subsystem_widget import SubsystemWidget
43 
44 class SystemWidget(QWidget):
45  """
46  main class inherits from the ui window class.
47 
48  You can specify the topics that the topic pane.
49 
50  SystemWidget.start must be called in order to update topic pane.
51  """
52 
53  _column_names = ['topic', 'type', 'bandwidth', 'rate', 'value']
54 
55  def __init__(self, plugin=None):
56  """
57  """
58  super(SystemWidget, self).__init__()
59 
60  rp = rospkg.RosPack()
61  ui_file = os.path.join(rp.get_path('rqt_agent'), 'resource', 'SystemWidget.ui')
62  loadUi(ui_file, self)
63 
64  self._plugin = plugin
65 
66  self._subsystems = {}
67 
68  self.all_subsystems = {}
69  self._widgets = {}
70  self._added_widgets = {}
71  self.prev_subsystems = []
72 
73  self.structure_changed = False
74 
75  self.structure_root = None
76  self.structure_graph = None
77 
78  #self.mainWidget = QWidget()
79  #self.scrollArea = QScrollArea()
80  #self.scrollArea.setWidgetResizable( True );
81  #self.scrollArea.setWidget(self.mainWidget)
82 
83  self.graphicsView.setScene(QGraphicsScene(0,0,10,10))
84 
85  # init and start update timer
86  self._timer_refresh_topics = QTimer(self)
87  self._timer_refresh_topics.timeout.connect(self.refresh_topics)
88 
90  result = False
91 
92  for subsystem_name in self.prev_subsystems:
93  if not subsystem_name in self._widgets:
94  result = True
95  break;
96 
97  if result == False:
98  for subsystem_name in self._widgets:
99  if not subsystem_name in self.prev_subsystems:
100  result = True
101  break
102 
103  self.prev_subsystems = []
104  for subsystem_name in self._widgets:
105  self.prev_subsystems.append(subsystem_name)
106 
107  return result
108 
109  def start(self):
110  """
111  This method needs to be called to start updating topic pane.
112  """
113  self._timer_refresh_topics.start(100)
114 
115  def layout_widgets(self, layout):
116  return (layout.itemAt(i) for i in range(layout.count()))
117 
118  @Slot()
119  def refresh_topics(self):
120  """
121  refresh tree view items
122  """
123  #
124  # update the list of subsystems
125  #
126  topic_list = rospy.get_published_topics()
127  if topic_list is None:
128  rospy.logerr('Not even a single published topic found. Check network configuration')
129  return
130 
131  # start new topic dict
132  new_subsystems = {}
133 
134  for topic_name, topic_type in topic_list:
135  name_split = topic_name.split('/')
136 
137  if (len(name_split) == 3) and (name_split[0] == '') and (name_split[2] == 'diag') and (topic_type == "diagnostic_msgs/DiagnosticArray"):
138  subsystem_name = name_split[1]
139  # if topic is new
140  if subsystem_name not in self._subsystems:
141  # create new TopicInfo
142  topic_info = TopicInfo(topic_name, topic_type)
143  new_subsystems[subsystem_name] = topic_info
144  topic_info.start_monitoring()
145  else:
146  # if topic has been seen before, copy it to new dict and
147  # remove it from the old one
148  new_subsystems[subsystem_name] = self._subsystems[subsystem_name]
149  del self._subsystems[subsystem_name]
150 
151  # remove unused subsystems
152  while True:
153  repeat = False
154  for s in self._subsystems:
155  if not s in new_subsystems:
156  del self._subsystems[s]
157  repeat = True
158  break
159  if not repeat:
160  break
161 
162  # switch to new topic dict
163  self._subsystems = new_subsystems
164 
165  #
166  # update each subsystem
167  #
168  new_widgets = {}
169  for subsystem_name in self._subsystems:
170  msg = self._subsystems[subsystem_name].last_message
171 
172  if (msg != None) and (len(msg.status) == 2) and \
173  msg.status[0].name == 'components' and msg.status[1].name == 'diagnostics':
174  name_split = subsystem_name.split('/')
175 
176  if not subsystem_name in self.all_subsystems:
177  self.all_subsystems[subsystem_name] = SubsystemWidget(self._plugin, subsystem_name)
178 
179  if not subsystem_name in self._widgets:
180  new_widgets[subsystem_name] = self.all_subsystems[subsystem_name]
181  else:
182  new_widgets[subsystem_name] = self._widgets[subsystem_name]
183 
184  self._widgets = new_widgets
185 
186  structure_changed = self.checkStructureChange()
187  if structure_changed:
188  self.structure_changed = True
189 
190  if self.structure_changed:
191  allInitialized = True
192  for subsystem_name, wg in new_widgets.iteritems():
193  if not wg.isInitialized():
194  allInitialized = False
195  break
196  if allInitialized:
197  #print dir(self.verticalLayout)
199 
200  # Remove all widgets from the layout
201  for subsystem_name, wg in self._added_widgets.iteritems():
202  wg.setParent(None)
203 
204  for it in range(100):
205  if self.verticalLayout.isEmpty():
206  break
207  self.verticalLayout.removeWidget()
208  self.verticalLayout.takeAt(0)
209  print 'removed widget', it
210 
211  self._added_widgets = {}
212  for subsystem_name, wg in self._widgets.iteritems():
213  self._added_widgets[subsystem_name] = wg
214 
215  for wg_name, wg in self._widgets.iteritems():
216  self.verticalLayout.addWidget(wg)
217  wg.show()
218  print 'added widget', wg_name
219 
220  self.structure_changed = False
221 
222  while True:
223  repeat = False
224  for s in self.all_subsystems:
225  if not s in self._widgets:
226  del self.all_subsystems[s]
227  repeat = True
228  break
229  if not repeat:
230  break
231 
232  for subsystem_name in self._widgets:
233  self._widgets[subsystem_name].update_subsystem(self._subsystems[subsystem_name].last_message)
234 
236  dot = 'digraph system {\n'
237  all_buf_names = set()
238  for subsystem_name, wg in self._widgets.iteritems():
239  if not wg.isInitialized():
240  dot += ' {} [style="filled,rounded" fillcolor=orange shape=box];\n'.format(subsystem_name)
241  continue
242 
243  dot += ' {} [style=rounded shape=box];\n'.format(subsystem_name)
244 
245  for buf_name in wg.subsystem_info.upper_inputs:
246  dot += ' {} -> {};\n'.format(buf_name, subsystem_name)
247  all_buf_names.add(buf_name)
248 
249  for buf_name in wg.subsystem_info.lower_inputs:
250  dot += ' {} -> {} [arrowhead=none arrowtail=normal dir=back];\n'.format(subsystem_name, buf_name)
251  all_buf_names.add(buf_name)
252 
253  for buf_name in wg.subsystem_info.upper_outputs:
254  dot += ' {} -> {} [arrowhead=none arrowtail=normal dir=back];\n'.format(buf_name, subsystem_name)
255  all_buf_names.add(buf_name)
256 
257  for buf_name in wg.subsystem_info.lower_outputs:
258  dot += ' {} -> {};\n'.format(subsystem_name, buf_name)
259  all_buf_names.add(buf_name)
260 
261  for buf_name in all_buf_names:
262  dot += ' {} [shape=box];\n'.format(buf_name)
263  dot += '}\n'
264 
265  tmpdirname = tempfile.mkdtemp()
266  in_read, in_write = os.pipe()
267  os.write(in_write, dot)
268  os.close(in_write)
269  subprocess.call(['dot', '-Tpng', '-o{}/system.png'.format(tmpdirname)], stdin=in_read)
270  os.close(in_read)
271 
272  # Clear the diagram
273  self.graphicsView.scene().clear()
274  self.graphicsView.scene().update()
275  pixmap = QPixmap('{}/system.png'.format(tmpdirname))
276  self.graphicsView.scene().addPixmap(pixmap)
277  self.graphicsView.scene().setSceneRect(QRectF(pixmap.rect()))
278  os.remove('{}/system.png'.format(tmpdirname))
279  os.rmdir(tmpdirname)
280 
281  def shutdown_plugin(self):
282  for topic in self._topics.values():
283  topic['info'].stop_monitoring()
284  self._timer_refresh_topics.stop()
285 
286  def set_selected_topics(self, selected_topics):
287  """
288  @param selected_topics: list of tuple. [(topic_name, topic_type)]
289  @type selected_topics: []
290  """
291  rospy.logdebug('set_selected_topics topics={}'.format(
292  len(selected_topics)))
293  self._selected_topics = selected_topics
294 
295  # TODO(Enhancement) Save/Restore tree expansion state
296  def save_settings(self, plugin_settings, instance_settings):
297  header_state = self.topics_tree_widget.header().saveState()
298  instance_settings.set_value('tree_widget_header_state', header_state)
299 
300  def restore_settings(self, pluggin_settings, instance_settings):
301  if instance_settings.contains('tree_widget_header_state'):
302  header_state = instance_settings.value('tree_widget_header_state')
303  if not self.topics_tree_widget.header().restoreState(header_state):
304  rospy.logwarn("rqt_topic: Failed to restore header state.")
305 
def __init__(self, plugin=None)
def save_settings(self, plugin_settings, instance_settings)
def restore_settings(self, pluggin_settings, instance_settings)
def set_selected_topics(self, selected_topics)