Created
September 4, 2009 20:31
-
-
Save bct/181104 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a stripped-down wxPython program with a custom wxPyGridCellEditor that | |
# crashes on my machine (wxPython 2.8.10.1 with Python 2.5 on Windows XP). | |
# | |
# I can make the program crash in two ways, I'm not sure they're related. | |
# Short way: | |
# 1. Edit the first row's "Class" field. You should get a drop-down with "Local" | |
# and "Temp". | |
# 2. Select a value from the drop-down. The program crashes. | |
# | |
# Long way: | |
# 1. Edit the first row's "Location" field. You should see a text area with a | |
# button in the cell. | |
# 2. Close that cell editor (click somewhere else in the screen). | |
# 3. Edit the first row's "Type" field. You should get a menu. | |
# 4. Select a value from the menu. | |
# 5. The program crashes. | |
# | |
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor | |
#based on the plcopen standard. | |
# | |
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD | |
# | |
#See COPYING file for copyrights details. | |
# | |
#This library is free software; you can redistribute it and/or | |
#modify it under the terms of the GNU General Public | |
#License as published by the Free Software Foundation; either | |
#version 2.1 of the License, or (at your option) any later version. | |
# | |
#This library is distributed in the hope that it will be useful, | |
#but WITHOUT ANY WARRANTY; without even the implied warranty of | |
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
#General Public License for more details. | |
# | |
#You should have received a copy of the GNU General Public | |
#License along with this library; if not, write to the Free Software | |
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
import wx, wx.grid | |
from types import TupleType | |
# these are mock values | |
# Compatibility function for wx versions < 2.6 | |
def AppendMenu(parent, help, id, kind, text): | |
if wx.VERSION >= (2, 6, 0): | |
parent.Append(help=help, id=id, kind=kind, text=text) | |
else: | |
parent.Append(helpString=help, id=id, kind=kind, item=text) | |
LOCATIONDATATYPES = {"X" : ["BOOL"], | |
"B" : ["SINT", "USINT", "BYTE", "STRING"], | |
"W" : ["INT", "UINT", "WORD", "WSTRING"], | |
"D" : ["DINT", "UDINT", "REAL", "DWORD"], | |
"L" : ["LINT", "ULINT", "LREAL", "LWORD"]} | |
IEC_KEYWORDS = [] | |
def TestIdentifier(identifier): | |
return True | |
def _(x): | |
return x | |
#------------------------------------------------------------------------------- | |
# Variables Editor Panel | |
#------------------------------------------------------------------------------- | |
def GetFilterChoiceTransfer(): | |
_ = lambda x : x | |
return {_("All"): _("All"), _("Interface"): _("Interface"), | |
_(" Input"): _("Input"), _(" Output"): _("Output"), _(" InOut"): _("InOut"), | |
_(" External"): _("External"), _("Variables"): _("Variables"), _(" Local"): _("Local"), | |
_(" Temp"): _("Temp"), _("Global"): _("Global")}#, _("Access") : _("Access")} | |
VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()]) | |
class VariableTable(wx.grid.PyGridTableBase): | |
""" | |
A custom wx.grid.Grid Table using user supplied data | |
""" | |
def __init__(self, parent, data): | |
# The base class must be initialized *first* | |
wx.grid.PyGridTableBase.__init__(self) | |
self.data = data | |
self.old_value = None | |
self.colnames = ["#", _("Name"), _("Class"), _("Type"), _("Location"), _("Initial Value")] | |
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, translate=True): | |
if col < len(self.colnames): | |
if translate: | |
return _(self.colnames[col]) | |
return self.colnames[col] | |
def GetRowLabelValues(self, row, translate=True): | |
return row | |
def GetValue(self, row, col): | |
if row < self.GetNumberRows(): | |
if col == 0: | |
return self.data[row]["Number"] | |
colname = self.GetColLabelValue(col, False) | |
value = str(self.data[row].get(colname, "")) | |
if colname == "Class": | |
return _(value) | |
return value | |
def SetValue(self, row, col, value): | |
if col < len(self.colnames): | |
colname = self.GetColLabelValue(col, False) | |
if colname == "Name": | |
self.old_value = self.data[row][colname] | |
elif colname == "Class": | |
value = VARIABLE_CLASSES_DICT[value] | |
self.data[row][colname] = value | |
def GetValueByName(self, row, colname): | |
if row < self.GetNumberRows(): | |
return self.data[row].get(colname) | |
def SetValueByName(self, row, colname, value): | |
if row < self.GetNumberRows(): | |
self.data[row][colname] = 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()): | |
editor = None | |
renderer = None | |
colname = self.GetColLabelValue(col, False) | |
if col != 0 and self.GetValueByName(row, "Edit"): | |
grid.SetReadOnly(row, col, False) | |
if colname == "Name": | |
editor = wx.grid.GridCellTextEditor() | |
renderer = wx.grid.GridCellStringRenderer() | |
elif colname == "Initial Value": | |
editor = wx.grid.GridCellTextEditor() | |
renderer = wx.grid.GridCellStringRenderer() | |
elif colname == "Location": | |
#editor = wx.grid.GridCellTextEditor() | |
editor = LocationCellEditor(self) | |
renderer = wx.grid.GridCellStringRenderer() | |
elif colname == "Class": | |
editor = wx.grid.GridCellChoiceEditor() | |
editor.SetParameters(",".join(["Local", "Temp"])) | |
elif colname == "Type": | |
editor = wx.grid.GridCellTextEditor() | |
else: | |
grid.SetReadOnly(row, col, True) | |
grid.SetCellEditor(row, col, editor) | |
grid.SetCellRenderer(row, col, renderer) | |
def SetData(self, data): | |
self.data = data | |
def GetRow(self, row_index): | |
return self.data[row_index] | |
[ID_VARIABLEEDITORPANEL, ID_VARIABLEEDITORPANELVARIABLESGRID, | |
ID_VARIABLEEDITORCONTROLPANEL, ID_VARIABLEEDITORPANELRETURNTYPE, | |
ID_VARIABLEEDITORPANELCLASSFILTER, ID_VARIABLEEDITORPANELADDBUTTON, | |
ID_VARIABLEEDITORPANELDELETEBUTTON, ID_VARIABLEEDITORPANELUPBUTTON, | |
ID_VARIABLEEDITORPANELDOWNBUTTON, ID_VARIABLEEDITORPANELSTATICTEXT1, | |
ID_VARIABLEEDITORPANELSTATICTEXT2, ID_VARIABLEEDITORPANELSTATICTEXT3, | |
] = [wx.NewId() for _init_ctrls in range(12)] | |
class VariablePanel(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.AddWindow(self.VariablesGrid, 0, border=0, flag=wx.GROW) | |
def _init_coll_MainSizer_Growables(self, parent): | |
parent.AddGrowableCol(0) | |
parent.AddGrowableRow(0) | |
def _init_sizers(self): | |
self.MainSizer = wx.FlexGridSizer(cols=2, hgap=10, rows=1, vgap=0) | |
self._init_coll_MainSizer_Items(self.MainSizer) | |
self._init_coll_MainSizer_Growables(self.MainSizer) | |
self.SetSizer(self.MainSizer) | |
def _init_ctrls(self, prnt): | |
wx.Panel.__init__(self, id=ID_VARIABLEEDITORPANEL, | |
name='VariableEditorPanel', parent=prnt, pos=wx.Point(0, 0), | |
size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) | |
self.VariablesGrid = wx.grid.Grid(id=ID_VARIABLEEDITORPANELVARIABLESGRID, | |
name='VariablesGrid', parent=self, pos=wx.Point(0, 0), | |
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) | |
if wx.VERSION >= (2, 6, 0): | |
self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange) | |
self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown) | |
else: | |
wx.grid.EVT_GRID_CELL_CHANGE(self.VariablesGrid, self.OnVariablesGridCellChange) | |
wx.grid.EVT_GRID_EDITOR_SHOWN(self.VariablesGrid, self.OnVariablesGridEditorShown) | |
self._init_sizers() | |
def __init__(self, parent): | |
self._init_ctrls(parent) | |
self.ParentWindow = parent | |
self.Table = VariableTable(self, []) | |
self.VariablesGrid.SetTable(self.Table) | |
self.VariablesGrid.SetRowLabelSize(0) | |
for col in range(self.Table.GetNumberCols()): | |
self.VariablesGrid.AutoSizeColumn(col, False) | |
# add a new row for testing | |
new_row = {"Name" : "", "Class" : "Local", "Type" : "INT", "Location" : "", "Initial Value" : "", "Edit" : True} | |
row_index = -1 | |
self.Values = [new_row] | |
self.RefreshValues(row_index) | |
def OnVariablesGridCellChange(self, event): | |
row, col = event.GetRow(), event.GetCol() | |
colname = self.Table.GetColLabelValue(col) | |
value = |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment