263 values = None |
260 values = None |
264 |
261 |
265 if message is not None: |
262 if message is not None: |
266 wx.CallAfter(self.ShowMessage, message) |
263 wx.CallAfter(self.ShowMessage, message) |
267 elif values is not None and values[1] == "debug": |
264 elif values is not None and values[1] == "debug": |
268 if self.ParentControl == self.ParentWindow.VariablesGrid: |
265 if isinstance(self.ParentControl, CustomGrid): |
269 x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y) |
266 x, y = self.ParentControl.CalcUnscrolledPosition(x, y) |
270 row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize()) |
267 row = self.ParentControl.YToRow(y - self.ParentControl.GetColLabelSize()) |
271 if row == wx.NOT_FOUND: |
268 if row == wx.NOT_FOUND: |
272 row = self.ParentWindow.Table.GetNumberRows() |
269 row = self.ParentWindow.Table.GetNumberRows() |
273 self.ParentWindow.InsertValue(values[0], row, force=True) |
270 self.ParentWindow.InsertValue(values[0], row, force=True) |
274 else: |
271 elif self.ParentControl is not None: |
275 width, height = self.ParentWindow.GraphicsCanvas.GetSize() |
272 width, height = self.ParentControl.Canvas.GetSize() |
276 target = None |
273 target_idx = self.ParentControl.GetIndex() |
277 merge_type = GRAPH_PARALLEL |
274 merge_type = GRAPH_PARALLEL |
278 for infos in self.ParentWindow.GraphicsAxes: |
275 if self.ParentControl.Is3DCanvas(): |
279 if infos["axes"] != self.ParentWindow.Graphics3DAxes: |
276 self.ParentWindow.InsertValue(values[0], target_idx, force=True) |
280 ax, ay, aw, ah = infos["axes"].get_position().bounds |
277 else: |
281 rect = wx.Rect(ax * width, height - (ay + ah) * height, |
278 ax, ay, aw, ah = self.ParentControl.Axes.get_position().bounds |
282 aw * width, ah * height) |
279 rect = wx.Rect(ax * width, height - (ay + ah) * height, |
283 if rect.InsideXY(x, y): |
280 aw * width, ah * height) |
284 target = infos |
281 if rect.InsideXY(x, y): |
285 merge_rect = wx.Rect(ax * width, height - (ay + ah) * height, |
282 merge_rect = wx.Rect(ax * width, height - (ay + ah) * height, |
286 aw * width / 2., ah * height) |
283 aw * width / 2., ah * height) |
287 if merge_rect.InsideXY(x, y): |
284 if merge_rect.InsideXY(x, y): |
288 merge_type = GRAPH_ORTHOGONAL |
285 merge_type = GRAPH_ORTHOGONAL |
289 break |
286 wx.CallAfter(self.ParentWindow.MergeGraphs, values[0], target_idx, merge_type, force=True) |
290 self.ParentWindow.MergeGraphs(values[0], target, merge_type, force=True) |
287 else: |
|
288 self.ParentWindow.InsertValue(values[0], target_idx, force=True) |
|
289 else: |
|
290 self.ParentWindow.InsertValue(values[0], force=True) |
291 |
291 |
292 def ShowMessage(self, message): |
292 def ShowMessage(self, message): |
293 dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) |
293 dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) |
294 dialog.ShowModal() |
294 dialog.ShowModal() |
295 dialog.Destroy() |
295 dialog.Destroy() |
296 |
296 |
297 SCROLLBAR_UNIT = 10 |
297 if USE_MPL: |
298 |
298 SECOND = 1000000000 |
299 def NextTick(variables): |
299 MINUTE = 60 * SECOND |
300 next_tick = None |
300 HOUR = 60 * MINUTE |
301 for var_name, data in variables: |
301 |
302 if len(data) > 0: |
302 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)]) |
303 if next_tick is None: |
303 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)]) |
304 next_tick = data[0][0] |
304 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \ |
305 else: |
305 [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \ |
306 next_tick = min(next_tick, data[0][0]) |
306 [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)] |
307 return next_tick |
307 |
308 |
308 GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2) |
309 def OrthogonalData(item, start_tick, end_tick): |
309 |
310 data = item.GetData(start_tick, end_tick) |
310 SCROLLBAR_UNIT = 10 |
311 min_value, max_value = item.GetRange() |
311 |
312 if min_value is not None and max_value is not None: |
312 def NextTick(variables): |
313 center = (min_value + max_value) / 2. |
313 next_tick = None |
314 range = max(1.0, max_value - min_value) |
314 for var_name, data in variables: |
315 else: |
315 if len(data) > 0: |
316 center = 0.5 |
316 if next_tick is None: |
317 range = 1.0 |
317 next_tick = data[0][0] |
318 return data, center - range * 0.55, center + range * 0.55 |
318 else: |
319 |
319 next_tick = min(next_tick, data[0][0]) |
320 class DebugVariablePanel(wx.SplitterWindow, DebugViewer): |
320 return next_tick |
|
321 |
|
322 def OrthogonalData(item, start_tick, end_tick): |
|
323 data = item.GetData(start_tick, end_tick) |
|
324 min_value, max_value = item.GetRange() |
|
325 if min_value is not None and max_value is not None: |
|
326 center = (min_value + max_value) / 2. |
|
327 range = max(1.0, max_value - min_value) |
|
328 else: |
|
329 center = 0.5 |
|
330 range = 1.0 |
|
331 return data, center - range * 0.55, center + range * 0.55 |
|
332 |
|
333 class DebugVariableViewer(wx.Panel): |
|
334 |
|
335 def AddViewer(self): |
|
336 pass |
|
337 |
|
338 def AddButtons(self): |
|
339 pass |
|
340 |
|
341 def __init__(self, parent, window, items=[]): |
|
342 wx.Panel.__init__(self, parent) |
|
343 self.SetBackgroundColour(wx.WHITE) |
|
344 self.SetDropTarget(DebugVariableDropTarget(window, self)) |
|
345 |
|
346 self.ParentWindow = window |
|
347 self.Items = items |
|
348 |
|
349 self.MainSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) |
|
350 self.AddViewer() |
|
351 self.AddButtons() |
|
352 self.MainSizer.AddGrowableCol(0) |
|
353 |
|
354 self.SetSizer(self.MainSizer) |
|
355 |
|
356 def __del__(self): |
|
357 self.ParentWindow = None |
|
358 |
|
359 def GetIndex(self): |
|
360 return self.ParentWindow.GetViewerIndex(self) |
|
361 |
|
362 def GetItem(self, variable): |
|
363 for item in self.Items: |
|
364 if item.GetVariable() == variable: |
|
365 return item |
|
366 return None |
|
367 |
|
368 def GetItems(self): |
|
369 return self.Items |
|
370 |
|
371 def GetVariables(self): |
|
372 if len(self.Items) > 1: |
|
373 variables = [item.GetVariable() for item in self.Items] |
|
374 if self.GraphType == GRAPH_ORTHOGONAL: |
|
375 return tuple(variables) |
|
376 return variables |
|
377 return self.Items[0].GetVariable() |
|
378 |
|
379 def AddItem(self, item): |
|
380 self.Items.append(item) |
|
381 |
|
382 def RemoveItem(self, item): |
|
383 if item in self.Items: |
|
384 self.Items.remove(item) |
|
385 |
|
386 def Clear(self): |
|
387 for item in self.Items: |
|
388 self.ParentWindow.RemoveDataConsumer(item) |
|
389 self.Items = [] |
|
390 |
|
391 def IsEmpty(self): |
|
392 return len(self.Items) == 0 |
|
393 |
|
394 def UnregisterObsoleteData(self): |
|
395 for item in self.Items[:]: |
|
396 iec_path = item.GetVariable().upper() |
|
397 if self.ParentWindow.GetDataType(iec_path) is None: |
|
398 self.ParentWindow.RemoveDataConsumer(item) |
|
399 self.RemoveItem(item) |
|
400 else: |
|
401 self.ParentWindow.AddDataConsumer(iec_path, item) |
|
402 item.RefreshVariableType() |
|
403 |
|
404 def ResetData(self): |
|
405 for item in self.Items: |
|
406 item.ResetData() |
|
407 |
|
408 def Refresh(self): |
|
409 pass |
|
410 |
|
411 def OnForceButton(self, event): |
|
412 if len(self.Items) == 1: |
|
413 wx.CallAfter(self.ForceValue, self.Items[0]) |
|
414 else: |
|
415 menu = wx.Menu(title='') |
|
416 for item in self.Items: |
|
417 new_id = wx.NewId() |
|
418 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, |
|
419 text=item.GetVariable(20)) |
|
420 self.Bind(wx.EVT_MENU, |
|
421 self.GetForceVariableMenuFunction(item), |
|
422 id=new_id) |
|
423 self.PopupMenu(menu) |
|
424 event.Skip() |
|
425 |
|
426 def OnReleaseButton(self, event): |
|
427 if len(self.Items) == 1: |
|
428 wx.CallAfter(self.ReleaseValue, self.Items[0]) |
|
429 else: |
|
430 menu = wx.Menu(title='') |
|
431 for item in self.Items: |
|
432 new_id = wx.NewId() |
|
433 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, |
|
434 text=item.GetVariable(20)) |
|
435 self.Bind(wx.EVT_MENU, |
|
436 self.GetReleaseVariableMenuFunction(item), |
|
437 id=new_id) |
|
438 |
|
439 self.PopupMenu(menu) |
|
440 event.Skip() |
|
441 |
|
442 def OnDeleteButton(self, event): |
|
443 if len(self.Items) == 1: |
|
444 wx.CallAfter(self.ParentWindow.DeleteValue, self, self.Items[0]) |
|
445 else: |
|
446 menu = wx.Menu(title='') |
|
447 for item in self.Items: |
|
448 new_id = wx.NewId() |
|
449 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, |
|
450 text=item.GetVariable(20)) |
|
451 self.Bind(wx.EVT_MENU, |
|
452 self.GetDeleteValueMenuFunction(item), |
|
453 id=new_id) |
|
454 |
|
455 new_id = wx.NewId() |
|
456 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("All")) |
|
457 self.Bind(wx.EVT_MENU, self.OnDeleteAllValuesMenu, id=new_id) |
|
458 |
|
459 self.PopupMenu(menu) |
|
460 event.Skip() |
|
461 |
|
462 def GetForceVariableMenuFunction(self, item): |
|
463 def ForceVariableFunction(event): |
|
464 self.ForceValue(item) |
|
465 return ForceVariableFunction |
|
466 |
|
467 def GetReleaseVariableMenuFunction(self, item): |
|
468 def ReleaseVariableFunction(event): |
|
469 self.ReleaseValue(item) |
|
470 return ReleaseVariableFunction |
|
471 |
|
472 def GetDeleteValueMenuFunction(self, item): |
|
473 def DeleteValueFunction(event): |
|
474 self.ParentWindow.DeleteValue(self, item) |
|
475 return DeleteValueFunction |
|
476 |
|
477 def ForceValue(self, item): |
|
478 iec_path = item.GetVariable().upper() |
|
479 iec_type = self.ParentWindow.GetDataType(iec_path) |
|
480 if iec_type is not None: |
|
481 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue())) |
|
482 if dialog.ShowModal() == wx.ID_OK: |
|
483 self.ParentWindow.ForceDataValue(iec_path, dialog.GetValue()) |
|
484 |
|
485 def ReleaseValue(self, item): |
|
486 iec_path = item.GetVariable().upper() |
|
487 self.ParentWindow.ReleaseDataValue(iec_path) |
|
488 |
|
489 def OnDeleteAllValuesMenu(self, event): |
|
490 wx.CallAfter(self.ParentWindow.DeleteValue, self) |
|
491 |
|
492 class DebugVariableText(DebugVariableViewer): |
|
493 |
|
494 def AddViewer(self): |
|
495 viewer_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) |
|
496 viewer_sizer.AddGrowableCol(0) |
|
497 self.MainSizer.AddSizer(viewer_sizer, border=5, |
|
498 flag=wx.ALL|wx.GROW|wx.ALIGN_CENTER_VERTICAL) |
|
499 |
|
500 variable_name_label = wx.TextCtrl(self, size=wx.Size(0, -1), |
|
501 value=self.Items[0].GetVariable(), style=wx.TE_READONLY|wx.TE_RIGHT|wx.NO_BORDER) |
|
502 variable_name_label.SetSelection(variable_name_label.GetLastPosition(), -1) |
|
503 viewer_sizer.AddWindow(variable_name_label, flag=wx.GROW) |
|
504 |
|
505 self.ValueLabel = wx.TextCtrl(self, |
|
506 size=wx.Size(100, -1), style=wx.TE_READONLY|wx.TE_RIGHT|wx.NO_BORDER) |
|
507 viewer_sizer.AddWindow(self.ValueLabel, |
|
508 border=5, flag=wx.LEFT) |
|
509 |
|
510 def AddButtons(self): |
|
511 button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
|
512 self.MainSizer.AddSizer(button_sizer, border=5, |
|
513 flag=wx.TOP|wx.BOTTOM|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) |
|
514 |
|
515 buttons = [ |
|
516 ("ForceButton", "force", _("Force value")), |
|
517 ("ReleaseButton", "release", _("Release value")), |
|
518 ("DeleteButton", "remove_element", _("Remove debug variable"))] |
|
519 |
|
520 for name, bitmap, help in buttons: |
|
521 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), |
|
522 size=wx.Size(28, 28), style=wx.NO_BORDER) |
|
523 button.SetToolTipString(help) |
|
524 setattr(self, name, button) |
|
525 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) |
|
526 button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
|
527 |
|
528 def Refresh(self): |
|
529 self.ValueLabel.ChangeValue(self.Items[0].GetValue()) |
|
530 if self.Items[0].IsForced(): |
|
531 self.ValueLabel.SetForegroundColour(wx.BLUE) |
|
532 else: |
|
533 self.ValueLabel.SetForegroundColour(wx.BLACK) |
|
534 self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1) |
|
535 |
|
536 class DebugVariableGraphic(DebugVariableViewer): |
|
537 |
|
538 def __init__(self, parent, window, items, graph_type): |
|
539 DebugVariableViewer.__init__(self, parent, window, items) |
|
540 |
|
541 self.GraphType = graph_type |
|
542 |
|
543 self.ResetGraphics() |
|
544 |
|
545 def AddViewer(self): |
|
546 self.Figure = matplotlib.figure.Figure(facecolor='w') |
|
547 |
|
548 self.Canvas = FigureCanvas(self, -1, self.Figure) |
|
549 self.Canvas.SetMinSize(wx.Size(200, 200)) |
|
550 self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self)) |
|
551 self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick) |
|
552 |
|
553 self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW) |
|
554 |
|
555 def AddButtons(self): |
|
556 button_sizer = wx.BoxSizer(wx.VERTICAL) |
|
557 self.MainSizer.AddSizer(button_sizer, border=5, |
|
558 flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) |
|
559 |
|
560 buttons = [ |
|
561 ("ForceButton", "force", _("Force value")), |
|
562 ("ReleaseButton", "release", _("Release value")), |
|
563 ("SplitButton", "split", _("Split graphs")), |
|
564 ("DeleteButton", "remove_element", _("Remove debug variable"))] |
|
565 |
|
566 for name, bitmap, help in buttons: |
|
567 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), |
|
568 size=wx.Size(28, 28), style=wx.NO_BORDER) |
|
569 button.SetToolTipString(help) |
|
570 setattr(self, name, button) |
|
571 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) |
|
572 button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
|
573 |
|
574 def OnCanvasClick(self, event): |
|
575 if len(self.Items) == 1: |
|
576 width, height = self.Canvas.GetSize() |
|
577 x, y = event.GetPosition() |
|
578 ax, ay, aw, ah = self.Axes.get_position().bounds |
|
579 rect = wx.Rect(ax * width, height - (ay + ah) * height, |
|
580 aw * width, ah * height) |
|
581 if rect.InsideXY(x, y): |
|
582 data = wx.TextDataObject(str((self.Items[0].GetVariable(), "debug"))) |
|
583 dragSource = wx.DropSource(self.Canvas) |
|
584 dragSource.SetData(data) |
|
585 dragSource.DoDragDrop() |
|
586 |
|
587 def OnMotion(self, event): |
|
588 current_time = gettime() |
|
589 if current_time - self.LastMotionTime > REFRESH_PERIOD: |
|
590 self.LastMotionTime = current_time |
|
591 Axes3D._on_move(self.Axes, event) |
|
592 |
|
593 def OnSplitButton(self, event): |
|
594 if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL: |
|
595 wx.CallAfter(self.ParentWindow.SplitGraphs, self) |
|
596 else: |
|
597 menu = wx.Menu(title='') |
|
598 for item in self.Items: |
|
599 new_id = wx.NewId() |
|
600 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, |
|
601 text=item.GetVariable(20)) |
|
602 self.Bind(wx.EVT_MENU, |
|
603 self.GetSplitGraphMenuFunction(item), |
|
604 id=new_id) |
|
605 |
|
606 new_id = wx.NewId() |
|
607 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("All")) |
|
608 self.Bind(wx.EVT_MENU, self.OnSplitAllGraphsMenu, id=new_id) |
|
609 |
|
610 self.PopupMenu(menu) |
|
611 event.Skip() |
|
612 |
|
613 def ResetGraphics(self): |
|
614 self.Figure.clear() |
|
615 if self.Is3DCanvas(): |
|
616 self.Axes = self.Figure.gca(projection='3d') |
|
617 self.Axes.set_color_cycle(['b']) |
|
618 self.LastMotionTime = gettime() |
|
619 setattr(self.Axes, "_on_move", self.OnMotion) |
|
620 self.Axes.mouse_init() |
|
621 else: |
|
622 self.Axes = self.Figure.gca() |
|
623 if self.GraphType == GRAPH_ORTHOGONAL: |
|
624 self.Figure.subplotpars.update(bottom=0.15) |
|
625 self.Plots = [] |
|
626 self.SplitButton.Enable(len(self.Items) > 1) |
|
627 |
|
628 def AddItem(self, item): |
|
629 DebugVariableViewer.AddItem(self, item) |
|
630 self.ResetGraphics() |
|
631 |
|
632 def RemoveItem(self, item): |
|
633 DebugVariableViewer.RemoveItem(self, item) |
|
634 if not self.IsEmpty(): |
|
635 self.ResetGraphics() |
|
636 |
|
637 def UnregisterObsoleteData(self): |
|
638 DebugVariableViewer.UnregisterObsoleteData(self) |
|
639 if not self.IsEmpty(): |
|
640 self.ResetGraphics() |
|
641 |
|
642 def Is3DCanvas(self): |
|
643 return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3 |
|
644 |
|
645 def Refresh(self, refresh_graphics=True): |
|
646 |
|
647 if refresh_graphics: |
|
648 start_tick, end_tick = self.ParentWindow.GetRange() |
|
649 |
|
650 if self.GraphType == GRAPH_PARALLEL: |
|
651 min_value = max_value = None |
|
652 |
|
653 for idx, item in enumerate(self.Items): |
|
654 data = item.GetData(start_tick, end_tick) |
|
655 if data is not None: |
|
656 item_min_value, item_max_value = item.GetRange() |
|
657 if min_value is None: |
|
658 min_value = item_min_value |
|
659 elif item_min_value is not None: |
|
660 min_value = min(min_value, item_min_value) |
|
661 if max_value is None: |
|
662 max_value = item_max_value |
|
663 elif item_max_value is not None: |
|
664 max_value = max(max_value, item_max_value) |
|
665 |
|
666 if len(self.Plots) <= idx: |
|
667 self.Plots.append( |
|
668 self.Axes.plot(data[:, 0], data[:, 1])[0]) |
|
669 else: |
|
670 self.Plots[idx].set_data(data[:, 0], data[:, 1]) |
|
671 |
|
672 if min_value is not None and max_value is not None: |
|
673 y_center = (min_value + max_value) / 2. |
|
674 y_range = max(1.0, max_value - min_value) |
|
675 else: |
|
676 y_center = 0.5 |
|
677 y_range = 1.0 |
|
678 x_min, x_max = start_tick, end_tick |
|
679 y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 |
|
680 |
|
681 else: |
|
682 min_start_tick = reduce(max, [item.GetData()[0, 0] |
|
683 for item in self.Items |
|
684 if len(item.GetData()) > 0], 0) |
|
685 start_tick = max(start_tick, min_start_tick) |
|
686 end_tick = max(end_tick, min_start_tick) |
|
687 x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick) |
|
688 y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick) |
|
689 length = 0 |
|
690 if x_data is not None and y_data is not None: |
|
691 length = min(len(x_data), len(y_data)) |
|
692 if len(self.Items) < 3: |
|
693 if x_data is not None and y_data is not None: |
|
694 if len(self.Plots) == 0: |
|
695 self.Plots.append( |
|
696 self.Axes.plot(x_data[:, 1][:length], |
|
697 y_data[:, 1][:length])[0]) |
|
698 else: |
|
699 self.Plots[0].set_data( |
|
700 x_data[:, 1][:length], |
|
701 y_data[:, 1][:length]) |
|
702 else: |
|
703 while len(self.Axes.lines) > 0: |
|
704 self.Axes.lines.pop() |
|
705 z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick) |
|
706 if x_data is not None and y_data is not None and z_data is not None: |
|
707 length = min(length, len(z_data)) |
|
708 self.Axes.plot(x_data[:, 1][:length], |
|
709 y_data[:, 1][:length], |
|
710 zs = z_data[:, 1][:length]) |
|
711 self.Axes.set_zlim(z_min, z_max) |
|
712 |
|
713 self.Axes.set_xlim(x_min, x_max) |
|
714 self.Axes.set_ylim(y_min, y_max) |
|
715 |
|
716 labels = ["%s: %s" % (item.GetVariable(), item.GetValue()) |
|
717 for item in self.Items] |
|
718 colors = [{True: 'b', False: 'k'}[item.IsForced()] for item in self.Items] |
|
719 if self.GraphType == GRAPH_PARALLEL: |
|
720 legend = self.Axes.legend(self.Plots, labels, |
|
721 loc="upper left", frameon=False, |
|
722 prop={'size':'small'}) |
|
723 for t, color in zip(legend.get_texts(), colors): |
|
724 t.set_color(color) |
|
725 else: |
|
726 self.Axes.set_xlabel(labels[0], fontdict={'size':'small','color':colors[0]}) |
|
727 self.Axes.set_ylabel(labels[1], fontdict={'size':'small','color':colors[1]}) |
|
728 if len(labels) > 2: |
|
729 self.Axes.set_zlabel(labels[2], fontdict={'size':'small','color':colors[2]}) |
|
730 self.Canvas.draw() |
|
731 |
|
732 def GetSplitGraphMenuFunction(self, item): |
|
733 def SplitGraphFunction(event): |
|
734 self.ParentWindow.SplitGraphs(self, item) |
|
735 return SplitGraphFunction |
|
736 |
|
737 def OnSplitAllGraphsMenu(self, event): |
|
738 self.ParentWindow.SplitGraphs(self) |
|
739 |
|
740 class DebugVariablePanel(wx.Panel, DebugViewer): |
321 |
741 |
322 def __init__(self, parent, producer, window): |
742 def __init__(self, parent, producer, window): |
323 wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D) |
743 wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) |
324 |
744 |
325 self.ParentWindow = window |
745 self.ParentWindow = window |
326 |
746 |
327 DebugViewer.__init__(self, producer, True) |
747 DebugViewer.__init__(self, producer, True) |
328 |
748 |
329 self.SetSashGravity(0.5) |
|
330 self.SetNeedUpdating(True) |
|
331 self.SetMinimumPaneSize(1) |
|
332 |
|
333 self.MainPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) |
|
334 |
|
335 main_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) |
|
336 main_panel_sizer.AddGrowableCol(0) |
|
337 main_panel_sizer.AddGrowableRow(1) |
|
338 |
|
339 button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
|
340 main_panel_sizer.AddSizer(button_sizer, border=5, |
|
341 flag=wx.ALIGN_RIGHT|wx.ALL) |
|
342 |
|
343 for name, bitmap, help in [ |
|
344 ("DeleteButton", "remove_element", _("Remove debug variable")), |
|
345 ("UpButton", "up", _("Move debug variable up")), |
|
346 ("DownButton", "down", _("Move debug variable down"))]: |
|
347 button = wx.lib.buttons.GenBitmapButton(self.MainPanel, bitmap=GetBitmap(bitmap), |
|
348 size=wx.Size(28, 28), style=wx.NO_BORDER) |
|
349 button.SetToolTipString(help) |
|
350 setattr(self, name, button) |
|
351 button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
|
352 |
|
353 self.VariablesGrid = CustomGrid(self.MainPanel, size=wx.Size(-1, 150), style=wx.VSCROLL) |
|
354 self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid)) |
|
355 self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, |
|
356 self.OnVariablesGridCellRightClick) |
|
357 self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, |
|
358 self.OnVariablesGridCellLeftClick) |
|
359 main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW) |
|
360 |
|
361 self.MainPanel.SetSizer(main_panel_sizer) |
|
362 |
|
363 self.HasNewData = False |
749 self.HasNewData = False |
364 self.Ticks = numpy.array([]) |
|
365 self.RangeValues = None |
|
366 self.StartTick = 0 |
|
367 self.Fixed = False |
|
368 self.Force = False |
|
369 |
|
370 self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames()) |
|
371 self.VariablesGrid.SetTable(self.Table) |
|
372 self.VariablesGrid.SetButtons({"Delete": self.DeleteButton, |
|
373 "Up": self.UpButton, |
|
374 "Down": self.DownButton}) |
|
375 |
|
376 def _AddVariable(new_row): |
|
377 return self.VariablesGrid.GetGridCursorRow() |
|
378 setattr(self.VariablesGrid, "_AddRow", _AddVariable) |
|
379 |
|
380 def _DeleteVariable(row): |
|
381 item = self.Table.GetItem(row) |
|
382 self.RemoveDataConsumer(item) |
|
383 for infos in self.GraphicsAxes: |
|
384 if item in infos["items"]: |
|
385 if len(infos["items"]) == 1: |
|
386 self.GraphicsAxes.remove(infos) |
|
387 else: |
|
388 infos["items"].remove(item) |
|
389 break |
|
390 self.Table.RemoveItem(row) |
|
391 self.ResetGraphics() |
|
392 self.RefreshGrid() |
|
393 setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) |
|
394 |
|
395 def _MoveVariable(row, move): |
|
396 new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1)) |
|
397 if new_row != row: |
|
398 self.Table.MoveItem(row, new_row) |
|
399 self.ResetGraphics() |
|
400 self.RefreshGrid() |
|
401 return new_row |
|
402 setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) |
|
403 |
|
404 self.VariablesGrid.SetRowLabelSize(0) |
|
405 |
|
406 self.GridColSizes = [200, 100] |
|
407 |
|
408 for col in range(self.Table.GetNumberCols()): |
|
409 attr = wx.grid.GridCellAttr() |
|
410 attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER) |
|
411 self.VariablesGrid.SetColAttr(col, attr) |
|
412 self.VariablesGrid.SetColSize(col, self.GridColSizes[col]) |
|
413 |
|
414 self.Table.ResetView(self.VariablesGrid) |
|
415 self.VariablesGrid.RefreshButtons() |
|
416 |
750 |
417 if USE_MPL: |
751 if USE_MPL: |
418 self.GraphicsPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) |
752 main_sizer = wx.BoxSizer(wx.VERTICAL) |
419 |
753 |
420 self.GraphicsPanelSizer = wx.BoxSizer(wx.VERTICAL) |
754 self.Ticks = numpy.array([]) |
|
755 self.RangeValues = None |
|
756 self.StartTick = 0 |
|
757 self.Fixed = False |
|
758 self.Force = False |
|
759 self.GraphicPanels = [] |
421 |
760 |
422 graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
761 graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
423 self.GraphicsPanelSizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) |
762 main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) |
424 |
763 |
425 range_label = wx.StaticText(self.GraphicsPanel, label=_('Range:')) |
764 range_label = wx.StaticText(self, label=_('Range:')) |
426 graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) |
765 graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) |
427 |
766 |
428 self.CanvasRange = wx.ComboBox(self.GraphicsPanel, style=wx.CB_READONLY) |
767 self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY) |
429 self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) |
768 self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) |
430 graphics_button_sizer.AddWindow(self.CanvasRange, 1, |
769 graphics_button_sizer.AddWindow(self.CanvasRange, 1, |
431 border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) |
770 border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) |
432 |
771 |
433 for name, bitmap, help in [ |
772 for name, bitmap, help in [ |
434 ("ResetButton", "reset", _("Clear the graph values")), |
773 ("ResetButton", "reset", _("Clear the graph values")), |
435 ("CurrentButton", "current", _("Go to current value")), |
774 ("CurrentButton", "current", _("Go to current value")), |
436 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: |
775 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: |
437 button = wx.lib.buttons.GenBitmapButton(self.GraphicsPanel, |
776 button = wx.lib.buttons.GenBitmapButton(self, |
438 bitmap=GetBitmap(bitmap), |
777 bitmap=GetBitmap(bitmap), |
439 size=wx.Size(28, 28), style=wx.NO_BORDER) |
778 size=wx.Size(28, 28), style=wx.NO_BORDER) |
440 button.SetToolTipString(help) |
779 button.SetToolTipString(help) |
441 setattr(self, name, button) |
780 setattr(self, name, button) |
442 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) |
781 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) |
443 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
782 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
444 |
783 |
445 self.CanvasPosition = wx.ScrollBar(self.GraphicsPanel, |
784 self.CanvasPosition = wx.ScrollBar(self, |
446 size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) |
785 size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) |
447 self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, |
786 self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, |
448 self.OnPositionChanging, self.CanvasPosition) |
787 self.OnPositionChanging, self.CanvasPosition) |
449 self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, |
788 self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, |
450 self.OnPositionChanging, self.CanvasPosition) |
789 self.OnPositionChanging, self.CanvasPosition) |
452 self.OnPositionChanging, self.CanvasPosition) |
791 self.OnPositionChanging, self.CanvasPosition) |
453 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, |
792 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, |
454 self.OnPositionChanging, self.CanvasPosition) |
793 self.OnPositionChanging, self.CanvasPosition) |
455 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, |
794 self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, |
456 self.OnPositionChanging, self.CanvasPosition) |
795 self.OnPositionChanging, self.CanvasPosition) |
457 self.GraphicsPanelSizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) |
796 main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) |
458 |
797 |
459 self.GraphicsCanvasWindow = wx.ScrolledWindow(self.GraphicsPanel, style=wx.HSCROLL|wx.VSCROLL) |
798 self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) |
460 self.GraphicsCanvasWindow.Bind(wx.EVT_SIZE, self.OnGraphicsCanvasWindowResize) |
799 self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) |
461 self.GraphicsPanelSizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW) |
800 self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) |
462 |
801 main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) |
463 graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL) |
802 |
464 |
803 self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) |
465 self.GraphicsFigure = matplotlib.figure.Figure() |
804 self.GraphicsWindow.SetSizer(self.GraphicsSizer) |
466 self.GraphicsAxes = [] |
805 |
467 |
806 self.RefreshCanvasRange() |
468 self.GraphicsCanvas = FigureCanvas(self.GraphicsCanvasWindow, -1, self.GraphicsFigure) |
807 |
469 self.GraphicsCanvas.mpl_connect("button_press_event", self.OnGraphicsCanvasClick) |
|
470 self.GraphicsCanvas.SetDropTarget(DebugVariableDropTarget(self, self.GraphicsCanvas)) |
|
471 graphics_canvas_window_sizer.AddWindow(self.GraphicsCanvas, 1, flag=wx.GROW) |
|
472 |
|
473 self.GraphicsCanvasWindow.SetSizer(graphics_canvas_window_sizer) |
|
474 |
|
475 self.Graphics3DFigure = matplotlib.figure.Figure() |
|
476 self.Graphics3DFigure.subplotpars.update(left=0.0, right=1.0, bottom=0.0, top=1.0) |
|
477 |
|
478 self.LastMotionTime = gettime() |
|
479 self.Graphics3DAxes = self.Graphics3DFigure.gca(projection='3d') |
|
480 self.Graphics3DAxes.set_color_cycle(['b']) |
|
481 setattr(self.Graphics3DAxes, "_on_move", self.OnGraphics3DMotion) |
|
482 |
|
483 self.Graphics3DCanvas = FigureCanvas(self.GraphicsPanel, -1, self.Graphics3DFigure) |
|
484 self.Graphics3DCanvas.SetMinSize(wx.Size(1, 1)) |
|
485 self.GraphicsPanelSizer.AddWindow(self.Graphics3DCanvas, 1, flag=wx.GROW) |
|
486 |
|
487 self.Graphics3DAxes.mouse_init() |
|
488 |
|
489 self.GraphicsPanel.SetSizer(self.GraphicsPanelSizer) |
|
490 |
|
491 self.SplitHorizontally(self.MainPanel, self.GraphicsPanel, -200) |
|
492 |
|
493 else: |
808 else: |
494 self.Initialize(self.MainPanel) |
809 main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) |
495 |
810 main_sizer.AddGrowableCol(0) |
496 self.ResetGraphics() |
811 main_sizer.AddGrowableRow(1) |
497 self.RefreshCanvasRange() |
812 |
498 self.RefreshScrollBar() |
813 button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
|
814 main_sizer.AddSizer(button_sizer, border=5, |
|
815 flag=wx.ALIGN_RIGHT|wx.ALL) |
|
816 |
|
817 for name, bitmap, help in [ |
|
818 ("DeleteButton", "remove_element", _("Remove debug variable")), |
|
819 ("UpButton", "up", _("Move debug variable up")), |
|
820 ("DownButton", "down", _("Move debug variable down"))]: |
|
821 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), |
|
822 size=wx.Size(28, 28), style=wx.NO_BORDER) |
|
823 button.SetToolTipString(help) |
|
824 setattr(self, name, button) |
|
825 button_sizer.AddWindow(button, border=5, flag=wx.LEFT) |
|
826 |
|
827 self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL) |
|
828 self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid)) |
|
829 self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, |
|
830 self.OnVariablesGridCellRightClick) |
|
831 self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, |
|
832 self.OnVariablesGridCellLeftClick) |
|
833 main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW) |
|
834 |
|
835 self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames()) |
|
836 self.VariablesGrid.SetTable(self.Table) |
|
837 self.VariablesGrid.SetButtons({"Delete": self.DeleteButton, |
|
838 "Up": self.UpButton, |
|
839 "Down": self.DownButton}) |
|
840 |
|
841 def _AddVariable(new_row): |
|
842 return self.VariablesGrid.GetGridCursorRow() |
|
843 setattr(self.VariablesGrid, "_AddRow", _AddVariable) |
|
844 |
|
845 def _DeleteVariable(row): |
|
846 item = self.Table.GetItem(row) |
|
847 self.RemoveDataConsumer(item) |
|
848 self.Table.RemoveItem(row) |
|
849 self.RefreshView() |
|
850 setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) |
|
851 |
|
852 def _MoveVariable(row, move): |
|
853 new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1)) |
|
854 if new_row != row: |
|
855 self.Table.MoveItem(row, new_row) |
|
856 self.RefreshView() |
|
857 return new_row |
|
858 setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) |
|
859 |
|
860 self.VariablesGrid.SetRowLabelSize(0) |
|
861 |
|
862 self.GridColSizes = [200, 100] |
|
863 |
|
864 for col in range(self.Table.GetNumberCols()): |
|
865 attr = wx.grid.GridCellAttr() |
|
866 attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER) |
|
867 self.VariablesGrid.SetColAttr(col, attr) |
|
868 self.VariablesGrid.SetColSize(col, self.GridColSizes[col]) |
|
869 |
|
870 self.Table.ResetView(self.VariablesGrid) |
|
871 self.VariablesGrid.RefreshButtons() |
|
872 |
|
873 self.SetSizer(main_sizer) |
499 |
874 |
500 def SetDataProducer(self, producer): |
875 def SetDataProducer(self, producer): |
501 DebugViewer.SetDataProducer(self, producer) |
876 DebugViewer.SetDataProducer(self, producer) |
502 |
877 |
503 if self.DataProducer is not None: |
878 if USE_MPL: |
504 self.Ticktime = self.DataProducer.GetTicktime() |
879 if self.DataProducer is not None: |
505 self.RefreshCanvasRange() |
880 self.Ticktime = self.DataProducer.GetTicktime() |
506 else: |
881 self.RefreshCanvasRange() |
507 self.Ticktime = 0 |
882 else: |
|
883 self.Ticktime = 0 |
508 |
884 |
509 def RefreshNewData(self, *args, **kwargs): |
885 def RefreshNewData(self, *args, **kwargs): |
510 if self.HasNewData or self.Force: |
886 if self.HasNewData or self.Force: |
511 self.HasNewData = False |
887 self.HasNewData = False |
512 self.RefreshGrid(only_values=True) |
888 self.RefreshView(only_values=True) |
513 DebugViewer.RefreshNewData(self, *args, **kwargs) |
889 DebugViewer.RefreshNewData(self, *args, **kwargs) |
514 |
890 |
515 def NewDataAvailable(self, tick, *args, **kwargs): |
891 def NewDataAvailable(self, tick, *args, **kwargs): |
516 if tick is not None: |
892 if USE_MPL and tick is not None: |
517 self.Ticks = numpy.append(self.Ticks, [tick]) |
893 self.Ticks = numpy.append(self.Ticks, [tick]) |
518 if not self.Fixed or tick < self.StartTick + self.CurrentRange: |
894 if not self.Fixed or tick < self.StartTick + self.CurrentRange: |
519 self.StartTick = max(self.StartTick, tick - self.CurrentRange) |
895 self.StartTick = max(self.StartTick, tick - self.CurrentRange) |
520 DebugViewer.NewDataAvailable(self, tick, *args, **kwargs) |
896 DebugViewer.NewDataAvailable(self, tick, *args, **kwargs) |
521 |
897 |
522 def RefreshGrid(self, only_values=False): |
898 def RefreshGraphicsSizer(self): |
523 self.Freeze() |
899 self.GraphicsSizer.Clear() |
524 if only_values: |
900 |
525 for col in xrange(self.Table.GetNumberCols()): |
901 for panel in self.GraphicPanels: |
526 if self.Table.GetColLabelValue(col, False) == "Value": |
902 self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) |
527 for row in xrange(self.Table.GetNumberRows()): |
903 |
528 self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value"))) |
904 self.GraphicsSizer.Layout() |
529 else: |
905 self.RefreshGraphicsWindowScrollbars() |
530 self.Table.ResetView(self.VariablesGrid) |
906 |
531 self.VariablesGrid.RefreshButtons() |
907 def RefreshView(self, only_values=False): |
532 |
|
533 self.RefreshScrollBar() |
|
534 |
|
535 self.Thaw() |
|
536 |
|
537 if USE_MPL: |
908 if USE_MPL: |
538 |
909 self.RefreshCanvasPosition() |
|
910 |
539 if not self.Fixed or self.Force: |
911 if not self.Fixed or self.Force: |
540 self.Force = False |
912 self.Force = False |
541 |
913 refresh_graphics = True |
542 # Refresh graphics |
914 else: |
543 start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange |
915 refresh_graphics = False |
544 for infos in self.GraphicsAxes: |
916 |
545 |
917 for panel in self.GraphicPanels: |
546 if infos["type"] == GRAPH_PARALLEL: |
918 if isinstance(panel, DebugVariableGraphic): |
547 min_value = max_value = None |
919 panel.Refresh(refresh_graphics) |
548 |
|
549 for idx, item in enumerate(infos["items"]): |
|
550 data = item.GetData(start_tick, end_tick) |
|
551 if data is not None: |
|
552 item_min_value, item_max_value = item.GetRange() |
|
553 if min_value is None: |
|
554 min_value = item_min_value |
|
555 elif item_min_value is not None: |
|
556 min_value = min(min_value, item_min_value) |
|
557 if max_value is None: |
|
558 max_value = item_max_value |
|
559 elif item_max_value is not None: |
|
560 max_value = max(max_value, item_max_value) |
|
561 |
|
562 if len(infos["plots"]) <= idx: |
|
563 infos["plots"].append( |
|
564 infos["axes"].plot(data[:, 0], data[:, 1])[0]) |
|
565 else: |
|
566 infos["plots"][idx].set_data(data[:, 0], data[:, 1]) |
|
567 |
|
568 if min_value is not None and max_value is not None: |
|
569 y_center = (min_value + max_value) / 2. |
|
570 y_range = max(1.0, max_value - min_value) |
|
571 else: |
|
572 y_center = 0.5 |
|
573 y_range = 1.0 |
|
574 x_min, x_max = start_tick, end_tick |
|
575 y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 |
|
576 |
|
577 else: |
|
578 min_start_tick = reduce(max, [item.GetData()[0, 0] |
|
579 for item in infos["items"] |
|
580 if len(item.GetData()) > 0], 0) |
|
581 start_tick = max(self.StartTick, min_start_tick) |
|
582 end_tick = max(self.StartTick + self.CurrentRange, min_start_tick) |
|
583 x_data, x_min, x_max = OrthogonalData(infos["items"][0], start_tick, end_tick) |
|
584 y_data, y_min, y_max = OrthogonalData(infos["items"][1], start_tick, end_tick) |
|
585 length = 0 |
|
586 if x_data is not None and y_data is not None: |
|
587 length = min(len(x_data), len(y_data)) |
|
588 if len(infos["items"]) < 3: |
|
589 if x_data is not None and y_data is not None: |
|
590 if len(infos["plots"]) == 0: |
|
591 infos["plots"].append( |
|
592 infos["axes"].plot(x_data[:, 1][:length], |
|
593 y_data[:, 1][:length])[0]) |
|
594 else: |
|
595 infos["plots"][0].set_data( |
|
596 x_data[:, 1][:length], |
|
597 y_data[:, 1][:length]) |
|
598 else: |
|
599 while len(infos["axes"].lines) > 0: |
|
600 infos["axes"].lines.pop() |
|
601 z_data, z_min, z_max = OrthogonalData(infos["items"][2], start_tick, end_tick) |
|
602 if x_data is not None and y_data is not None and z_data is not None: |
|
603 length = min(length, len(z_data)) |
|
604 infos["axes"].plot(x_data[:, 1][:length], |
|
605 y_data[:, 1][:length], |
|
606 zs = z_data[:, 1][:length]) |
|
607 infos["axes"].set_zlim(z_min, z_max) |
|
608 |
|
609 infos["axes"].set_xlim(x_min, x_max) |
|
610 infos["axes"].set_ylim(y_min, y_max) |
|
611 |
|
612 plot2d = plot3d = 0 |
|
613 for infos in self.GraphicsAxes: |
|
614 labels = ["%s: %s" % (item.GetVariable(), item.GetValue()) |
|
615 for item in infos["items"]] |
|
616 if infos["type"] == GRAPH_PARALLEL: |
|
617 infos["axes"].legend(infos["plots"], labels, |
|
618 loc="upper left", frameon=False, |
|
619 prop={'size':'small'}) |
|
620 plot2d += 1 |
|
621 else: |
920 else: |
622 infos["axes"].set_xlabel(labels[0], fontdict={'size':'small'}) |
921 panel.Refresh() |
623 infos["axes"].set_ylabel(labels[1], fontdict={'size':'small'}) |
922 |
624 if len(labels) > 2: |
923 else: |
625 infos["axes"].set_zlabel(labels[2], fontdict={'size':'small'}) |
924 self.Freeze() |
626 plot3d += 1 |
925 |
627 else: |
926 if only_values: |
628 plot2d += 1 |
927 for col in xrange(self.Table.GetNumberCols()): |
629 |
928 if self.Table.GetColLabelValue(col, False) == "Value": |
630 if plot2d > 0: |
929 for row in xrange(self.Table.GetNumberRows()): |
631 self.GraphicsCanvas.draw() |
930 self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value"))) |
632 if plot3d > 0: |
931 else: |
633 self.Graphics3DCanvas.draw() |
932 self.Table.ResetView(self.VariablesGrid) |
634 |
933 self.VariablesGrid.RefreshButtons() |
|
934 |
|
935 self.Thaw() |
|
936 |
635 def UnregisterObsoleteData(self): |
937 def UnregisterObsoleteData(self): |
636 items = [(idx, item) for idx, item in enumerate(self.Table.GetData())] |
938 if USE_MPL: |
637 items.reverse() |
939 if self.DataProducer is not None: |
638 for idx, item in items: |
940 self.Ticktime = self.DataProducer.GetTicktime() |
639 iec_path = item.GetVariable().upper() |
941 self.RefreshCanvasRange() |
640 if self.GetDataType(iec_path) is None: |
942 |
641 self.RemoveDataConsumer(item) |
943 for panel in self.GraphicPanels: |
642 self.Table.RemoveItem(idx) |
944 panel.UnregisterObsoleteData() |
643 else: |
945 |
644 self.AddDataConsumer(iec_path, item) |
946 else: |
645 item.RefreshVariableType() |
947 items = [(idx, item) for idx, item in enumerate(self.Table.GetData())] |
646 self.Freeze() |
948 items.reverse() |
647 self.Table.ResetView(self.VariablesGrid) |
949 for idx, item in items: |
648 self.VariablesGrid.RefreshButtons() |
950 iec_path = item.GetVariable().upper() |
649 self.Thaw() |
951 if self.GetDataType(iec_path) is None: |
650 if self.DataProducer is not None: |
952 self.RemoveDataConsumer(item) |
651 self.Ticktime = self.DataProducer.GetTicktime() |
953 self.Table.RemoveItem(idx) |
652 self.RefreshCanvasRange() |
954 else: |
653 |
955 self.AddDataConsumer(iec_path, item) |
654 def ResetGrid(self): |
956 item.RefreshVariableType() |
|
957 self.Freeze() |
|
958 self.Table.ResetView(self.VariablesGrid) |
|
959 self.VariablesGrid.RefreshButtons() |
|
960 self.Thaw() |
|
961 |
|
962 def ResetView(self): |
655 self.DeleteDataConsumers() |
963 self.DeleteDataConsumers() |
656 self.Table.Empty() |
964 if USE_MPL: |
657 self.Freeze() |
965 for panel in self.GraphicPanels: |
658 self.Table.ResetView(self.VariablesGrid) |
966 panel.Destroy() |
659 self.VariablesGrid.RefreshButtons() |
967 self.GraphicPanels = [] |
660 self.Thaw() |
968 self.RefreshGraphicsSizer() |
661 self.ResetGraphics() |
969 else: |
|
970 self.Table.Empty() |
|
971 self.Freeze() |
|
972 self.Table.ResetView(self.VariablesGrid) |
|
973 self.VariablesGrid.RefreshButtons() |
|
974 self.Thaw() |
662 |
975 |
663 def RefreshCanvasRange(self): |
976 def RefreshCanvasRange(self): |
664 if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES: |
977 if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES: |
665 self.RangeValues = RANGE_VALUES |
978 self.RangeValues = RANGE_VALUES |
666 self.CanvasRange.Clear() |
979 self.CanvasRange.Clear() |
667 for text, value in RANGE_VALUES: |
980 for text, value in RANGE_VALUES: |
668 self.CanvasRange.Append(text) |
981 self.CanvasRange.Append(text) |
669 self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0]) |
982 self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0]) |
670 self.CurrentRange = RANGE_VALUES[0][1] |
983 self.CurrentRange = RANGE_VALUES[0][1] |
671 self.RefreshGrid(True) |
984 self.RefreshView(True) |
672 elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES: |
985 elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES: |
673 self.RangeValues = TIME_RANGE_VALUES |
986 self.RangeValues = TIME_RANGE_VALUES |
674 self.CanvasRange.Clear() |
987 self.CanvasRange.Clear() |
675 for text, value in TIME_RANGE_VALUES: |
988 for text, value in TIME_RANGE_VALUES: |
676 self.CanvasRange.Append(text) |
989 self.CanvasRange.Append(text) |
677 self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0]) |
990 self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0]) |
678 self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime |
991 self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime |
679 self.RefreshGrid(True) |
992 self.RefreshView(True) |
680 |
993 |
681 def RefreshScrollBar(self): |
994 def RefreshCanvasPosition(self): |
682 if len(self.Ticks) > 0: |
995 if len(self.Ticks) > 0: |
683 pos = int(self.StartTick - self.Ticks[0]) |
996 pos = int(self.StartTick - self.Ticks[0]) |
684 range = int(self.Ticks[-1] - self.Ticks[0]) |
997 range = int(self.Ticks[-1] - self.Ticks[0]) |
685 else: |
998 else: |
686 pos = 0 |
999 pos = 0 |
804 self.Fixed = True |
1117 self.Fixed = True |
805 self.Force = True |
1118 self.Force = True |
806 wx.CallAfter(self.NewDataAvailable, None, True) |
1119 wx.CallAfter(self.NewDataAvailable, None, True) |
807 event.Skip() |
1120 event.Skip() |
808 |
1121 |
|
1122 def GetRange(self): |
|
1123 return self.StartTick, self.StartTick + self.CurrentRange |
|
1124 |
|
1125 def GetViewerIndex(self, viewer): |
|
1126 if viewer in self.GraphicPanels: |
|
1127 return self.GraphicPanels.index(viewer) |
|
1128 return None |
|
1129 |
809 def InsertValue(self, iec_path, idx = None, force=False): |
1130 def InsertValue(self, iec_path, idx = None, force=False): |
810 if idx is None: |
1131 if USE_MPL: |
811 idx = self.Table.GetNumberRows() |
1132 for panel in self.GraphicPanels: |
812 for item in self.Table.GetData(): |
1133 if panel.GetItem(iec_path) is not None: |
813 if iec_path == item.GetVariable(): |
1134 return |
814 return |
1135 if idx is None: |
|
1136 idx = len(self.GraphicPanels) |
|
1137 else: |
|
1138 for item in self.Table.GetData(): |
|
1139 if iec_path == item.GetVariable(): |
|
1140 return |
|
1141 if idx is None: |
|
1142 idx = self.Table.GetNumberRows() |
815 item = VariableTableItem(self, iec_path) |
1143 item = VariableTableItem(self, iec_path) |
816 result = self.AddDataConsumer(iec_path.upper(), item) |
1144 result = self.AddDataConsumer(iec_path.upper(), item) |
817 if result is not None or force: |
1145 if result is not None or force: |
818 self.Table.InsertItem(idx, item) |
1146 |
819 if item.IsNumVariable(): |
1147 if USE_MPL: |
820 self.GraphicsAxes.append({ |
1148 if item.IsNumVariable(): |
821 "items": [item], |
1149 panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) |
822 "axes": None, |
1150 else: |
823 "type": GRAPH_PARALLEL, |
1151 panel = DebugVariableText(self.GraphicsWindow, self, [item]) |
824 "plots": []}) |
1152 if idx is not None: |
825 self.ResetGraphics() |
1153 self.GraphicPanels.insert(idx, panel) |
826 self.RefreshGrid() |
1154 else: |
827 |
1155 self.GraphicPanels.append(panel) |
828 def MergeGraphs(self, source, target_infos, merge_type, force=False): |
1156 self.RefreshGraphicsSizer() |
|
1157 else: |
|
1158 self.Table.InsertItem(idx, item) |
|
1159 |
|
1160 self.RefreshView() |
|
1161 |
|
1162 def SplitGraphs(self, source_panel, item=None): |
|
1163 source_idx = self.GetViewerIndex(source_panel) |
|
1164 if source_idx is not None: |
|
1165 |
|
1166 if item is None: |
|
1167 source_items = source_panel.GetItems() |
|
1168 while len(source_items) > 1: |
|
1169 item = source_items.pop(-1) |
|
1170 if item.IsNumVariable(): |
|
1171 panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) |
|
1172 else: |
|
1173 panel = DebugVariableText(self.GraphicsWindow, self, [item]) |
|
1174 self.GraphicPanels.insert(source_idx + 1, panel) |
|
1175 if isinstance(source_panel, DebugVariableGraphic): |
|
1176 source_panel.GraphType = GRAPH_PARALLEL |
|
1177 source_panel.ResetGraphics() |
|
1178 |
|
1179 else: |
|
1180 source_panel.RemoveItem(item) |
|
1181 if item.IsNumVariable(): |
|
1182 panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) |
|
1183 else: |
|
1184 panel = DebugVariableText(self.GraphicsWindow, self, [item]) |
|
1185 self.GraphicPanels.insert(source_idx + 1, panel) |
|
1186 |
|
1187 self.RefreshGraphicsSizer() |
|
1188 self.RefreshView() |
|
1189 |
|
1190 def MergeGraphs(self, source, target_idx, merge_type, force=False): |
829 source_item = None |
1191 source_item = None |
830 for item in self.Table.GetData(): |
1192 source_panel = None |
831 if item.GetVariable() == source: |
1193 for panel in self.GraphicPanels: |
832 source_item = item |
1194 source_item = panel.GetItem(source) |
|
1195 if source_item is not None: |
|
1196 source_panel = panel |
|
1197 break |
833 if source_item is None: |
1198 if source_item is None: |
834 item = VariableTableItem(self, source) |
1199 item = VariableTableItem(self, source) |
835 if item.IsNumVariable(): |
1200 if item.IsNumVariable(): |
836 result = self.AddDataConsumer(source.upper(), item) |
1201 result = self.AddDataConsumer(source.upper(), item) |
837 if result is not None or force: |
1202 if result is not None or force: |
838 self.Table.InsertItem(self.Table.GetNumberRows(), item) |
|
839 source_item = item |
1203 source_item = item |
840 if source_item is not None: |
1204 if source_item is not None: |
841 source_infos = None |
1205 target_panel = self.GraphicPanels[target_idx] |
842 for infos in self.GraphicsAxes: |
1206 graph_type = target_panel.GraphType |
843 if source_item in infos["items"]: |
1207 if target_panel != source_panel: |
844 source_infos = infos |
1208 if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or |
845 break |
|
846 if target_infos is None and source_infos is None: |
|
847 self.GraphicsAxes.append({ |
|
848 "items": [source_item], |
|
849 "axes": None, |
|
850 "type": GRAPH_PARALLEL, |
|
851 "plots": []}) |
|
852 |
|
853 self.ResetGraphics() |
|
854 self.RefreshGrid() |
|
855 |
|
856 elif target_infos is not None and target_infos != source_infos: |
|
857 if (merge_type == GRAPH_PARALLEL and target_infos["type"] != merge_type or |
|
858 merge_type == GRAPH_ORTHOGONAL and |
1209 merge_type == GRAPH_ORTHOGONAL and |
859 (target_infos["type"] == GRAPH_PARALLEL and len(target_infos["items"]) > 1 or |
1210 (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or |
860 target_infos["type"] == GRAPH_ORTHOGONAL and len(target_infos["items"]) >= 3)): |
1211 graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)): |
861 print "Graphs not compatible" |
|
862 return |
1212 return |
863 |
1213 |
864 if source_infos is not None: |
1214 if source_panel is not None: |
865 source_infos["items"].remove(source_item) |
1215 source_panel.RemoveItem(source_item) |
866 if len(source_infos["items"]) == 0: |
1216 if source_panel.IsEmpty(): |
867 self.GraphicsAxes.remove(source_infos) |
1217 if source_panel.Canvas.HasCapture(): |
|
1218 source_panel.Canvas.ReleaseMouse() |
|
1219 self.GraphicPanels.remove(source_panel) |
|
1220 source_panel.Destroy() |
|
1221 |
|
1222 target_panel.AddItem(source_item) |
|
1223 target_panel.GraphType = merge_type |
|
1224 target_panel.ResetGraphics() |
868 |
1225 |
869 target_infos["items"].append(source_item) |
1226 self.RefreshGraphicsSizer() |
870 target_infos["type"] = merge_type |
1227 self.RefreshView() |
871 |
1228 |
872 self.ResetGraphics() |
1229 def DeleteValue(self, source_panel, item=None): |
873 self.RefreshGrid() |
1230 source_idx = self.GetViewerIndex(source_panel) |
874 |
1231 if source_idx is not None: |
875 else: |
1232 |
876 print "No modification to do" |
1233 if item is None: |
877 |
1234 source_panel.Clear() |
|
1235 source_panel.Destroy() |
|
1236 self.GraphicPanels.remove(source_panel) |
|
1237 self.RefreshGraphicsSizer() |
|
1238 else: |
|
1239 source_panel.RemoveItem(item) |
|
1240 if source_panel.IsEmpty(): |
|
1241 source_panel.Destroy() |
|
1242 self.GraphicPanels.remove(source_panel) |
|
1243 self.RefreshGraphicsSizer() |
|
1244 self.RefreshView() |
|
1245 |
878 def GetDebugVariables(self): |
1246 def GetDebugVariables(self): |
879 return [item.GetVariable() for item in self.Table.GetData()] |
1247 if USE_MPL: |
880 |
1248 return [panel.GetVariables() for panel in self.GraphicPanels] |
881 def OnGraphicsCanvasClick(self, event): |
1249 else: |
882 for infos in self.GraphicsAxes: |
1250 return [item.GetVariable() for item in self.Table.GetData()] |
883 if infos["axes"] == event.inaxes: |
1251 |
884 if len(infos["items"]) == 1: |
1252 def SetDebugVariables(self, variables): |
885 data = wx.TextDataObject(str((infos["items"][0].GetVariable(), "debug"))) |
1253 if USE_MPL: |
886 dragSource = wx.DropSource(self) |
1254 for variable in variables: |
887 dragSource.SetData(data) |
1255 if isinstance(variable, (TupleType, ListType)): |
888 dragSource.DoDragDrop() |
1256 items = [] |
889 if self.GraphicsCanvas.HasCapture(): |
1257 for iec_path in variable: |
890 self.GraphicsCanvas.ReleaseMouse() |
1258 item = VariableTableItem(self, iec_path) |
891 break |
1259 if not item.IsNumVariable(): |
|
1260 continue |
|
1261 self.AddDataConsumer(iec_path.upper(), item) |
|
1262 items.append(item) |
|
1263 if isinstance(variable, ListType): |
|
1264 panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_PARALLEL) |
|
1265 elif isinstance(variable, TupleType) and len(items) <= 3: |
|
1266 panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_ORTHOGONAL) |
|
1267 else: |
|
1268 continue |
|
1269 self.GraphicPanels.append(panel) |
|
1270 self.RefreshGraphicsSizer() |
|
1271 else: |
|
1272 self.InsertValue(variable, force=True) |
|
1273 self.RefreshView() |
|
1274 else: |
|
1275 for variable in variables: |
|
1276 if isinstance(variable, (ListType, TupleType)): |
|
1277 for iec_path in variable: |
|
1278 self.InsertValue(iec_path, force=True) |
|
1279 else: |
|
1280 self.InsertValue(variable, force=True) |
892 |
1281 |
893 def ResetGraphicsValues(self): |
1282 def ResetGraphicsValues(self): |
894 self.Ticks = numpy.array([]) |
|
895 self.StartTick = 0 |
|
896 for item in self.Table.GetData(): |
|
897 item.ResetData() |
|
898 |
|
899 def ResetGraphics(self): |
|
900 if USE_MPL: |
1283 if USE_MPL: |
901 self.GraphicsFigure.clear() |
1284 self.Ticks = numpy.array([]) |
902 |
1285 self.StartTick = 0 |
903 axes_num = 0 |
1286 for panel in self.GraphicPanels: |
904 for infos in self.GraphicsAxes: |
1287 panel.ResetData() |
905 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3: |
1288 |
906 axes_num += 1 |
1289 def RefreshGraphicsWindowScrollbars(self): |
907 if axes_num == len(self.GraphicsAxes): |
1290 xstart, ystart = self.GraphicsWindow.GetViewStart() |
908 self.Graphics3DCanvas.Hide() |
1291 window_size = self.GraphicsWindow.GetClientSize() |
909 else: |
1292 vwidth, vheight = self.GraphicsSizer.GetMinSize() |
910 self.Graphics3DCanvas.Show() |
|
911 self.GraphicsPanelSizer.Layout() |
|
912 idx = 1 |
|
913 for infos in self.GraphicsAxes: |
|
914 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3: |
|
915 axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx) |
|
916 infos["axes"] = axes |
|
917 idx += 1 |
|
918 else: |
|
919 infos["axes"] = self.Graphics3DAxes |
|
920 infos["plots"] = [] |
|
921 self.RefreshGraphicsCanvasWindowScrollbars() |
|
922 self.GraphicsCanvas.draw() |
|
923 |
|
924 def OnGraphics3DMotion(self, event): |
|
925 current_time = gettime() |
|
926 if current_time - self.LastMotionTime > REFRESH_PERIOD: |
|
927 self.LastMotionTime = current_time |
|
928 Axes3D._on_move(self.Graphics3DAxes, event) |
|
929 |
|
930 def RefreshGraphicsCanvasWindowScrollbars(self): |
|
931 xstart, ystart = self.GraphicsCanvasWindow.GetViewStart() |
|
932 window_size = self.GraphicsCanvasWindow.GetClientSize() |
|
933 axes_num = 0 |
|
934 for infos in self.GraphicsAxes: |
|
935 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3: |
|
936 axes_num += 1 |
|
937 vwidth, vheight = (window_size[0], (axes_num + 1) * 80) |
|
938 self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight)) |
|
939 posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT)) |
1293 posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT)) |
940 posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT)) |
1294 posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT)) |
941 self.GraphicsCanvasWindow.Scroll(posx, posy) |
1295 self.GraphicsWindow.Scroll(posx, posy) |
942 self.GraphicsCanvasWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, |
1296 self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, |
943 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy) |
1297 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy) |
944 |
1298 |
945 def OnGraphicsCanvasWindowResize(self, event): |
1299 def OnGraphicsWindowResize(self, event): |
946 self.RefreshGraphicsCanvasWindowScrollbars() |
1300 self.RefreshGraphicsWindowScrollbars() |
947 event.Skip() |
1301 event.Skip() |