# HG changeset patch # User laurent # Date 1253809084 -7200 # Node ID f3bb091f803f7e54e8dcd5164106114f04f90892 # Parent 893d04aff70840ffdae5d1de1c1e8df2ee80118e Fix BrowseVariableDialog to be used in PLCOpenEditor without Beremiz plugins diff -r 893d04aff708 -r f3bb091f803f VariablePanel.py --- a/VariablePanel.py Thu Sep 24 18:16:04 2009 +0200 +++ b/VariablePanel.py Thu Sep 24 18:18:04 2009 +0200 @@ -21,11 +21,15 @@ #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 os import wx, wx.grid from types import TupleType from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS +from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY + +CWD = os.path.split(os.path.realpath(__file__))[0] # Compatibility function for wx versions < 2.6 def AppendMenu(parent, help, id, kind, text): @@ -168,7 +172,6 @@ Otherwise default to the default renderer. """ - for row in range(self.GetNumberRows()): for col in range(self.GetNumberCols()): editor = None @@ -187,7 +190,7 @@ renderer = wx.grid.GridCellStringRenderer() elif colname == "Location": if self.GetValueByName(row, "Class") in ["Local", "Global"]: - editor = LocationCellEditor(self.Parent) + editor = LocationCellEditor(self, self.Parent.Controler) renderer = wx.grid.GridCellStringRenderer() else: grid.SetReadOnly(row, col, True) @@ -854,325 +857,311 @@ self.Table.ResetView(self.VariablesGrid) class LocationCellControl(wx.PyControl): + + def _init_coll_MainSizer_Items(self, parent): + parent.AddWindow(self.Location, 0, border=0, flag=wx.GROW) + parent.AddWindow(self.BrowseButton, 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=0, 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.Control.__init__(self, id=-1, + name='LocationCellControl', parent=prnt, + size=wx.DefaultSize, style=0) + + # create location text control + self.Location = wx.TextCtrl(id=-1, name='Location', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0) + + # create browse button + self.BrowseButton = wx.Button(id=-1, label='...', + name='staticText1', parent=self, pos=wx.Point(0, 0), + size=wx.Size(30, 0), style=0) + self.BrowseButton.Bind(wx.EVT_BUTTON, self.OnBrowseButtonClick) + + self.Bind(wx.EVT_SIZE, self.OnSize) + + self._init_sizers() + ''' Custom cell editor control with a text box and a button that launches the BrowseVariableLocationsDialog. ''' - def __init__(self, parent, var_panel): - wx.Control.__init__(self, parent, -1) - self.ParentWindow = parent - self.VarPanel = var_panel - self.Row = -1 - - self.Bind(wx.EVT_SIZE, self.OnSize) - - # create text control - self.txt = wx.TextCtrl(self, -1, '', size=wx.Size(0, 0)) - - # create browse button - self.btn = wx.Button(self, -1, label='...', size=wx.Size(30, 0)) - self.btn.Bind(wx.EVT_BUTTON, self.OnBtnBrowseClick) - - self.Sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) - self.Sizer.AddWindow(self.txt, 0, border=0, flag=wx.ALL|wx.GROW) - self.Sizer.AddWindow(self.btn, 0, border=0, flag=wx.ALL|wx.GROW) - self.Sizer.AddGrowableCol(0) - self.Sizer.AddGrowableRow(0) - - self.SetSizer(self.Sizer) - - def SetRow(self, row): - '''set the grid row that we're working on''' - self.Row = row + def __init__(self, parent, locations): + self._init_ctrls(parent) + self.Locations = locations + self.VarType = None + + def SetVarType(self, vartype): + self.VarType = vartype + + def SetValue(self, value): + self.Location.SetValue(value) + + def GetValue(self): + return self.Location.GetValue() def OnSize(self, event): - '''resize the button and text control to fit''' - #overall_width = self.GetSize()[0] - #btn_width, btn_height = self.btn.GetSize() - #new_txt_width = overall_width - btn_width - - #self.txt.SetSize(wx.Size(new_txt_width, -1)) - #self.btn.SetDimensions(new_txt_width, -1, btn_width, btn_height) self.Layout() - def OnBtnBrowseClick(self, event): + def OnBrowseButtonClick(self, event): # pop up the location browser dialog - dia = BrowseVariableLocationsDialog(self.ParentWindow, self.VarPanel) - dia.ShowModal() - - if dia.Selection: - loc, iec_type, doc = dia.Selection + dialog = BrowseLocationsDialog(self, self.VarType, self.Locations) + if dialog.ShowModal() == wx.ID_OK: + infos = dialog.GetValues() # set the location - self.SetText(loc) - - # set the variable type and documentation - # NOTE: this update won't be displayed until editing is complete - # (when EndEdit is called). - # we can't call VarPanel.RefreshValues() here because it causes - # an exception. - self.VarPanel.Table.SetValueByName(self.Row, 'Type', iec_type) - self.VarPanel.Table.SetValueByName(self.Row, 'Documentation', doc) - - self.txt.SetFocus() - - def SetText(self, text): - self.txt.SetValue(text) + self.Location.SetValue(infos["location"]) + + dialog.Destroy() + + self.Location.SetFocus() def SetInsertionPoint(self, i): - self.txt.SetInsertionPoint(i) - - def GetText(self): - return self.txt.GetValue() - + self.Location.SetInsertionPoint(i) + def SetFocus(self): - self.txt.SetFocus() + self.Location.SetFocus() class LocationCellEditor(wx.grid.PyGridCellEditor): ''' Grid cell editor that uses LocationCellControl to display a browse button. ''' - def __init__(self, var_panel): + def __init__(self, table, controler): wx.grid.PyGridCellEditor.__init__(self) - self.VarPanel = var_panel - + self.Table = table + self.Controler = controler + def Create(self, parent, id, evt_handler): - self.text_browse = LocationCellControl(parent, self.VarPanel) - self.SetControl(self.text_browse) + locations = self.Controler.GetVariableLocationTree() + if len(locations) > 0: + self.CellControl = LocationCellControl(parent, locations) + else: + self.CellControl = wx.TextCtrl(parent, -1) + self.SetControl(self.CellControl) if evt_handler: - self.text_browse.PushEventHandler(evt_handler) + self.CellControl.PushEventHandler(evt_handler) def BeginEdit(self, row, col, grid): - loc = self.VarPanel.Table.GetValueByName(row, 'Location') - self.text_browse.SetText(loc) - self.text_browse.SetRow(row) - self.text_browse.SetFocus() + self.CellControl.SetValue(self.Table.GetValueByName(row, 'Location')) + if isinstance(self.CellControl, LocationCellControl): + self.CellControl.SetVarType(self.Controler.GetBaseType(self.Table.GetValueByName(row, 'Type'))) + self.CellControl.SetFocus() def EndEdit(self, row, col, grid): - loc = self.text_browse.GetText() - old_loc = self.VarPanel.Table.GetValueByName(row, 'Location') - + loc = self.CellControl.GetValue() + old_loc = self.Table.GetValueByName(row, 'Location') if loc != old_loc: - self.VarPanel.Table.SetValueByName(row, 'Location', loc) - - # NOTE: this is a really lame hack to force this row's - # 'Type' cell to redraw (since it may have changed). - # There's got to be a better way than this. - self.VarPanel.VariablesGrid.AutoSizeRow(row) + self.Table.SetValueByName(row, 'Location', loc) return True + return False def SetSize(self, rect): - self.text_browse.SetDimensions(rect.x + 1, rect.y, + self.CellControl.SetDimensions(rect.x + 1, rect.y, rect.width, rect.height, wx.SIZE_ALLOW_MINUS_ONE) def Clone(self): - return LocationCellEditor(self.VarPanel) - -class BrowseVariableLocationsDialog(wx.Dialog): - # turn LOCATIONDATATYPES inside-out - LOCATION_SIZES = {} - for size, types in LOCATIONDATATYPES.iteritems(): - for type in types: - LOCATION_SIZES[type] = size - - class PluginData: - '''contains a plugin's VariableLocationTree''' - def __init__(self, plugin): - self.subtree = plugin.GetVariableLocationTree() - - class SubtreeData: - '''contains a subtree of a plugin's VariableLocationTree''' - def __init__(self, subtree): - self.subtree = subtree - - class VariableData: - '''contains all the information about a valid variable location''' - def __init__(self, type, dir, loc): - self.type = type - - loc_suffix = '.'.join([str(x) for x in loc]) - - size = BrowseVariableLocationsDialog.LOCATION_SIZES[type] - self.loc = size + loc_suffix - - # if a direction was given, use it - # (if not we'll prompt the user to select one) - if dir: - self.loc = '%' + dir + self.loc - - def __init__(self, parent, var_panel): - self.VarPanel = var_panel - self.Selection = None - - # create the dialog - wx.Dialog.__init__(self, parent=parent, title=_('Browse Variables'), - style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, - size=(-1, 400)) - - # create the root sizer - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - - # create the tree control - self.tree = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT) - sizer.Add(self.tree, 1, wx.EXPAND|wx.ALL, border=20) - - # create the Direction sizer and field - dsizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(dsizer, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, border=20) - - # direction label - ltext = wx.StaticText(self, -1, _('Direction:')) - dsizer.Add(ltext, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, border=20) - - # direction choice - self.DirChoice = wx.Choice(id=-1, parent=self, choices=[_('Input'), _('Output'), _('Memory')]) - dsizer.Add(self.DirChoice, flag=wx.EXPAND) - # set a default for the choice - self.SetSelectedDirection('I') - - # create the button sizer - btsizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(btsizer, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_RIGHT, border=20) - - # add plugins to the tree - root = self.tree.AddRoot(_('Plugins')) - ctrl = self.VarPanel.Controler - self.AddChildPluginsToTree(ctrl, root) - - # -- buttons -- - - # ok button - self.OkButton = wx.Button(self, wx.ID_OK, _('Use Location')) - self.OkButton.SetDefault() - btsizer.Add(self.OkButton, flag=wx.RIGHT, border=5) - - # cancel button - b = wx.Button(self, wx.ID_CANCEL, _('Cancel')) - btsizer.Add(b) - - # -- event handlers -- - - # accept the location on doubleclick or clicking the Use Location button - self.Bind(wx.EVT_BUTTON, self.OnOk, self.OkButton) - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnOk, self.tree) - - # disable the Add button when we're not on a valid variable - self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChange, self.tree) - - # handle the Expand event. this lets us create the tree as it's expanded - # (trying to create it all at once is slow since plugin variable location - # trees can be big) - wx.EVT_TREE_ITEM_EXPANDING(self.tree, self.tree.GetId(), self.OnExpand) - - def OnExpand(self, event): - item = event.GetItem() - if not item.IsOk(): - item = self.tree.GetSelection() - - data = self.tree.GetPyData(item) - self.ExpandTree(item, data.subtree) - - def AddChildPluginsToTree(self, plugin, root): - for p in plugin.IECSortedChilds(): - plug_name = p.BaseParams.getName() - new_item = self.tree.AppendItem(root, plug_name) - - # make it look like the tree item has children (since it doesn't yet) - self.tree.SetItemHasChildren(new_item) - - # attach the plugin data to the tree item - self.tree.SetPyData(new_item, BrowseVariableLocationsDialog.PluginData(p)) - - # add child plugins recursively - self.AddChildPluginsToTree(p, new_item) - - def ExpandTree(self, root, subtree): - items = subtree.items() - items.sort() - - for node_name, data in items: - if isinstance(data, dict): - # this is a new subtree - - new_item = self.tree.AppendItem(root, node_name) - self.tree.SetItemHasChildren(new_item) - - # attach the new subtree's data to the tree item - new_data = BrowseVariableLocationsDialog.SubtreeData(data) - self.tree.SetPyData(new_item, new_data) + return LocationCellEditor(self.Table, self.Locations) + +def GetDirChoiceOptions(): + _ = lambda x : x + return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), + (_("Input"), [LOCATION_VAR_INPUT]), + (_("Output"), [LOCATION_VAR_OUTPUT]), + (_("Memory"), [LOCATION_VAR_MEMORY])] +DIRCHOICE_OPTIONS_FILTER = dict([(_(option), filter) for option, filter in GetDirChoiceOptions()]) + +# turn LOCATIONDATATYPES inside-out +LOCATION_SIZES = {} +for size, types in LOCATIONDATATYPES.iteritems(): + for type in types: + LOCATION_SIZES[type] = size + +[ID_BROWSELOCATIONSDIALOG, ID_BROWSELOCATIONSDIALOGLOCATIONSTREE, + ID_BROWSELOCATIONSDIALOGDIRCHOICE, ID_BROWSELOCATIONSDIALOGSTATICTEXT1, + ID_BROWSELOCATIONSDIALOGSTATICTEXT2, +] = [wx.NewId() for _init_ctrls in range(5)] + +class BrowseLocationsDialog(wx.Dialog): + + 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.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) + parent.AddWindow(self.LocationsTree, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW) + parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) + + def _init_coll_MainSizer_Growables(self, parent): + parent.AddGrowableCol(0) + parent.AddGrowableRow(1) + + def _init_coll_ButtonGridSizer_Items(self, parent): + parent.AddWindow(self.staticText2, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL) + parent.AddWindow(self.DirChoice, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL) + parent.AddSizer(self.ButtonSizer, 0, border=0, flag=wx.ALIGN_RIGHT) + + def _init_coll_ButtonGridSizer_Growables(self, parent): + parent.AddGrowableCol(2) + parent.AddGrowableRow(0) + + def _init_sizers(self): + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) + self.ButtonGridSizer = wx.FlexGridSizer(cols=3, hgap=5, rows=1, vgap=0) + + self._init_coll_MainSizer_Items(self.MainSizer) + self._init_coll_MainSizer_Growables(self.MainSizer) + self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer) + self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer) + + self.SetSizer(self.MainSizer) + + def _init_ctrls(self, prnt): + wx.Dialog.__init__(self, id=ID_BROWSELOCATIONSDIALOG, + name='BrowseLocationsDialog', parent=prnt, + size=wx.Size(600, 400), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, + title=_('Browse Locations')) + + self.staticText1 = wx.StaticText(id=ID_BROWSELOCATIONSDIALOGSTATICTEXT1, + label=_('Locations available:'), name='staticText1', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + if wx.Platform == '__WXMSW__': + treestyle = wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER + else: + treestyle = wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_SINGLE|wx.SUNKEN_BORDER + self.LocationsTree = wx.TreeCtrl(id=ID_BROWSELOCATIONSDIALOGLOCATIONSTREE, + name='LocationsTree', parent=self, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=treestyle) + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, + id=ID_BROWSELOCATIONSDIALOGLOCATIONSTREE) + + self.staticText2 = wx.StaticText(id=ID_BROWSELOCATIONSDIALOGSTATICTEXT2, + label=_('Direction:'), name='staticText2', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.DirChoice = wx.ComboBox(id=ID_BROWSELOCATIONSDIALOGDIRCHOICE, + name='DirChoice', parent=self, pos=wx.Point(0, 0), + size=wx.DefaultSize, style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnDirChoice, id=ID_BROWSELOCATIONSDIALOGDIRCHOICE) + + self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + if wx.VERSION >= (2, 5, 0): + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId()) + else: + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId()) + + self._init_sizers() + + def __init__(self, parent, var_type, locations): + self._init_ctrls(parent) + self.VarType = var_type + self.Locations = locations + + # Define Tree item icon list + self.TreeImageList = wx.ImageList(16, 16) + self.TreeImageDict = {} + + # Icons for items + for imgname, itemtype in [ + ("CONFIGURATION", LOCATION_PLUGIN), + ("RESOURCE", LOCATION_MODULE), + ("PROGRAM", LOCATION_GROUP), + ("VAR_INPUT", LOCATION_VAR_INPUT), + ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), + ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: + self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname))) + + # Assign icon list to TreeCtrls + self.LocationsTree.SetImageList(self.TreeImageList) + + # Set a options for the choice + for option, filter in GetDirChoiceOptions(): + self.DirChoice.Append(_(option)) + self.DirChoice.SetStringSelection(_("All")) + self.RefreshFilter() + + self.RefreshLocationsTree() + + def RefreshFilter(self): + self.Filter = DIRCHOICE_OPTIONS_FILTER[self.DirChoice.GetStringSelection()] + + def RefreshLocationsTree(self): + root = self.LocationsTree.GetRootItem() + if not root.IsOk(): + if wx.Platform == '__WXMSW__': + root = self.LocationsTree.AddRoot(_('Plugins')) + else: + root = self.LocationsTree.AddRoot("") + self.GenerateLocationsTreeBranch(root, self.Locations) + self.LocationsTree.Expand(root) + + def GenerateLocationsTreeBranch(self, root, locations): + to_delete = [] + if wx.VERSION >= (2, 6, 0): + item, root_cookie = self.LocationsTree.GetFirstChild(root) + else: + item, root_cookie = self.LocationsTree.GetFirstChild(root, 0) + for loc_infos in locations: + infos = loc_infos.copy() + if infos["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP] or\ + infos["type"] in self.Filter and (infos["IEC_type"] == self.VarType or + infos["IEC_type"] is None and LOCATION_SIZES[self.VarType] == infos["size"]): + children = [child for child in infos.pop("children")] + if not item.IsOk(): + item = self.LocationsTree.AppendItem(root, infos["name"]) + if wx.Platform != '__WXMSW__': + item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) else: - # this is a new leaf. - - # data is a tuple containing (IEC type, I/Q/M/None, IEC path tuple) - type, dir, loc = data - - node_name = '%s (%s)' % (node_name, type) - new_item = self.tree.AppendItem(root, node_name) - - vd = BrowseVariableLocationsDialog.VariableData(type, dir, loc) - self.tree.SetPyData(new_item, vd) - - def OnSelChange(self, event): - '''updates the text field and the "Use Location" button.''' - item = self.tree.GetSelection() - data = self.tree.GetPyData(item) - - if isinstance(data, BrowseVariableLocationsDialog.VariableData): - self.OkButton.Enable() - - location = data.loc - if location[0] == '%': - # location has a fixed direction - self.SetSelectedDirection(location[1]) - self.DirChoice.Disable() - else: - # this location can have any direction (user selects) - self.DirChoice.Enable() - else: - self.OkButton.Disable() - self.DirChoice.Disable() - - def GetSelectedDirection(self): - selected = self.DirChoice.GetSelection() - if selected == 0: - return 'I' - elif selected == 1: - return 'Q' - else: - return 'M' - - def SetSelectedDirection(self, dir_letter): - if dir_letter == 'I': - self.DirChoice.SetSelection(0) - elif dir_letter == 'Q': - self.DirChoice.SetSelection(1) - else: - self.DirChoice.SetSelection(2) - - def OnOk(self, event): - item = self.tree.GetSelection() - data = self.tree.GetPyData(item) - - if not isinstance(data, BrowseVariableLocationsDialog.VariableData): - return - - location = data.loc - - if location[0] != '%': - # no direction was given, grab the one from the wxChoice - dir = self.GetSelectedDirection() - location = '%' + dir + location - - # walk up the tree, building documentation for the variable - documentation = self.tree.GetItemText(item) - parent = self.tree.GetItemParent(item) - root = self.tree.GetRootItem() - while parent != root: - text = self.tree.GetItemText(parent) - documentation = text + ":" + documentation - parent = self.tree.GetItemParent(parent) - - self.Selection = (location, data.type, documentation) - self.Destroy() + self.LocationsTree.SetItemText(item, infos["name"]) + self.LocationsTree.SetPyData(item, infos) + self.LocationsTree.SetItemImage(item, self.TreeImageDict[infos["type"]]) + self.GenerateLocationsTreeBranch(item, children) + item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) + while item.IsOk(): + to_delete.append(item) + item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) + for item in to_delete: + self.LocationsTree.Delete(item) + + def OnLocationsTreeItemActivated(self, event): + infos = self.LocationsTree.GetPyData(event.GetItem()) + if infos["type"] not in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]: + wx.CallAfter(self.EndModal, wx.ID_OK) + event.Skip() + + def OnDirChoice(self, event): + self.RefreshFilter() + self.RefreshLocationsTree() + + def GetValues(self): + selected = self.LocationsTree.GetSelection() + return self.LocationsTree.GetPyData(selected) + + def OnOK(self, event): + selected = self.LocationsTree.GetSelection() + var_infos = None + if selected.IsOk(): + var_infos = self.LocationsTree.GetPyData(selected) + if var_infos is None or var_infos["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]: + message = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + else: + self.EndModal(wx.ID_OK) + \ No newline at end of file