dialogs.py 35 KB

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