32
32
from core import globalvar
33
33
from core import utils
34
from gui_core.widgets import GNotebook
34
from core.utils import _
35
from gui_core.widgets import SearchModuleWidget, SimpleValidator
35
36
from core.gcmd import GError, EncodeString
36
from gui_core.dialogs import ElementDialog, MapLayersDialog
37
from gui_core.ghelp import SearchModuleWindow
37
from gui_core.dialogs import SimpleDialog, MapLayersDialogForModeler
38
38
from gui_core.prompt import GPromptSTC
39
from gui_core.forms import CmdPanel
40
from gui_core.gselect import Select
39
from gui_core.gselect import Select, ElementSelect
41
40
from gmodeler.model import *
41
from lmgr.menudata import LayerManagerMenuData
43
43
from grass.script import task as gtask
45
class ModelDataDialog(ElementDialog):
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):
45
class ModelDataDialog(SimpleDialog):
46
"""Data item properties dialog"""
47
def __init__(self, parent, shape, title = _("Data properties")):
49
48
self.parent = parent
52
51
label, etype = self._getLabel()
53
ElementDialog.__init__(self, parent, title, label = label, etype = etype)
53
SimpleDialog.__init__(self, parent, title)
55
self.element = Select(parent = self.panel)
55
self.element = Select(parent = self.panel,
56
validator = SimpleValidator(callback = self.ValidatorCallback))
56
57
self.element.SetValue(shape.GetValue())
58
59
self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
59
60
self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
62
self.typeSelect = ElementSelect(parent = self.panel,
63
size = globalvar.DIALOG_GSELECT_SIZE)
64
self.typeSelect.Bind(wx.EVT_CHOICE, self.OnType)
63
66
if shape.GetValue():
64
67
self.btnOK.Enable()
80
83
return label, etype
88
self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
89
label = _("Type of element:")),
90
proportion = 0, flag = wx.ALL, border = 1)
91
self.dataSizer.Add(item = self.typeSelect,
92
proportion = 0, flag = wx.ALL, border = 1)
93
self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
94
label = _("Name of element:")),
95
proportion = 0, flag = wx.ALL, border = 1)
84
96
self.dataSizer.Add(self.element, proportion=0,
85
97
flag=wx.EXPAND | wx.ALL, border=1)
87
99
self.panel.SetSizer(self.sizer)
88
100
self.sizer.Fit(self)
103
"""Get element type"""
106
return self.element.tcp.GetType()
108
def OnType(self, event):
109
"""Select element type"""
110
evalue = self.typeSelect.GetValue(event.GetString())
111
self.element.SetType(evalue)
90
113
def OnOK(self, event):
92
self.shape.SetValue(self.GetElement())
115
self.shape.SetValue(self.element.GetValue())
94
117
elem = self.GetType()
96
119
self.shape.SetPrompt('raster')
120
elif elem == 'vector':
98
121
self.shape.SetPrompt('raster')
100
123
self.parent.canvas.Refresh()
117
140
class ModelSearchDialog(wx.Dialog):
118
def __init__(self, parent, id = wx.ID_ANY, title = _("Add new GRASS module to the model"),
141
def __init__(self, parent, title = _("Add GRASS command to the model"),
119
142
style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
120
"""!Graphical modeler module search window
143
"""Graphical modeler module search window
122
@param parent parent window
124
@param title window title
125
@param kwargs wx.Dialogs' arguments
145
:param parent: parent window
147
:param title: window title
148
:param kwargs: wx.Dialogs' arguments
127
150
self.parent = parent
129
wx.Dialog.__init__(self, parent = parent, id = id, title = title, **kwargs)
152
wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, **kwargs)
130
153
self.SetName("ModelerDialog")
131
self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
154
self.SetIcon(wx.Icon(os.path.join(globalvar.ICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
133
157
self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
135
159
self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
136
160
label=" %s " % _("Command"))
138
self.cmd_prompt = GPromptSTC(parent = self)
139
self.search = SearchModuleWindow(parent = self.panel, cmdPrompt = self.cmd_prompt, showTip = True)
161
self.labelBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
162
label=" %s " % _("Label and comment"))
164
# menu data for search widget and prompt
165
menuModel = LayerManagerMenuData()
167
self.cmd_prompt = GPromptSTC(parent = self, menuModel = menuModel.GetModel())
168
self.cmd_prompt.promptRunCmd.connect(self.OnCommand)
169
self.cmd_prompt.commandSelected.connect(lambda command: self.label.SetValue(command))
170
self.search = SearchModuleWidget(parent = self.panel,
171
model = menuModel.GetModel(),
173
self.search.moduleSelected.connect(lambda name:
174
self.cmd_prompt.SetTextAndFocus(name + ' '))
140
175
wx.CallAfter(self.cmd_prompt.SetFocus)
143
items = self.cmd_prompt.GetCommandItems()
177
self.label = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
178
self.comment = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, style = wx.TE_MULTILINE)
145
180
self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
146
181
self.btnOk = wx.Button(self.panel, wx.ID_OK)
147
182
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
184
self.Bind(wx.EVT_BUTTON, self.OnOk, self.btnOk)
185
self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
156
self.SetSize((500, 275))
189
self.SetSize((500, -1))
158
191
def _layout(self):
159
192
cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL)
160
193
cmdSizer.Add(item = self.cmd_prompt, proportion = 1,
161
194
flag = wx.EXPAND)
195
labelSizer = wx.StaticBoxSizer(self.labelBox, wx.VERTICAL)
196
gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
197
gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
198
label = _("Label:")),
199
flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
200
gridSizer.Add(item = self.label, pos = (0, 1), flag = wx.EXPAND)
201
gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
202
label = _("Comment:")),
203
flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 0))
204
gridSizer.Add(item = self.comment, pos = (1, 1), flag = wx.EXPAND)
205
gridSizer.AddGrowableCol(1)
206
labelSizer.Add(item = gridSizer, proportion = 1, flag = wx.EXPAND)
163
208
btnSizer = wx.StdDialogButtonSizer()
164
209
btnSizer.AddButton(self.btnCancel)
170
215
flag = wx.EXPAND | wx.ALL, border = 3)
171
216
mainSizer.Add(item = cmdSizer, proportion = 1,
172
217
flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3)
218
mainSizer.Add(item = labelSizer, proportion = 1,
219
flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3)
173
220
mainSizer.Add(item = btnSizer, proportion = 0,
174
221
flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
176
223
self.panel.SetSizer(mainSizer)
177
mainSizer.Fit(self.panel)
181
228
def GetPanel(self):
182
"""!Get dialog panel"""
229
"""Get dialog panel"""
183
230
return self.panel
187
233
line = self.cmd_prompt.GetCurLine()[0].strip()
188
234
if len(line) == 0:
192
cmd = utils.split(str(line))
194
cmd = utils.split(utils.EncodeString((line)))
238
cmd = utils.split(str(line))
240
cmd = utils.split(EncodeString((line)))
248
"""Get label and comment"""
249
return self.label.GetValue(), self.comment.GetValue()
198
def OnOk(self, event):
199
"""!Button 'OK' pressed"""
200
self.btnOk.SetFocus()
251
def ValidateCmd(self, cmd):
204
253
GError(parent = self,
205
254
message = _("Command not defined.\n\n"
206
255
"Unable to add new action to the model."))
209
258
if cmd[0] not in globalvar.grassCmd:
210
259
GError(parent = self,
211
260
message = _("'%s' is not a GRASS module.\n\n"
212
261
"Unable to add new action to the model.") % cmd[0])
215
self.EndModal(wx.ID_OK)
217
def OnText(self, event):
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)
265
def OnCommand(self, cmd):
266
"""Command in prompt confirmed"""
267
if self.ValidateCmd(cmd):
269
self.EndModal(wx.ID_OK)
271
def OnOk(self, event):
272
"""Button 'OK' pressed"""
274
if self.ValidateCmd(cmd):
276
self.EndModal(wx.ID_OK)
278
def OnCancel(self, event):
279
"""Cancel pressed, close window"""
239
284
self.search.Reset()
285
self.label.SetValue('')
286
self.comment.SetValue('')
240
287
self.cmd_prompt.OnCmdErase(None)
241
self.btnOk.Enable(False)
242
288
self.cmd_prompt.SetFocus()
244
290
class ModelRelationDialog(wx.Dialog):
245
"""!Relation properties dialog"""
291
"""Relation properties dialog"""
246
292
def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Relation properties"),
247
293
style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
248
294
self.parent = parent
553
599
self.itemListIf.CheckItemById(aId, False)
555
601
def GetItems(self):
557
603
return { 'if' : self.itemListIf.GetItems(),
558
604
'else' : self.itemListElse.GetItems() }
560
606
class ModelListCtrl(wx.ListCtrl,
561
607
listmix.ListCtrlAutoWidthMixin,
562
listmix.TextEditMixin,
563
listmix.ColumnSorterMixin):
564
def __init__(self, parent, columns, id = wx.ID_ANY,
608
listmix.TextEditMixin):
609
def __init__(self, parent, columns, frame, id = wx.ID_ANY, columnsNotEditable = [],
565
610
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"""
611
wx.LC_HRULES | wx.LC_VRULES, **kwargs):
612
"""List of model variables"""
569
613
self.parent = parent
570
614
self.columns = columns
571
615
self.shape = None
573
self.frame = parent.parent
574
except AttributeError:
617
self.columnNotEditable = columnsNotEditable
577
619
wx.ListCtrl.__init__(self, parent, id = id, style = style, **kwargs)
578
620
listmix.ListCtrlAutoWidthMixin.__init__(self)
579
621
listmix.TextEditMixin.__init__(self)
580
listmix.ColumnSorterMixin.__init__(self, 4)
583
624
for col in columns:
584
625
self.InsertColumn(i, col)
595
636
self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
597
638
def OnBeginEdit(self, event):
598
"""!Editing of item started"""
639
"""Editing of item started"""
640
if self.columnNotEditable and event.m_col in self.columnNotEditable:
642
self.SetItemState(event.m_itemIndex,
643
wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
601
647
def OnEndEdit(self, event):
602
"""!Finish editing of item"""
648
"""Finish editing of item"""
651
def GetListCtrl(self):
652
"""Used by ColumnSorterMixin"""
605
655
def OnColClick(self, event):
606
"""!Click on column header (order by)"""
656
"""Click on column header (order by)"""
609
659
class VariableListCtrl(ModelListCtrl):
610
660
def __init__(self, parent, columns, **kwargs):
611
"""!List of model variables"""
661
"""List of model variables"""
612
662
ModelListCtrl.__init__(self, parent, columns, **kwargs)
614
664
self.SetColumnWidth(2, 200) # default value
616
def GetListCtrl(self):
617
"""!Used by ColumnSorterMixin"""
620
666
def GetData(self):
622
668
return self.itemDataMap
624
670
def Populate(self, data):
625
"""!Populate the list"""
671
"""Populate the list"""
626
672
self.itemDataMap = dict()
628
674
for name, values in data.iteritems():
736
782
class ItemListCtrl(ModelListCtrl):
737
def __init__(self, parent, columns, disablePopup = False, **kwargs):
738
"""!List of model actions"""
783
def __init__(self, parent, columns, frame, disablePopup = False, **kwargs):
784
"""List of model actions"""
739
785
self.disablePopup = disablePopup
741
ModelListCtrl.__init__(self, parent, columns, **kwargs)
742
self.SetColumnWidth(1, 100)
787
ModelListCtrl.__init__(self, parent, columns, frame, **kwargs)
788
self.itemIdMap = list()
790
self.SetColumnWidth(0, 100)
791
self.SetColumnWidth(1, 75)
743
792
self.SetColumnWidth(2, 65)
745
def GetListCtrl(self):
746
"""!Used by ColumnSorterMixin"""
749
794
def GetData(self):
751
796
return self.itemDataMap
753
798
def Populate(self, data):
754
"""!Populate the list"""
799
"""Populate the list"""
755
800
self.itemDataMap = dict()
801
self.itemIdMap = list()
804
items = self.frame.GetModel().GetItems(objType=ModelAction)
758
805
if isinstance(self.shape, ModelCondition):
759
if self.GetName() == 'ElseBlockList':
760
shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['else'])
806
if self.GetLabel() == 'ElseBlockList':
807
shapeItems = map(lambda x: x.GetId(), self.shape.GetItems(items)['else'])
762
shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['if'])
809
shapeItems = map(lambda x: x.GetId(), self.shape.GetItems(items)['if'])
764
shapeItems = map(lambda x: x.GetId(), self.shape.GetItems())
811
shapeItems = map(lambda x: x.GetId(), self.shape.GetItems(items))
766
813
shapeItems = list()
769
if len(self.columns) == 3: # ItemCheckList
816
if len(self.columns) == 2: # ItemCheckList
771
818
for action in data:
772
819
if isinstance(action, ModelData) or \
773
820
action == self.shape:
776
if len(self.columns) == 3:
777
self.itemDataMap[i] = [str(action.GetId()),
823
self.itemIdMap.append(action.GetId())
825
if len(self.columns) == 2:
826
self.itemDataMap[i] = [action.GetLabel(),
780
828
aId = action.GetBlockId()
781
829
if action.GetId() in shapeItems:
796
845
self.itemCount = len(self.itemDataMap.keys())
797
846
self.DeleteAllItems()
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)
848
if len(self.columns) == 2:
849
for name, desc in self.itemDataMap.itervalues():
850
index = self.InsertStringItem(sys.maxint, str(i))
851
self.SetStringItem(index, 0, name)
852
self.SetStringItem(index, 1, desc)
805
853
self.SetItemData(index, i)
807
855
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)
858
for name, inloop, desc in self.itemDataMap.itervalues():
859
index = self.InsertStringItem(sys.maxint, str(i))
860
self.SetStringItem(index, 0, name)
861
self.SetStringItem(index, 1, inloop)
862
self.SetStringItem(index, 2, desc)
816
863
self.SetItemData(index, i)
819
866
def OnRemove(self, event):
820
"""!Remove selected action(s) from the model"""
867
"""Remove selected action(s) from the model"""
821
868
model = self.frame.GetModel()
822
869
canvas = self.frame.GetCanvas()
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:
856
self.DeleteAllItems()
857
self.itemDataMap = dict()
859
self.parent.UpdateModelVariables()
861
890
def OnEndEdit(self, event):
862
"""!Finish editing of item"""
891
"""Finish editing of item"""
863
892
itemIndex = event.GetIndex()
864
893
columnIndex = event.GetColumn()
866
894
self.itemDataMap[itemIndex][columnIndex] = event.GetText()
868
aId = int(self.GetItem(itemIndex, 0).GetText())
869
action = self.frame.GetModel().GetItem(aId)
895
action = self.frame.GetModel().GetItem(itemIndex + 1)
873
action.SetId(int(event.GetText()))
900
action.SetLabel(label = event.GetText())
875
901
self.frame.ModelChanged()
877
903
def OnReload(self, event = None):
878
"""!Reload list of actions"""
879
self.Populate(self.frame.GetModel().GetItems())
904
"""Reload list of actions"""
905
self.Populate(self.frame.GetModel().GetItems(objType=ModelAction))
881
907
def OnRightUp(self, event):
882
"""!Mouse right button up"""
908
"""Mouse right button up"""
883
909
if self.disablePopup:
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)
912
if not hasattr(self, "popupId"):
913
self.popupID = dict()
914
self.popupID['remove'] = wx.NewId()
915
self.popupID['reload'] = wx.NewId()
916
self.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID['remove'])
917
self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID['reload'])
896
919
# generate popup-menu
898
menu.Append(self.popupID1, _("Delete selected"))
899
menu.Append(self.popupID2, _("Delete all"))
921
menu.Append(self.popupID['remove'], _("Delete selected"))
900
922
if self.GetFirstSelected() == -1:
901
menu.Enable(self.popupID1, False)
902
menu.Enable(self.popupID2, False)
923
menu.Enable(self.popupID['remove'], False)
904
924
menu.AppendSeparator()
905
menu.Append(self.popupID4, _("Normalize"))
906
menu.Append(self.popupID3, _("Reload"))
925
menu.Append(self.popupID['reload'], _("Reload"))
908
927
self.PopupMenu(menu)
911
def OnNormalize(self, event):
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()
930
def MoveItems(self, items, up):
931
"""Move items in the list
933
:param items: list of items to move
934
:param up: True to move up otherwise down
939
if items[0] == 0 and up:
944
if items[-1] == len(self.itemDataMap.keys())-1 and not up:
949
model = self.frame.GetModel()
950
modelActions = model.GetItems(objType=ModelAction)
952
itemsToSelect = list()
958
itemsToSelect.append(idx)
959
idxList[model.GetItemIndex(modelActions[i])] = model.GetItemIndex(modelActions[idx])
961
# reorganize model items
962
model.ReorderItems(idxList)
964
self.Populate(model.GetItems(objType=ModelAction))
966
# re-selected originaly selected item
967
for item in itemsToSelect:
968
self.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
924
970
class ItemCheckListCtrl(ItemListCtrl, listmix.CheckListCtrlMixin):
925
def __init__(self, parent, shape, columns, window = None, **kwargs):
971
def __init__(self, parent, shape, columns, frame, **kwargs):
926
972
self.parent = parent
929
ItemListCtrl.__init__(self, parent, columns, disablePopup = True, **kwargs)
975
ItemListCtrl.__init__(self, parent, columns, frame,
976
disablePopup = True, **kwargs)
930
977
listmix.CheckListCtrlMixin.__init__(self)
931
self.SetColumnWidth(0, 50)
978
self.SetColumnWidth(0, 100)
933
980
self.shape = shape
935
982
def OnBeginEdit(self, event):
936
"""!Disable editing"""
983
"""Disable editing"""
939
986
def OnCheckItem(self, index, flag):
940
"""!Item checked/unchecked"""
941
name = self.GetName()
987
"""Item checked/unchecked"""
988
name = self.GetLabel()
942
989
if name == 'IfBlockList' and self.window:
943
990
self.window.OnCheckItemIf(index, flag)
944
991
elif name == 'ElseBlockList' and self.window:
945
992
self.window.OnCheckItemElse(index, flag)
947
994
def GetItems(self):
948
"""!Get list of selected actions"""
995
"""Get list of selected actions"""
949
996
ids = { 'checked' : list(),
950
997
'unchecked' : list() }
999
# action ids start at 1
951
1000
for i in range(self.GetItemCount()):
952
iId = int(self.GetItem(i, 0).GetText())
953
1001
if self.IsChecked(i):
954
ids['checked'].append(iId)
1002
ids['checked'].append(self.itemIdMap[i])
956
ids['unchecked'].append(iId)
1004
ids['unchecked'].append(self.itemIdMap[i])
960
1008
def CheckItemById(self, aId, flag):
961
"""!Check/uncheck given item by id"""
1009
"""Check/uncheck given item by id"""
962
1010
for i in range(self.GetItemCount()):
963
1011
iId = int(self.GetItem(i, 0).GetText())