$search
00001 # Software License Agreement (BSD License) 00002 # 00003 # Copyright (c) 2009, Willow Garage, Inc. 00004 # All rights reserved. 00005 # 00006 # Redistribution and use in source and binary forms, with or without 00007 # modification, are permitted provided that the following conditions 00008 # are met: 00009 # 00010 # * Redistributions of source code must retain the above copyright 00011 # notice, this list of conditions and the following disclaimer. 00012 # * Redistributions in binary form must reproduce the above 00013 # copyright notice, this list of conditions and the following 00014 # disclaimer in the documentation and/or other materials provided 00015 # with the distribution. 00016 # * Neither the name of Willow Garage, Inc. nor the names of its 00017 # contributors may be used to endorse or promote products derived 00018 # from this software without specific prior written permission. 00019 # 00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00031 # POSSIBILITY OF SUCH DAMAGE. 00032 00033 """ 00034 Defines a raw view: a TopicMessageView that displays the message contents in a tree. 00035 """ 00036 00037 PKG = 'rxbag' 00038 import roslib; roslib.load_manifest(PKG) 00039 00040 import codecs 00041 import math 00042 00043 import wx 00044 00045 import rospy 00046 00047 import bag_helper 00048 from plugin.topic_message_view import TopicMessageView 00049 00050 class RawView(TopicMessageView): 00051 name = 'Raw' 00052 00053 def __init__(self, timeline, parent): 00054 TopicMessageView.__init__(self, timeline, parent) 00055 00056 self.message_tree = MessageTree(self.parent) 00057 00058 self.parent.Bind(wx.EVT_SIZE, self.on_size) 00059 00060 ## MessageView implementation 00061 00062 def message_viewed(self, bag, msg_details): 00063 TopicMessageView.message_viewed(self, bag, msg_details) 00064 00065 topic, msg, t = msg_details 00066 if t is None: 00067 self.message_cleared() 00068 else: 00069 wx.CallAfter(self.message_tree.set_message, msg) 00070 00071 def message_cleared(self): 00072 TopicMessageView.message_cleared(self) 00073 wx.CallAfter(self.message_tree.set_message, None) 00074 00075 ## Events 00076 00077 def on_size(self, event): 00078 self.message_tree.Size = self.parent.ClientSize 00079 00080 class MessageTree(wx.TreeCtrl): 00081 def __init__(self, parent): 00082 wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_MULTIPLE) 00083 00084 self._font = wx.Font(9, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) 00085 00086 self._msg = None 00087 00088 self._expanded_paths = None 00089 00090 self.Bind(wx.EVT_KEY_UP, self.on_key_up) 00091 00092 @property 00093 def msg(self): 00094 return self._msg 00095 00096 def set_message(self, msg): 00097 # Remember whether items were expanded or not before deleting 00098 if self._msg: 00099 for item in self.get_all_items(): 00100 path = self.get_item_path(item) 00101 if self.IsExpanded(item): 00102 self._expanded_paths.add(path) 00103 elif path in self._expanded_paths: 00104 self._expanded_paths.remove(path) 00105 00106 self.DeleteAllItems() 00107 00108 if msg: 00109 # Populate the tree 00110 self._add_msg_object(None, '', 'msg', msg, msg._type) 00111 00112 if self._expanded_paths is None: 00113 self._expanded_paths = set() 00114 else: 00115 # Expand those that were previously expanded, and collapse any paths that we've seen for the first time 00116 for item in self.get_all_items(): 00117 path = self.get_item_path(item) 00118 if path in self._expanded_paths: 00119 self.Expand(item) 00120 else: 00121 self.Collapse(item) 00122 00123 self._msg = msg 00124 00125 self.Refresh() 00126 00127 def on_key_up(self, event): 00128 key, ctrl = event.KeyCode, event.ControlDown() 00129 00130 if ctrl: 00131 if key == ord('C') or key == ord('c'): 00132 # Ctrl-C: copy text from selected items to clipboard 00133 self._copy_text_to_clipboard() 00134 elif key == ord('A') or key == ord('a'): 00135 # Ctrl-A: select all 00136 self._select_all() 00137 00138 def _select_all(self): 00139 first_selected = self.GetFirstVisibleItem() 00140 for i in self.get_all_items(): 00141 if not self.IsSelected(i): 00142 self.SelectItem(i, True) 00143 00144 if first_selected is not None: 00145 self.ScrollTo(first_selected) 00146 00147 def _copy_text_to_clipboard(self): 00148 # Get the indented text for all selected items 00149 00150 def get_distance(item, ancestor, distance=0): 00151 parent = self.GetItemParent(item) 00152 if parent == ancestor: 00153 return distance 00154 else: 00155 return get_distance(parent, ancestor, distance + 1) 00156 00157 root = self.GetRootItem() 00158 text = '\n'.join([('\t' * get_distance(i, root)) + self.GetItemText(i) for i in self.GetSelections()]) 00159 00160 # Copy the text to the clipboard 00161 if wx.TheClipboard.Open(): 00162 try: 00163 wx.TheClipboard.SetData(wx.TextDataObject(text)) 00164 finally: 00165 wx.TheClipboard.Close() 00166 00167 def get_item_path(self, item): 00168 return self.GetItemPyData(item)[0].replace(' ', '') # remove spaces that may get introduced in indexing, e.g. [ 3] is [3] 00169 00170 def get_all_items(self): 00171 items = [] 00172 try: 00173 self.traverse(self.RootItem, items.append) 00174 except Exception: 00175 # @todo: large messages can cause a stack overflow due to recursion 00176 pass 00177 return items 00178 00179 def traverse(self, root, function): 00180 if self.ItemHasChildren(root): 00181 first_child = self.GetFirstChild(root)[0] 00182 function(first_child) 00183 self.traverse(first_child, function) 00184 00185 child = self.GetNextSibling(root) 00186 if child: 00187 function(child) 00188 self.traverse(child, function) 00189 00190 def _add_msg_object(self, parent, path, name, obj, obj_type): 00191 label = name 00192 00193 if hasattr(obj, '__slots__'): 00194 subobjs = [(slot, getattr(obj, slot)) for slot in obj.__slots__] 00195 elif type(obj) in [list, tuple]: 00196 len_obj = len(obj) 00197 if len_obj == 0: 00198 subobjs = [] 00199 else: 00200 w = int(math.ceil(math.log10(len_obj))) 00201 subobjs = [('[%*d]' % (w, i), subobj) for (i, subobj) in enumerate(obj)] 00202 else: 00203 subobjs = [] 00204 00205 if type(obj) in [int, long, float]: 00206 if type(obj) == float: 00207 obj_repr = '%.6f' % obj 00208 else: 00209 obj_repr = str(obj) 00210 00211 if obj_repr[0] == '-': 00212 label += ': %s' % obj_repr 00213 else: 00214 label += ': %s' % obj_repr 00215 00216 elif type(obj) in [str, bool, int, long, float, complex, rospy.Time]: 00217 # Ignore any binary data 00218 obj_repr = codecs.utf_8_decode(str(obj), 'ignore')[0] 00219 00220 # Truncate long representations 00221 if len(obj_repr) >= 50: 00222 obj_repr = obj_repr[:50] + '...' 00223 00224 label += ': ' + obj_repr 00225 00226 if parent is None: 00227 item = self.AddRoot(label) 00228 else: 00229 item = self.AppendItem(parent, label) 00230 00231 self.SetItemFont(item, self._font) 00232 self.SetItemPyData(item, (path, obj_type)) 00233 00234 for subobj_name, subobj in subobjs: 00235 if subobj is None: 00236 continue 00237 00238 if path == '': 00239 subpath = subobj_name # root field 00240 elif subobj_name.startswith('['): 00241 subpath = '%s%s' % (path, subobj_name) # list, dict, or tuple 00242 else: 00243 subpath = '%s.%s' % (path, subobj_name) # attribute (prefix with '.') 00244 00245 if hasattr(subobj, '_type'): 00246 subobj_type = subobj._type 00247 else: 00248 subobj_type = type(subobj).__name__ 00249 00250 self._add_msg_object(item, subpath, subobj_name, subobj, subobj_type)