frame_viewer_panel.py
Go to the documentation of this file.
00001 #! /usr/bin/python
00002 #***********************************************************
00003 #* Software License Agreement (BSD License)
00004 #*
00005 #*  Copyright (c) 2009, Willow Garage, Inc.
00006 #*  All rights reserved.
00007 #*
00008 #*  Redistribution and use in source and binary forms, with or without
00009 #*  modification, are permitted provided that the following conditions
00010 #*  are met:
00011 #*
00012 #*   * Redistributions of source code must retain the above copyright
00013 #*     notice, this list of conditions and the following disclaimer.
00014 #*   * Redistributions in binary form must reproduce the above
00015 #*     copyright notice, this list of conditions and the following
00016 #*     disclaimer in the documentation and/or other materials provided
00017 #*     with the distribution.
00018 #*   * Neither the name of Willow Garage, Inc. nor the names of its
00019 #*     contributors may be used to endorse or promote products derived
00020 #*     from this software without specific prior written permission.
00021 #*
00022 #*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 #*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 #*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 #*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 #*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 #*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 #*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 #*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 #*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 #*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 #*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 #*  POSSIBILITY OF SUCH DAMAGE.
00034 #* 
00035 #* Author: Eitan Marder-Eppstein
00036 #***********************************************************
00037 import roslib; roslib.load_manifest('tf2_visualization')
00038 import rospy
00039 
00040 import os
00041 import sys
00042 import yaml
00043 
00044 import wxversion
00045 WXVER = '2.8'
00046 if wxversion.checkInstalled(WXVER):
00047     wxversion.select(WXVER)
00048 else:
00049     print >> sys.stderr, 'This application requires wxPython version %s' % WXVER
00050     sys.exit(1)
00051 
00052 import wx
00053 import wx.richtext
00054 import xdot
00055 import threading
00056 
00057 class FrameViewerPanel(wx.Panel):
00058     def __init__(self, parent, tf_interface, auto_update_list=True):
00059         wx.Panel.__init__(self, parent, -1)
00060 
00061         self.tf_interface   = tf_interface
00062         self.namespace      = ('local', False)
00063         self.tf_namespaces = []
00064         self.loaded_files = []
00065         self.need_dot_zoom = True
00066         self.keep_running = True
00067         self.ns_list_lock = threading.RLock()
00068 
00069         #Create a main pane
00070         vbox = wx.BoxSizer(wx.VERTICAL)
00071         
00072         #Create a splitter
00073         self.content_splitter = wx.SplitterWindow(self, -1, style=wx.SP_LIVE_UPDATE)
00074         self.content_splitter.SetMinimumPaneSize(24)
00075         self.content_splitter.SetSashGravity(0.85)
00076 
00077         #Create a viewer panel
00078         viewer = wx.Panel(self.content_splitter, -1)
00079 
00080         #Create a graph panel
00081         graph_viewer = wx.Panel(viewer, -1)
00082         gv_vbox = wx.BoxSizer(wx.VERTICAL)
00083         graph_viewer.SetSizer(gv_vbox)
00084 
00085         viewer_box = wx.BoxSizer()
00086         viewer_box.Add(graph_viewer,1,wx.EXPAND | wx.ALL, 4)
00087         viewer.SetSizer(viewer_box)
00088 
00089         #Construct toolbar
00090         toolbar = wx.ToolBar(graph_viewer, -1)
00091         toolbar.AddControl(wx.StaticText(toolbar, -1, "Graph:  "))
00092         self.namespaces = wx.ComboBox(toolbar, 1, value='local', choices=['local'], size=(-1, -1), style=wx.CB_DROPDOWN)
00093         self.namespaces.Editable = False
00094         toolbar.AddControl(self.namespaces)
00095         self.Bind(wx.EVT_COMBOBOX, self.on_select_ns, id=1)
00096 
00097         if auto_update_list:
00098             self.namespace_list_thread = threading.Thread(target=self._update_namespace_list)
00099             self.namespace_list_thread.start()
00100 
00101         toolbar.Realize();
00102 
00103         #Create graph_view widget
00104         self.widget = xdot.wxxdot.WxDotWindow(graph_viewer, -1)
00105         self.widget.zoom_to_fit()
00106 
00107         gv_vbox.Add(toolbar,     0, wx.EXPAND)
00108         gv_vbox.Add(self.widget, 1, wx.EXPAND)
00109 
00110         nb = wx.Notebook(self.content_splitter, -1, style=wx.NB_TOP | wx.WANTS_CHARS)
00111         nb_box = wx.BoxSizer()
00112         nb_box.Add(nb, 1, wx.EXPAND | wx.ALL, 4)
00113 
00114         #Create an info pane
00115         borders = wx.LEFT | wx.RIGHT | wx.TOP
00116         border = 4
00117         self.info_win = wx.ScrolledWindow(nb, -1)
00118         self.info_sizer = wx.BoxSizer(wx.VERTICAL)
00119         self.info_txt = wx.TextCtrl(self.info_win, -1, style=wx.TE_MULTILINE | wx.TE_READONLY)
00120         self.info_sizer.Add(self.info_txt, 1, wx.EXPAND | borders, border)
00121         self.info_win.SetSizer(self.info_sizer)
00122 
00123         #Create a tf echo pane
00124         echo_panel = wx.Panel(nb, -1)
00125         echo_box = wx.BoxSizer(wx.VERTICAL)
00126         echo_panel.SetSizer(echo_box)
00127         wx.StaticText(echo_panel, -1, "Target: ", pos=(5, 15))
00128         self.target_frame = wx.ComboBox(echo_panel, 2, value='Select Target', choices=['Select Target'], pos=(60, 10), size=(200, -1), style=wx.CB_DROPDOWN)
00129         self.target_frame.SetEditable(False)
00130         self.Bind(wx.EVT_COMBOBOX, self.on_select_target, id=2)
00131 
00132         wx.StaticText(echo_panel, -1, "Source: ", pos=(5, 55))
00133         self.source_frame = wx.ComboBox(echo_panel, 3, value='Select Source', choices=['Select Source'], pos=(60, 50), size=(200, -1), style=wx.CB_DROPDOWN)
00134         self.source_frame.SetEditable(False)
00135         self.Bind(wx.EVT_COMBOBOX, self.on_select_source, id=3)
00136 
00137         self.echo_txt = wx.TextCtrl(echo_panel, -1, style=wx.TE_MULTILINE | wx.TE_READONLY, pos=(5, 90), size=(255,500))
00138 
00139         #Add our panels to the notebook
00140         nb.AddPage(self.info_win, "Info")
00141         nb.AddPage(echo_panel, "TF Echo")
00142 
00143         # Set content splitter
00144         #self.content_splitter.SplitHorizontally(nb, viewer, 120)
00145 
00146         vbox.Add(self.content_splitter, 1, wx.EXPAND | wx.ALL)
00147         self.SetSizer(vbox)
00148         self.Center()
00149 
00150         self.content_splitter.SplitVertically(nb, viewer, 300)
00151 
00152         self.timer = wx.Timer(self)
00153 
00154         self.Bind(wx.EVT_TIMER, lambda event: self.update_tf_data(), self.timer)
00155         self.widget.register_select_callback(self.select_cb)
00156 
00157         self.timer.Start(1000)
00158 
00159     def update_file_list(self, file):
00160         if file:
00161             filename = file.rstrip('.tf')+'.tf'
00162             self.loaded_files.append(filename)
00163             self.refresh_list()
00164             self.namespaces.SetValue(filename)
00165             cmd = wx.CommandEvent(wx.EVT_COMBOBOX.evtType[0])
00166             cmd.SetEventObject(self.namespaces)
00167             cmd.SetId(self.namespaces.GetId())
00168             self.namespaces.GetEventHandler().ProcessEvent(cmd)
00169 
00170     def reset_frame_lists(self):
00171         self.source_frame.SetItems(['Select Source'])
00172         self.source_frame.SetValue('Select Source')
00173         self.target_frame.SetItems(['Select Target'])
00174         self.target_frame.SetValue('Select Target')
00175         self.set_echo_text("")
00176 
00177     def update_tf_data(self):
00178         self.tf_interface.update_data(*self.namespace)
00179 
00180         dotcode    = self.tf_interface.get_dot()
00181         frame_list = self.tf_interface.get_frame_list()
00182 
00183         if self.namespace[0] == 'local':
00184             self.source_frame.SetItems(frame_list)
00185             self.target_frame.SetItems(frame_list)
00186         else:
00187             self.reset_frame_lists()
00188 
00189         self.set_info_text(yaml.dump(self.tf_interface.get_info(), default_flow_style=False))
00190         self.set_dotcode(dotcode)
00191 
00192         self.check_echo()
00193         self.refresh_list()
00194 
00195     def set_dotcode(self, dotcode):
00196         if not dotcode:
00197             return
00198         
00199         self.widget.set_dotcode(dotcode, None)
00200 
00201         #We'll only zoom to fit the first time we get data
00202         if self.need_dot_zoom:
00203             self.need_dot_zoom = False
00204             self.widget.zoom_to_fit()
00205 
00206         wx.CallAfter(self.Refresh)
00207 
00208     def set_info_text(self, text):
00209         self.info_txt.Value = text
00210         self.Refresh()
00211 
00212     def set_echo_text(self, text):
00213         self.echo_txt.Value = text
00214         self.Refresh()
00215 
00216     ## Event handling
00217 
00218     def select_cb(self, target, event):
00219         if event.ButtonDown(wx.MOUSE_BTN_LEFT) and target.url is not None:
00220             self.tf_interface.set_detail(target.url)
00221 
00222     def _update_namespace_list(self):
00223         r = rospy.Rate(0.5)
00224         while not rospy.is_shutdown() and self.keep_running:
00225             tf_namespaces = self.tf_interface.find_tf_namespaces()
00226             with self.ns_list_lock:
00227                 self.tf_namespaces = tf_namespaces
00228             r.sleep()
00229 
00230     def refresh_list(self):
00231         with self.ns_list_lock:
00232             self.namespaces.Items = ['local'] + self.tf_namespaces + self.loaded_files
00233         
00234     def on_select_ns(self, event):
00235         value = event.EventObject.Value
00236         #we need to check if we're looking up a namespace or just reading a file
00237         if value == self.namespace[0]:
00238             return
00239 
00240         with self.ns_list_lock:
00241             if value == 'local' or self.tf_namespaces.count(value) > 0:
00242                 self.namespace = (value, False)
00243             else:
00244                 self.namespace = (value, True)
00245 
00246             self.tf_interface.clear_detail()
00247             self.reset_frame_lists()
00248             self.need_dot_zoom = True
00249         
00250     def on_select_target(self, event):
00251         print "target"
00252         print type(event)
00253         print event.EventObject.Value
00254 
00255     def on_select_source(self, event):
00256         print "source"
00257         print type(event)
00258         print event.EventObject.Value
00259 
00260     def check_echo(self):
00261         target = self.target_frame.GetValue()
00262         source = self.source_frame.GetValue()
00263         if source != 'Select Source' and target != 'Select Target':
00264             self.set_echo_text(self.tf_interface.get_echo_string(target, source))
00265 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Friends


tf2_visualization
Author(s): Wim Meeussen
autogenerated on Mon Aug 19 2013 11:37:23