2 @package gmodeler.dialogs
4 @brief wxGUI Graphical Modeler - dialogs
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
18 (C) 2010-2011 by the GRASS Development Team
20 This program is free software under the GNU General Public License
21 (>=v2). Read the file COPYING that comes with GRASS for details.
23 @author Martin Landa <landa.martin gmail.com>
30 import wx.lib.mixins.listctrl
as listmix
32 from core
import globalvar
33 from core
import utils
35 from core.gcmd import GError, EncodeString
39 from gui_core.forms
import CmdPanel
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):
53 ElementDialog.__init__(self, parent, title, label = label, etype = etype)
56 self.element.SetValue(shape.GetValue())
58 self.Bind(wx.EVT_BUTTON, self.
OnOK, self.
btnOK)
67 self.SetMinSize(self.GetSize())
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:')
78 label = _(
'Name of element:')
84 self.dataSizer.Add(self.
element, proportion=0,
85 flag=wx.EXPAND | wx.ALL, border=1)
87 self.panel.SetSizer(self.
sizer)
96 self.shape.SetPrompt(
'raster')
98 self.shape.SetPrompt(
'raster')
100 self.parent.canvas.Refresh()
101 self.parent.SetStatusText(
'', 0)
102 self.shape.SetPropDialog(
None)
110 """!Cancel pressed"""
111 self.shape.SetPropDialog(
None)
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
122 @param parent parent window
124 @param title window title
125 @param kwargs wx.Dialogs' arguments
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))
133 self.
panel = wx.Panel(parent = self, id = wx.ID_ANY)
136 label=
" %s " % _(
"Command"))
140 wx.CallAfter(self.cmd_prompt.SetFocus)
143 items = self.cmd_prompt.GetCommandItems()
147 self.btnOk.SetDefault()
148 self.btnOk.Enable(
False)
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)
156 self.SetSize((500, 275))
159 cmdSizer = wx.StaticBoxSizer(self.
cmdBox, wx.VERTICAL)
160 cmdSizer.Add(item = self.
cmd_prompt, proportion = 1,
163 btnSizer = wx.StdDialogButtonSizer()
165 btnSizer.AddButton(self.
btnOk)
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)
176 self.panel.SetSizer(mainSizer)
177 mainSizer.Fit(self.
panel)
182 """!Get dialog panel"""
187 line = self.cmd_prompt.GetCurLine()[0].strip()
199 """!Button 'OK' pressed"""
200 self.btnOk.SetFocus()
204 GError(parent = self,
205 message = _(
"Command not defined.\n\n"
206 "Unable to add new action to the model."))
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])
215 self.EndModal(wx.ID_OK)
218 """!Text in prompt changed"""
219 if self.cmd_prompt.AutoCompActive():
223 if isinstance(event, wx.KeyEvent):
224 entry = self.cmd_prompt.GetTextLeft()
225 elif isinstance(event, wx.stc.StyledTextEvent):
226 entry = event.GetText()
228 entry = event.GetString()
233 self.btnOk.Enable(
False)
240 self.cmd_prompt.OnCmdErase(
None)
241 self.btnOk.Enable(
False)
242 self.cmd_prompt.SetFocus()
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):
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))
260 self.
panel = wx.Panel(parent = self, id = wx.ID_ANY)
263 label =
" %s " % _(
"From"))
264 self.
toBox = wx.StaticBox(parent = self.
panel, id = wx.ID_ANY,
265 label =
" %s " % _(
"To"))
268 style = wx.CB_READONLY,
270 self.option.Bind(wx.EVT_COMBOBOX, self.
OnOption)
274 self.btnOk.Enable(
False)
279 mainSizer = wx.BoxSizer(wx.VERTICAL)
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)
286 btnSizer = wx.StdDialogButtonSizer()
288 btnSizer.AddButton(self.
btnOk)
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)
298 self.panel.SetSizer(mainSizer)
299 mainSizer.Fit(self.
panel)
302 self.SetSize(self.GetBestSize())
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,
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:")),
315 gridSizer.Add(item = wx.StaticText(parent = self.
panel, id = wx.ID_ANY,
316 label = shape.GetName()),
318 gridSizer.Add(item = wx.StaticText(parent = self.
panel, id = wx.ID_ANY,
319 label = _(
"Option:")),
320 flag = wx.ALIGN_CENTER_VERTICAL,
322 gridSizer.Add(item = self.
option,
324 sizer.Add(item = gridSizer,
325 proportion = 1, flag = wx.EXPAND | wx.ALL,
328 def _getOptions(self):
329 """!Get relevant options"""
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."))
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."))
345 prompt = fromShape.GetPrompt()
346 task = toShape.GetTask()
347 for p
in task.get_options()[
'params']:
348 if p.get(
'prompt',
'') == prompt
and \
350 items.append(p[
'name'])
353 GError(parent = self.
parent,
354 message = _(
"No relevant option found.\n"
355 "Unable to add relation."))
359 """!Get selected option"""
360 return self.option.GetStringSelection()
363 """!Check if relation is valid"""
368 if event.GetString():
371 self.btnOk.Enable(
False)
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):
380 wx.Dialog.__init__(self, parent, id, title = title, style = style, **kwargs)
382 self.
panel = wx.Panel(parent = self, id = wx.ID_ANY)
385 label=
" %s " % _(
"Condition"))
387 value = shape.GetText())
391 columns = [_(
"ID"), _(
"Name"),
394 self.itemList.Populate(self.parent.GetModel().GetItems())
398 self.btnOk.SetDefault()
401 """!Do layout (virtual method)"""
405 """!Get loop condition"""
406 return self.condText.GetValue()
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)
416 label=
" %s " % _(
"List of items in loop"))
420 self.btnSeries.SetToolTipString(_(
"Define map series as condition for the loop"))
421 self.btnSeries.Bind(wx.EVT_BUTTON, self.
OnSeries)
424 self.SetMinSize(self.GetSize())
425 self.SetSize((500, 400))
429 sizer = wx.BoxSizer(wx.VERTICAL)
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,
437 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
438 listSizer.Add(item = self.
itemList, proportion = 1,
439 flag = wx.EXPAND | wx.ALL, border = 3)
441 btnSizer = wx.StdDialogButtonSizer()
443 btnSizer.AddButton(self.
btnOk)
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)
453 self.panel.SetSizer(sizer)
454 sizer.Fit(self.
panel)
459 """!Get list of selected actions"""
460 return self.itemList.GetItems()
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:
469 cond = dialog.GetDSeries()
471 cond =
'map in %s' % map(
lambda x: str(x), dialog.GetMapLayers())
473 self.condText.SetValue(cond)
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)
485 label=
" %s " % _(
"List of items in 'if' block"))
487 self.itemListIf.SetName(
'IfBlockList')
490 label=
" %s " % _(
"List of items in 'else' block"))
493 columns = [_(
"ID"), _(
"Name"),
496 self.itemListElse.SetName(
'ElseBlockList')
497 self.itemListElse.Populate(self.parent.GetModel().
GetItems())
500 self.SetMinSize(self.GetSize())
501 self.SetSize((500, 400))
505 sizer = wx.BoxSizer(wx.VERTICAL)
507 condSizer = wx.StaticBoxSizer(self.
condBox, wx.VERTICAL)
508 condSizer.Add(item = self.
condText, proportion = 1,
511 listIfSizer = wx.StaticBoxSizer(self.
listBoxIf, wx.VERTICAL)
512 listIfSizer.Add(item = self.
itemListIf, proportion = 1,
514 listElseSizer = wx.StaticBoxSizer(self.
listBoxElse, wx.VERTICAL)
515 listElseSizer.Add(item = self.
itemListElse, proportion = 1,
518 btnSizer = wx.StdDialogButtonSizer()
520 btnSizer.AddButton(self.
btnOk)
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)
532 self.panel.SetSizer(sizer)
533 sizer.Fit(self.
panel)
538 """!Item in if-block checked/unchecked"""
542 aId = int(self.itemListIf.GetItem(index, 0).GetText())
543 if aId
in self.itemListElse.GetItems()[
'checked']:
544 self.itemListElse.CheckItemById(aId,
False)
547 """!Item in else-block checked/unchecked"""
551 aId = int(self.itemListElse.GetItem(index, 0).GetText())
552 if aId
in self.itemListIf.GetItems()[
'checked']:
553 self.itemListIf.CheckItemById(aId,
False)
557 return {
'if' : self.itemListIf.GetItems(),
558 'else' : self.itemListElse.GetItems() }
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"""
574 except AttributeError:
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)
584 self.InsertColumn(i, col)
585 self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
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)
595 self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
598 """!Editing of item started"""
602 """!Finish editing of item"""
606 """!Click on column header (order by)"""
611 """!List of model variables"""
612 ModelListCtrl.__init__(self, parent, columns, **kwargs)
614 self.SetColumnWidth(2, 200)
617 """!Used by ColumnSorterMixin"""
625 """!Populate the list"""
628 for name, values
in data.iteritems():
630 values.get(
'value',
''),
631 values.get(
'description',
'')]
635 self.DeleteAllItems()
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)
646 def Append(self, name, vtype, value, desc):
647 """!Append new item to the list
649 @return None on success
652 for iname, ivtype, ivalue, idesc
in self.itemDataMap.itervalues():
654 return _(
"Variable <%s> already exists in the model. "
655 "Adding variable failed.") % name
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)
670 """!Remove selected variable(s) from the model"""
671 item = self.GetFirstSelected()
673 self.DeleteItem(item)
675 item = self.GetFirstSelected()
676 self.parent.UpdateModelVariables()
681 """!Remove all variable(s) from the model"""
682 dlg = wx.MessageBox(parent=self,
683 message=_(
"Do you want to delete all variables from "
685 caption=_(
"Delete variables"),
686 style=wx.YES_NO | wx.CENTRE)
690 self.DeleteAllItems()
693 self.parent.UpdateModelVariables()
696 """!Finish editing of item"""
697 itemIndex = event.GetIndex()
698 columnIndex = event.GetColumn()
699 nameOld = self.GetItem(itemIndex, 0).GetText()
704 self.
itemDataMap[itemIndex][columnIndex] = event.GetText()
706 self.parent.UpdateModelVariables()
709 """!Reload list of variables"""
710 self.
Populate(self.parent.parent.GetModel().GetVariables())
713 """!Mouse right button up"""
714 if not hasattr(self,
"popupID1"):
724 menu.Append(self.
popupID1, _(
"Delete selected"))
725 menu.Append(self.
popupID2, _(
"Delete all"))
726 if self.GetFirstSelected() == -1:
730 menu.AppendSeparator()
731 menu.Append(self.
popupID3, _(
"Reload"))
737 def __init__(self, parent, columns, disablePopup = False, **kwargs):
738 """!List of model actions"""
741 ModelListCtrl.__init__(self, parent, columns, **kwargs)
742 self.SetColumnWidth(1, 100)
743 self.SetColumnWidth(2, 65)
746 """!Used by ColumnSorterMixin"""
754 """!Populate the list"""
758 if isinstance(self.
shape, ModelCondition):
759 if self.GetName() ==
'ElseBlockList':
760 shapeItems = map(
lambda x: x.GetId(), self.shape.GetItems()[
'else'])
762 shapeItems = map(
lambda x: x.GetId(), self.shape.GetItems()[
'if'])
764 shapeItems = map(
lambda x: x.GetId(), self.shape.GetItems())
772 if isinstance(action, ModelData)
or \
773 action == self.
shape:
780 aId = action.GetBlockId()
781 if action.GetId()
in shapeItems:
786 bId = action.GetBlockId()
791 ','.join(map(str, bId)),
797 self.DeleteAllItems()
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)
807 self.CheckItem(index,
True)
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)
820 """!Remove selected action(s) from the model"""
821 model = self.frame.GetModel()
822 canvas = self.frame.GetCanvas()
824 item = self.GetFirstSelected()
826 self.DeleteItem(item)
829 aId = self.GetItem(item, 0).GetText()
830 action = model.GetItem(int(aId))
832 item = self.GetFirstSelected()
835 model.RemoveItem(action)
836 canvas.GetDiagram().RemoveShape(action)
837 self.frame.ModelChanged()
839 item = self.GetFirstSelected()
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:
856 self.DeleteAllItems()
859 self.parent.UpdateModelVariables()
862 """!Finish editing of item"""
863 itemIndex = event.GetIndex()
864 columnIndex = event.GetColumn()
866 self.
itemDataMap[itemIndex][columnIndex] = event.GetText()
868 aId = int(self.GetItem(itemIndex, 0).GetText())
869 action = self.frame.GetModel().GetItem(aId)
873 action.SetId(int(event.GetText()))
875 self.frame.ModelChanged()
878 """!Reload list of actions"""
879 self.
Populate(self.frame.GetModel().GetItems())
882 """!Mouse right button up"""
886 if not hasattr(self,
"popupID1"):
898 menu.Append(self.
popupID1, _(
"Delete selected"))
899 menu.Append(self.
popupID2, _(
"Delete all"))
900 if self.GetFirstSelected() == -1:
904 menu.AppendSeparator()
905 menu.Append(self.
popupID4, _(
"Normalize"))
906 menu.Append(self.
popupID3, _(
"Reload"))
912 """!Update id of actions"""
913 model = self.frame.GetModel()
916 for item
in model.GetItems():
921 self.frame.GetCanvas().Refresh()
922 self.frame.ModelChanged()
925 def __init__(self, parent, shape, columns, window = None, **kwargs):
929 ItemListCtrl.__init__(self, parent, columns, disablePopup =
True, **kwargs)
930 listmix.CheckListCtrlMixin.__init__(self)
931 self.SetColumnWidth(0, 50)
936 """!Disable editing"""
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)
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)
956 ids[
'unchecked'].append(iId)
961 """!Check/uncheck given item by id"""
962 for i
in range(self.GetItemCount()):
963 iId = int(self.GetItem(i, 0).GetText())
965 self.CheckItem(i, flag)