82 Class that implements a custom drop target class for Debug Variable Graphic |
82 Class that implements a custom drop target class for Debug Variable Graphic |
83 Panel |
83 Panel |
84 """ |
84 """ |
85 |
85 |
86 class DebugVariableDropTarget(wx.TextDropTarget): |
86 class DebugVariableDropTarget(wx.TextDropTarget): |
87 |
87 |
88 def __init__(self, window): |
88 def __init__(self, window): |
89 """ |
89 """ |
90 Constructor |
90 Constructor |
91 @param window: Reference to the Debug Variable Panel |
91 @param window: Reference to the Debug Variable Panel |
92 """ |
92 """ |
93 wx.TextDropTarget.__init__(self) |
93 wx.TextDropTarget.__init__(self) |
94 self.ParentWindow = window |
94 self.ParentWindow = window |
95 |
95 |
96 def __del__(self): |
96 def __del__(self): |
97 """ |
97 """ |
98 Destructor |
98 Destructor |
99 """ |
99 """ |
100 # Remove reference to Debug Variable Panel |
100 # Remove reference to Debug Variable Panel |
101 self.ParentWindow = None |
101 self.ParentWindow = None |
102 |
102 |
103 def OnDragOver(self, x, y, d): |
103 def OnDragOver(self, x, y, d): |
104 """ |
104 """ |
105 Function called when mouse is dragged over Drop Target |
105 Function called when mouse is dragged over Drop Target |
106 @param x: X coordinate of mouse pointer |
106 @param x: X coordinate of mouse pointer |
107 @param y: Y coordinate of mouse pointer |
107 @param y: Y coordinate of mouse pointer |
108 @param d: Suggested default for return value |
108 @param d: Suggested default for return value |
109 """ |
109 """ |
110 # Signal Debug Variable Panel to refresh highlight giving mouse position |
110 # Signal Debug Variable Panel to refresh highlight giving mouse position |
111 self.ParentWindow.RefreshHighlight(x, y) |
111 self.ParentWindow.RefreshHighlight(x, y) |
112 return wx.TextDropTarget.OnDragOver(self, x, y, d) |
112 return wx.TextDropTarget.OnDragOver(self, x, y, d) |
113 |
113 |
114 def OnDropText(self, x, y, data): |
114 def OnDropText(self, x, y, data): |
115 """ |
115 """ |
116 Function called when mouse is released in Drop Target |
116 Function called when mouse is released in Drop Target |
117 @param x: X coordinate of mouse pointer |
117 @param x: X coordinate of mouse pointer |
118 @param y: Y coordinate of mouse pointer |
118 @param y: Y coordinate of mouse pointer |
119 @param data: Text associated to drag'n drop |
119 @param data: Text associated to drag'n drop |
120 """ |
120 """ |
121 # Signal Debug Variable Panel to reset highlight |
121 # Signal Debug Variable Panel to reset highlight |
122 self.ParentWindow.ResetHighlight() |
122 self.ParentWindow.ResetHighlight() |
123 |
123 |
124 message = None |
124 message = None |
125 |
125 |
126 # Check that data is valid regarding DebugVariablePanel |
126 # Check that data is valid regarding DebugVariablePanel |
127 try: |
127 try: |
128 values = eval(data) |
128 values = eval(data) |
129 if not isinstance(values, TupleType): |
129 if not isinstance(values, TupleType): |
130 raise ValueError |
130 raise ValueError |
131 except: |
131 except: |
132 message = _("Invalid value \"%s\" for debug variable")%data |
132 message = _("Invalid value \"%s\" for debug variable")%data |
133 values = None |
133 values = None |
134 |
134 |
135 # Display message if data is invalid |
135 # Display message if data is invalid |
136 if message is not None: |
136 if message is not None: |
137 wx.CallAfter(self.ShowMessage, message) |
137 wx.CallAfter(self.ShowMessage, message) |
138 |
138 |
139 # Data contain a reference to a variable to debug |
139 # Data contain a reference to a variable to debug |
140 elif values[1] == "debug": |
140 elif values[1] == "debug": |
141 |
141 |
142 # Drag'n Drop is an internal is an internal move inside Debug |
142 # Drag'n Drop is an internal is an internal move inside Debug |
143 # Variable Panel |
143 # Variable Panel |
144 if len(values) > 2 and values[2] == "move": |
144 if len(values) > 2 and values[2] == "move": |
145 self.ParentWindow.MoveValue(values[0]) |
145 self.ParentWindow.MoveValue(values[0]) |
146 |
146 |
147 # Drag'n Drop was initiated by another control of Beremiz |
147 # Drag'n Drop was initiated by another control of Beremiz |
148 else: |
148 else: |
149 self.ParentWindow.InsertValue(values[0], force=True) |
149 self.ParentWindow.InsertValue(values[0], force=True) |
150 |
150 |
151 def OnLeave(self): |
151 def OnLeave(self): |
152 """ |
152 """ |
153 Function called when mouse is leave Drop Target |
153 Function called when mouse is leave Drop Target |
154 """ |
154 """ |
155 # Signal Debug Variable Panel to reset highlight |
155 # Signal Debug Variable Panel to reset highlight |
156 self.ParentWindow.ResetHighlight() |
156 self.ParentWindow.ResetHighlight() |
157 return wx.TextDropTarget.OnLeave(self) |
157 return wx.TextDropTarget.OnLeave(self) |
158 |
158 |
159 def ShowMessage(self, message): |
159 def ShowMessage(self, message): |
160 """ |
160 """ |
161 Show error message in Error Dialog |
161 Show error message in Error Dialog |
162 @param message: Error message to display |
162 @param message: Error message to display |
163 """ |
163 """ |
164 dialog = wx.MessageDialog(self.ParentWindow, |
164 dialog = wx.MessageDialog(self.ParentWindow, |
165 message, |
165 message, |
166 _("Error"), |
166 _("Error"), |
167 wx.OK|wx.ICON_ERROR) |
167 wx.OK|wx.ICON_ERROR) |
168 dialog.ShowModal() |
168 dialog.ShowModal() |
169 dialog.Destroy() |
169 dialog.Destroy() |
170 |
170 |
171 |
171 |
176 """ |
176 """ |
177 Class that implements a Viewer that display variable values as a graphs |
177 Class that implements a Viewer that display variable values as a graphs |
178 """ |
178 """ |
179 |
179 |
180 class DebugVariablePanel(wx.Panel, DebugViewer): |
180 class DebugVariablePanel(wx.Panel, DebugViewer): |
181 |
181 |
182 def __init__(self, parent, producer, window): |
182 def __init__(self, parent, producer, window): |
183 """ |
183 """ |
184 Constructor |
184 Constructor |
185 @param parent: Reference to the parent wx.Window |
185 @param parent: Reference to the parent wx.Window |
186 @param producer: Object receiving debug value and dispatching them to |
186 @param producer: Object receiving debug value and dispatching them to |
187 consumers |
187 consumers |
188 @param window: Reference to Beremiz frame |
188 @param window: Reference to Beremiz frame |
189 """ |
189 """ |
190 wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) |
190 wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) |
191 |
191 |
192 # Save Reference to Beremiz frame |
192 # Save Reference to Beremiz frame |
193 self.ParentWindow = window |
193 self.ParentWindow = window |
194 |
194 |
195 # Variable storing flag indicating that variable displayed in table |
195 # Variable storing flag indicating that variable displayed in table |
196 # received new value and then table need to be refreshed |
196 # received new value and then table need to be refreshed |
197 self.HasNewData = False |
197 self.HasNewData = False |
198 |
198 |
199 # Variable storing flag indicating that refresh has been forced, and |
199 # Variable storing flag indicating that refresh has been forced, and |
200 # that next time refresh is possible, it will be done even if no new |
200 # that next time refresh is possible, it will be done even if no new |
201 # data is available |
201 # data is available |
202 self.Force = False |
202 self.Force = False |
203 |
203 |
204 self.SetBackgroundColour(wx.WHITE) |
204 self.SetBackgroundColour(wx.WHITE) |
205 |
205 |
206 main_sizer = wx.BoxSizer(wx.VERTICAL) |
206 main_sizer = wx.BoxSizer(wx.VERTICAL) |
207 |
207 |
208 self.Ticks = numpy.array([]) # List of tick received |
208 self.Ticks = numpy.array([]) # List of tick received |
209 self.StartTick = 0 # Tick starting range of data displayed |
209 self.StartTick = 0 # Tick starting range of data displayed |
210 self.Fixed = False # Flag that range of data is fixed |
210 self.Fixed = False # Flag that range of data is fixed |
211 self.CursorTick = None # Tick of cursor for displaying values |
211 self.CursorTick = None # Tick of cursor for displaying values |
212 |
212 |
213 self.DraggingAxesPanel = None |
213 self.DraggingAxesPanel = None |
214 self.DraggingAxesBoundingBox = None |
214 self.DraggingAxesBoundingBox = None |
215 self.DraggingAxesMousePos = None |
215 self.DraggingAxesMousePos = None |
216 self.VetoScrollEvent = False |
216 self.VetoScrollEvent = False |
217 |
217 |
218 self.VariableNameMask = [] |
218 self.VariableNameMask = [] |
219 |
219 |
220 self.GraphicPanels = [] |
220 self.GraphicPanels = [] |
221 |
221 |
222 graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
222 graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
223 main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) |
223 main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) |
224 |
224 |
225 range_label = wx.StaticText(self, label=_('Range:')) |
225 range_label = wx.StaticText(self, label=_('Range:')) |
226 graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) |
226 graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) |
227 |
227 |
228 self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY) |
228 self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY) |
229 self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) |
229 self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) |
230 graphics_button_sizer.AddWindow(self.CanvasRange, 1, |
230 graphics_button_sizer.AddWindow(self.CanvasRange, 1, |
231 border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) |
231 border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) |
232 |
232 |
233 self.CanvasRange.Clear() |
233 self.CanvasRange.Clear() |
234 default_range_idx = 0 |
234 default_range_idx = 0 |
235 for idx, (text, value) in enumerate(RANGE_VALUES): |
235 for idx, (text, value) in enumerate(RANGE_VALUES): |
236 self.CanvasRange.Append(text) |
236 self.CanvasRange.Append(text) |
237 if text == "1s": |
237 if text == "1s": |
238 default_range_idx = idx |
238 default_range_idx = idx |
239 self.CanvasRange.SetSelection(default_range_idx) |
239 self.CanvasRange.SetSelection(default_range_idx) |
240 |
240 |
241 for name, bitmap, help in [ |
241 for name, bitmap, help in [ |
242 ("CurrentButton", "current", _("Go to current value")), |
242 ("CurrentButton", "current", _("Go to current value")), |
243 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: |
243 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: |
244 button = wx.lib.buttons.GenBitmapButton(self, |
244 button = wx.lib.buttons.GenBitmapButton(self, |
245 bitmap=GetBitmap(bitmap), |
245 bitmap=GetBitmap(bitmap), |
246 size=wx.Size(28, 28), style=wx.NO_BORDER) |
246 size=wx.Size(28, 28), style=wx.NO_BORDER) |
247 button.SetToolTipString(help) |
247 button.SetToolTipString(help) |
248 setattr(self, name, button) |
248 setattr(self, name, button) |
249 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) |
249 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) |
250 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
250 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
251 |
251 |
252 self.CanvasPosition = wx.ScrollBar(self, |
252 self.CanvasPosition = wx.ScrollBar(self, |
253 size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) |
253 size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) |
254 self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, |
254 self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, |
255 self.OnPositionChanging, self.CanvasPosition) |
255 self.OnPositionChanging, self.CanvasPosition) |
256 self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, |
256 self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, |
257 self.OnPositionChanging, self.CanvasPosition) |
257 self.OnPositionChanging, self.CanvasPosition) |
258 self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, |
258 self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, |
259 self.OnPositionChanging, self.CanvasPosition) |
259 self.OnPositionChanging, self.CanvasPosition) |
260 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, |
260 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, |
261 self.OnPositionChanging, self.CanvasPosition) |
261 self.OnPositionChanging, self.CanvasPosition) |
262 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, |
262 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, |
263 self.OnPositionChanging, self.CanvasPosition) |
263 self.OnPositionChanging, self.CanvasPosition) |
264 main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) |
264 main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) |
265 |
265 |
266 self.TickSizer = wx.BoxSizer(wx.HORIZONTAL) |
266 self.TickSizer = wx.BoxSizer(wx.HORIZONTAL) |
267 main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW) |
267 main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW) |
268 |
268 |
269 self.TickLabel = wx.StaticText(self) |
269 self.TickLabel = wx.StaticText(self) |
270 self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT) |
270 self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT) |
271 |
271 |
272 self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER) |
272 self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER) |
273 self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW) |
273 self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW) |
274 |
274 |
275 self.TickTimeLabel = wx.StaticText(self) |
275 self.TickTimeLabel = wx.StaticText(self) |
276 self.TickSizer.AddWindow(self.TickTimeLabel) |
276 self.TickSizer.AddWindow(self.TickTimeLabel) |
277 |
277 |
278 self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) |
278 self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) |
279 self.GraphicsWindow.SetBackgroundColour(wx.WHITE) |
279 self.GraphicsWindow.SetBackgroundColour(wx.WHITE) |
280 self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) |
280 self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) |
281 self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground) |
281 self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground) |
282 self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint) |
282 self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint) |
283 self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) |
283 self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) |
284 self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel) |
284 self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel) |
285 |
285 |
286 main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) |
286 main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) |
287 |
287 |
288 self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) |
288 self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) |
289 self.GraphicsWindow.SetSizer(self.GraphicsSizer) |
289 self.GraphicsWindow.SetSizer(self.GraphicsSizer) |
290 |
290 |
291 DebugViewer.__init__(self, producer, True) |
291 DebugViewer.__init__(self, producer, True) |
292 |
292 |
293 self.SetSizer(main_sizer) |
293 self.SetSizer(main_sizer) |
294 self.SetTickTime() |
294 self.SetTickTime() |
295 |
295 |
296 def SetTickTime(self, ticktime=0): |
296 def SetTickTime(self, ticktime=0): |
297 """ |
297 """ |
298 Set Ticktime for calculate data range according to time range selected |
298 Set Ticktime for calculate data range according to time range selected |
299 @param ticktime: Ticktime to apply to range (default: 0) |
299 @param ticktime: Ticktime to apply to range (default: 0) |
300 """ |
300 """ |
301 # Save ticktime |
301 # Save ticktime |
302 self.Ticktime = ticktime |
302 self.Ticktime = ticktime |
303 |
303 |
304 # Set ticktime to millisecond if undefined |
304 # Set ticktime to millisecond if undefined |
305 if self.Ticktime == 0: |
305 if self.Ticktime == 0: |
306 self.Ticktime = MILLISECOND |
306 self.Ticktime = MILLISECOND |
307 |
307 |
308 # Calculate range to apply to data |
308 # Calculate range to apply to data |
309 self.CurrentRange = RANGE_VALUES[ |
309 self.CurrentRange = RANGE_VALUES[ |
310 self.CanvasRange.GetSelection()][1] / self.Ticktime |
310 self.CanvasRange.GetSelection()][1] / self.Ticktime |
311 |
311 |
312 def SetDataProducer(self, producer): |
312 def SetDataProducer(self, producer): |
313 """ |
313 """ |
314 Set Data Producer |
314 Set Data Producer |
315 @param producer: Data Producer |
315 @param producer: Data Producer |
316 """ |
316 """ |
317 DebugViewer.SetDataProducer(self, producer) |
317 DebugViewer.SetDataProducer(self, producer) |
318 |
318 |
319 # Set ticktime if data producer is available |
319 # Set ticktime if data producer is available |
320 if self.DataProducer is not None: |
320 if self.DataProducer is not None: |
321 self.SetTickTime(self.DataProducer.GetTicktime()) |
321 self.SetTickTime(self.DataProducer.GetTicktime()) |
322 |
322 |
323 def RefreshNewData(self): |
323 def RefreshNewData(self): |
324 """ |
324 """ |
325 Called to refresh Panel according to values received by variables |
325 Called to refresh Panel according to values received by variables |
326 Can receive any parameters (not used here) |
326 Can receive any parameters (not used here) |
327 """ |
327 """ |
328 # Refresh graphs if new data is available or refresh is forced |
328 # Refresh graphs if new data is available or refresh is forced |
329 if self.HasNewData or self.Force: |
329 if self.HasNewData or self.Force: |
330 self.HasNewData = False |
330 self.HasNewData = False |
331 self.RefreshView() |
331 self.RefreshView() |
332 |
332 |
333 DebugViewer.RefreshNewData(self) |
333 DebugViewer.RefreshNewData(self) |
334 |
334 |
335 def NewDataAvailable(self, ticks): |
335 def NewDataAvailable(self, ticks): |
336 """ |
336 """ |
337 Called by DataProducer for each tick captured or by panel to refresh |
337 Called by DataProducer for each tick captured or by panel to refresh |
338 graphs |
338 graphs |
339 @param tick: PLC tick captured |
339 @param tick: PLC tick captured |
340 All other parameters are passed to refresh function |
340 All other parameters are passed to refresh function |
341 """ |
341 """ |
342 # If tick given |
342 # If tick given |
343 if ticks is not None: |
343 if ticks is not None: |
344 tick = ticks[-1] |
344 tick = ticks[-1] |
345 |
345 |
346 # Save tick as start tick for range if data is still empty |
346 # Save tick as start tick for range if data is still empty |
347 if len(self.Ticks) == 0: |
347 if len(self.Ticks) == 0: |
348 self.StartTick = ticks[0] |
348 self.StartTick = ticks[0] |
349 |
349 |
350 # Add tick to list of ticks received |
350 # Add tick to list of ticks received |
351 self.Ticks = numpy.append(self.Ticks, ticks) |
351 self.Ticks = numpy.append(self.Ticks, ticks) |
352 |
352 |
353 # Update start tick for range if range follow ticks received |
353 # Update start tick for range if range follow ticks received |
354 if not self.Fixed or tick < self.StartTick + self.CurrentRange: |
354 if not self.Fixed or tick < self.StartTick + self.CurrentRange: |
355 self.StartTick = max(self.StartTick, tick - self.CurrentRange) |
355 self.StartTick = max(self.StartTick, tick - self.CurrentRange) |
356 |
356 |
357 # Force refresh if graph is fixed because range of data received |
357 # Force refresh if graph is fixed because range of data received |
358 # is too small to fill data range selected |
358 # is too small to fill data range selected |
359 if self.Fixed and \ |
359 if self.Fixed and \ |
360 self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: |
360 self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: |
361 self.Force = True |
361 self.Force = True |
362 |
362 |
363 self.HasNewData = False |
363 self.HasNewData = False |
364 self.RefreshView() |
364 self.RefreshView() |
365 |
365 |
366 else: |
366 else: |
367 DebugViewer.NewDataAvailable(self, ticks) |
367 DebugViewer.NewDataAvailable(self, ticks) |
368 |
368 |
369 def ForceRefresh(self): |
369 def ForceRefresh(self): |
370 """ |
370 """ |
371 Called to force refresh of graphs |
371 Called to force refresh of graphs |
372 """ |
372 """ |
373 self.Force = True |
373 self.Force = True |
374 wx.CallAfter(self.NewDataAvailable, None) |
374 wx.CallAfter(self.NewDataAvailable, None) |
375 |
375 |
376 def SetCursorTick(self, cursor_tick): |
376 def SetCursorTick(self, cursor_tick): |
377 """ |
377 """ |
378 Set Cursor for displaying values of items at a tick given |
378 Set Cursor for displaying values of items at a tick given |
379 @param cursor_tick: Tick of cursor |
379 @param cursor_tick: Tick of cursor |
380 """ |
380 """ |
381 # Save cursor tick |
381 # Save cursor tick |
382 self.CursorTick = cursor_tick |
382 self.CursorTick = cursor_tick |
383 self.Fixed = cursor_tick is not None |
383 self.Fixed = cursor_tick is not None |
384 self.UpdateCursorTick() |
384 self.UpdateCursorTick() |
385 |
385 |
386 def MoveCursorTick(self, move): |
386 def MoveCursorTick(self, move): |
387 if self.CursorTick is not None: |
387 if self.CursorTick is not None: |
388 cursor_tick = max(self.Ticks[0], |
388 cursor_tick = max(self.Ticks[0], |
389 min(self.CursorTick + move, |
389 min(self.CursorTick + move, |
390 self.Ticks[-1])) |
390 self.Ticks[-1])) |
391 cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick)) |
391 cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick)) |
392 if self.Ticks[cursor_tick_idx] == self.CursorTick: |
392 if self.Ticks[cursor_tick_idx] == self.CursorTick: |
393 cursor_tick_idx = max(0, |
393 cursor_tick_idx = max(0, |
394 min(cursor_tick_idx + abs(move) / move, |
394 min(cursor_tick_idx + abs(move) / move, |
395 len(self.Ticks) - 1)) |
395 len(self.Ticks) - 1)) |
396 self.CursorTick = self.Ticks[cursor_tick_idx] |
396 self.CursorTick = self.Ticks[cursor_tick_idx] |
397 self.StartTick = max(self.Ticks[ |
397 self.StartTick = max(self.Ticks[ |
398 numpy.argmin(numpy.abs(self.Ticks - |
398 numpy.argmin(numpy.abs(self.Ticks - |
399 self.CursorTick + self.CurrentRange))], |
399 self.CursorTick + self.CurrentRange))], |
400 min(self.StartTick, self.CursorTick)) |
400 min(self.StartTick, self.CursorTick)) |
401 self.RefreshCanvasPosition() |
401 self.RefreshCanvasPosition() |
402 self.UpdateCursorTick() |
402 self.UpdateCursorTick() |
403 |
403 |
404 def ResetCursorTick(self): |
404 def ResetCursorTick(self): |
405 self.CursorTick = None |
405 self.CursorTick = None |
406 self.Fixed = False |
406 self.Fixed = False |
407 self.UpdateCursorTick() |
407 self.UpdateCursorTick() |
408 |
408 |
409 def UpdateCursorTick(self): |
409 def UpdateCursorTick(self): |
410 for panel in self.GraphicPanels: |
410 for panel in self.GraphicPanels: |
411 if isinstance(panel, DebugVariableGraphicViewer): |
411 if isinstance(panel, DebugVariableGraphicViewer): |
412 panel.SetCursorTick(self.CursorTick) |
412 panel.SetCursorTick(self.CursorTick) |
413 self.ForceRefresh() |
413 self.ForceRefresh() |
414 |
414 |
415 def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start): |
415 def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start): |
416 if len(panel.GetItems()) > 1: |
416 if len(panel.GetItems()) > 1: |
417 self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) |
417 self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) |
418 self.DraggingAxesPanel.SetCursorTick(self.CursorTick) |
418 self.DraggingAxesPanel.SetCursorTick(self.CursorTick) |
419 width, height = panel.GetSize() |
419 width, height = panel.GetSize() |
422 self.DraggingAxesPanel.SetPosition(wx.Point(0, -height)) |
422 self.DraggingAxesPanel.SetPosition(wx.Point(0, -height)) |
423 else: |
423 else: |
424 self.DraggingAxesPanel = panel |
424 self.DraggingAxesPanel = panel |
425 self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True) |
425 self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True) |
426 self.DraggingAxesMousePos = wx.Point( |
426 self.DraggingAxesMousePos = wx.Point( |
427 x_mouse_start - self.DraggingAxesBoundingBox.x, |
427 x_mouse_start - self.DraggingAxesBoundingBox.x, |
428 y_mouse_start - self.DraggingAxesBoundingBox.y) |
428 y_mouse_start - self.DraggingAxesBoundingBox.y) |
429 self.MoveDragNDrop(x_mouse, y_mouse) |
429 self.MoveDragNDrop(x_mouse, y_mouse) |
430 |
430 |
431 def MoveDragNDrop(self, x_mouse, y_mouse): |
431 def MoveDragNDrop(self, x_mouse, y_mouse): |
432 self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x |
432 self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x |
433 self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y |
433 self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y |
434 self.RefreshHighlight(x_mouse, y_mouse) |
434 self.RefreshHighlight(x_mouse, y_mouse) |
435 |
435 |
436 def RefreshHighlight(self, x_mouse, y_mouse): |
436 def RefreshHighlight(self, x_mouse, y_mouse): |
437 for idx, panel in enumerate(self.GraphicPanels): |
437 for idx, panel in enumerate(self.GraphicPanels): |
438 x, y = panel.GetPosition() |
438 x, y = panel.GetPosition() |
439 width, height = panel.GetSize() |
439 width, height = panel.GetSize() |
440 rect = wx.Rect(x, y, width, height) |
440 rect = wx.Rect(x, y, width, height) |
441 if (rect.InsideXY(x_mouse, y_mouse) or |
441 if (rect.InsideXY(x_mouse, y_mouse) or |
442 idx == 0 and y_mouse < 0 or |
442 idx == 0 and y_mouse < 0 or |
443 idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]): |
443 idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]): |
444 panel.RefreshHighlight(x_mouse - x, y_mouse - y) |
444 panel.RefreshHighlight(x_mouse - x, y_mouse - y) |
445 else: |
445 else: |
446 panel.SetHighlight(HIGHLIGHT_NONE) |
446 panel.SetHighlight(HIGHLIGHT_NONE) |
447 if wx.Platform == "__WXMSW__": |
447 if wx.Platform == "__WXMSW__": |
448 self.RefreshView() |
448 self.RefreshView() |
449 else: |
449 else: |
450 self.ForceRefresh() |
450 self.ForceRefresh() |
451 |
451 |
452 def ResetHighlight(self): |
452 def ResetHighlight(self): |
453 for panel in self.GraphicPanels: |
453 for panel in self.GraphicPanels: |
454 panel.SetHighlight(HIGHLIGHT_NONE) |
454 panel.SetHighlight(HIGHLIGHT_NONE) |
455 if wx.Platform == "__WXMSW__": |
455 if wx.Platform == "__WXMSW__": |
456 self.RefreshView() |
456 self.RefreshView() |
457 else: |
457 else: |
458 self.ForceRefresh() |
458 self.ForceRefresh() |
459 |
459 |
460 def IsDragging(self): |
460 def IsDragging(self): |
461 return self.DraggingAxesPanel is not None |
461 return self.DraggingAxesPanel is not None |
462 |
462 |
463 def GetDraggingAxesClippingRegion(self, panel): |
463 def GetDraggingAxesClippingRegion(self, panel): |
464 x, y = panel.GetPosition() |
464 x, y = panel.GetPosition() |
465 width, height = panel.GetSize() |
465 width, height = panel.GetSize() |
466 bbox = wx.Rect(x, y, width, height) |
466 bbox = wx.Rect(x, y, width, height) |
467 bbox = bbox.Intersect(self.DraggingAxesBoundingBox) |
467 bbox = bbox.Intersect(self.DraggingAxesBoundingBox) |
468 bbox.x -= x |
468 bbox.x -= x |
469 bbox.y -= y |
469 bbox.y -= y |
470 return bbox |
470 return bbox |
471 |
471 |
472 def GetDraggingAxesPosition(self, panel): |
472 def GetDraggingAxesPosition(self, panel): |
473 x, y = panel.GetPosition() |
473 x, y = panel.GetPosition() |
474 return wx.Point(self.DraggingAxesBoundingBox.x - x, |
474 return wx.Point(self.DraggingAxesBoundingBox.x - x, |
475 self.DraggingAxesBoundingBox.y - y) |
475 self.DraggingAxesBoundingBox.y - y) |
476 |
476 |
477 def StopDragNDrop(self, variable, x_mouse, y_mouse): |
477 def StopDragNDrop(self, variable, x_mouse, y_mouse): |
478 if self.DraggingAxesPanel not in self.GraphicPanels: |
478 if self.DraggingAxesPanel not in self.GraphicPanels: |
479 self.DraggingAxesPanel.Destroy() |
479 self.DraggingAxesPanel.Destroy() |
480 self.DraggingAxesPanel = None |
480 self.DraggingAxesPanel = None |
481 self.DraggingAxesBoundingBox = None |
481 self.DraggingAxesBoundingBox = None |
502 else: |
502 else: |
503 if y_mouse > yw + height / 2: |
503 if y_mouse > yw + height / 2: |
504 idx += 1 |
504 idx += 1 |
505 wx.CallAfter(self.MoveValue, variable, idx, True) |
505 wx.CallAfter(self.MoveValue, variable, idx, True) |
506 self.ForceRefresh() |
506 self.ForceRefresh() |
507 return |
507 return |
508 width, height = self.GraphicsWindow.GetVirtualSize() |
508 width, height = self.GraphicsWindow.GetVirtualSize() |
509 rect = wx.Rect(0, 0, width, height) |
509 rect = wx.Rect(0, 0, width, height) |
510 if rect.InsideXY(x_mouse, y_mouse): |
510 if rect.InsideXY(x_mouse, y_mouse): |
511 wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True) |
511 wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True) |
512 self.ForceRefresh() |
512 self.ForceRefresh() |
513 |
513 |
514 def RefreshGraphicsSizer(self): |
514 def RefreshGraphicsSizer(self): |
515 self.GraphicsSizer.Clear() |
515 self.GraphicsSizer.Clear() |
516 |
516 |
517 for panel in self.GraphicPanels: |
517 for panel in self.GraphicPanels: |
518 self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) |
518 self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) |
519 |
519 |
520 self.GraphicsSizer.Layout() |
520 self.GraphicsSizer.Layout() |
521 self.RefreshGraphicsWindowScrollbars() |
521 self.RefreshGraphicsWindowScrollbars() |
522 |
522 |
523 def RefreshView(self): |
523 def RefreshView(self): |
524 self.RefreshCanvasPosition() |
524 self.RefreshCanvasPosition() |
525 |
525 |
526 width, height = self.GraphicsWindow.GetVirtualSize() |
526 width, height = self.GraphicsWindow.GetVirtualSize() |
527 bitmap = wx.EmptyBitmap(width, height) |
527 bitmap = wx.EmptyBitmap(width, height) |
528 dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap) |
528 dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap) |
529 dc.Clear() |
529 dc.Clear() |
530 dc.BeginDrawing() |
530 dc.BeginDrawing() |
531 if self.DraggingAxesPanel is not None: |
531 if self.DraggingAxesPanel is not None: |
532 destBBox = self.DraggingAxesBoundingBox |
532 destBBox = self.DraggingAxesBoundingBox |
533 srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox() |
533 srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox() |
534 |
534 |
535 srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None) |
535 srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None) |
536 srcDC = wx.MemoryDC() |
536 srcDC = wx.MemoryDC() |
537 srcDC.SelectObject(srcBmp) |
537 srcDC.SelectObject(srcBmp) |
538 |
538 |
539 dc.Blit(destBBox.x, destBBox.y, |
539 dc.Blit(destBBox.x, destBBox.y, |
540 int(destBBox.width), int(destBBox.height), |
540 int(destBBox.width), int(destBBox.height), |
541 srcDC, srcBBox.x, srcBBox.y) |
541 srcDC, srcBBox.x, srcBBox.y) |
542 dc.EndDrawing() |
542 dc.EndDrawing() |
543 |
543 |
544 if not self.Fixed or self.Force: |
544 if not self.Fixed or self.Force: |
545 self.Force = False |
545 self.Force = False |
546 refresh_graphics = True |
546 refresh_graphics = True |
547 else: |
547 else: |
548 refresh_graphics = False |
548 refresh_graphics = False |
549 |
549 |
550 if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels: |
550 if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels: |
551 self.DraggingAxesPanel.RefreshViewer(refresh_graphics) |
551 self.DraggingAxesPanel.RefreshViewer(refresh_graphics) |
552 for panel in self.GraphicPanels: |
552 for panel in self.GraphicPanels: |
553 if isinstance(panel, DebugVariableGraphicViewer): |
553 if isinstance(panel, DebugVariableGraphicViewer): |
554 panel.RefreshViewer(refresh_graphics) |
554 panel.RefreshViewer(refresh_graphics) |
555 else: |
555 else: |
556 panel.RefreshViewer() |
556 panel.RefreshViewer() |
557 |
557 |
558 if self.CursorTick is not None: |
558 if self.CursorTick is not None: |
559 tick = self.CursorTick |
559 tick = self.CursorTick |
560 elif len(self.Ticks) > 0: |
560 elif len(self.Ticks) > 0: |
561 tick = self.Ticks[-1] |
561 tick = self.Ticks[-1] |
562 else: |
562 else: |
568 duration = "" |
568 duration = "" |
569 for value, format in [(tick_duration / DAY, _("%dd")), |
569 for value, format in [(tick_duration / DAY, _("%dd")), |
570 ((tick_duration % DAY) / HOUR, _("%dh")), |
570 ((tick_duration % DAY) / HOUR, _("%dh")), |
571 ((tick_duration % HOUR) / MINUTE, _("%dm")), |
571 ((tick_duration % HOUR) / MINUTE, _("%dm")), |
572 ((tick_duration % MINUTE) / SECOND, _("%ds"))]: |
572 ((tick_duration % MINUTE) / SECOND, _("%ds"))]: |
573 |
573 |
574 if value > 0 or not_null: |
574 if value > 0 or not_null: |
575 duration += format % value |
575 duration += format % value |
576 not_null = True |
576 not_null = True |
577 |
577 |
578 duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) |
578 duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) |
579 self.TickTimeLabel.SetLabel("t: %s" % duration) |
579 self.TickTimeLabel.SetLabel("t: %s" % duration) |
580 else: |
580 else: |
581 self.TickLabel.SetLabel("") |
581 self.TickLabel.SetLabel("") |
582 self.TickTimeLabel.SetLabel("") |
582 self.TickTimeLabel.SetLabel("") |
583 self.TickSizer.Layout() |
583 self.TickSizer.Layout() |
584 |
584 |
585 def SubscribeAllDataConsumers(self): |
585 def SubscribeAllDataConsumers(self): |
586 DebugViewer.SubscribeAllDataConsumers(self) |
586 DebugViewer.SubscribeAllDataConsumers(self) |
587 |
587 |
588 if self.DataProducer is not None: |
588 if self.DataProducer is not None: |
589 if self.DataProducer is not None: |
589 if self.DataProducer is not None: |
590 self.SetTickTime(self.DataProducer.GetTicktime()) |
590 self.SetTickTime(self.DataProducer.GetTicktime()) |
591 |
591 |
592 self.ResetCursorTick() |
592 self.ResetCursorTick() |
593 |
593 |
594 for panel in self.GraphicPanels[:]: |
594 for panel in self.GraphicPanels[:]: |
595 panel.SubscribeAllDataConsumers() |
595 panel.SubscribeAllDataConsumers() |
596 if panel.ItemsIsEmpty(): |
596 if panel.ItemsIsEmpty(): |
597 if panel.HasCapture(): |
597 if panel.HasCapture(): |
598 panel.ReleaseMouse() |
598 panel.ReleaseMouse() |
599 self.GraphicPanels.remove(panel) |
599 self.GraphicPanels.remove(panel) |
600 panel.Destroy() |
600 panel.Destroy() |
601 |
601 |
602 self.ResetVariableNameMask() |
602 self.ResetVariableNameMask() |
603 self.RefreshGraphicsSizer() |
603 self.RefreshGraphicsSizer() |
604 self.ForceRefresh() |
604 self.ForceRefresh() |
605 |
605 |
606 def ResetView(self): |
606 def ResetView(self): |
607 self.UnsubscribeAllDataConsumers() |
607 self.UnsubscribeAllDataConsumers() |
608 |
608 |
609 self.Fixed = False |
609 self.Fixed = False |
610 for panel in self.GraphicPanels: |
610 for panel in self.GraphicPanels: |
611 panel.Destroy() |
611 panel.Destroy() |
612 self.GraphicPanels = [] |
612 self.GraphicPanels = [] |
613 self.ResetVariableNameMask() |
613 self.ResetVariableNameMask() |
614 self.RefreshGraphicsSizer() |
614 self.RefreshGraphicsSizer() |
615 |
615 |
616 def SetCanvasPosition(self, tick): |
616 def SetCanvasPosition(self, tick): |
617 tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange)) |
617 tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange)) |
618 self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))] |
618 self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))] |
619 self.Fixed = True |
619 self.Fixed = True |
620 self.RefreshCanvasPosition() |
620 self.RefreshCanvasPosition() |
621 self.ForceRefresh() |
621 self.ForceRefresh() |
622 |
622 |
623 def RefreshCanvasPosition(self): |
623 def RefreshCanvasPosition(self): |
624 if len(self.Ticks) > 0: |
624 if len(self.Ticks) > 0: |
625 pos = int(self.StartTick - self.Ticks[0]) |
625 pos = int(self.StartTick - self.Ticks[0]) |
626 range = int(self.Ticks[-1] - self.Ticks[0]) |
626 range = int(self.Ticks[-1] - self.Ticks[0]) |
627 else: |
627 else: |
628 pos = 0 |
628 pos = 0 |
629 range = 0 |
629 range = 0 |
630 self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) |
630 self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) |
631 |
631 |
632 def ChangeRange(self, dir, tick=None): |
632 def ChangeRange(self, dir, tick=None): |
633 current_range = self.CurrentRange |
633 current_range = self.CurrentRange |
634 current_range_idx = self.CanvasRange.GetSelection() |
634 current_range_idx = self.CanvasRange.GetSelection() |
635 new_range_idx = max(0, min(current_range_idx + dir, len(RANGE_VALUES) - 1)) |
635 new_range_idx = max(0, min(current_range_idx + dir, len(RANGE_VALUES) - 1)) |
636 if new_range_idx != current_range_idx: |
636 if new_range_idx != current_range_idx: |