GRASS Programmer's Manual  6.4.3(2013)-r
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
gmodeler/dialogs.py
Go to the documentation of this file.
1 """!
2 @package gmodeler.dialogs
3 
4 @brief wxGUI Graphical Modeler - dialogs
5 
6 Classes:
7  - dialogs::ModelDataDialog
8  - dialogs::ModelSearchDialog
9  - dialogs::ModelRelationDialog
10  - dialogs::ModelItemDialog
11  - dialogs::ModelLoopDialog
12  - dialogs::ModelConditionDialog
13  - dialogs::ModelListCtrl
14  - dialogs::ValiableListCtrl
15  - dialogs::ItemListCtrl
16  - dialogs::ItemCheckListCtrl
17 
18 (C) 2010-2011 by the GRASS Development Team
19 
20 This program is free software under the GNU General Public License
21 (>=v2). Read the file COPYING that comes with GRASS for details.
22 
23 @author Martin Landa <landa.martin gmail.com>
24 """
25 
26 import os
27 import sys
28 
29 import wx
30 import wx.lib.mixins.listctrl as listmix
31 
32 from core import globalvar
33 from core import utils
34 from gui_core.widgets import GNotebook
35 from core.gcmd import GError, EncodeString
36 from gui_core.dialogs import ElementDialog, MapLayersDialog
37 from gui_core.ghelp import SearchModuleWindow
38 from gui_core.prompt import GPromptSTC
39 from gui_core.forms import CmdPanel
40 from gui_core.gselect import Select
41 from gmodeler.model import *
42 
43 from grass.script import task as gtask
44 
46  """!Data item properties dialog"""
47  def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Data properties"),
48  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
49  self.parent = parent
50  self.shape = shape
51 
52  label, etype = self._getLabel()
53  ElementDialog.__init__(self, parent, title, label = label, etype = etype)
54 
55  self.element = Select(parent = self.panel)
56  self.element.SetValue(shape.GetValue())
57 
58  self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
59  self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
60 
61  self.PostInit()
62 
63  if shape.GetValue():
64  self.btnOK.Enable()
65 
66  self._layout()
67  self.SetMinSize(self.GetSize())
68 
69  def _getLabel(self):
70  etype = False
71  prompt = self.shape.GetPrompt()
72  if prompt == 'raster':
73  label = _('Name of raster map:')
74  elif prompt == 'vector':
75  label = _('Name of vector map:')
76  else:
77  etype = True
78  label = _('Name of element:')
79 
80  return label, etype
81 
82  def _layout(self):
83  """!Do layout"""
84  self.dataSizer.Add(self.element, proportion=0,
85  flag=wx.EXPAND | wx.ALL, border=1)
86 
87  self.panel.SetSizer(self.sizer)
88  self.sizer.Fit(self)
89 
90  def OnOK(self, event):
91  """!Ok pressed"""
92  self.shape.SetValue(self.GetElement())
93  if self.etype:
94  elem = self.GetType()
95  if elem == 'rast':
96  self.shape.SetPrompt('raster')
97  elif elem == 'vect':
98  self.shape.SetPrompt('raster')
99 
100  self.parent.canvas.Refresh()
101  self.parent.SetStatusText('', 0)
102  self.shape.SetPropDialog(None)
103 
104  if self.IsModal():
105  event.Skip()
106  else:
107  self.Destroy()
108 
109  def OnCancel(self, event):
110  """!Cancel pressed"""
111  self.shape.SetPropDialog(None)
112  if self.IsModal():
113  event.Skip()
114  else:
115  self.Destroy()
116 
117 class ModelSearchDialog(wx.Dialog):
118  def __init__(self, parent, id = wx.ID_ANY, title = _("Add new GRASS module to the model"),
119  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
120  """!Graphical modeler module search window
121 
122  @param parent parent window
123  @param id window id
124  @param title window title
125  @param kwargs wx.Dialogs' arguments
126  """
127  self.parent = parent
128 
129  wx.Dialog.__init__(self, parent = parent, id = id, title = title, **kwargs)
130  self.SetName("ModelerDialog")
131  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
132 
133  self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
134 
135  self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
136  label=" %s " % _("Command"))
137 
138  self.cmd_prompt = GPromptSTC(parent = self)
139  self.search = SearchModuleWindow(parent = self.panel, cmdPrompt = self.cmd_prompt, showTip = True)
140  wx.CallAfter(self.cmd_prompt.SetFocus)
141 
142  # get commands
143  items = self.cmd_prompt.GetCommandItems()
144 
145  self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
146  self.btnOk = wx.Button(self.panel, wx.ID_OK)
147  self.btnOk.SetDefault()
148  self.btnOk.Enable(False)
149 
150  self.cmd_prompt.Bind(wx.EVT_KEY_UP, self.OnText)
151  self.search.searchChoice.Bind(wx.EVT_CHOICE, self.OnText)
152  self.Bind(wx.EVT_BUTTON, self.OnOk, self.btnOk)
153 
154  self._layout()
155 
156  self.SetSize((500, 275))
157 
158  def _layout(self):
159  cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL)
160  cmdSizer.Add(item = self.cmd_prompt, proportion = 1,
161  flag = wx.EXPAND)
162 
163  btnSizer = wx.StdDialogButtonSizer()
164  btnSizer.AddButton(self.btnCancel)
165  btnSizer.AddButton(self.btnOk)
166  btnSizer.Realize()
167 
168  mainSizer = wx.BoxSizer(wx.VERTICAL)
169  mainSizer.Add(item = self.search, proportion = 0,
170  flag = wx.EXPAND | wx.ALL, border = 3)
171  mainSizer.Add(item = cmdSizer, proportion = 1,
172  flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3)
173  mainSizer.Add(item = btnSizer, proportion = 0,
174  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
175 
176  self.panel.SetSizer(mainSizer)
177  mainSizer.Fit(self.panel)
178 
179  self.Layout()
180 
181  def GetPanel(self):
182  """!Get dialog panel"""
183  return self.panel
184 
185  def GetCmd(self):
186  """!Get command"""
187  line = self.cmd_prompt.GetCurLine()[0].strip()
188  if len(line) == 0:
189  list()
190 
191  try:
192  cmd = utils.split(str(line))
193  except UnicodeError:
194  cmd = utils.split(utils.EncodeString((line)))
195 
196  return cmd
197 
198  def OnOk(self, event):
199  """!Button 'OK' pressed"""
200  self.btnOk.SetFocus()
201  cmd = self.GetCmd()
202 
203  if len(cmd) < 1:
204  GError(parent = self,
205  message = _("Command not defined.\n\n"
206  "Unable to add new action to the model."))
207  return
208 
209  if cmd[0] not in globalvar.grassCmd:
210  GError(parent = self,
211  message = _("'%s' is not a GRASS module.\n\n"
212  "Unable to add new action to the model.") % cmd[0])
213  return
214 
215  self.EndModal(wx.ID_OK)
216 
217  def OnText(self, event):
218  """!Text in prompt changed"""
219  if self.cmd_prompt.AutoCompActive():
220  event.Skip()
221  return
222 
223  if isinstance(event, wx.KeyEvent):
224  entry = self.cmd_prompt.GetTextLeft()
225  elif isinstance(event, wx.stc.StyledTextEvent):
226  entry = event.GetText()
227  else:
228  entry = event.GetString()
229 
230  if entry:
231  self.btnOk.Enable()
232  else:
233  self.btnOk.Enable(False)
234 
235  event.Skip()
236 
237  def Reset(self):
238  """!Reset dialog"""
239  self.search.Reset()
240  self.cmd_prompt.OnCmdErase(None)
241  self.btnOk.Enable(False)
242  self.cmd_prompt.SetFocus()
243 
244 class ModelRelationDialog(wx.Dialog):
245  """!Relation properties dialog"""
246  def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Relation properties"),
247  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
248  self.parent = parent
249  self.shape = shape
250 
251  options = self._getOptions()
252  if not options:
253  self.valid = False
254  return
255 
256  self.valid = True
257  wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
258  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
259 
260  self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
261 
262  self.fromBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
263  label = " %s " % _("From"))
264  self.toBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
265  label = " %s " % _("To"))
266 
267  self.option = wx.ComboBox(parent = self.panel, id = wx.ID_ANY,
268  style = wx.CB_READONLY,
269  choices = options)
270  self.option.Bind(wx.EVT_COMBOBOX, self.OnOption)
271 
272  self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
273  self.btnOk = wx.Button(self.panel, wx.ID_OK)
274  self.btnOk.Enable(False)
275 
276  self._layout()
277 
278  def _layout(self):
279  mainSizer = wx.BoxSizer(wx.VERTICAL)
280 
281  fromSizer = wx.StaticBoxSizer(self.fromBox, wx.VERTICAL)
282  self._layoutShape(shape = self.shape.GetFrom(), sizer = fromSizer)
283  toSizer = wx.StaticBoxSizer(self.toBox, wx.VERTICAL)
284  self._layoutShape(shape = self.shape.GetTo(), sizer = toSizer)
285 
286  btnSizer = wx.StdDialogButtonSizer()
287  btnSizer.AddButton(self.btnCancel)
288  btnSizer.AddButton(self.btnOk)
289  btnSizer.Realize()
290 
291  mainSizer.Add(item = fromSizer, proportion = 0,
292  flag = wx.EXPAND | wx.ALL, border = 5)
293  mainSizer.Add(item = toSizer, proportion = 0,
294  flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
295  mainSizer.Add(item = btnSizer, proportion = 0,
296  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
297 
298  self.panel.SetSizer(mainSizer)
299  mainSizer.Fit(self.panel)
300 
301  self.Layout()
302  self.SetSize(self.GetBestSize())
303 
304  def _layoutShape(self, shape, sizer):
305  if isinstance(shape, ModelData):
306  sizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
307  label = _("Data: %s") % shape.GetLog()),
308  proportion = 1, flag = wx.EXPAND | wx.ALL,
309  border = 5)
310  elif isinstance(shape, ModelAction):
311  gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
312  gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
313  label = _("Command:")),
314  pos = (0, 0))
315  gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
316  label = shape.GetName()),
317  pos = (0, 1))
318  gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
319  label = _("Option:")),
320  flag = wx.ALIGN_CENTER_VERTICAL,
321  pos = (1, 0))
322  gridSizer.Add(item = self.option,
323  pos = (1, 1))
324  sizer.Add(item = gridSizer,
325  proportion = 1, flag = wx.EXPAND | wx.ALL,
326  border = 5)
327 
328  def _getOptions(self):
329  """!Get relevant options"""
330  items = []
331  fromShape = self.shape.GetFrom()
332  if not isinstance(fromShape, ModelData):
333  GError(parent = self.parent,
334  message = _("Relation doesn't start with data item.\n"
335  "Unable to add relation."))
336  return items
337 
338  toShape = self.shape.GetTo()
339  if not isinstance(toShape, ModelAction):
340  GError(parent = self.parent,
341  message = _("Relation doesn't point to GRASS command.\n"
342  "Unable to add relation."))
343  return items
344 
345  prompt = fromShape.GetPrompt()
346  task = toShape.GetTask()
347  for p in task.get_options()['params']:
348  if p.get('prompt', '') == prompt and \
349  'name' in p:
350  items.append(p['name'])
351 
352  if not items:
353  GError(parent = self.parent,
354  message = _("No relevant option found.\n"
355  "Unable to add relation."))
356  return items
357 
358  def GetOption(self):
359  """!Get selected option"""
360  return self.option.GetStringSelection()
361 
362  def IsValid(self):
363  """!Check if relation is valid"""
364  return self.valid
365 
366  def OnOption(self, event):
367  """!Set option"""
368  if event.GetString():
369  self.btnOk.Enable()
370  else:
371  self.btnOk.Enable(False)
372 
373 class ModelItemDialog(wx.Dialog):
374  """!Abstract item properties dialog"""
375  def __init__(self, parent, shape, title, id = wx.ID_ANY,
376  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
377  self.parent = parent
378  self.shape = shape
379 
380  wx.Dialog.__init__(self, parent, id, title = title, style = style, **kwargs)
381 
382  self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
383 
384  self.condBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
385  label=" %s " % _("Condition"))
386  self.condText = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
387  value = shape.GetText())
388 
389  self.itemList = ItemCheckListCtrl(parent = self.panel,
390  window = self,
391  columns = [_("ID"), _("Name"),
392  _("Command")],
393  shape = shape)
394  self.itemList.Populate(self.parent.GetModel().GetItems())
395 
396  self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
397  self.btnOk = wx.Button(parent = self.panel, id = wx.ID_OK)
398  self.btnOk.SetDefault()
399 
400  def _layout(self):
401  """!Do layout (virtual method)"""
402  pass
403 
404  def GetCondition(self):
405  """!Get loop condition"""
406  return self.condText.GetValue()
407 
409  """!Loop properties dialog"""
410  def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Loop properties"),
411  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
412  ModelItemDialog.__init__(self, parent, shape, title,
413  style = style, **kwargs)
414 
415  self.listBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
416  label=" %s " % _("List of items in loop"))
417 
418  self.btnSeries = wx.Button(parent = self.panel, id = wx.ID_ANY,
419  label = _("Series"))
420  self.btnSeries.SetToolTipString(_("Define map series as condition for the loop"))
421  self.btnSeries.Bind(wx.EVT_BUTTON, self.OnSeries)
422 
423  self._layout()
424  self.SetMinSize(self.GetSize())
425  self.SetSize((500, 400))
426 
427  def _layout(self):
428  """!Do layout"""
429  sizer = wx.BoxSizer(wx.VERTICAL)
430 
431  condSizer = wx.StaticBoxSizer(self.condBox, wx.HORIZONTAL)
432  condSizer.Add(item = self.condText, proportion = 1,
433  flag = wx.ALL, border = 3)
434  condSizer.Add(item = self.btnSeries, proportion = 0,
435  flag = wx.EXPAND)
436 
437  listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
438  listSizer.Add(item = self.itemList, proportion = 1,
439  flag = wx.EXPAND | wx.ALL, border = 3)
440 
441  btnSizer = wx.StdDialogButtonSizer()
442  btnSizer.AddButton(self.btnCancel)
443  btnSizer.AddButton(self.btnOk)
444  btnSizer.Realize()
445 
446  sizer.Add(item = condSizer, proportion = 0,
447  flag = wx.EXPAND | wx.ALL, border = 3)
448  sizer.Add(item = listSizer, proportion = 1,
449  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
450  sizer.Add(item = btnSizer, proportion=0,
451  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
452 
453  self.panel.SetSizer(sizer)
454  sizer.Fit(self.panel)
455 
456  self.Layout()
457 
458  def GetItems(self):
459  """!Get list of selected actions"""
460  return self.itemList.GetItems()
461 
462  def OnSeries(self, event):
463  """!Define map series as condition"""
464  dialog = MapLayersDialog(parent = self, title = _("Define series of maps"), modeler = True)
465  if dialog.ShowModal() != wx.ID_OK:
466  dialog.Destroy()
467  return
468 
469  cond = dialog.GetDSeries()
470  if not cond:
471  cond = 'map in %s' % map(lambda x: str(x), dialog.GetMapLayers())
472 
473  self.condText.SetValue(cond)
474 
475  dialog.Destroy()
476 
478  """!Condition properties dialog"""
479  def __init__(self, parent, shape, id = wx.ID_ANY, title = _("If-else properties"),
480  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
481  ModelItemDialog.__init__(self, parent, shape, title,
482  style = style, **kwargs)
483 
484  self.listBoxIf = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
485  label=" %s " % _("List of items in 'if' block"))
486  self.itemListIf = self.itemList
487  self.itemListIf.SetName('IfBlockList')
488 
489  self.listBoxElse = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
490  label=" %s " % _("List of items in 'else' block"))
491  self.itemListElse = ItemCheckListCtrl(parent = self.panel,
492  window = self,
493  columns = [_("ID"), _("Name"),
494  _("Command")],
495  shape = shape)
496  self.itemListElse.SetName('ElseBlockList')
497  self.itemListElse.Populate(self.parent.GetModel().GetItems())
498 
499  self._layout()
500  self.SetMinSize(self.GetSize())
501  self.SetSize((500, 400))
502 
503  def _layout(self):
504  """!Do layout"""
505  sizer = wx.BoxSizer(wx.VERTICAL)
506 
507  condSizer = wx.StaticBoxSizer(self.condBox, wx.VERTICAL)
508  condSizer.Add(item = self.condText, proportion = 1,
509  flag = wx.EXPAND)
510 
511  listIfSizer = wx.StaticBoxSizer(self.listBoxIf, wx.VERTICAL)
512  listIfSizer.Add(item = self.itemListIf, proportion = 1,
513  flag = wx.EXPAND)
514  listElseSizer = wx.StaticBoxSizer(self.listBoxElse, wx.VERTICAL)
515  listElseSizer.Add(item = self.itemListElse, proportion = 1,
516  flag = wx.EXPAND)
517 
518  btnSizer = wx.StdDialogButtonSizer()
519  btnSizer.AddButton(self.btnCancel)
520  btnSizer.AddButton(self.btnOk)
521  btnSizer.Realize()
522 
523  sizer.Add(item = condSizer, proportion = 0,
524  flag = wx.EXPAND | wx.ALL, border = 3)
525  sizer.Add(item = listIfSizer, proportion = 1,
526  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
527  sizer.Add(item = listElseSizer, proportion = 1,
528  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
529  sizer.Add(item = btnSizer, proportion=0,
530  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
531 
532  self.panel.SetSizer(sizer)
533  sizer.Fit(self.panel)
534 
535  self.Layout()
536 
537  def OnCheckItemIf(self, index, flag):
538  """!Item in if-block checked/unchecked"""
539  if flag is False:
540  return
541 
542  aId = int(self.itemListIf.GetItem(index, 0).GetText())
543  if aId in self.itemListElse.GetItems()['checked']:
544  self.itemListElse.CheckItemById(aId, False)
545 
546  def OnCheckItemElse(self, index, flag):
547  """!Item in else-block checked/unchecked"""
548  if flag is False:
549  return
550 
551  aId = int(self.itemListElse.GetItem(index, 0).GetText())
552  if aId in self.itemListIf.GetItems()['checked']:
553  self.itemListIf.CheckItemById(aId, False)
554 
555  def GetItems(self):
556  """!Get items"""
557  return { 'if' : self.itemListIf.GetItems(),
558  'else' : self.itemListElse.GetItems() }
559 
560 class ModelListCtrl(wx.ListCtrl,
561  listmix.ListCtrlAutoWidthMixin,
562  listmix.TextEditMixin,
563  listmix.ColumnSorterMixin):
564  def __init__(self, parent, columns, id = wx.ID_ANY,
565  style = wx.LC_REPORT | wx.BORDER_NONE |
566  wx.LC_SORT_ASCENDING |wx.LC_HRULES |
567  wx.LC_VRULES, **kwargs):
568  """!List of model variables"""
569  self.parent = parent
570  self.columns = columns
571  self.shape = None
572  try:
573  self.frame = parent.parent
574  except AttributeError:
575  self.frame = None
576 
577  wx.ListCtrl.__init__(self, parent, id = id, style = style, **kwargs)
578  listmix.ListCtrlAutoWidthMixin.__init__(self)
579  listmix.TextEditMixin.__init__(self)
580  listmix.ColumnSorterMixin.__init__(self, 4)
581 
582  i = 0
583  for col in columns:
584  self.InsertColumn(i, col)
585  self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
586  i += 1
587 
588  self.itemDataMap = {} # requested by sorter
589  self.itemCount = 0
590 
591  self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit)
592  self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit)
593  self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
594  self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
595  self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
596 
597  def OnBeginEdit(self, event):
598  """!Editing of item started"""
599  event.Allow()
600 
601  def OnEndEdit(self, event):
602  """!Finish editing of item"""
603  pass
604 
605  def OnColClick(self, event):
606  """!Click on column header (order by)"""
607  event.Skip()
608 
610  def __init__(self, parent, columns, **kwargs):
611  """!List of model variables"""
612  ModelListCtrl.__init__(self, parent, columns, **kwargs)
613 
614  self.SetColumnWidth(2, 200) # default value
615 
616  def GetListCtrl(self):
617  """!Used by ColumnSorterMixin"""
618  return self
619 
620  def GetData(self):
621  """!Get list data"""
622  return self.itemDataMap
623 
624  def Populate(self, data):
625  """!Populate the list"""
626  self.itemDataMap = dict()
627  i = 0
628  for name, values in data.iteritems():
629  self.itemDataMap[i] = [name, values['type'],
630  values.get('value', ''),
631  values.get('description', '')]
632  i += 1
633 
634  self.itemCount = len(self.itemDataMap.keys())
635  self.DeleteAllItems()
636  i = 0
637  for name, vtype, value, desc in self.itemDataMap.itervalues():
638  index = self.InsertStringItem(sys.maxint, name)
639  self.SetStringItem(index, 0, name)
640  self.SetStringItem(index, 1, vtype)
641  self.SetStringItem(index, 2, value)
642  self.SetStringItem(index, 3, desc)
643  self.SetItemData(index, i)
644  i += 1
645 
646  def Append(self, name, vtype, value, desc):
647  """!Append new item to the list
648 
649  @return None on success
650  @return error string
651  """
652  for iname, ivtype, ivalue, idesc in self.itemDataMap.itervalues():
653  if iname == name:
654  return _("Variable <%s> already exists in the model. "
655  "Adding variable failed.") % name
656 
657  index = self.InsertStringItem(sys.maxint, name)
658  self.SetStringItem(index, 0, name)
659  self.SetStringItem(index, 1, vtype)
660  self.SetStringItem(index, 2, value)
661  self.SetStringItem(index, 3, desc)
662  self.SetItemData(index, self.itemCount)
663 
664  self.itemDataMap[self.itemCount] = [name, vtype, value, desc]
665  self.itemCount += 1
666 
667  return None
668 
669  def OnRemove(self, event):
670  """!Remove selected variable(s) from the model"""
671  item = self.GetFirstSelected()
672  while item != -1:
673  self.DeleteItem(item)
674  del self.itemDataMap[item]
675  item = self.GetFirstSelected()
676  self.parent.UpdateModelVariables()
677 
678  event.Skip()
679 
680  def OnRemoveAll(self, event):
681  """!Remove all variable(s) from the model"""
682  dlg = wx.MessageBox(parent=self,
683  message=_("Do you want to delete all variables from "
684  "the model?"),
685  caption=_("Delete variables"),
686  style=wx.YES_NO | wx.CENTRE)
687  if dlg != wx.YES:
688  return
689 
690  self.DeleteAllItems()
691  self.itemDataMap = dict()
692 
693  self.parent.UpdateModelVariables()
694 
695  def OnEndEdit(self, event):
696  """!Finish editing of item"""
697  itemIndex = event.GetIndex()
698  columnIndex = event.GetColumn()
699  nameOld = self.GetItem(itemIndex, 0).GetText()
700 
701  if columnIndex == 0: # TODO
702  event.Veto()
703 
704  self.itemDataMap[itemIndex][columnIndex] = event.GetText()
705 
706  self.parent.UpdateModelVariables()
707 
708  def OnReload(self, event):
709  """!Reload list of variables"""
710  self.Populate(self.parent.parent.GetModel().GetVariables())
711 
712  def OnRightUp(self, event):
713  """!Mouse right button up"""
714  if not hasattr(self, "popupID1"):
715  self.popupID1 = wx.NewId()
716  self.popupID2 = wx.NewId()
717  self.popupID3 = wx.NewId()
718  self.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID1)
719  self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2)
720  self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
721 
722  # generate popup-menu
723  menu = wx.Menu()
724  menu.Append(self.popupID1, _("Delete selected"))
725  menu.Append(self.popupID2, _("Delete all"))
726  if self.GetFirstSelected() == -1:
727  menu.Enable(self.popupID1, False)
728  menu.Enable(self.popupID2, False)
729 
730  menu.AppendSeparator()
731  menu.Append(self.popupID3, _("Reload"))
732 
733  self.PopupMenu(menu)
734  menu.Destroy()
735 
737  def __init__(self, parent, columns, disablePopup = False, **kwargs):
738  """!List of model actions"""
739  self.disablePopup = disablePopup
740 
741  ModelListCtrl.__init__(self, parent, columns, **kwargs)
742  self.SetColumnWidth(1, 100)
743  self.SetColumnWidth(2, 65)
744 
745  def GetListCtrl(self):
746  """!Used by ColumnSorterMixin"""
747  return self
748 
749  def GetData(self):
750  """!Get list data"""
751  return self.itemDataMap
752 
753  def Populate(self, data):
754  """!Populate the list"""
755  self.itemDataMap = dict()
756 
757  if self.shape:
758  if isinstance(self.shape, ModelCondition):
759  if self.GetName() == 'ElseBlockList':
760  shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['else'])
761  else:
762  shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['if'])
763  else:
764  shapeItems = map(lambda x: x.GetId(), self.shape.GetItems())
765  else:
766  shapeItems = list()
767 
768  i = 0
769  if len(self.columns) == 3: # ItemCheckList
770  checked = list()
771  for action in data:
772  if isinstance(action, ModelData) or \
773  action == self.shape:
774  continue
775 
776  if len(self.columns) == 3:
777  self.itemDataMap[i] = [str(action.GetId()),
778  action.GetName(),
779  action.GetLog()]
780  aId = action.GetBlockId()
781  if action.GetId() in shapeItems:
782  checked.append(aId)
783  else:
784  checked.append(None)
785  else:
786  bId = action.GetBlockId()
787  if not bId:
788  bId = ''
789  self.itemDataMap[i] = [str(action.GetId()),
790  action.GetName(),
791  ','.join(map(str, bId)),
792  action.GetLog()]
793 
794  i += 1
795 
796  self.itemCount = len(self.itemDataMap.keys())
797  self.DeleteAllItems()
798  i = 0
799  if len(self.columns) == 3:
800  for aid, name, desc in self.itemDataMap.itervalues():
801  index = self.InsertStringItem(sys.maxint, aid)
802  self.SetStringItem(index, 0, aid)
803  self.SetStringItem(index, 1, name)
804  self.SetStringItem(index, 2, desc)
805  self.SetItemData(index, i)
806  if checked[i]:
807  self.CheckItem(index, True)
808  i += 1
809  else:
810  for aid, name, inloop, desc in self.itemDataMap.itervalues():
811  index = self.InsertStringItem(sys.maxint, aid)
812  self.SetStringItem(index, 0, aid)
813  self.SetStringItem(index, 1, name)
814  self.SetStringItem(index, 2, inloop)
815  self.SetStringItem(index, 3, desc)
816  self.SetItemData(index, i)
817  i += 1
818 
819  def OnRemove(self, event):
820  """!Remove selected action(s) from the model"""
821  model = self.frame.GetModel()
822  canvas = self.frame.GetCanvas()
823 
824  item = self.GetFirstSelected()
825  while item != -1:
826  self.DeleteItem(item)
827  del self.itemDataMap[item]
828 
829  aId = self.GetItem(item, 0).GetText()
830  action = model.GetItem(int(aId))
831  if not action:
832  item = self.GetFirstSelected()
833  continue
834 
835  model.RemoveItem(action)
836  canvas.GetDiagram().RemoveShape(action)
837  self.frame.ModelChanged()
838 
839  item = self.GetFirstSelected()
840 
841  canvas.Refresh()
842 
843  event.Skip()
844 
845  def OnRemoveAll(self, event):
846  """!Remove all variable(s) from the model"""
847  deleteDialog = wx.MessageBox(parent=self,
848  message=_("Selected data records (%d) will permanently deleted "
849  "from table. Do you want to delete them?") % \
850  (len(self.listOfSQLStatements)),
851  caption=_("Delete records"),
852  style=wx.YES_NO | wx.CENTRE)
853  if deleteDialog != wx.YES:
854  return False
855 
856  self.DeleteAllItems()
857  self.itemDataMap = dict()
858 
859  self.parent.UpdateModelVariables()
860 
861  def OnEndEdit(self, event):
862  """!Finish editing of item"""
863  itemIndex = event.GetIndex()
864  columnIndex = event.GetColumn()
865 
866  self.itemDataMap[itemIndex][columnIndex] = event.GetText()
867 
868  aId = int(self.GetItem(itemIndex, 0).GetText())
869  action = self.frame.GetModel().GetItem(aId)
870  if not action:
871  event.Veto()
872  if columnIndex == 0:
873  action.SetId(int(event.GetText()))
874 
875  self.frame.ModelChanged()
876 
877  def OnReload(self, event = None):
878  """!Reload list of actions"""
879  self.Populate(self.frame.GetModel().GetItems())
880 
881  def OnRightUp(self, event):
882  """!Mouse right button up"""
883  if self.disablePopup:
884  return
885 
886  if not hasattr(self, "popupID1"):
887  self.popupID1 = wx.NewId()
888  self.popupID2 = wx.NewId()
889  self.popupID3 = wx.NewId()
890  self.popupID4 = wx.NewId()
891  self.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID1)
892  self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2)
893  self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
894  self.Bind(wx.EVT_MENU, self.OnNormalize, id = self.popupID4)
895 
896  # generate popup-menu
897  menu = wx.Menu()
898  menu.Append(self.popupID1, _("Delete selected"))
899  menu.Append(self.popupID2, _("Delete all"))
900  if self.GetFirstSelected() == -1:
901  menu.Enable(self.popupID1, False)
902  menu.Enable(self.popupID2, False)
903 
904  menu.AppendSeparator()
905  menu.Append(self.popupID4, _("Normalize"))
906  menu.Append(self.popupID3, _("Reload"))
907 
908  self.PopupMenu(menu)
909  menu.Destroy()
910 
911  def OnNormalize(self, event):
912  """!Update id of actions"""
913  model = self.frame.GetModel()
914 
915  aId = 1
916  for item in model.GetItems():
917  item.SetId(aId)
918  aId += 1
919 
920  self.OnReload(None)
921  self.frame.GetCanvas().Refresh()
922  self.frame.ModelChanged()
923 
924 class ItemCheckListCtrl(ItemListCtrl, listmix.CheckListCtrlMixin):
925  def __init__(self, parent, shape, columns, window = None, **kwargs):
926  self.parent = parent
927  self.window = window
928 
929  ItemListCtrl.__init__(self, parent, columns, disablePopup = True, **kwargs)
930  listmix.CheckListCtrlMixin.__init__(self)
931  self.SetColumnWidth(0, 50)
932 
933  self.shape = shape
934 
935  def OnBeginEdit(self, event):
936  """!Disable editing"""
937  event.Veto()
938 
939  def OnCheckItem(self, index, flag):
940  """!Item checked/unchecked"""
941  name = self.GetName()
942  if name == 'IfBlockList' and self.window:
943  self.window.OnCheckItemIf(index, flag)
944  elif name == 'ElseBlockList' and self.window:
945  self.window.OnCheckItemElse(index, flag)
946 
947  def GetItems(self):
948  """!Get list of selected actions"""
949  ids = { 'checked' : list(),
950  'unchecked' : list() }
951  for i in range(self.GetItemCount()):
952  iId = int(self.GetItem(i, 0).GetText())
953  if self.IsChecked(i):
954  ids['checked'].append(iId)
955  else:
956  ids['unchecked'].append(iId)
957 
958  return ids
959 
960  def CheckItemById(self, aId, flag):
961  """!Check/uncheck given item by id"""
962  for i in range(self.GetItemCount()):
963  iId = int(self.GetItem(i, 0).GetText())
964  if iId == aId:
965  self.CheckItem(i, flag)
966  break