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 import codecs
00038 import math
00039
00040 import wx
00041
00042 import rospy
00043
00044 import bag_helper
00045 from plugin.topic_message_view import TopicMessageView
00046
00047 class RawView(TopicMessageView):
00048 name = 'Raw'
00049
00050 def __init__(self, timeline, parent):
00051 TopicMessageView.__init__(self, timeline, parent)
00052
00053 self.message_tree = MessageTree(self.parent)
00054
00055 self.parent.Bind(wx.EVT_SIZE, self.on_size)
00056
00057
00058
00059 def message_viewed(self, bag, msg_details):
00060 TopicMessageView.message_viewed(self, bag, msg_details)
00061
00062 topic, msg, t = msg_details
00063 if t is None:
00064 self.message_cleared()
00065 else:
00066 wx.CallAfter(self.message_tree.set_message, msg)
00067
00068 def message_cleared(self):
00069 TopicMessageView.message_cleared(self)
00070 wx.CallAfter(self.message_tree.set_message, None)
00071
00072
00073
00074 def on_size(self, event):
00075 self.message_tree.Size = self.parent.ClientSize
00076
00077 class MessageTree(wx.TreeCtrl):
00078 def __init__(self, parent):
00079 wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_MULTIPLE)
00080
00081 self._font = wx.Font(9, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
00082
00083 self._msg = None
00084
00085 self._expanded_paths = None
00086
00087 self.Bind(wx.EVT_KEY_UP, self.on_key_up)
00088
00089 @property
00090 def msg(self):
00091 return self._msg
00092
00093 def set_message(self, msg):
00094
00095 if self._msg:
00096 for item in self.get_all_items():
00097 path = self.get_item_path(item)
00098 if self.IsExpanded(item):
00099 self._expanded_paths.add(path)
00100 elif path in self._expanded_paths:
00101 self._expanded_paths.remove(path)
00102
00103 self.DeleteAllItems()
00104
00105 if msg:
00106
00107 self._add_msg_object(None, '', 'msg', msg, msg._type)
00108
00109 if self._expanded_paths is None:
00110 self._expanded_paths = set()
00111 else:
00112
00113 for item in self.get_all_items():
00114 path = self.get_item_path(item)
00115 if path in self._expanded_paths:
00116 self.Expand(item)
00117 else:
00118 self.Collapse(item)
00119
00120 self._msg = msg
00121
00122 self.Refresh()
00123
00124 def on_key_up(self, event):
00125 key, ctrl = event.KeyCode, event.ControlDown()
00126
00127 if ctrl:
00128 if key == ord('C') or key == ord('c'):
00129
00130 self._copy_text_to_clipboard()
00131 elif key == ord('A') or key == ord('a'):
00132
00133 self._select_all()
00134
00135 def _select_all(self):
00136 first_selected = self.GetFirstVisibleItem()
00137 for i in self.get_all_items():
00138 if not self.IsSelected(i):
00139 self.SelectItem(i, True)
00140
00141 if first_selected is not None:
00142 self.ScrollTo(first_selected)
00143
00144 def _copy_text_to_clipboard(self):
00145
00146
00147 def get_distance(item, ancestor, distance=0):
00148 parent = self.GetItemParent(item)
00149 if parent == ancestor:
00150 return distance
00151 else:
00152 return get_distance(parent, ancestor, distance + 1)
00153
00154 root = self.GetRootItem()
00155 text = '\n'.join([('\t' * get_distance(i, root)) + self.GetItemText(i) for i in self.GetSelections()])
00156
00157
00158 if wx.TheClipboard.Open():
00159 try:
00160 wx.TheClipboard.SetData(wx.TextDataObject(text))
00161 finally:
00162 wx.TheClipboard.Close()
00163
00164 def get_item_path(self, item):
00165 return self.GetItemPyData(item)[0].replace(' ', '')
00166
00167 def get_all_items(self):
00168 items = []
00169 try:
00170 self.traverse(self.RootItem, items.append)
00171 except Exception:
00172
00173 pass
00174 return items
00175
00176 def traverse(self, root, function):
00177 if self.ItemHasChildren(root):
00178 first_child = self.GetFirstChild(root)[0]
00179 function(first_child)
00180 self.traverse(first_child, function)
00181
00182 child = self.GetNextSibling(root)
00183 if child:
00184 function(child)
00185 self.traverse(child, function)
00186
00187 def _add_msg_object(self, parent, path, name, obj, obj_type):
00188 label = name
00189
00190 if hasattr(obj, '__slots__'):
00191 subobjs = [(slot, getattr(obj, slot)) for slot in obj.__slots__]
00192 elif type(obj) in [list, tuple]:
00193 len_obj = len(obj)
00194 if len_obj == 0:
00195 subobjs = []
00196 else:
00197 w = int(math.ceil(math.log10(len_obj)))
00198 subobjs = [('[%*d]' % (w, i), subobj) for (i, subobj) in enumerate(obj)]
00199 else:
00200 subobjs = []
00201
00202 if type(obj) in [int, long, float]:
00203 if type(obj) == float:
00204 obj_repr = '%.6f' % obj
00205 else:
00206 obj_repr = str(obj)
00207
00208 if obj_repr[0] == '-':
00209 label += ': %s' % obj_repr
00210 else:
00211 label += ': %s' % obj_repr
00212
00213 elif type(obj) in [str, bool, int, long, float, complex, rospy.Time]:
00214
00215 obj_repr = codecs.utf_8_decode(str(obj), 'ignore')[0]
00216
00217
00218 if len(obj_repr) >= 50:
00219 obj_repr = obj_repr[:50] + '...'
00220
00221 label += ': ' + obj_repr
00222
00223 if parent is None:
00224 item = self.AddRoot(label)
00225 else:
00226 item = self.AppendItem(parent, label)
00227
00228 self.SetItemFont(item, self._font)
00229 self.SetItemPyData(item, (path, obj_type))
00230
00231 for subobj_name, subobj in subobjs:
00232 if subobj is None:
00233 continue
00234
00235 if path == '':
00236 subpath = subobj_name
00237 elif subobj_name.startswith('['):
00238 subpath = '%s%s' % (path, subobj_name)
00239 else:
00240 subpath = '%s.%s' % (path, subobj_name)
00241
00242 if hasattr(subobj, '_type'):
00243 subobj_type = subobj._type
00244 else:
00245 subobj_type = type(subobj).__name__
00246
00247 self._add_msg_object(item, subpath, subobj_name, subobj, subobj_type)