00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
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
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
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
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
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
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
00133 self._copy_text_to_clipboard()
00134 elif key == ord('A') or key == ord('a'):
00135
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
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
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(' ', '')
00169
00170 def get_all_items(self):
00171 items = []
00172 try:
00173 self.traverse(self.RootItem, items.append)
00174 except Exception:
00175
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
00218 obj_repr = codecs.utf_8_decode(str(obj), 'ignore')[0]
00219
00220
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
00240 elif subobj_name.startswith('['):
00241 subpath = '%s%s' % (path, subobj_name)
00242 else:
00243 subpath = '%s.%s' % (path, subobj_name)
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)