lpcmanager

Fix options handling : real space resistant parsing. Values surrounded by double quotes in key-value pairs. Dialog stops messing with other columnns. OnChange code overriden in PythonFileCTNMixin so that content of options can add Alaram and StoredValue calls. + various rework in SetOption GetOption to avoid being poisoned by user input
from __future__ import absolute_import
import re
import wx
import wx.grid
import controls
from collections import namedtuple
from OptionsParsing import ParseOptions, VARIABLETYPE
[ID_OPTIONSWIZARDDIALOG,ID_ONCHANGE,ID_OPTIONSTYPECHOICE,ID_SUBGROUPTEXT,ID_UNITTEXT,ID_VALUECHECKBOX,ID_MINSPIN,ID_MAXSPIN,ID_PRECISIONSPIN, ID_INITIALSPIN,ID_SCADACHECKBOX,ID_OTHERTEXT,ID_DESCRIPTION,ID_STATIC] = [wx.NewId() for _init_ctrls in range(14)]
excluded_chars = [ord(i) for i in '\n"']
sanitizer = "".join([chr(i if i not in excluded_chars else ord(' ')) for i in xrange(256)])
eraser = '\r'
class WampOptionsEditor(wx.Dialog):
def _init_sizers(self):
self.MainSizer = wx.BoxSizer(wx.VERTICAL)
self.PropertySizer = wx.FlexGridSizer(cols=2, hgap=10, rows=13, vgap=10)
self.PropertySizer.AddWindow(wx.StaticText(self,wx.ID_ANY,_("Type:")), flag=wx.ALIGN_CENTER_VERTICAL | wx.GROW | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.OptionsTypeChoice, 1, flag= wx.EXPAND)
self.PropertySizer.AddWindow(self.OnChangeCheckbox, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(wx.StaticText(self, wx.ID_ANY, _("On Change")), 1, flag=wx.GROW | wx.EXPAND)
self.PropertySizer.AddWindow(self.ScadaCheckbox, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(wx.StaticText(self, wx.ID_ANY, _("Scada")), 1, flag=wx.GROW | wx.EXPAND)
self.PropertySizer.AddWindow(self.StaticCheckbox, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(wx.StaticText(self, wx.ID_ANY, _("Static")), 1, flag=wx.GROW | wx.EXPAND)
self.PropertySizer.AddWindow(wx.StaticText(self,wx.ID_ANY, _("Subgroup:")), flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.SubgroupText, 1, flag=wx.GROW | wx.EXPAND)
self.PropertySizer.AddWindow(wx.StaticText(self,wx.ID_ANY, _("Unit:")), flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.UnitText, 1, flag=wx.GROW | wx.EXPAND)
self.PropertySizer.AddWindow(self.ValueCheckbox, flag=wx.GROW | wx.LEFT, border=15)
self.PropertySizer.AddWindow(wx.StaticText(self,wx.ID_ANY, _("Enable min/max value")), flag=wx.ALIGN_CENTER_VERTICAL)
self.minValue=wx.StaticText(self, wx.ID_ANY, _("Min value:"))
self.maxValue=wx.StaticText(self, wx.ID_ANY, _("Max value:"))
self.PrecisionValue = wx.StaticText(self, wx.ID_ANY, _("Precision:"))
self.PropertySizer.AddWindow(self.minValue, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.MinSpin, flag=wx.GROW)
self.PropertySizer.AddWindow(self.maxValue, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.MaxSpin, flag=wx.GROW)
self.PropertySizer.AddWindow(self.PrecisionValue, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.PrecisionSpin, flag=wx.GROW)
self.PropertySizer.AddWindow(wx.StaticText(self, wx.ID_ANY, _("Other:")), flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.OtherText, flag=wx.GROW)
self.PropertySizer.AddWindow(wx.StaticText(self, wx.ID_ANY, _("Tags:")), flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=15)
self.PropertySizer.AddWindow(self.Tags, flag=wx.GROW)
self.PropertySizer.AddGrowableCol(1, 1)
self.MainSizer.AddSizer(self.PropertySizer, flag=wx.GROW | wx.UP | wx.RIGHT, border=15)
self.ButtonSizer.AddWindow(self.ClearButton)
self.MainSizer.AddSizer(self.ButtonSizer,
flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 10)
self.SetSizer(self.MainSizer)
self.Fit()
self.CenterOnScreen()
def _init_ctrls(self, prnt, opt):
wx.Dialog.__init__(self, id=ID_OPTIONSWIZARDDIALOG,
name='OptionsWizard', parent=prnt,
size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE,
title=_('Options'))
self.OptionsTypeChoice = wx.Choice(parent=self, id=ID_OPTIONSWIZARDDIALOG,
choices=VARIABLETYPE)
self.Bind(wx.EVT_CHOICE, self.OnTypeChoice, self.OptionsTypeChoice)
self.OnChangeCheckbox = wx.CheckBox(parent=self, id=ID_ONCHANGE)
self.SubgroupText = wx.TextCtrl(parent=self, id=ID_SUBGROUPTEXT)
self.UnitText = wx.TextCtrl(parent=self, id=ID_UNITTEXT)
self.ValueCheckbox = wx.CheckBox(parent=self, id=ID_VALUECHECKBOX)
self.MinSpin = wx.SpinCtrl(parent=self, id=ID_MINSPIN, style=wx.SP_VERTICAL)
self.MaxSpin = wx.SpinCtrl(parent=self, id=ID_MAXSPIN, style=wx.SP_VERTICAL)
self.PrecisionSpin = wx.SpinCtrl(parent=self, id=ID_PRECISIONSPIN, style=wx.SP_VERTICAL)
self.MinSpin.SetRange(-1000000, 1000000)
self.MaxSpin.SetRange(-1000000, 1000000)
self.ScadaCheckbox = wx.CheckBox(parent=self, id=ID_SCADACHECKBOX)
self.StaticCheckbox = wx.CheckBox(parent=self, id=ID_STATIC)
self.OtherText = wx.TextCtrl(parent=self, id=ID_OTHERTEXT)
self.Tags = wx.TextCtrl(parent=self, id=ID_DESCRIPTION, size=(-1, 100), style=wx.TE_MULTILINE|wx.SUNKEN_BORDER)
self.ClearButton = wx.Button(self, wx.ID_CLEAR, _("Clear"))
self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL)
self.ValueCheckbox.Bind(wx.EVT_CHECKBOX, self.EnableValue)
self.OnChangeCheckbox.Enable(False)
self.OtherText.Enable(False)
self.UnitText.Enable(False)
self.ScadaCheckbox.Enable(False)
self.SubgroupText.Enable(False)
self.ValueCheckbox.Enable(False)
self.MinSpin.Enable(False)
self.MaxSpin.Enable(False)
self.PrecisionSpin.Enable(False)
self.StaticCheckbox.Enable(False)
self.Bind(wx.EVT_CHOICE, self.Enabler, self.OptionsTypeChoice)
self.Bind(wx.EVT_BUTTON, self.clearOptions, self.ClearButton)
self.Bind(wx.EVT_CHECKBOX, self.Enabler, self.ScadaCheckbox)
def __init__(self, parent, opt):
self.options = opt
self.value = ""
self.OnChange = False
self._init_ctrls(parent, opt)
self._init_sizers()
self.SetOptions(opt)
self.Enabler(None)
def Enabler(self, event):
if self.OptionsTypeChoice.GetSelection() == 0:
self.clearOptions(event)
elif self.OptionsTypeChoice.GetSelection() in [1,3]:
self.ScadaCheckbox.Enable(True)
self.OtherText.Enable(True)
self.SubgroupText.Enable(True)
self.UnitText.Enable(True)
self.OnChangeCheckbox.Enable(True)
self.ValueCheckbox.Enable(True)
self.PrecisionSpin.Enable(True)
else:
self.ScadaCheckbox.Enable(True)
self.OtherText.Enable(True)
self.SubgroupText.Enable(True)
self.UnitText.Enable(True)
self.OnChangeCheckbox.SetValue(False)
self.OnChangeCheckbox.Enable(False)
self.ValueCheckbox.Enable(True)
self.PrecisionSpin.Enable(True)
if self.OptionsTypeChoice.GetSelection() == 3 and self.ScadaCheckbox.GetValue():
self.StaticCheckbox.Enable(True)
self.OnChangeCheckbox.SetValue(True)
else:
self.StaticCheckbox.SetValue(False)
self.StaticCheckbox.Enable(False)
def clearOptions(self, event):
"""Zbrisi vsebino"""
self.OptionsTypeChoice.SetSelection(0)
self.SubgroupText.SetValue("")
self.UnitText.SetValue("")
self.MinSpin.SetValue(0)
self.MaxSpin.SetValue(0)
self.ValueCheckbox.SetValue(False)
self.ScadaCheckbox.SetValue(False)
self.StaticCheckbox.SetValue(False)
self.OtherText.SetValue("")
self.MinSpin.Enable(False)
self.MaxSpin.Enable(False)
self.StaticCheckbox.Enable(False)
self.PrecisionSpin.SetValue(0)
self.OnChangeCheckbox.SetValue(False)
self.Tags.SetValue("")
self.OnChangeCheckbox.Enable(False)
self.OtherText.Enable(False)
self.SubgroupText.Enable(False)
self.UnitText.Enable(False)
self.ScadaCheckbox.Enable(False)
self.ValueCheckbox.Enable(False)
self.MinSpin.Enable(False)
self.MaxSpin.Enable(False)
self.PrecisionSpin.Enable(False)
self.Tags.Enable(False)
self.StaticCheckbox.Enable(False)
def OnTypeChoice(self, event):
self.selected = event.GetSelection()
def EnableValue(self, event):
self.MinSpin.Enable(self.ValueCheckbox.GetValue())
self.MaxSpin.Enable(self.ValueCheckbox.GetValue())
event.Skip()
def SetOptions(self, opts):
res = ParseOptions(opts)
self.OptionsTypeChoice.SetSelection(res.variable_type_selection)
self.OnChangeCheckbox.SetValue(res.is_onchange)
self.ScadaCheckbox.SetValue(res.is_scada)
self.StaticCheckbox.SetValue(res.is_static)
self.StaticCheckbox.Enable(
res.is_static or (res.variable_type_selection == 3 and res.is_scada))
if res.subgroup is not None:
self.SubgroupText.SetValue(res.subgroup)
if res.unit is not None:
self.UnitText.SetValue(res.unit)
if res.min is not None or res.max is not None:
self.ValueCheckbox.SetValue(True)
self.MinSpin.Enable(True)
self.MaxSpin.Enable(True)
self.MinSpin.SetValue(res.min if res.min else 0)
self.MaxSpin.SetValue(res.max if res.max else 0)
if res.other is not None:
self.OtherText.SetValue(res.other)
if res.precision is not None:
self.PrecisionSpin.SetValue(res.precision)
if res.tags is not None:
self.Tags.SetValue(res.tags)
def GetOptions(self):
TypeSelected = self.OptionsTypeChoice.GetSelection()
if TypeSelected<0:
TypeSelected = 0
options = VARIABLETYPE[TypeSelected]
if options == "None":
options = ""
if self.OnChangeCheckbox.GetValue():
options += " onchange"
if self.ScadaCheckbox.GetValue():
options += " scada"
if self.StaticCheckbox.GetValue():
options += " static"
if self.MinSpin.IsEnabled():
options += ' min="' + str(self.MinSpin.GetValue()) + '"'
options += ' max="' + str(self.MaxSpin.GetValue()) + '"'
if self.PrecisionSpin.GetValue() != 0:
options += ' precision=' + str(self.PrecisionSpin.GetValue()) + '"'
for name, ctrl in [('subgroup', self.SubgroupText),
('unit', self.UnitText),
('other', self.OtherText),
('tags', self.Tags)]:
content = ctrl.GetValue().encode('ascii','ignore')
if content:
options += ' ' + name + '="' + content.translate(sanitizer, eraser) + '"'
return options
class WampOptionsCellControl(wx.PyControl):
'''
Custom cell editor control with a text box and a button that launches
the WampOptionsEditor.
'''
def __init__(self, parent, table, row):
wx.Control.__init__(self, parent, row)
main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
main_sizer.AddGrowableCol(0)
main_sizer.AddGrowableRow(0)
self.parent = parent
# create location text control
self.Options = wx.TextCtrl(self, size=wx.Size(0, -1),
style=wx.TE_PROCESS_ENTER)
self.Options.Bind(wx.EVT_KEY_DOWN, self.OnChar)
main_sizer.AddWindow(self.Options, flag=wx.GROW)
# create browse button
self.EditButton = wx.Button(self, label='...', size=wx.Size(30, -1))
self.Bind(wx.EVT_BUTTON, self.OnEditButtonClick, self.EditButton)
main_sizer.AddWindow(self.EditButton, flag=wx.GROW)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.SetSizer(main_sizer)
self.Default = None
self.table = table
self.row = row
def SetValue(self, value):
self.Default = value
self.Options.SetValue(value)
def GetValue(self):
return self.Options.GetValue()
def OnSize(self, event):
self.Layout()
def OnEditButtonClick(self, event):
# pop up the Duration Editor dialog
options = self.GetValue()
dialog = WampOptionsEditor(self.parent, options)
answer = dialog.ShowModal()
opt = dialog.GetOptions()
if answer == wx.ID_OK:
self.SetValue(opt)
dialog.Destroy()
self.Options.SetFocus()
def OnChar(self, event):
keycode = event.GetKeyCode()
if keycode in [wx.WXK_RETURN]:
self.Parent.Parent.ProcessEvent(event)
elif keycode == wx.WXK_ESCAPE:
self.Options.SetValue(self.Default)
self.Parent.Parent.CloseEditControl()
else:
event.Skip()
def SetFocus(self):
self.Options.SetFocus()
class WampOptionsCellEditor(wx.grid.PyGridCellEditor):
'''
Grid cell editor that uses DurationCellControl to display an edit button.
'''
def __init__(self, table, row, col):
wx.grid.PyGridCellEditor.__init__(self)
self.table = table
self.colname = self.table.GetColLabelValue(col, False)
self.row = row
def __del__(self):
self.CellControl = None
def Create(self, parent, id, evt_handler):
self.CellControl = WampOptionsCellControl(parent, self.table, self.row)
self.SetControl(self.CellControl)
if evt_handler:
self.CellControl.PushEventHandler(evt_handler)
def BeginEdit(self, row, col, grid):
self.CellControl.Enable()
self.CellControl.SetValue(self.table.GetValueByName(self.row, self.colname))
self.CellControl.SetFocus()
def EndEditInternal(self, row, col, grid, old_duration):
duration = self.CellControl.GetValue()
changed = duration != old_duration
if changed:
self.table.SetValueByName(row, self.colname, duration)
self.CellControl.Disable()
return changed
if wx.VERSION >= (3, 0, 0):
def EndEdit(self, row, col, grid, oldval):
return self.EndEditInternal(row, col, grid, oldval)
else:
def EndEdit(self, row, col, grid):
oldval = self.table.GetValueByName(row, self.colname)
return self.EndEditInternal(row, col, grid, oldval)
def SetSize(self, rect):
self.CellControl.SetDimensions(rect.x + 1, rect.y,
rect.width, rect.height,
wx.SIZE_ALLOW_MINUS_ONE)