--- a/controls/DebugVariablePanel.py Thu Jan 24 00:42:59 2013 +0100
+++ b/controls/DebugVariablePanel.py Thu Jan 24 00:45:54 2013 +0100
@@ -56,16 +56,15 @@
[("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
[("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
+GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2) def AppendMenu(parent, help, id, kind, text):
parent.Append(help=help, id=id, kind=kind, text=text)
def GetDebugVariablesTableColnames():
- cols = [_("Variable"), _("Value")]
- cols.append(_("3DAxis"))
+ return [_("Variable"), _("Value")] class VariableTableItem(DebugDataConsumer):
def __init__(self, parent, variable):
@@ -74,8 +73,7 @@
self.RefreshVariableType()
@@ -165,15 +163,6 @@
return "%.6g" % self.Value
- def SetAxis3D(self, axis_3d):
- if self.IsNumVariable():
- if self.IsNumVariable():
def GetNearestData(self, tick, adjust):
@@ -202,8 +191,6 @@
return self.data[row].GetVariable()
return self.data[row].GetValue()
- elif colname == "3DAxis":
- return self.data[row].GetAxis3D()
def SetValueByName(self, row, colname, value):
@@ -212,8 +199,6 @@
self.data[row].SetVariable(value)
self.data[row].SetValue(value)
- elif colname == "3DAxis":
- self.data[row].SetAxis3D(value)
if row < self.GetNumberRows():
@@ -236,20 +221,12 @@
for row in range(self.GetNumberRows()):
for col in range(self.GetNumberCols()):
colname = self.GetColLabelValue(col, False)
- if colname == "3DAxis":
- if self.IsNumVariable(row):
- grid.SetCellRenderer(row, col, wx.grid.GridCellBoolRenderer())
- grid.SetCellEditor(row, col, wx.grid.GridCellBoolEditor())
- grid.SetReadOnly(row, col, False)
+ grid.SetCellTextColour(row, col, wx.BLUE) - grid.SetReadOnly(row, col, True)
- grid.SetCellTextColour(row, col, wx.BLUE)
- grid.SetCellTextColour(row, col, wx.BLACK)
- grid.SetReadOnly(row, col, True)
+ grid.SetCellTextColour(row, col, wx.BLACK) + grid.SetReadOnly(row, col, True) self.ResizeRow(grid, row)
def AppendItem(self, data):
@@ -269,15 +246,12 @@
class DebugVariableDropTarget(wx.TextDropTarget):
- def __init__(self, parent):
+ def __init__(self, parent, control): wx.TextDropTarget.__init__(self)
self.ParentWindow = parent
+ self.ParentControl = control def OnDropText(self, x, y, data):
- x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
- row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
- if row == wx.NOT_FOUND:
- row = self.ParentWindow.Table.GetNumberRows()
@@ -287,10 +261,33 @@
if not isinstance(values, TupleType):
message = _("Invalid value \"%s\" for debug variable")%data
- if values is not None and values[1] == "debug":
- self.ParentWindow.InsertValue(values[0], row)
wx.CallAfter(self.ShowMessage, message)
+ elif values is not None and values[1] == "debug": + if self.ParentControl == self.ParentWindow.VariablesGrid: + x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y) + row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize()) + if row == wx.NOT_FOUND: + row = self.ParentWindow.Table.GetNumberRows() + self.ParentWindow.InsertValue(values[0], row, force=True) + x, y = self.ParentWindow.GraphicsCanvasWindow.CalcUnscrolledPosition(x, y) + width, height = self.ParentWindow.GraphicsCanvas.GetSize() + merge_type = GRAPH_PARALLEL + for infos in self.ParentWindow.GraphicsAxes: + ax, ay, aw, ah = infos["axes"].get_position().bounds + rect = wx.Rect(ax * width, height - (ay + ah) * height, + aw * width, ah * height) + if rect.InsideXY(x, y): + merge_rect = wx.Rect(ax * width, height - (ay + ah) * height, + aw * width / 2., ah * height) + if merge_rect.InsideXY(x, y): + merge_type = GRAPH_ORTHOGONAL + self.ParentWindow.MergeGraphs(values[0], target, merge_type, force=True) def ShowMessage(self, message):
dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
@@ -309,6 +306,17 @@
next_tick = min(next_tick, data[0][0])
+def OrthogonalData(item, start_tick, end_tick): + data = item.GetData(start_tick, end_tick) + min_value, max_value = item.GetRange() + if min_value is not None and max_value is not None: + center = (min_value + max_value) / 2. + range = max(1.0, max_value - min_value) + return data, center - range * 0.55, center + range * 0.55 class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
def __init__(self, parent, producer, window):
@@ -343,13 +351,11 @@
button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
self.VariablesGrid = CustomGrid(self.MainPanel, size=wx.Size(-1, 150), style=wx.VSCROLL)
- self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self))
+ self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid)) self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK,
self.OnVariablesGridCellRightClick)
self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK,
self.OnVariablesGridCellLeftClick)
- self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,
- self.OnVariablesGridCellChange)
main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
self.MainPanel.SetSizer(main_panel_sizer)
@@ -397,14 +403,13 @@
self.VariablesGrid.SetRowLabelSize(0)
+ self.GridColSizes = [200, 100] for col in range(self.Table.GetNumberCols()):
attr = wx.grid.GridCellAttr()
- if self.Table.GetColLabelValue(col, False) == "3DAxis":
- attr.SetAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER)
- attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
+ attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER) self.VariablesGrid.SetColAttr(col, attr)
- self.VariablesGrid.SetColSize(col, 100)
+ self.VariablesGrid.SetColSize(col, self.GridColSizes[col]) self.Table.ResetView(self.VariablesGrid)
self.VariablesGrid.RefreshButtons()
@@ -458,10 +463,11 @@
graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
self.GraphicsFigure = matplotlib.figure.Figure()
- self.GraphicsFigure.subplots_adjust(hspace=0)
self.GraphicsCanvas = FigureCanvas(self.GraphicsCanvasWindow, -1, self.GraphicsFigure)
+ self.GraphicsCanvas.mpl_connect("button_press_event", self.OnGraphicsCanvasClick) + self.GraphicsCanvas.SetDropTarget(DebugVariableDropTarget(self, self.GraphicsCanvas)) graphics_canvas_window_sizer.AddWindow(self.GraphicsCanvas, 1, flag=wx.GROW)
self.GraphicsCanvasWindow.SetSizer(graphics_canvas_window_sizer)
@@ -528,56 +534,102 @@
- if USE_MPL and (not self.Fixed or self.Force):
- start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange
- for infos in self.GraphicsAxes:
- min_value = max_value = None
+ if not self.Fixed or self.Force: - for idx, item in enumerate(infos["items"]):
- data = item.GetData(start_tick, end_tick)
- item_min_value, item_max_value = item.GetRange()
- min_value = item_min_value
- elif item_min_value is not None:
- min_value = min(min_value, item_min_value)
- max_value = item_max_value
- elif item_max_value is not None:
- max_value = max(max_value, item_max_value)
+ start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange + for infos in self.GraphicsAxes: + if infos["type"] == GRAPH_PARALLEL: + min_value = max_value = None - if len(infos["plots"]) <= idx:
- infos["plots"].append(infos["axes"].plot(data[:, 0], data[:, 1])[0])
+ for idx, item in enumerate(infos["items"]): + data = item.GetData(start_tick, end_tick) + item_min_value, item_max_value = item.GetRange() + min_value = item_min_value + elif item_min_value is not None: + min_value = min(min_value, item_min_value) + max_value = item_max_value + elif item_max_value is not None: + max_value = max(max_value, item_max_value) + if len(infos["plots"]) <= idx: + infos["axes"].plot(data[:, 0], data[:, 1])[0]) + infos["plots"][idx].set_data(data[:, 0], data[:, 1]) + if min_value is not None and max_value is not None: + y_center = (min_value + max_value) / 2. + y_range = max(1.0, max_value - min_value) - infos["plots"][idx].set_data(data[:, 0], data[:, 1])
+ x_min, x_max = start_tick, end_tick + y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 + min_start_tick = reduce(max, [item.GetData()[0, 0] + for item in infos["items"] + if len(item.GetData()) > 0], 0) + start_tick = max(self.StartTick, min_start_tick) + end_tick = max(self.StartTick + self.CurrentRange, min_start_tick) + x_data, x_min, x_max = OrthogonalData(infos["items"][0], start_tick, end_tick) + y_data, y_min, y_max = OrthogonalData(infos["items"][1], start_tick, end_tick) + if x_data is not None and y_data is not None: + length = min(len(x_data), len(y_data)) + if len(infos["items"]) < 3: + if x_data is not None and y_data is not None: + if len(infos["plots"]) == 0: + infos["axes"].plot(x_data[:, 1][:length], + y_data[:, 1][:length])[0]) + infos["plots"][0].set_data( + while len(infos["axes"].lines) > 0: + infos["axes"].lines.pop() + z_data, z_min, z_max = OrthogonalData(infos["items"][2], start_tick, end_tick) + if x_data is not None and y_data is not None and z_data is not None: + length = min(length, len(z_data)) + infos["axes"].plot(x_data[:, 1][:length], + zs = z_data[:, 1][:length]) + infos["axes"].set_zlim(z_min, z_max) + infos["axes"].set_xlim(x_min, x_max) + infos["axes"].set_ylim(y_min, y_max) - if min_value is not None and max_value is not None:
- y_center = (min_value + max_value) / 2.
- y_range = max(1.0, max_value - min_value)
+ for infos in self.GraphicsAxes: + labels = ["%s: %s" % (item.GetVariable(), item.GetValue()) + for item in infos["items"]] + if infos["type"] == GRAPH_PARALLEL: + infos["axes"].legend(infos["plots"], labels, + loc="upper left", frameon=False,
- infos["axes"].set_xlim(start_tick, end_tick)
- infos["axes"].set_ylim(y_center - y_range * 0.55, y_center + y_range * 0.55)
+ infos["axes"].set_xlabel(labels[0], fontdict={'size':'small'}) + infos["axes"].set_ylabel(labels[1], fontdict={'size':'small'}) + infos["axes"].set_zlabel(labels[2], fontdict={'size':'small'}) - if len(self.GraphicsAxes) > 0:
self.GraphicsCanvas.draw()
- while len(self.Graphics3DAxes.lines) > 0:
- self.Graphics3DAxes.lines.pop()
- if self.Axis3DValues is not None:
- axis = self.Axis3DValues[0]
- start_tick = max(self.StartTick, self.Axis3DValues[1])
- end_tick = max(self.StartTick + self.CurrentRange, self.Axis3DValues[1])
- xyz_data = [axe.GetData(start_tick, end_tick)[:, 1] for axe in axis]
- length = reduce(min, [len(data) for data in xyz_data])
- self.Graphics3DAxes.plot(xyz_data[0][:length],
- zs = xyz_data[2][:length])
self.Graphics3DCanvas.draw()
def UnregisterObsoleteData(self):
@@ -683,12 +735,6 @@
- def OnVariablesGridCellChange(self, event):
- row, col = event.GetRow(), event.GetCol()
- if self.Table.GetColLabelValue(col, False) == "3DAxis":
- wx.CallAfter(self.Reset3DGraphics)
if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
@@ -760,7 +806,7 @@
wx.CallAfter(self.NewDataAvailable, None, True)
- def InsertValue(self, iec_path, idx = None, force=False, axis3D=False):
+ def InsertValue(self, iec_path, idx = None, force=False): idx = self.Table.GetNumberRows()
for item in self.Table.GetData():
@@ -774,17 +820,71 @@
self.GraphicsAxes.append({
+ "type": GRAPH_PARALLEL, - item.SetAxis3D(int(axis3D))
+ def MergeGraphs(self, source, target_infos, merge_type, force=False): + for item in self.Table.GetData(): + if item.GetVariable() == source: + if source_item is None: + item = VariableTableItem(self, source) + if item.IsNumVariable(): + result = self.AddDataConsumer(source.upper(), item) + if result is not None or force: + self.Table.InsertItem(self.Table.GetNumberRows(), item) + if source_item is not None: + for infos in self.GraphicsAxes: + if source_item in infos["items"]: + if target_infos is None and source_infos is None: + self.GraphicsAxes.append({ + "items": [source_item], + "type": GRAPH_PARALLEL, + elif target_infos is not None: + if (merge_type == GRAPH_PARALLEL and target_infos["type"] != merge_type or + merge_type == GRAPH_ORTHOGONAL and + (target_infos["type"] == GRAPH_PARALLEL and len(target_infos["items"]) > 1 or + target_infos["type"] == GRAPH_ORTHOGONAL and len(target_infos["items"]) >= 3)): + if source_infos is not None: + source_infos["items"].remove(source_item) + if len(source_infos["items"]) == 0: + self.GraphicsAxes.remove(source_infos) + target_infos["items"].append(source_item) + target_infos["type"] = merge_type def GetDebugVariables(self):
return [item.GetVariable() for item in self.Table.GetData()]
- return [item.GetVariable() for item in self.Table.GetData() if item.GetAxis3D()]
+ def OnGraphicsCanvasClick(self, event): + for infos in self.GraphicsAxes: + if infos["axes"] == event.inaxes: + if len(infos["items"]) == 1: + data = wx.TextDataObject(str((infos["items"][0].GetVariable(), "debug"))) + dragSource = wx.DropSource(self.GraphicsCanvas) + dragSource.SetData(data) + dragSource.DoDragDrop() + if self.GraphicsCanvas.HasCapture(): + self.GraphicsCanvas.ReleaseMouse() def ResetGraphicsValues(self):
self.Ticks = numpy.array([])
@@ -796,36 +896,26 @@
self.GraphicsFigure.clear()
- axes_num = len(self.GraphicsAxes)
- for idx in xrange(axes_num):
- axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx + 1)
+ for infos in self.GraphicsAxes: + if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3: + if axes_num == len(self.GraphicsAxes): + self.Graphics3DCanvas.Hide() + self.Graphics3DCanvas.Show() + self.GraphicsPanelSizer.Layout() + for infos in self.GraphicsAxes: + if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3: + axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx) - axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx + 1, sharex=self.GraphicsAxes[0]["axes"])
- self.GraphicsAxes[idx]["axes"] = axes
- self.GraphicsAxes[idx]["plots"] = []
+ infos["axes"] = self.Graphics3DAxes self.RefreshGraphicsCanvasWindowScrollbars()
self.GraphicsCanvas.draw()
- def Reset3DGraphics(self):
- self.Axis3DValues = None
- axis = [item for item in self.Table.GetData() if item.GetAxis3D()]
- xaxis, yaxis, zaxis = [item.GetData() for item in axis]
- if len(xaxis) > 0 and len(yaxis) > 0 and len(zaxis) > 0:
- max_tick = max(xaxis[0, 0], yaxis[0, 0], zaxis[0, 0])
- if max_tick is not None:
- self.Axis3DValues = (axis, max_tick)
- self.Axis3DValues = (axis, 0)
- self.Graphics3DCanvas.Show()
- self.Graphics3DCanvas.Hide()
- self.GraphicsPanelSizer.Layout()
def OnGraphics3DMotion(self, event):
@@ -836,7 +926,7 @@
def RefreshGraphicsCanvasWindowScrollbars(self):
xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
window_size = self.GraphicsCanvasWindow.GetClientSize()
- vwidth, vheight = (window_size[0], (len(self.GraphicsAxes) + 1) * 50)
+ vwidth, vheight = (window_size[0], (len(self.GraphicsAxes) + 1) * 100) self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))
posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))