68 if self.Parent and self.Variable != variable: |
72 if self.Parent and self.Variable != variable: |
69 self.Variable = variable |
73 self.Variable = variable |
70 self.RefreshVariableType() |
74 self.RefreshVariableType() |
71 self.Parent.RefreshView() |
75 self.Parent.RefreshView() |
72 |
76 |
73 def GetVariable(self, max_size=None): |
77 def GetVariable(self, mask=None): |
74 variable = self.Variable |
78 variable = self.Variable |
75 if max_size is not None: |
79 if mask is not None: |
76 max_size = max(max_size, 10) |
80 parts = variable.split('.') |
77 if len(variable) > max_size: |
81 mask = mask + ['*'] * max(0, len(parts) - len(mask)) |
78 variable = "..." + variable[-(max_size - 3):] |
82 last = None |
|
83 variable = "" |
|
84 for m, v in zip(mask, parts): |
|
85 if m == '*': |
|
86 if last == '*': |
|
87 variable += '.' |
|
88 variable += v |
|
89 elif last is None or last == '*': |
|
90 variable += '..' |
|
91 last = m |
79 return variable |
92 return variable |
80 |
93 |
81 def RefreshVariableType(self): |
94 def RefreshVariableType(self): |
82 self.VariableType = self.Parent.GetDataType(self.Variable) |
95 self.VariableType = self.Parent.GetDataType(self.Variable) |
83 if USE_MPL: |
96 if USE_MPL: |
103 else: |
116 else: |
104 return self.Data[start_idx:] |
117 return self.Data[start_idx:] |
105 |
118 |
106 return None |
119 return None |
107 |
120 |
|
121 def GetRawValue(self, idx): |
|
122 if self.VariableType in ["STRING", "WSTRING"] and idx < len(self.RawData): |
|
123 return self.RawData[idx][0] |
|
124 return "" |
|
125 |
108 def GetRange(self): |
126 def GetRange(self): |
109 return self.MinValue, self.MaxValue |
127 return self.MinValue, self.MaxValue |
110 |
128 |
111 def ResetData(self): |
129 def ResetData(self): |
112 if self.IsNumVariable(): |
130 if self.IsNumVariable(): |
113 self.Data = numpy.array([]).reshape(0, 2) |
131 self.Data = numpy.array([]).reshape(0, 3) |
|
132 if self.VariableType in ["STRING", "WSTRING"]: |
|
133 self.RawData = [] |
114 self.MinValue = None |
134 self.MinValue = None |
115 self.MaxValue = None |
135 self.MaxValue = None |
116 else: |
136 else: |
117 self.Data = None |
137 self.Data = None |
118 |
138 |
119 def IsNumVariable(self): |
139 def IsNumVariable(self): |
120 return self.Parent.IsNumType(self.VariableType) |
140 return (self.Parent.IsNumType(self.VariableType) or |
|
141 self.VariableType in ["STRING", "WSTRING"]) |
121 |
142 |
122 def NewValue(self, tick, value, forced=False): |
143 def NewValue(self, tick, value, forced=False): |
123 if USE_MPL and self.IsNumVariable(): |
144 if USE_MPL and self.IsNumVariable(): |
124 num_value = {True:1., False:0.}.get(value, float(value)) |
145 if self.VariableType in ["STRING", "WSTRING"]: |
|
146 num_value = binascii.crc32(value) & CRC_MASK |
|
147 else: |
|
148 num_value = float(value) |
125 if self.MinValue is None: |
149 if self.MinValue is None: |
126 self.MinValue = num_value |
150 self.MinValue = num_value |
127 else: |
151 else: |
128 self.MinValue = min(self.MinValue, num_value) |
152 self.MinValue = min(self.MinValue, num_value) |
129 if self.MaxValue is None: |
153 if self.MaxValue is None: |
130 self.MaxValue = num_value |
154 self.MaxValue = num_value |
131 else: |
155 else: |
132 self.MaxValue = max(self.MaxValue, num_value) |
156 self.MaxValue = max(self.MaxValue, num_value) |
133 self.Data = numpy.append(self.Data, [[float(tick), num_value]], axis=0) |
157 forced_value = float(forced) |
|
158 if self.VariableType in ["STRING", "WSTRING"]: |
|
159 raw_data = (value, forced_value) |
|
160 if len(self.RawData) == 0 or self.RawData[-1] != raw_data: |
|
161 extra_value = len(self.RawData) |
|
162 self.RawData.append(raw_data) |
|
163 else: |
|
164 extra_value = len(self.RawData) - 1 |
|
165 else: |
|
166 extra_value = forced_value |
|
167 self.Data = numpy.append(self.Data, [[float(tick), num_value, extra_value]], axis=0) |
134 self.Parent.HasNewData = True |
168 self.Parent.HasNewData = True |
135 DebugDataConsumer.NewValue(self, tick, value, forced) |
169 DebugDataConsumer.NewValue(self, tick, value, forced) |
136 |
170 |
137 def SetForced(self, forced): |
171 def SetForced(self, forced): |
138 if self.Forced != forced: |
172 if self.Forced != forced: |
145 value = value[1:-1] |
179 value = value[1:-1] |
146 if self.Value != value: |
180 if self.Value != value: |
147 self.Value = value |
181 self.Value = value |
148 self.Parent.HasNewData = True |
182 self.Parent.HasNewData = True |
149 |
183 |
150 def GetValue(self): |
184 def GetValue(self, tick=None, raw=False): |
151 if self.VariableType == "STRING": |
185 if tick is not None and self.IsNumVariable() and len(self.Data) > 0: |
152 return "'%s'" % self.Value |
186 idx = numpy.argmin(abs(self.Data[:, 0] - tick)) |
153 elif self.VariableType == "WSTRING": |
187 if self.VariableType in ["STRING", "WSTRING"]: |
154 return "\"%s\"" % self.Value |
188 value, forced = self.RawData[int(self.Data[idx, 2])] |
155 elif isinstance(self.Value, FloatType): |
189 if not raw: |
156 return "%.6g" % self.Value |
190 if self.VariableType == "STRING": |
|
191 value = "'%s'" % value |
|
192 else: |
|
193 value = '"%s"' % value |
|
194 return value, forced |
|
195 else: |
|
196 value = self.Data[idx, 1] |
|
197 if not raw and isinstance(value, FloatType): |
|
198 value = "%.6g" % value |
|
199 return value, self.IsForced() |
|
200 elif not raw: |
|
201 if self.VariableType == "STRING": |
|
202 return "'%s'" % self.Value |
|
203 elif self.VariableType == "WSTRING": |
|
204 return '"%s"' % self.Value |
|
205 elif isinstance(self.Value, FloatType): |
|
206 return "%.6g" % self.Value |
157 return self.Value |
207 return self.Value |
158 |
208 |
159 def GetNearestData(self, tick, adjust): |
209 def GetNearestData(self, tick, adjust): |
160 if USE_MPL and self.IsNumVariable(): |
210 if USE_MPL and self.IsNumVariable(): |
161 ticks = self.Data[:, 0] |
211 ticks = self.Data[:, 0] |
305 dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) |
355 dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) |
306 dialog.ShowModal() |
356 dialog.ShowModal() |
307 dialog.Destroy() |
357 dialog.Destroy() |
308 |
358 |
309 if USE_MPL: |
359 if USE_MPL: |
310 SECOND = 1000000000 |
360 MILLISECOND = 1000000 |
|
361 SECOND = 1000 * MILLISECOND |
311 MINUTE = 60 * SECOND |
362 MINUTE = 60 * SECOND |
312 HOUR = 60 * MINUTE |
363 HOUR = 60 * MINUTE |
|
364 DAY = 24 * HOUR |
313 |
365 |
314 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)]) |
366 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)]) |
315 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)]) |
367 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)]) |
316 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \ |
368 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \ |
317 [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \ |
369 [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \ |
386 if self.GraphType == GRAPH_ORTHOGONAL: |
439 if self.GraphType == GRAPH_ORTHOGONAL: |
387 return tuple(variables) |
440 return tuple(variables) |
388 return variables |
441 return variables |
389 return self.Items[0].GetVariable() |
442 return self.Items[0].GetVariable() |
390 |
443 |
|
444 def ResetVariableNameMask(self): |
|
445 if len(self.Items) > 1: |
|
446 self.VariableNameMask = reduce(compute_mask, |
|
447 [item.GetVariable().split('.') for item in self.Items]) |
|
448 elif len(self.Items) > 0: |
|
449 self.VariableNameMask = self.Items[0].GetVariable().split('.')[:-1] + ['*'] |
|
450 else: |
|
451 self.VariableNameMask = [] |
|
452 |
391 def AddItem(self, item): |
453 def AddItem(self, item): |
392 self.Items.append(item) |
454 self.Items.append(item) |
|
455 self.ResetVariableNameMask() |
393 |
456 |
394 def RemoveItem(self, item): |
457 def RemoveItem(self, item): |
395 if item in self.Items: |
458 if item in self.Items: |
396 self.Items.remove(item) |
459 self.Items.remove(item) |
|
460 self.ResetVariableNameMask() |
397 |
461 |
398 def Clear(self): |
462 def Clear(self): |
399 for item in self.Items: |
463 for item in self.Items: |
400 self.ParentWindow.RemoveDataConsumer(item) |
464 self.ParentWindow.RemoveDataConsumer(item) |
401 self.Items = [] |
465 self.Items = [] |
|
466 self.ResetVariableNameMask() |
402 |
467 |
403 def IsEmpty(self): |
468 def IsEmpty(self): |
404 return len(self.Items) == 0 |
469 return len(self.Items) == 0 |
405 |
470 |
406 def UnregisterObsoleteData(self): |
471 def UnregisterObsoleteData(self): |
543 self.ValueLabel.SetForegroundColour(wx.BLUE) |
609 self.ValueLabel.SetForegroundColour(wx.BLUE) |
544 else: |
610 else: |
545 self.ValueLabel.SetForegroundColour(wx.BLACK) |
611 self.ValueLabel.SetForegroundColour(wx.BLACK) |
546 self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1) |
612 self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1) |
547 |
613 |
|
614 def compute_mask(x, y): |
|
615 mask = [] |
|
616 for xp, yp in zip(x, y): |
|
617 if xp == yp: |
|
618 mask.append(xp) |
|
619 else: |
|
620 mask.append("*") |
|
621 return mask |
|
622 |
548 class DebugVariableGraphic(DebugVariableViewer): |
623 class DebugVariableGraphic(DebugVariableViewer): |
549 |
624 |
550 def __init__(self, parent, window, items, graph_type): |
625 def __init__(self, parent, window, items, graph_type): |
551 DebugVariableViewer.__init__(self, parent, window, items) |
626 DebugVariableViewer.__init__(self, parent, window, items) |
552 |
627 |
553 self.GraphType = graph_type |
628 self.GraphType = graph_type |
|
629 self.CursorTick = None |
554 |
630 |
555 self.ResetGraphics() |
631 self.ResetGraphics() |
556 |
632 |
557 def AddViewer(self): |
633 def AddViewer(self): |
558 self.Figure = matplotlib.figure.Figure(facecolor='w') |
634 self.Figure = matplotlib.figure.Figure(facecolor='w') |
559 |
635 |
560 self.Canvas = FigureCanvas(self, -1, self.Figure) |
636 self.Canvas = FigureCanvas(self, -1, self.Figure) |
561 self.Canvas.SetMinSize(wx.Size(200, 200)) |
637 self.Canvas.SetMinSize(wx.Size(200, 200)) |
562 self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self)) |
638 self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self)) |
|
639 self.Canvas.mpl_connect('motion_notify_event', self.OnCanvasMotion) |
563 self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick) |
640 self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick) |
564 |
641 |
565 self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW) |
642 self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW) |
566 |
643 |
567 def AddButtons(self): |
644 def AddButtons(self): |
605 self.DoDragDrop(item_idx) |
682 self.DoDragDrop(item_idx) |
606 return |
683 return |
607 event.Skip() |
684 event.Skip() |
608 |
685 |
609 def DoDragDrop(self, item_idx): |
686 def DoDragDrop(self, item_idx): |
|
687 self.ParentWindow.ResetCursorTickRatio() |
610 data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move"))) |
688 data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move"))) |
611 dragSource = wx.DropSource(self.Canvas) |
689 dragSource = wx.DropSource(self.Canvas) |
612 dragSource.SetData(data) |
690 dragSource.SetData(data) |
613 dragSource.DoDragDrop() |
691 dragSource.DoDragDrop() |
614 |
692 |
615 def OnMotion(self, event): |
693 def OnAxesMotion(self, event): |
616 if self.Is3DCanvas(): |
694 if self.Is3DCanvas(): |
617 current_time = gettime() |
695 current_time = gettime() |
618 if current_time - self.LastMotionTime > REFRESH_PERIOD: |
696 if current_time - self.LastMotionTime > REFRESH_PERIOD: |
619 self.LastMotionTime = current_time |
697 self.LastMotionTime = current_time |
620 Axes3D._on_move(self.Axes, event) |
698 Axes3D._on_move(self.Axes, event) |
621 |
699 |
|
700 def OnCanvasMotion(self, event): |
|
701 if not self.Is3DCanvas(): |
|
702 if event.inaxes == self.Axes: |
|
703 start_tick, end_tick = self.ParentWindow.GetRange() |
|
704 cursor_tick_ratio = None |
|
705 if self.GraphType == GRAPH_ORTHOGONAL: |
|
706 x_data = self.Items[0].GetData(start_tick, end_tick) |
|
707 y_data = self.Items[1].GetData(start_tick, end_tick) |
|
708 if len(x_data) > 0 and len(y_data) > 0: |
|
709 length = min(len(x_data), len(y_data)) |
|
710 d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + (y_data[:length,1]-event.ydata) ** 2) |
|
711 cursor_tick_ratio = float(x_data[numpy.argmin(d), 0] - start_tick) / (end_tick - start_tick) |
|
712 else: |
|
713 data = self.Items[0].GetData(start_tick, end_tick) |
|
714 if len(data) > 0: |
|
715 x_min, x_max = self.Axes.get_xlim() |
|
716 cursor_tick_ratio = float(event.xdata - x_min) / (x_max - x_min) |
|
717 if cursor_tick_ratio is not None: |
|
718 self.ParentWindow.SetCursorTickRatio(cursor_tick_ratio) |
|
719 else: |
|
720 self.ParentWindow.ResetCursorTickRatio() |
|
721 |
622 def OnSplitButton(self, event): |
722 def OnSplitButton(self, event): |
623 if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL: |
723 if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL: |
624 wx.CallAfter(self.ParentWindow.SplitGraphs, self) |
724 wx.CallAfter(self.ParentWindow.SplitGraphs, self) |
625 else: |
725 else: |
626 menu = wx.Menu(title='') |
726 menu = wx.Menu(title='') |
627 for item in self.Items: |
727 for item in self.Items: |
628 new_id = wx.NewId() |
728 new_id = wx.NewId() |
629 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, |
729 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, |
630 text=item.GetVariable(20)) |
730 text=item.GetVariable(self.VariableNameMask)) |
631 self.Bind(wx.EVT_MENU, |
731 self.Bind(wx.EVT_MENU, |
632 self.GetSplitGraphMenuFunction(item), |
732 self.GetSplitGraphMenuFunction(item), |
633 id=new_id) |
733 id=new_id) |
634 |
734 |
635 new_id = wx.NewId() |
735 new_id = wx.NewId() |
643 self.Figure.clear() |
743 self.Figure.clear() |
644 if self.Is3DCanvas(): |
744 if self.Is3DCanvas(): |
645 self.Axes = self.Figure.gca(projection='3d') |
745 self.Axes = self.Figure.gca(projection='3d') |
646 self.Axes.set_color_cycle(['b']) |
746 self.Axes.set_color_cycle(['b']) |
647 self.LastMotionTime = gettime() |
747 self.LastMotionTime = gettime() |
648 setattr(self.Axes, "_on_move", self.OnMotion) |
748 setattr(self.Axes, "_on_move", self.OnAxesMotion) |
649 self.Axes.mouse_init() |
749 self.Axes.mouse_init() |
650 else: |
750 else: |
651 self.Axes = self.Figure.gca() |
751 self.Axes = self.Figure.gca() |
652 if self.GraphType == GRAPH_ORTHOGONAL: |
752 if self.GraphType == GRAPH_ORTHOGONAL: |
653 self.Figure.subplotpars.update(bottom=0.15) |
753 self.Figure.subplotpars.update(bottom=0.15) |
|
754 self.Axes.set_title('.'.join(self.VariableNameMask)) |
654 self.Plots = [] |
755 self.Plots = [] |
|
756 self.VLine = None |
|
757 self.HLine = None |
|
758 self.TickLabel = None |
655 self.SplitButton.Enable(len(self.Items) > 1) |
759 self.SplitButton.Enable(len(self.Items) > 1) |
656 |
760 |
657 def AddItem(self, item): |
761 def AddItem(self, item): |
658 DebugVariableViewer.AddItem(self, item) |
762 DebugVariableViewer.AddItem(self, item) |
659 self.ResetGraphics() |
763 self.ResetGraphics() |
660 |
764 |
661 def RemoveItem(self, item): |
765 def RemoveItem(self, item): |
662 DebugVariableViewer.RemoveItem(self, item) |
766 DebugVariableViewer.RemoveItem(self, item) |
663 if not self.IsEmpty(): |
767 if not self.IsEmpty(): |
|
768 self.ResetVariableNameMask() |
664 self.ResetGraphics() |
769 self.ResetGraphics() |
665 |
770 |
666 def UnregisterObsoleteData(self): |
771 def UnregisterObsoleteData(self): |
667 DebugVariableViewer.UnregisterObsoleteData(self) |
772 DebugVariableViewer.UnregisterObsoleteData(self) |
668 if not self.IsEmpty(): |
773 if not self.IsEmpty(): |
|
774 self.ResetVariableNameMask() |
669 self.ResetGraphics() |
775 self.ResetGraphics() |
670 |
776 |
671 def Is3DCanvas(self): |
777 def Is3DCanvas(self): |
672 return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3 |
778 return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3 |
673 |
779 |
|
780 def SetCursorTick(self, cursor_tick): |
|
781 self.CursorTick = cursor_tick |
|
782 |
674 def Refresh(self, refresh_graphics=True): |
783 def Refresh(self, refresh_graphics=True): |
675 |
784 |
676 if refresh_graphics: |
785 if refresh_graphics: |
677 start_tick, end_tick = self.ParentWindow.GetRange() |
786 start_tick, end_tick = self.ParentWindow.GetRange() |
678 |
787 |
705 y_center = 0.5 |
814 y_center = 0.5 |
706 y_range = 1.0 |
815 y_range = 1.0 |
707 x_min, x_max = start_tick, end_tick |
816 x_min, x_max = start_tick, end_tick |
708 y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 |
817 y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 |
709 |
818 |
|
819 if self.CursorTick is not None: |
|
820 if self.VLine is None: |
|
821 self.VLine = self.Axes.axvline(self.CursorTick, color='r') |
|
822 else: |
|
823 self.VLine.set_xdata((self.CursorTick, self.CursorTick)) |
|
824 self.VLine.set_visible(True) |
|
825 tick_label = self.ParentWindow.GetTickLabel(self.CursorTick) |
|
826 if self.TickLabel is None: |
|
827 self.TickLabel = self.Axes.text(0.5, 0.05, tick_label, |
|
828 size = 'small', transform = self.Axes.transAxes) |
|
829 else: |
|
830 self.TickLabel.set_text(tick_label) |
|
831 else: |
|
832 if self.VLine is not None: |
|
833 self.VLine.set_visible(False) |
|
834 if self.TickLabel is not None: |
|
835 self.TickLabel.set_text("") |
710 else: |
836 else: |
711 min_start_tick = reduce(max, [item.GetData()[0, 0] |
837 min_start_tick = reduce(max, [item.GetData()[0, 0] |
712 for item in self.Items |
838 for item in self.Items |
713 if len(item.GetData()) > 0], 0) |
839 if len(item.GetData()) > 0], 0) |
714 start_tick = max(start_tick, min_start_tick) |
840 start_tick = max(start_tick, min_start_tick) |
715 end_tick = max(end_tick, min_start_tick) |
841 end_tick = max(end_tick, min_start_tick) |
716 x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick) |
842 x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick) |
717 y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick) |
843 y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick) |
|
844 if self.CursorTick is not None: |
|
845 x_cursor, x_forced = self.Items[0].GetValue(self.CursorTick, raw=True) |
|
846 y_cursor, y_forced = self.Items[1].GetValue(self.CursorTick, raw=True) |
718 length = 0 |
847 length = 0 |
719 if x_data is not None and y_data is not None: |
848 if x_data is not None and y_data is not None: |
720 length = min(len(x_data), len(y_data)) |
849 length = min(len(x_data), len(y_data)) |
721 if len(self.Items) < 3: |
850 if len(self.Items) < 3: |
722 if x_data is not None and y_data is not None: |
851 if x_data is not None and y_data is not None: |
726 y_data[:, 1][:length])[0]) |
855 y_data[:, 1][:length])[0]) |
727 else: |
856 else: |
728 self.Plots[0].set_data( |
857 self.Plots[0].set_data( |
729 x_data[:, 1][:length], |
858 x_data[:, 1][:length], |
730 y_data[:, 1][:length]) |
859 y_data[:, 1][:length]) |
|
860 |
|
861 if self.CursorTick is not None: |
|
862 if self.VLine is None: |
|
863 self.VLine = self.Axes.axvline(x_cursor, color='r') |
|
864 else: |
|
865 self.VLine.set_xdata((x_cursor, x_cursor)) |
|
866 if self.HLine is None: |
|
867 self.HLine = self.Axes.axhline(y_cursor, color='r') |
|
868 else: |
|
869 self.HLine.set_ydata((y_cursor, y_cursor)) |
|
870 self.VLine.set_visible(True) |
|
871 self.HLine.set_visible(True) |
|
872 tick_label = self.ParentWindow.GetTickLabel(self.CursorTick) |
|
873 if self.TickLabel is None: |
|
874 self.TickLabel = self.Axes.text(0.05, 0.90, tick_label, |
|
875 size = 'small', transform = self.Axes.transAxes) |
|
876 else: |
|
877 self.TickLabel.set_text(tick_label) |
|
878 else: |
|
879 if self.VLine is not None: |
|
880 self.VLine.set_visible(False) |
|
881 if self.HLine is not None: |
|
882 self.HLine.set_visible(False) |
|
883 if self.TickLabel is not None: |
|
884 self.TickLabel.set_text("") |
731 else: |
885 else: |
732 while len(self.Axes.lines) > 0: |
886 while len(self.Axes.lines) > 0: |
733 self.Axes.lines.pop() |
887 self.Axes.lines.pop() |
734 z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick) |
888 z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick) |
|
889 if self.CursorTick is not None: |
|
890 z_cursor, z_forced = self.Items[2].GetValue(self.CursorTick, raw=True) |
735 if x_data is not None and y_data is not None and z_data is not None: |
891 if x_data is not None and y_data is not None and z_data is not None: |
736 length = min(length, len(z_data)) |
892 length = min(length, len(z_data)) |
737 self.Axes.plot(x_data[:, 1][:length], |
893 self.Axes.plot(x_data[:, 1][:length], |
738 y_data[:, 1][:length], |
894 y_data[:, 1][:length], |
739 zs = z_data[:, 1][:length]) |
895 zs = z_data[:, 1][:length]) |
740 self.Axes.set_zlim(z_min, z_max) |
896 self.Axes.set_zlim(z_min, z_max) |
741 |
897 if self.CursorTick is not None: |
|
898 for kwargs in [{"xs": numpy.array([x_min, x_max])}, |
|
899 {"ys": numpy.array([y_min, y_max])}, |
|
900 {"zs": numpy.array([z_min, z_max])}]: |
|
901 for param, value in [("xs", numpy.array([x_cursor, x_cursor])), |
|
902 ("ys", numpy.array([y_cursor, y_cursor])), |
|
903 ("zs", numpy.array([z_cursor, z_cursor]))]: |
|
904 kwargs.setdefault(param, value) |
|
905 kwargs["color"] = 'r' |
|
906 self.Axes.plot(**kwargs) |
|
907 |
742 self.Axes.set_xlim(x_min, x_max) |
908 self.Axes.set_xlim(x_min, x_max) |
743 self.Axes.set_ylim(y_min, y_max) |
909 self.Axes.set_ylim(y_min, y_max) |
744 |
910 |
745 labels = ["%s: %s" % (item.GetVariable(40), item.GetValue()) |
911 if self.CursorTick is not None: |
746 for item in self.Items] |
912 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items]) |
747 colors = [{True: 'b', False: 'k'}[item.IsForced()] for item in self.Items] |
913 else: |
|
914 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items]) |
|
915 names = [item.GetVariable(self.VariableNameMask) for item in self.Items] |
|
916 labels = map(lambda x: "%s: %s" % x, zip(names, values)) |
|
917 colors = map(lambda x: {True: 'b', False: 'k'}[x], forced) |
748 if self.GraphType == GRAPH_PARALLEL: |
918 if self.GraphType == GRAPH_PARALLEL: |
749 self.Legend = self.Axes.legend(self.Plots, labels, |
919 self.Legend = self.Axes.legend(self.Plots, labels, |
750 loc="upper left", frameon=False, |
920 loc="upper left", frameon=False, |
751 prop={'size':'small'}) |
921 prop={'size':'small'}) |
752 for t, color in zip(self.Legend.get_texts(), colors): |
922 for t, color in zip(self.Legend.get_texts(), colors): |
921 def NewDataAvailable(self, tick, *args, **kwargs): |
1093 def NewDataAvailable(self, tick, *args, **kwargs): |
922 if USE_MPL and tick is not None: |
1094 if USE_MPL and tick is not None: |
923 self.Ticks = numpy.append(self.Ticks, [tick]) |
1095 self.Ticks = numpy.append(self.Ticks, [tick]) |
924 if not self.Fixed or tick < self.StartTick + self.CurrentRange: |
1096 if not self.Fixed or tick < self.StartTick + self.CurrentRange: |
925 self.StartTick = max(self.StartTick, tick - self.CurrentRange) |
1097 self.StartTick = max(self.StartTick, tick - self.CurrentRange) |
|
1098 self.ResetCursorTick(False) |
926 DebugViewer.NewDataAvailable(self, tick, *args, **kwargs) |
1099 DebugViewer.NewDataAvailable(self, tick, *args, **kwargs) |
|
1100 |
|
1101 def ForceRefresh(self): |
|
1102 self.Force = True |
|
1103 wx.CallAfter(self.NewDataAvailable, None, True) |
927 |
1104 |
928 def RefreshGraphicsSizer(self): |
1105 def RefreshGraphicsSizer(self): |
929 self.GraphicsSizer.Clear() |
1106 self.GraphicsSizer.Clear() |
930 |
1107 |
931 for panel in self.GraphicPanels: |
1108 for panel in self.GraphicPanels: |
932 self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) |
1109 self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) |
933 |
1110 |
934 self.GraphicsSizer.Layout() |
1111 self.GraphicsSizer.Layout() |
935 self.RefreshGraphicsWindowScrollbars() |
1112 self.RefreshGraphicsWindowScrollbars() |
|
1113 |
|
1114 def SetCursorTickRatio(self, cursor_tick_ratio): |
|
1115 self.CursorTickRatio = cursor_tick_ratio |
|
1116 self.ResetCursorTick() |
|
1117 |
|
1118 def ResetCursorTickRatio(self): |
|
1119 self.CursorTickRatio = None |
|
1120 self.ResetCursorTick() |
|
1121 |
|
1122 def ResetCursorTick(self, force_refresh=True): |
|
1123 if self.CursorTickRatio is not None and len(self.Ticks) > 0: |
|
1124 raw_tick = self.StartTick + self.CursorTickRatio * self.CurrentRange |
|
1125 cursor_tick = self.Ticks[numpy.argmin(abs(self.Ticks - raw_tick))] |
|
1126 else: |
|
1127 cursor_tick = None |
|
1128 for panel in self.GraphicPanels: |
|
1129 if isinstance(panel, DebugVariableGraphic): |
|
1130 panel.SetCursorTick(cursor_tick) |
|
1131 if force_refresh: |
|
1132 self.ForceRefresh() |
|
1133 |
|
1134 def GetTickLabel(self, tick): |
|
1135 label = "Tick: %d" % tick |
|
1136 if self.Ticktime > 0: |
|
1137 tick_duration = int(tick * self.Ticktime) |
|
1138 not_null = False |
|
1139 duration = "" |
|
1140 for value, format in [(tick_duration / DAY, "%dd"), |
|
1141 ((tick_duration % DAY) / HOUR, "%dh"), |
|
1142 ((tick_duration % HOUR) / MINUTE, "%dm"), |
|
1143 ((tick_duration % MINUTE) / SECOND, "%ds")]: |
|
1144 |
|
1145 if value > 0 or not_null: |
|
1146 duration += format % value |
|
1147 not_null = True |
|
1148 |
|
1149 duration += "%gms" % (float(tick_duration % SECOND) / MILLISECOND) |
|
1150 label += "(%s)" % duration |
|
1151 return label |
936 |
1152 |
937 def RefreshView(self, only_values=False): |
1153 def RefreshView(self, only_values=False): |
938 if USE_MPL: |
1154 if USE_MPL: |
939 self.RefreshCanvasPosition() |
1155 self.RefreshCanvasPosition() |
940 |
1156 |
1103 def OnResetButton(self, event): |
1326 def OnResetButton(self, event): |
1104 self.StartTick = 0 |
1327 self.StartTick = 0 |
1105 self.Fixed = False |
1328 self.Fixed = False |
1106 for panel in self.GraphicPanels: |
1329 for panel in self.GraphicPanels: |
1107 panel.ResetData() |
1330 panel.ResetData() |
1108 self.RefreshView(True) |
1331 self.ForceRefresh() |
1109 event.Skip() |
1332 event.Skip() |
1110 |
1333 |
1111 def OnCurrentButton(self, event): |
1334 def OnCurrentButton(self, event): |
1112 if len(self.Ticks) > 0: |
1335 if len(self.Ticks) > 0: |
1113 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) |
1336 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) |
1114 self.Fixed = False |
1337 self.Fixed = False |
1115 self.Force = True |
1338 self.ForceRefresh() |
1116 self.RefreshView(True) |
|
1117 event.Skip() |
1339 event.Skip() |
1118 |
1340 |
1119 def CopyDataToClipboard(self, variables): |
1341 def CopyDataToClipboard(self, variables): |
1120 text = "tick;%s;\n" % ";".join([var_name for var_name, data in variables]) |
1342 text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables]) |
1121 next_tick = NextTick(variables) |
1343 next_tick = NextTick(variables) |
1122 while next_tick is not None: |
1344 while next_tick is not None: |
1123 values = [] |
1345 values = [] |
1124 for var_name, data in variables: |
1346 for item, data in variables: |
1125 if len(data) > 0: |
1347 if len(data) > 0: |
1126 if next_tick == data[0][0]: |
1348 if next_tick == data[0][0]: |
1127 values.append("%.3f" % data.pop(0)[1]) |
1349 var_type = item.GetVariableType() |
|
1350 if var_type in ["STRING", "WSTRING"]: |
|
1351 value = item.GetRawValue(int(data.pop(0)[2])) |
|
1352 if var_type == "STRING": |
|
1353 values.append("'%s'" % value) |
|
1354 else: |
|
1355 values.append('"%s"' % value) |
|
1356 else: |
|
1357 values.append("%.3f" % data.pop(0)[1]) |
1128 else: |
1358 else: |
1129 values.append("") |
1359 values.append("") |
1130 else: |
1360 else: |
1131 values.append("") |
1361 values.append("") |
1132 text += "%d;%s;\n" % (next_tick, ";".join(values)) |
1362 text += "%d;%s;\n" % (next_tick, ";".join(values)) |
1133 next_tick = NextTick(variables) |
1363 next_tick = NextTick(variables) |
1134 self.ParentWindow.SetCopyBuffer(text) |
1364 self.ParentWindow.SetCopyBuffer(text) |
1135 |
1365 |
1136 def OnExportGraphButton(self, event): |
1366 def OnExportGraphButton(self, event): |
1137 variables = [] |
1367 variables = [] |
1138 for item in self.Table.GetData(): |
1368 if USE_MPL: |
|
1369 items = [] |
|
1370 for panel in self.GraphicPanels: |
|
1371 items.extend(panel.GetItems()) |
|
1372 else: |
|
1373 items = self.Table.GetData() |
|
1374 for item in items: |
1139 if item.IsNumVariable(): |
1375 if item.IsNumVariable(): |
1140 variables.append((item.GetVariable(), [entry for entry in item.GetData()])) |
1376 variables.append((item, [entry for entry in item.GetData()])) |
1141 wx.CallAfter(self.CopyDataToClipboard, variables) |
1377 wx.CallAfter(self.CopyDataToClipboard, variables) |
1142 event.Skip() |
1378 event.Skip() |
1143 |
1379 |
1144 def OnPositionChanging(self, event): |
1380 def OnPositionChanging(self, event): |
1145 if len(self.Ticks) > 0: |
1381 if len(self.Ticks) > 0: |
1146 self.StartTick = self.Ticks[0] + event.GetPosition() |
1382 self.StartTick = self.Ticks[0] + event.GetPosition() |
1147 self.Fixed = True |
1383 self.Fixed = True |
1148 self.Force = True |
1384 self.ForceRefresh() |
1149 wx.CallAfter(self.NewDataAvailable, None, True) |
|
1150 event.Skip() |
1385 event.Skip() |
1151 |
1386 |
1152 def GetRange(self): |
1387 def GetRange(self): |
1153 return self.StartTick, self.StartTick + self.CurrentRange |
1388 return self.StartTick, self.StartTick + self.CurrentRange |
1154 |
1389 |