dialogs.py 35 KB

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