smach_viewer.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # Copyright (c) 2010, Willow Garage, Inc.
4 # All rights reserved.
5 # Copyright (c) 2013, Jonathan Bohren, The Johns Hopkins University
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions 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 copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # * Neither the name of the Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
30 #
31 # Author: Jonathan Bohren
32 
33 import rospkg
34 import rospy
35 
36 from sensor_msgs.msg import Image
37 
38 import cv_bridge
39 import os
40 import sys
41 import threading
42 import time
43 import numpy as np
44 
45 from distutils.version import LooseVersion
46 
47 try:
48  import wxversion
49  if wxversion.checkInstalled("2.8"):
50  wxversion.select("2.8")
51  else:
52  print("wxversion 2.8 is not installed, installed versions are {}".format(wxversion.getInstalled()))
53 
54 
58  def import_non_local(name, custom_name=None):
59  import imp, sys
60 
61  custom_name = custom_name or name
62 
63  path = filter(lambda x: x != os.path.dirname(os.path.abspath(__file__)), sys.path)
64  f, pathname, desc = imp.find_module(name, path)
65 
66  module = imp.load_module(custom_name, f, pathname, desc)
67  if f:
68  f.close()
69 
70  return module
71 
72  smach_viewer = import_non_local('smach_viewer')
73  from smach_viewer import xdot
74  from smach_viewer.xdot import wxxdot
75  from smach_viewer.xdot.xdot import TextShape
76 except:
77  # Guard against self import
78  this_dir = os.path.dirname(__file__)
79  # Use os.getcwd() to aovid weird symbolic link problems
80  cur_dir = os.getcwd()
81  os.chdir(this_dir)
82  this_dir_cwd = os.getcwd()
83  os.chdir(cur_dir)
84  # Remove this dir from path
85  sys.path = [a for a in sys.path if a not in [this_dir, this_dir_cwd]]
86  # Ignore path ending with smach_viewer/lib/smach_viewer
87  sys.path = [a for a in sys.path if not a.endswith('smach_viewer/lib/smach_viewer')]
88  #
89  from smach_viewer.xdot import wxxdot
90  from xdot.ui.elements import *
91 
92 
93 import wx
94 import wx.richtext
95 
96 from smach_viewer.smach_viewer_base import ContainerNode
97 from smach_viewer.smach_viewer_base import SmachViewerBase
98 from smach_viewer.utils import get_label
99 from smach_viewer.utils import get_parent_path
100 from smach_viewer.utils import hex2t
101 
102 
104  def set_styles(self, selected_paths, depth, max_depth, items, subgraph_shapes, containers):
105  """Update the styles for a list of containers without regenerating the dotcode.
106 
107  This function is called recursively to update an entire tree.
108 
109  @param selected_paths: A list of paths to nodes that are currently selected.
110  @param depth: The depth to start traversing the tree
111  @param max_depth: The depth to traverse into the tree
112  @param items: A dict of all the graph items, keyed by url
113  @param subgraph_shapes: A dictionary of shapes from the rendering engine
114  @param containers: A dict of all the containers
115  """
116 
117  # Color root container
118  """
119  if depth == 0:
120  container_shapes = subgraph_shapes['cluster_'+self._path]
121  container_color = (0,0,0,0)
122  container_fillcolor = (0,0,0,0)
123 
124  for shape in container_shapes:
125  shape.pen.color = container_color
126  shape.pen.fillcolor = container_fillcolor
127  """
128 
129  # Color shapes for outcomes
130 
131  # Color children
132  if max_depth == -1 or depth <= max_depth:
133  # Iterate over children
134  for child_label in self._children:
135  child_path = '/'.join([self._path,child_label])
136 
137  child_color = [0.5,0.5,0.5,1]
138  child_fillcolor = [1,1,1,1]
139  child_linewidth = 2
140 
141  active_color = hex2t('#5C7600FF')
142  active_fillcolor = hex2t('#C0F700FF')
143 
144  initial_color = hex2t('#000000FF')
145 
146  if child_label in self._active_states:
147  # Check if the child is active
148  child_color = active_color
149  child_fillcolor = active_fillcolor
150  child_linewidth = 5
151  elif child_label in self._initial_states:
152  # Initial style
153  #child_fillcolor = initial_fillcolor
154  child_color = initial_color
155  child_linewidth = 2
156 
157  # Check if the child is selected
158  if child_path in selected_paths:
159  child_color = hex2t('#FB000DFF')
160 
161  # Generate dotcode for child containers
162  if child_path in containers:
163  subgraph_id = 'cluster_'+child_path
164  if subgraph_id in subgraph_shapes:
165  if child_label in self._active_states:
166  child_fillcolor[3] = 0.25
167  elif 0 and child_label in self._initial_states:
168  child_fillcolor[3] = 0.25
169  else:
170  if max_depth > 0:
171  v = 1.0-0.25*((depth+1)/float(max_depth))
172  else:
173  v = 0.85
174  child_fillcolor = [v,v,v,1.0]
175 
176  for shape in subgraph_shapes['cluster_'+child_path]:
177  pen = shape.pen
178  if len(pen.color) > 3:
179  pen_color_opacity = pen.color[3]
180  if pen_color_opacity < 0.01:
181  pen_color_opacity = 0
182  else:
183  pen_color_opacity = 0.5
184  shape.pen.color = child_color[0:3]+[pen_color_opacity]
185  shape.pen.fillcolor = [child_fillcolor[i] for i in range(min(3,len(pen.fillcolor)))]
186  shape.pen.linewidth = child_linewidth
187 
188  # Recurse on this child
189  containers[child_path].set_styles(
190  selected_paths,
191  depth+1, max_depth,
192  items,
193  subgraph_shapes,
194  containers)
195  else:
196  if child_path in items:
197  for shape in items[child_path].shapes:
198  if not isinstance(shape, TextShape):
199  shape.pen.color = child_color
200  shape.pen.fillcolor = child_fillcolor
201  shape.pen.linewidth = child_linewidth
202  else:
203  # print child_path+" NOT IN "+str(items.keys())
204  pass
205 
206 
208  """
209  This class provides a GUI application for viewing SMACH plans.
210  """
211 
212  _container_class = WxContainerNode
213 
214  def __init__(self):
215  wx.Frame.__init__(self, None, -1, "Smach Viewer", size=(720,480))
216  SmachViewerBase.__init__(self)
217  vbox = wx.BoxSizer(wx.VERTICAL)
218 
219  # Create Splitter
220  self.content_splitter = wx.SplitterWindow(self, -1,style = wx.SP_LIVE_UPDATE)
221  self.content_splitter.SetMinimumPaneSize(24)
222  self.content_splitter.SetSashGravity(0.85)
223 
224  # Create viewer pane
225  viewer = wx.Panel(self.content_splitter,-1)
226 
227  # Create smach viewer
228  nb = wx.Notebook(viewer,-1,style=wx.NB_TOP | wx.WANTS_CHARS)
229  viewer_box = wx.BoxSizer()
230  viewer_box.Add(nb,1,wx.EXPAND | wx.ALL, 4)
231  viewer.SetSizer(viewer_box)
232 
233  # Create graph view
234  graph_view = wx.Panel(nb,-1)
235  gv_vbox = wx.BoxSizer(wx.VERTICAL)
236  graph_view.SetSizer(gv_vbox)
237 
238  # Construct toolbar
239  toolbar = wx.ToolBar(graph_view, -1)
240 
241  toolbar.AddControl(wx.StaticText(toolbar,-1,"Path: "))
242 
243  # Path list
244  self.path_combo = wx.ComboBox(toolbar, -1, style=wx.CB_DROPDOWN)
245  self.path_combo .Bind(wx.EVT_COMBOBOX, self.set_path)
246  self.path_combo.Append('/')
247  self.path_combo.SetValue('/')
248  toolbar.AddControl(self.path_combo)
249 
250  # Depth spinner
251  self.depth_spinner = wx.SpinCtrl(toolbar, -1,
252  size=wx.Size(50,-1),
253  min=-1,
254  max=1337,
255  initial=-1)
256  self.depth_spinner.Bind(wx.EVT_SPINCTRL,self.set_depth)
257  toolbar.AddControl(wx.StaticText(toolbar,-1," Depth: "))
258  toolbar.AddControl(self.depth_spinner)
259 
260  # Label width spinner
261  self.width_spinner = wx.SpinCtrl(toolbar, -1,
262  size=wx.Size(50,-1),
263  min=1,
264  max=1337,
265  initial=40)
266  self.width_spinner.Bind(wx.EVT_SPINCTRL,self.set_label_width)
267  toolbar.AddControl(wx.StaticText(toolbar,-1," Label Width: "))
268  toolbar.AddControl(self.width_spinner)
269 
270  # Implicit transition display
271  toggle_all = wx.ToggleButton(toolbar,-1,'Show Implicit')
272  toggle_all.Bind(wx.EVT_TOGGLEBUTTON, self.toggle_all_transitions)
273 
274  toolbar.AddControl(wx.StaticText(toolbar,-1," "))
275  toolbar.AddControl(toggle_all)
276 
277  toggle_auto_focus = wx.ToggleButton(toolbar, -1, 'Auto Focus')
278  toggle_auto_focus.Bind(wx.EVT_TOGGLEBUTTON, self.toggle_auto_focus)
279  self._auto_focus = False
280 
281  toolbar.AddControl(wx.StaticText(toolbar, -1, " "))
282  toolbar.AddControl(toggle_auto_focus)
283 
284  toolbar.AddControl(wx.StaticText(toolbar,-1," "))
285  if LooseVersion(wx.__version__) >= LooseVersion('4.0'):
286  toolbar.AddTool(wx.ID_HELP, 'Help',
287  wx.ArtProvider.GetBitmap(wx.ART_HELP,wx.ART_OTHER,(16,16)) )
288  toolbar.AddTool(wx.ID_SAVE, 'Save',
289  wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE,wx.ART_OTHER,(16,16)) )
290  else:
291  toolbar.AddLabelTool(wx.ID_HELP, 'Help',
292  wx.ArtProvider.GetBitmap(wx.ART_HELP,wx.ART_OTHER,(16,16)) )
293  toolbar.AddLabelTool(wx.ID_SAVE, 'Save',
294  wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE,wx.ART_OTHER,(16,16)) )
295  toolbar.Realize()
296 
297  self.Bind(wx.EVT_TOOL, self.ShowControlsDialog, id=wx.ID_HELP)
298  self.Bind(wx.EVT_TOOL, self.SaveDotGraph, id=wx.ID_SAVE)
299 
300  # Create dot graph widget
301  self.widget = wxxdot.WxDotWindow(graph_view, -1)
302 
303  gv_vbox.Add(toolbar, 0, wx.EXPAND)
304  gv_vbox.Add(self.widget, 1, wx.EXPAND)
305 
306  # Create tree view widget
307  self.tree = wx.TreeCtrl(nb,-1,style=wx.TR_HAS_BUTTONS)
308  nb.AddPage(graph_view,"Graph View")
309  nb.AddPage(self.tree,"Tree View")
310 
311 
312  # Create userdata widget
313  borders = wx.LEFT | wx.RIGHT | wx.TOP
314  border = 4
315  self.ud_win = wx.ScrolledWindow(self.content_splitter, -1)
316  self.ud_gs = wx.BoxSizer(wx.VERTICAL)
317 
318  self.ud_gs.Add(wx.StaticText(self.ud_win,-1,"Path:"),0, borders, border)
319 
320  self.path_input = wx.ComboBox(self.ud_win,-1,style=wx.CB_DROPDOWN)
321  self.path_input.Bind(wx.EVT_COMBOBOX,self.selection_changed)
322  self.ud_gs.Add(self.path_input,0,wx.EXPAND | borders, border)
323 
324 
325  self.ud_gs.Add(wx.StaticText(self.ud_win,-1,"Userdata:"),0, borders, border)
326 
327  self.ud_txt = wx.TextCtrl(self.ud_win,-1,style=wx.TE_MULTILINE | wx.TE_READONLY)
328  self.ud_gs.Add(self.ud_txt,1,wx.EXPAND | borders, border)
329 
330  # Add initial state button
331  self.is_button = wx.Button(self.ud_win,-1,"Set as Initial State")
332  self.is_button.Bind(wx.EVT_BUTTON, self.on_set_initial_state)
333  self.is_button.Disable()
334  self.ud_gs.Add(self.is_button,0,wx.EXPAND | wx.BOTTOM | borders, border)
335 
336  self.ud_win.SetSizer(self.ud_gs)
337 
338 
339  # Set content splitter
340  self.content_splitter.SplitVertically(viewer, self.ud_win, 512)
341 
342  # Add statusbar
343  self.statusbar = wx.StatusBar(self,-1)
344 
345  # Add elements to sizer
346  vbox.Add(self.content_splitter, 1, wx.EXPAND | wx.ALL)
347  vbox.Add(self.statusbar, 0, wx.EXPAND)
348 
349  self.SetSizer(vbox)
350  self.Center()
351 
352  self._pub = rospy.Publisher('~image', Image, queue_size=1)
353 
354  self.Bind(wx.EVT_IDLE,self.OnIdle)
355  self.Bind(wx.EVT_CLOSE,self.OnQuit)
356 
357  # Register mouse event callback
358  self.widget.register_select_callback(self.select_cb)
359 
360  # Start a thread in the background to update the server list
361  self._update_tree_thread = threading.Thread(target=self._update_tree)
362  self._update_tree_thread.start()
363 
364  # image publish timer
365  self.timer = wx.Timer(self, 0)
366  self.Bind(wx.EVT_TIMER, self.OnTimer)
367  self.timer.Start(200)
368 
369 
370  def OnQuit(self,event):
371  """Quit Event: kill threads and wait for join."""
372  self.kill()
373  self._update_tree_thread.join()
374  event.Skip()
375 
376  def update_graph(self):
377  """Notify all that the graph needs to be updated."""
378  with self._update_cond:
379  self._update_cond.notify_all()
380 
381  def on_set_initial_state(self, event):
382  """Event: Change the initial state of the server."""
383  state_path = self._selected_paths[0]
384  parent_path = get_parent_path(state_path)
385  state = get_label(state_path)
386 
387  server_name = self._containers[parent_path]._server_name
388  self._client.set_initial_state(server_name,parent_path,[state],timeout = rospy.Duration(60.0))
389 
390  def set_path(self, event):
391  """Event: Change the viewable path and update the graph."""
392  self._path = self.path_combo.GetValue()
393  self._needs_zoom = True
394  self.update_graph()
395 
396  def _set_path(self, path):
397  self._path = path
398  self._needs_zoom = True
399  self.path_combo.SetValue(path)
400  self.update_graph()
401 
402  def set_depth(self, event):
403  """Event: Change the maximum depth and update the graph."""
404  self._max_depth = self.depth_spinner.GetValue()
405  self._needs_zoom = True
406  self.update_graph()
407 
408  def _set_max_depth(self, max_depth):
409  self._max_depth = max_depth
410  self.depth_spinner.SetValue(max_depth)
411  self._needs_zoom = True
412  self.update_graph()
413 
414  def set_label_width(self, event):
415  """Event: Change the label wrapper width and update the graph."""
416  self._label_wrapper.width = self.width_spinner.GetValue()
417  self._needs_zoom = True
418  self.update_graph()
419 
420  def toggle_all_transitions(self, event):
421  """Event: Change whether automatic transitions are hidden and update the graph."""
423  self._structure_changed = True
424  self.update_graph()
425 
426  def toggle_auto_focus(self, event):
427  """Event: Enable/Disable automatically focusing"""
428  self._auto_focus = not self._auto_focus
429  self._needs_zoom = self._auto_focus
430  self._structure_changed = True
431  if not self._auto_focus:
432  self._set_path('/')
433  self._set_max_depth(-1)
434  self.update_graph()
435 
436  def select_cb(self, item, event):
437  """Event: Click to select a graph node to display user data and update the graph."""
438 
439  # Only set string status
440  try:
441  if not type(item.url) is str:
442  return
443  except AttributeError:
444  return
445 
446  self.statusbar.SetStatusText(item.url)
447  # Left button-up
448  if event.ButtonUp(wx.MOUSE_BTN_LEFT):
449  # Store this item's url as the selected path
450  self._selected_paths = [item.url]
451  # Update the selection dropdown
452  self.path_input.SetValue(item.url)
453  wx.PostEvent(
454  self.path_input.GetEventHandler(),
455  wx.CommandEvent(wx.wxEVT_COMMAND_COMBOBOX_SELECTED,self.path_input.GetId()))
456  self.update_graph()
457 
458  def selection_changed(self, event):
459  """Event: Selection dropdown changed."""
460  path_input_str = self.path_input.GetValue()
461 
462  # Check the path is non-zero length
463  if len(path_input_str) > 0:
464  # Split the path (state:outcome), and get the state path
465  path = path_input_str.split(':')[0]
466 
467  # Get the container corresponding to this path, since userdata is
468  # stored in the containers
469  if path not in self._containers:
470  parent_path = get_parent_path(path)
471  else:
472  parent_path = path
473 
474  if parent_path in self._containers:
475  # Enable the initial state button for the selection
476  self.is_button.Enable()
477 
478  # Get the container
479  container = self._containers[parent_path]
480 
481  # Store the scroll position and selection
482  pos = self.ud_txt.HitTestPos(wx.Point(0,0))
483  sel = self.ud_txt.GetSelection()
484 
485  # Generate the userdata string
486  ud_str = ''
487  if not isinstance(container._local_data._data, dict):
488  rospy.logwarn("userdata is not dictionary({}), please fix sender proram".format(container._local_data._data))
489  container._local_data._data = {}
490  for (k,v) in container._local_data._data.items():
491  ud_str += str(k)+": "
492  vstr = str(v)
493  # Add a line break if this is a multiline value
494  if vstr.find('\n') != -1:
495  ud_str += '\n'
496  ud_str+=vstr+'\n\n'
497 
498  # Set the userdata string
499  self.ud_txt.SetValue(ud_str)
500 
501  # Restore the scroll position and selection
502  self.ud_txt.ShowPosition(pos[1])
503  if sel != (0,0):
504  self.ud_txt.SetSelection(sel[0],sel[1])
505  else:
506  # Disable the initial state button for this selection
507  self.is_button.Disable()
508 
509  def _structure_msg_update(self, msg, server_name):
510  """Update the structure of the SMACH plan (re-generate the dotcode)."""
511 
512  # Just return if we're shutting down
513  if not self._keep_running:
514  return
515 
516  SmachViewerBase._structure_msg_update(self, msg, server_name)
517  path = msg.path
518  if path not in self._containers:
519  self.path_combo.Append(path)
520  self.path_input.Append(path)
521 
522  def _status_msg_update(self, msg):
523  """Process status messages."""
524 
525  # Check if we're in the process of shutting down
526  if not self._keep_running:
527  return
528 
529  if self._auto_focus and len(msg.info) > 0:
530  self._set_path(msg.info)
531  self._set_max_depth(msg.info.count('/')-1)
532 
533  SmachViewerBase._status_msg_update(self, msg)
534  path = msg.path
535  # Check if this is a known container
536  if path not in self._containers:
537  return
538 
539  # TODO(???): Is this necessary?
540  path_input_str = self.path_input.GetValue()
541  if path_input_str == path or get_parent_path(path_input_str) == path:
542  wx.PostEvent(
543  self.path_input.GetEventHandler(),
544  wx.CommandEvent(
545  wx.wxEVT_COMMAND_COMBOBOX_SELECTED,
546  self.path_input.GetId()))
547 
549  containers_to_update = SmachViewerBase._update_graph_step(self)
550 
551  # Get the containers to update
552  if self._structure_changed or self._needs_zoom:
553  # Set the dotcode to the new dotcode, reset the flags
554  try:
555  self.set_dotcode(self.dotstr, zoom=False)
556  except UnicodeDecodeError as e:
557  # multibyte language only accepts even number
558  label_width = self._label_wrapper.width
559  rospy.logerr('label width {} causes error'.format(label_width))
560  rospy.logerr('maybe multibyte word is in your label.')
561  rospy.logerr(e)
562  rospy.logerr('changing width label width to {}'.format(
563  label_width + 1))
564  self._label_wrapper.width = label_width + 1
565  self._structure_changed = False
566 
567  if hasattr(self.widget, 'subgraph_shapes'):
568  # Update the styles for the graph if there are any updates
569  for path, tc in containers_to_update.items():
570  tc.set_styles(
571  self._selected_paths,
572  0, self._max_depth,
573  self.widget.items_by_url,
574  self.widget.subgraph_shapes,
575  self._containers)
576 
577  # Redraw
578  self.widget.Refresh()
579 
580  def set_dotcode(self, dotcode, zoom=True):
581  """Set the xdot view's dotcode and refresh the display."""
582  # Set the new dotcode
583  if self.widget.set_dotcode(dotcode, None):
584  self.SetTitle('Smach Viewer')
585  # Re-zoom if necessary
586  if zoom or self._needs_zoom:
587  self.widget.zoom_to_fit()
588  self._needs_zoom = False
589  # Set the refresh flag
590  self._needs_refresh = True
591  wx.PostEvent(self.GetEventHandler(), wx.IdleEvent())
592 
593  def _update_tree(self):
594  """Update the tree view."""
595  while self._keep_running and not rospy.is_shutdown():
596  with self._update_cond:
597  self._update_cond.wait()
598  self.tree.DeleteAllItems()
599  self._tree_nodes = {}
600  for path,tc in self._top_containers.items():
601  self.add_to_tree(path, None)
602 
603  def add_to_tree(self, path, parent):
604  """Add a path to the tree view."""
605  if parent is None:
606  container = self.tree.AddRoot(get_label(path))
607  else:
608  container = self.tree.AppendItem(parent,get_label(path))
609 
610  # Add children to tree
611  for label in self._containers[path]._children:
612  child_path = '/'.join([path,label])
613  if child_path in list(self._containers.keys()):
614  self.add_to_tree(child_path, container)
615  else:
616  self.tree.AppendItem(container,label)
617 
618  def append_tree(self, container, parent = None):
619  """Append an item to the tree view."""
620  if not parent:
621  node = self.tree.AddRoot(container._label)
622  for child_label in container._children:
623  self.tree.AppendItem(node,child_label)
624 
625  def OnIdle(self, event):
626  """Event: On Idle, refresh the display if necessary, then un-set the flag."""
627  if self._needs_refresh:
628  self.Refresh()
629  # Re-populate path combo
630  self._needs_refresh = False
631 
632  def OnTimer(self, event):
633  if self._pub.get_num_connections() < 1:
634  rospy.logwarn_once("Publishing {} requires at least one subscriber".format(self._pub.name))
635  return
636  # image
637  context = wx.ClientDC(self)
638  memory = wx.MemoryDC()
639  x, y = self.ClientSize
640  if LooseVersion(wx.__version__) >= LooseVersion('4.0'):
641  bitmap = wx.Bitmap(x, y, -1)
642  else:
643  bitmap = wx.EmptyBitmap(x, y, -1)
644  memory.SelectObject(bitmap)
645  memory.Blit(0, 0, x, y, context, 0, 0)
646  memory.SelectObject(wx.NullBitmap)
647  if LooseVersion(wx.__version__) >= LooseVersion('4.0'):
648  buf = bitmap.ConvertToImage().GetDataBuffer()
649  else:
650  buf = wx.ImageFromBitmap(bitmap).GetDataBuffer()
651  img = np.frombuffer(buf, dtype=np.uint8)
652  bridge = cv_bridge.CvBridge()
653  img_msg = bridge.cv2_to_imgmsg(img.reshape((y, x, 3)), encoding='rgb8')
654  img_msg.header.stamp = rospy.Time.now()
655  self._pub.publish(img_msg)
656 
657  def ShowControlsDialog(self,event):
658  dial = wx.MessageDialog(None,
659  "Pan: Arrow Keys\nZoom: PageUp / PageDown\nZoom To Fit: F\nRefresh: R",
660  'Keyboard Controls', wx.OK)
661  dial.ShowModal()
662 
663  def SaveDotGraph(self,event):
664  timestr = time.strftime("%Y%m%d-%H%M%S")
665  directory = rospkg.get_ros_home()+'/dotfiles/'
666  if not os.path.exists(directory):
667  os.makedirs(directory)
668  filename = directory+timestr+'.dot'
669  print('Writing to file: %s' % filename)
670  with open(filename, 'w') as f:
671  f.write(self.dotstr)
672 
673  def OnExit(self, event):
674  pass
675 
676  def set_filter(self, filter):
677  self.widget.set_filter(filter)
678 
679 def main():
680  from argparse import ArgumentParser
681  p = ArgumentParser()
682  p.add_argument('-f', '--auto-focus',
683  action='store_true',
684  help="Enable 'AutoFocus to subgraph' as default",
685  dest='enable_auto_focus')
686  args = p.parse_args()
687  app = wx.App()
688 
689  frame = SmachViewerFrame()
690  frame.set_filter('dot')
691 
692  frame.Show()
693 
694  if args.enable_auto_focus:
695  frame.toggle_auto_focus(None)
696 
697  app.MainLoop()
698 
699 if __name__ == '__main__':
700  rospy.init_node('smach_viewer',anonymous=False, disable_signals=True,log_level=rospy.INFO)
701  sys.argv = rospy.myargv()
702  main()
smach_viewer.smach_viewer_base.ContainerNode
Definition: smach_viewer_base.py:22
smach_viewer.smach_viewer_base.SmachViewerBase.dotstr
dotstr
Definition: smach_viewer_base.py:311
smach_viewer.SmachViewerFrame.depth_spinner
depth_spinner
Definition: smach_viewer.py:251
smach_viewer.SmachViewerFrame.content_splitter
content_splitter
Definition: smach_viewer.py:220
smach_viewer.WxContainerNode.set_styles
def set_styles(self, selected_paths, depth, max_depth, items, subgraph_shapes, containers)
Definition: smach_viewer.py:104
smach_viewer.SmachViewerFrame.set_dotcode
def set_dotcode(self, dotcode, zoom=True)
Definition: smach_viewer.py:580
smach_viewer.SmachViewerFrame._set_path
def _set_path(self, path)
Definition: smach_viewer.py:396
smach_viewer.SmachViewerFrame._max_depth
_max_depth
Definition: smach_viewer.py:404
smach_viewer.smach_viewer_base.SmachViewerBase.kill
def kill(self)
Definition: smach_viewer_base.py:498
smach_viewer.smach_viewer_base.ContainerNode._initial_states
_initial_states
Definition: smach_viewer_base.py:47
smach_viewer.SmachViewerFrame.path_input
path_input
Definition: smach_viewer.py:320
smach_viewer.SmachViewerFrame.timer
timer
Definition: smach_viewer.py:365
smach_viewer.xdot.xdot
Definition: xdot.py:1
smach_viewer.SmachViewerFrame.set_label_width
def set_label_width(self, event)
Definition: smach_viewer.py:414
smach_viewer.smach_viewer_base.SmachViewerBase._update_cond
_update_cond
Definition: smach_viewer_base.py:309
smach_viewer.SmachViewerFrame.__init__
def __init__(self)
Definition: smach_viewer.py:214
smach_viewer.utils.hex2t
def hex2t(color_str)
Definition: utils.py:30
smach_viewer.SmachViewerFrame.width_spinner
width_spinner
Definition: smach_viewer.py:261
smach_viewer.SmachViewerFrame.append_tree
def append_tree(self, container, parent=None)
Definition: smach_viewer.py:618
smach_viewer.SmachViewerFrame.toggle_all_transitions
def toggle_all_transitions(self, event)
Definition: smach_viewer.py:420
smach_viewer.SmachViewerFrame.OnExit
def OnExit(self, event)
Definition: smach_viewer.py:673
smach_viewer.SmachViewerFrame.set_path
def set_path(self, event)
Definition: smach_viewer.py:390
smach_viewer.SmachViewerFrame._update_graph_step
def _update_graph_step(self)
Definition: smach_viewer.py:548
smach_viewer.SmachViewerFrame._selected_paths
_selected_paths
Definition: smach_viewer.py:450
smach_viewer.WxContainerNode
Definition: smach_viewer.py:103
smach_viewer.SmachViewerFrame.is_button
is_button
Definition: smach_viewer.py:331
smach_viewer.SmachViewerFrame.ud_win
ud_win
Definition: smach_viewer.py:315
smach_viewer.SmachViewerFrame.ud_txt
ud_txt
Definition: smach_viewer.py:327
smach_viewer.SmachViewerFrame.update_graph
def update_graph(self)
Definition: smach_viewer.py:376
smach_viewer.SmachViewerFrame._structure_changed
_structure_changed
Definition: smach_viewer.py:423
smach_viewer.smach_viewer_base.SmachViewerBase._label_wrapper
_label_wrapper
Definition: smach_viewer_base.py:327
elements
smach_viewer.xdot
Definition: xdot/__init__.py:1
smach_viewer.SmachViewerFrame._needs_refresh
_needs_refresh
Definition: smach_viewer.py:590
smach_viewer.SmachViewerFrame.widget
widget
Definition: smach_viewer.py:301
smach_viewer.SmachViewerFrame.path_combo
path_combo
Definition: smach_viewer.py:244
smach_viewer.SmachViewerFrame.OnIdle
def OnIdle(self, event)
Definition: smach_viewer.py:625
smach_viewer.SmachViewerFrame.selection_changed
def selection_changed(self, event)
Definition: smach_viewer.py:458
smach_viewer.main
def main()
Definition: smach_viewer.py:679
smach_viewer.SmachViewerFrame._update_tree
def _update_tree(self)
Definition: smach_viewer.py:593
smach_viewer.SmachViewerFrame._set_max_depth
def _set_max_depth(self, max_depth)
Definition: smach_viewer.py:408
smach_viewer.import_non_local
def import_non_local(name, custom_name=None)
this import system (or ros-released) xdot import xdot need to import currnt package,...
Definition: smach_viewer.py:58
smach_viewer.smach_viewer_base.ContainerNode._active_states
_active_states
Definition: smach_viewer_base.py:48
smach_viewer.SmachViewerFrame.select_cb
def select_cb(self, item, event)
Definition: smach_viewer.py:436
smach_viewer.SmachViewerFrame._status_msg_update
def _status_msg_update(self, msg)
Definition: smach_viewer.py:522
smach_viewer.SmachViewerFrame.set_filter
def set_filter(self, filter)
Definition: smach_viewer.py:676
smach_viewer.smach_viewer_base.SmachViewerBase._keep_running
_keep_running
Definition: smach_viewer_base.py:330
smach_viewer.smach_viewer_base.ContainerNode._path
_path
Definition: smach_viewer_base.py:34
smach_viewer.SmachViewerFrame._show_all_transitions
_show_all_transitions
Definition: smach_viewer.py:422
smach_viewer.SmachViewerFrame._tree_nodes
_tree_nodes
Definition: smach_viewer.py:599
smach_viewer.SmachViewerFrame.SaveDotGraph
def SaveDotGraph(self, event)
Definition: smach_viewer.py:663
smach_viewer.smach_viewer_base.SmachViewerBase._client
_client
Definition: smach_viewer_base.py:314
smach_viewer.SmachViewerFrame.toggle_auto_focus
def toggle_auto_focus(self, event)
Definition: smach_viewer.py:426
smach_viewer.smach_viewer_base.SmachViewerBase._containers
_containers
Definition: smach_viewer_base.py:307
smach_viewer.SmachViewerFrame._auto_focus
_auto_focus
Definition: smach_viewer.py:279
smach_viewer.smach_viewer_base.ContainerNode._children
_children
Definition: smach_viewer_base.py:39
smach_viewer.SmachViewerFrame._path
_path
Definition: smach_viewer.py:392
smach_viewer.SmachViewerFrame.OnQuit
def OnQuit(self, event)
Definition: smach_viewer.py:370
smach_viewer.SmachViewerFrame.add_to_tree
def add_to_tree(self, path, parent)
Definition: smach_viewer.py:603
smach_viewer.smach_viewer_base
Definition: smach_viewer_base.py:1
smach_viewer.SmachViewerFrame.OnTimer
def OnTimer(self, event)
Definition: smach_viewer.py:632
smach_viewer.SmachViewerFrame._needs_zoom
_needs_zoom
Definition: smach_viewer.py:393
smach_viewer.utils.get_label
def get_label(path)
Definition: utils.py:24
smach_viewer.SmachViewerFrame._update_tree_thread
_update_tree_thread
Definition: smach_viewer.py:361
smach_viewer.smach_viewer_base.SmachViewerBase._top_containers
_top_containers
Definition: smach_viewer_base.py:308
smach_viewer.SmachViewerFrame.tree
tree
Definition: smach_viewer.py:307
smach_viewer.utils
Definition: utils.py:1
smach_viewer.SmachViewerFrame.ShowControlsDialog
def ShowControlsDialog(self, event)
Definition: smach_viewer.py:657
smach_viewer.utils.get_parent_path
def get_parent_path(path)
Definition: utils.py:14
smach_viewer.SmachViewerFrame.statusbar
statusbar
Definition: smach_viewer.py:343
smach_viewer.smach_viewer_base.SmachViewerBase
Definition: smach_viewer_base.py:301
smach_viewer.SmachViewerFrame
Definition: smach_viewer.py:207
smach_viewer.SmachViewerFrame.ud_gs
ud_gs
Definition: smach_viewer.py:316
smach_viewer.SmachViewerFrame.set_depth
def set_depth(self, event)
Definition: smach_viewer.py:402
smach_viewer.SmachViewerFrame._structure_msg_update
def _structure_msg_update(self, msg, server_name)
Definition: smach_viewer.py:509
smach_viewer.SmachViewerFrame.on_set_initial_state
def on_set_initial_state(self, event)
Definition: smach_viewer.py:381
smach_viewer.SmachViewerFrame._pub
_pub
Definition: smach_viewer.py:352


smach_viewer
Author(s): Jonathan Bohren
autogenerated on Thu Feb 20 2025 03:09:09