dialogs.py 35 KB

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