# HG changeset patch # User lbessard # Date 1239284075 -7200 # Node ID 88030497e29ea8cdb92f38b5ae33d2a2bc6f9f93 # Parent 5a305b7c67355dba59ed294711c6dd041c8d29ba Bug with AUINotebook double click diff -r 5a305b7c6735 -r 88030497e29e PLCOpenEditor.py --- a/PLCOpenEditor.py Thu Apr 09 15:31:56 2009 +0200 +++ b/PLCOpenEditor.py Thu Apr 09 15:34:35 2009 +0200 @@ -447,10 +447,22 @@ self.OnPouSelectedChanged) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnPageClose) + self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG, + self.OnPageDragged) self.AUIManager.AddPane(self.TabsOpened, wx.aui.AuiPaneInfo().CentrePane()) + + self.DebugVariablePanel = DebugVariablePanel(self, self.Controler) + self.AUIManager.AddPane(self.DebugVariablePanel, wx.aui.AuiPaneInfo().Caption("Variables").Right().Layer(0).BestSize(wx.Size(250, 600)).CloseButton(False)) else: + self.SecondSplitter = wx.SplitterWindow(id=ID_PLCOPENEDITORSECONDSPLITTER, + name='SecondSplitter', parent=self.MainSplitter, point=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.SP_3D) + self.SecondSplitter.SetMinimumPaneSize(1) + + self.MainSplitter.SplitVertically(self.TreeNoteBook, self.SecondSplitter, 200) + self.TabsOpened = wx.Notebook(id=ID_PLCOPENEDITORTABSOPENED, - name='TabsOpened', parent=self.MainSplitter, pos=wx.Point(0, + name='TabsOpened', parent=self.SecondSplitter, pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0) if wx.VERSION >= (2, 6, 0): self.TabsOpened.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, @@ -458,8 +470,10 @@ else: wx.EVT_NOTEBOOK_PAGE_CHANGED(self.TabsOpened, ID_PLCOPENEDITORTABSOPENED, self.OnPouSelectedChanged) - - self.MainSplitter.SplitVertically(self.TreeNoteBook, self.TabsOpened, 200) + + self.DebugVariablePanel = DebugVariablePanel(self.SecondSplitter, self.Controler) + + self.SecondSplitter.SplitVertically(self.TabsOpened, self.DebugVariablePanel, -250) else: self.TreeNoteBook.AddPage(self.TypesTree, "Types") self.TreeNoteBook.AddPage(self.InstancesTree, "Instances") @@ -496,6 +510,8 @@ self.OnPouSelectedChanged) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnPageClose) + self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG, + self.OnPageDragged) self.AUIManager.AddPane(self.TabsOpened, wx.aui.AuiPaneInfo().CentrePane().MinSize(wx.Size(0, 0))) self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL, @@ -621,6 +637,8 @@ self.Errors = [] self.DrawingMode = FREEDRAWING_MODE #self.DrawingMode = DRIVENDRAWING_MODE + if USE_AUI: + self.AuiTabCtrl = [] self.PrintData = wx.PrintData() self.PrintData.SetPaperId(wx.PAPER_A4) @@ -660,6 +678,11 @@ self.RefreshEditMenu() self.RefreshDisplayMenu() self.RefreshToolBar() + wx.CallAfter(self.RefreshTabCtrlEvent) + event.Skip() + + def OnPageDragged(self, event): + wx.CallAfter(self.RefreshTabCtrlEvent) event.Skip() def GetCopyBuffer(self): @@ -755,11 +778,22 @@ def AddPage(self, window, text): self.TabsOpened.AddPage(window, text) + self.RefreshTabCtrlEvent() + + def RefreshTabCtrlEvent(self): if USE_AUI: - children = self.TabsOpened.GetChildren() - last_element = children[len(children) - 1] - if isinstance(last_element, wx.aui.AuiTabCtrl): - last_element.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(last_element)) + auitabctrl = [] + for child in self.TabsOpened.GetChildren(): + if isinstance(child, wx.aui.AuiTabCtrl): + auitabctrl.append(child) + if child not in self.AuiTabCtrl: + child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child)) + self.AuiTabCtrl = auitabctrl + if self.TabsOpened.GetPageCount() == 0: + pane = self.AUIManager.GetPane(self.TabsOpened) + if pane.IsMaximized(): + self.AUIManager.RestorePane(pane) + self.AUIManager.Update() def DeleteAllPages(self): if USE_AUI: @@ -1121,10 +1155,13 @@ event.Skip() def OnSelectAllMenu(self, event): - selected = self.TabsOpened.GetSelection() - if selected != -1: - window = self.TabsOpened.GetPage(selected) - window.SelectAll() + control = self.FindFocus() + if isinstance(control, (Viewer, TextViewer)): + control.SelectAll() + elif isinstance(control, wx.TextCtrl): + control.SetSelection(0, control.GetLastPosition()) + elif isinstance(control, wx.ComboBox): + control.SetMark(0, control.GetLastPosition() + 1) event.Skip() def OnDeleteMenu(self, event): @@ -1938,11 +1975,14 @@ var_path = "%s.%s"%(parent_name, var_path) parent_item = self.InstancesTree.GetItemParent(parent_item) - new_window = GraphicViewer(self.TabsOpened, self, self.Controler, var_path) - self.AddPage(new_window, "") - new_window.SetFocus() - self.RefreshPageTitles() - event.Skip() + self.OpenGraphicViewer(var_path) + event.Skip() + + def OpenGraphicViewer(self, var_path): + new_window = GraphicViewer(self.TabsOpened, self, self.Controler, var_path) + self.AddPage(new_window, "") + new_window.SetFocus() + self.RefreshPageTitles() def OnInstancesTreeRightUp(self, event): if wx.Platform == '__WXMSW__': @@ -1973,10 +2013,7 @@ def AddVariableGraphicFunction(self, iec_path): def AddVariableGraphic(event): - new_window = GraphicViewer(self.TabsOpened, self, self.Controler, iec_path) - self.AddPage(new_window, "") - new_window.SetFocus() - self.RefreshPageTitles() + self.OpenGraphicViewer(iec_path) event.Skip() return AddVariableGraphic @@ -4078,23 +4115,25 @@ self.VariablesGrid = wx.grid.Grid(id=ID_VARIABLEEDITORPANELVARIABLESGRID, name='VariablesGrid', parent=self, pos=wx.Point(0, 0), - size=wx.Size(0, 150), style=wx.VSCROLL) + size=wx.Size(0, 0), style=wx.VSCROLL) self.VariablesGrid.SetFont(wx.Font(12, 77, wx.NORMAL, wx.NORMAL, False, 'Sans')) self.VariablesGrid.SetLabelFont(wx.Font(10, 77, wx.NORMAL, wx.NORMAL, False, 'Sans')) self.VariablesGrid.SetSelectionBackground(wx.WHITE) - self.VariablesGrid.SetSelectionForeground(wx.BLACK) + self.VariablesGrid.SetSelectionBackground(wx.BLACK) if wx.VERSION >= (2, 6, 0): self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange) self.VariablesGrid.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnVariablesGridSelectCell) self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick) self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown) + #self.VariablesGrid.Bind(wx.EVT_KEY_DOWN, self.OnChar) else: wx.grid.EVT_GRID_CELL_CHANGE(self.VariablesGrid, self.OnVariablesGridCellChange) wx.grid.EVT_GRID_SELECT_CELL(self.VariablesGrid, self.OnVariablesGridSelectCell) wx.grid.EVT_GRID_CELL_LEFT_CLICK(self.VariablesGrid, self.OnVariablesGridCellLeftClick) wx.grid.EVT_GRID_EDITOR_SHOWN(self.VariablesGrid, self.OnVariablesGridEditorShown) + #wx.EVT_KEY_DOWN(self.VariablesGrid, self.OnChar) self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) self.ControlPanel = wx.ScrolledWindow(id=ID_VARIABLEEDITORCONTROLPANEL, @@ -4420,6 +4459,16 @@ wx.CallAfter(self.RefreshButtons) event.Skip() + def OnChar(self, event): + keycode = event.GetKeyCode() + if keycode == wx.WXK_DELETE: + row = self.Table.GetRow(self.VariablesGrid.GetGridCursorRow()) + self.Values.remove(row) + self.SaveValues() + self.RefreshValues() + self.RefreshButtons() + event.Skip() + def MoveValue(self, value_index, move): new_index = max(0, min(value_index + move, len(self.Values) - 1)) if new_index != value_index: @@ -4473,6 +4522,320 @@ self.Table.ClearErrors() self.Table.ResetView(self.VariablesGrid) +#------------------------------------------------------------------------------- +# Variables Editor Panel +#------------------------------------------------------------------------------- + +class VariableTableItem: + + def __init__(self, parent, variable, value): + self.Parent = parent + self.Variable = variable + self.Value = value + + def __del__(self): + self.Parent = None + + def SetVariable(self, variable): + if self.Parent and self.Variable != variable: + self.Variable = variable + self.Parent.RefreshGrid() + + def GetVariable(self): + return self.Variable + + def SetValue(self, value): + if self.Parent and self.Value != value: + self.Value = value + self.Parent.RefreshGrid() + + def GetValue(self): + return self.Value + +class DebugVariableTable(wx.grid.PyGridTableBase): + + """ + A custom wx.grid.Grid Table using user supplied data + """ + def __init__(self, parent, data, colnames): + # The base class must be initialized *first* + wx.grid.PyGridTableBase.__init__(self) + self.data = data + self.colnames = colnames + self.Parent = parent + # XXX + # we need to store the row length and collength to + # see if the table has changed size + self._rows = self.GetNumberRows() + self._cols = self.GetNumberCols() + + def GetNumberCols(self): + return len(self.colnames) + + def GetNumberRows(self): + return len(self.data) + + def GetColLabelValue(self, col): + if col < len(self.colnames): + return self.colnames[col] + + def GetRowLabelValues(self, row): + return row + + def GetValue(self, row, col): + if row < self.GetNumberRows(): + return self.GetValueByName(row, self.GetColLabelValue(col)) + return "" + + def SetValue(self, row, col, value): + if col < len(self.colnames): + self.SetValueByName(row, self.GetColLabelValue(col), value) + + def GetValueByName(self, row, colname): + if row < self.GetNumberRows(): + if colname == "Variable": + return self.data[row].GetVariable() + elif colname == "Value": + return self.data[row].GetValue() + return "" + + def SetValueByName(self, row, colname, value): + if row < self.GetNumberRows(): + if colname == "Variable": + self.data[row].SetVariable(value) + elif colname == "Value": + self.data[row].SetValue(value) + + def ResetView(self, grid): + """ + (wx.grid.Grid) -> Reset the grid view. Call this to + update the grid if rows and columns have been added or deleted + """ + grid.BeginBatch() + for current, new, delmsg, addmsg in [ + (self._rows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED), + (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED), + ]: + if new < current: + msg = wx.grid.GridTableMessage(self,delmsg,new,current-new) + grid.ProcessTableMessage(msg) + elif new > current: + msg = wx.grid.GridTableMessage(self,addmsg,new-current) + grid.ProcessTableMessage(msg) + self.UpdateValues(grid) + grid.EndBatch() + + self._rows = self.GetNumberRows() + self._cols = self.GetNumberCols() + # update the column rendering scheme + self._updateColAttrs(grid) + + # update the scrollbars and the displayed part of the grid + grid.AdjustScrollbars() + grid.ForceRefresh() + + def UpdateValues(self, grid): + """Update all displayed values""" + # This sends an event to the grid table to update all of the values + msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES) + grid.ProcessTableMessage(msg) + + def _updateColAttrs(self, grid): + """ + wx.grid.Grid -> update the column attributes to add the + appropriate renderer given the column name. + + Otherwise default to the default renderer. + """ + + for row in range(self.GetNumberRows()): + for col in range(self.GetNumberCols()): + grid.SetReadOnly(row, col, True) + + def SetData(self, data): + self.data = data + + def GetData(self): + return self.data + + def AppendItem(self, data): + self.data.append(data) + + def InsertItem(self, idx, data): + self.data.insert(idx, data) + + def RemoveItem(self, idx): + self.data.pop(idx) + + def MoveItem(self, idx, new_idx): + self.data.insert(new_idx, self.data.pop(idx)) + + def GetItem(self, idx): + return self.data[idx] + + def Empty(self): + self.data = [] + +class DebugVariableDropTarget(wx.TextDropTarget): + + def __init__(self, parent): + wx.TextDropTarget.__init__(self) + self.ParentWindow = parent + + 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() + message = None + try: + values = eval(data) + except: + message = "Invalid value \"%s\" for debug variable"%data + values = None + if not isinstance(values, TupleType): + message = "Invalid value \"%s\" for debug variable"%data + values = None + if values is not None and values[1] == "debug": + self.ParentWindow.InsertValue(row, values[0]) + if message is not None: + wx.CallAfter(self.ShowMessage, message) + + def ShowMessage(self, message): + message = wx.MessageDialog(self.ParentWindow, message, "Error", wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + +[ID_DEBUGVARIABLEPANEL, ID_DEBUGVARIABLEPANELVARIABLESGRID, + ID_DEBUGVARIABLEPANELUPBUTTON, ID_DEBUGVARIABLEPANELDOWNBUTTON, + ID_DEBUGVARIABLEPANELDELETEBUTTON, +] = [wx.NewId() for _init_ctrls in range(5)] + +class DebugVariablePanel(wx.Panel): + + if wx.VERSION < (2, 6, 0): + def Bind(self, event, function, id = None): + if id is not None: + event(self, id, function) + else: + event(self, function) + + def _init_coll_MainSizer_Items(self, parent): + parent.AddSizer(self.ButtonPanelSizer, 0, border=5, flag=wx.ALIGN_RIGHT|wx.ALL) + parent.AddWindow(self.VariablesGrid, 0, border=0, flag=wx.GROW) + + def _init_coll_MainSizer_Growables(self, parent): + parent.AddGrowableCol(0) + parent.AddGrowableRow(1) + + def _init_coll_ButtonPanelSizer_Items(self, parent): + parent.AddWindow(self.UpButton, 0, border=5, flag=wx.RIGHT) + parent.AddWindow(self.DownButton, 0, border=5, flag=wx.RIGHT) + parent.AddWindow(self.DeleteButton, 0, border=0, flag=0) + + def _init_sizers(self): + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0) + self.ButtonPanelSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._init_coll_MainSizer_Items(self.MainSizer) + self._init_coll_MainSizer_Growables(self.MainSizer) + self._init_coll_ButtonPanelSizer_Items(self.ButtonPanelSizer) + + self.SetSizer(self.MainSizer) + + def _init_ctrls(self, prnt): + wx.Panel.__init__(self, id=ID_DEBUGVARIABLEPANEL, + name='DebugVariablePanel', parent=prnt, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + + self.VariablesGrid = wx.grid.Grid(id=ID_DEBUGVARIABLEPANELVARIABLESGRID, + name='VariablesGrid', parent=self, pos=wx.Point(0, 0), + size=wx.Size(0, 150), style=wx.VSCROLL) + self.VariablesGrid.SetFont(wx.Font(12, 77, wx.NORMAL, wx.NORMAL, False, + 'Sans')) + self.VariablesGrid.SetLabelFont(wx.Font(10, 77, wx.NORMAL, wx.NORMAL, + False, 'Sans')) + self.VariablesGrid.SetSelectionBackground(wx.WHITE) + self.VariablesGrid.SetSelectionForeground(wx.BLACK) + self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self)) + + self.UpButton = wx.Button(id=ID_DEBUGVARIABLEPANELUPBUTTON, label='^', + name='UpButton', parent=self, pos=wx.Point(0, 0), + size=wx.Size(32, 32), style=0) + self.Bind(wx.EVT_BUTTON, self.OnUpButton, id=ID_DEBUGVARIABLEPANELUPBUTTON) + + self.DownButton = wx.Button(id=ID_DEBUGVARIABLEPANELDOWNBUTTON, label='v', + name='DownButton', parent=self, pos=wx.Point(0, 0), + size=wx.Size(32, 32), style=0) + self.Bind(wx.EVT_BUTTON, self.OnDownButton, id=ID_DEBUGVARIABLEPANELDOWNBUTTON) + + self.DeleteButton = wx.Button(id=ID_DEBUGVARIABLEPANELDELETEBUTTON, label='Delete', + name='DeleteButton', parent=self, pos=wx.Point(0, 0), + size=wx.Size(72, 32), style=0) + self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, id=ID_DEBUGVARIABLEPANELDELETEBUTTON) + + self._init_sizers() + + def __init__(self, parent, controler): + self._init_ctrls(parent) + self.Controler = controler + + self.Table = DebugVariableTable(self, [], ["Variable", "Value"]) + self.VariablesGrid.SetTable(self.Table) + self.VariablesGrid.SetRowLabelSize(0) + + for col in range(self.Table.GetNumberCols()): + attr = wx.grid.GridCellAttr() + attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER) + self.VariablesGrid.SetColAttr(col, attr) + self.VariablesGrid.SetColSize(col, 100) + + self.Table.ResetView(self.VariablesGrid) + + def __del__(self): + for item in self.Table.GetData(): + self.Controler.UnsubscribeDebugIECVariable(item.GetVariable().upper(), item) + + def RefreshGrid(self): + self.Table.ResetView(self.VariablesGrid) + + def OnDeleteButton(self, event): + idx = self.VariablesGrid.GetGridCursorRow() + item = self.Table.GetItem(idx) + self.Controler.UnsubscribeDebugIECVariable(item.GetVariable().upper(), item) + self.Table.RemoveItem(idx) + self.RefreshGrid() + event.Skip() + + def OnUpButton(self, event): + self.MoveValue(self.VariablesGrid.GetGridCursorRow(), -1) + event.Skip() + + def OnDownButton(self, event): + self.MoveValue(self.VariablesGrid.GetGridCursorRow(), 1) + event.Skip() + + def InsertValue(self, idx, iec_path): + for item in self.Table.GetData(): + if iec_path == item.GetVariable(): + return + item = VariableTableItem(self, iec_path, "") + result = self.Controler.SubscribeDebugIECVariable(iec_path.upper(), item) + if result is not None: + self.Table.InsertItem(idx, item) + self.RefreshGrid() + + def MoveValue(self, idx, move): + new_idx = max(0, min(idx + move, self.Table.GetNumberRows() - 1)) + if new_idx != idx: + self.Table.MoveItem(idx, new_idx) + self.RefreshGrid() + self.VariablesGrid.SetGridCursor(new_idx, self.VariablesGrid.GetGridCursorCol()) + +#------------------------------------------------------------------------------- +# Viewer Printout +#------------------------------------------------------------------------------- + UPPER_DIV = lambda x, y: (x / y) + {True : 0, False : 1}[(x % y) == 0] class GraphicPrintout(wx.Printout):