dialogs.py 37 KB

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