model.py 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137
  1. """!
  2. @package gmodeler.model
  3. @brief wxGUI Graphical Modeler (base classes & read/write)
  4. Classes:
  5. - model::Model
  6. - model::ModelObject
  7. - model::ModelAction
  8. - model::ModelData
  9. - model::ModelRelation
  10. - model::ModelItem
  11. - model::ModelLoop
  12. - model::ModelCondition
  13. - model::ProcessModelFile
  14. - model::WriteModelFile
  15. - model::WritePythonFile
  16. (C) 2010-2011 by the GRASS Development Team
  17. This program is free software under the GNU General Public License
  18. (>=v2). Read the file COPYING that comes with GRASS for details.
  19. @author Martin Landa <landa.martin gmail.com>
  20. """
  21. import os
  22. import getpass
  23. import copy
  24. import re
  25. import mimetypes
  26. import time
  27. try:
  28. import xml.etree.ElementTree as etree
  29. except ImportError:
  30. import elementtree.ElementTree as etree # Python <= 2.4
  31. import wx
  32. from wx.lib import ogl
  33. from core.globalvar import ETCWXDIR
  34. from core import utils
  35. from core.gcmd import GMessage, GException, GError, RunCommand, EncodeString, GWarning
  36. from gmodeler.dialogs import ModelParamDialog
  37. from core.settings import UserSettings
  38. from gui_core.forms import GUI
  39. from grass.script import core as grass
  40. from grass.script import task as gtask
  41. class Model(object):
  42. """!Class representing the model"""
  43. def __init__(self, canvas = None):
  44. self.items = list() # list of actions/loops/...
  45. # model properties
  46. self.properties = { 'name' : _("model"),
  47. 'description' : _("Script generated by wxGUI Graphical Modeler."),
  48. 'author' : getpass.getuser() }
  49. # model variables
  50. self.variables = dict()
  51. self.variablesParams = dict()
  52. self.canvas = canvas
  53. def GetCanvas(self):
  54. """!Get canvas or None"""
  55. return self.canvas
  56. def GetItems(self, objType = None):
  57. """!Get list of model items
  58. @param objType Object type to filter model objects
  59. """
  60. if not objType:
  61. return self.items
  62. result = list()
  63. for item in self.items:
  64. if isinstance(item, objType):
  65. result.append(item)
  66. return result
  67. def GetItem(self, aId):
  68. """!Get item of given id
  69. @param aId item id
  70. @return Model* instance
  71. @return None if no item found
  72. """
  73. ilist = self.GetItems()
  74. for item in ilist:
  75. if item.GetId() == aId:
  76. return item
  77. return None
  78. def GetNumItems(self, actionOnly = False):
  79. """!Get number of items"""
  80. if actionOnly:
  81. return len(self.GetItems(objType = ModelAction))
  82. return len(self.GetItems())
  83. def GetNextId(self):
  84. """!Get next id (data ignored)
  85. @return next id to be used (default: 1)
  86. """
  87. if len(self.items) < 1:
  88. return 1
  89. currId = self.items[-1].GetId()
  90. if currId > 0:
  91. return currId + 1
  92. return 1
  93. def GetProperties(self):
  94. """!Get model properties"""
  95. return self.properties
  96. def GetVariables(self, params = False):
  97. """!Get model variables"""
  98. if params:
  99. return self.variablesParams
  100. return self.variables
  101. def SetVariables(self, data):
  102. """!Set model variables"""
  103. self.variables = data
  104. def Reset(self):
  105. """!Reset model"""
  106. self.items = list()
  107. def RemoveItem(self, item):
  108. """!Remove item from model
  109. @return list of related items to remove/update
  110. """
  111. relList = list()
  112. upList = list()
  113. if not isinstance(item, ModelData):
  114. self.items.remove(item)
  115. if isinstance(item, ModelAction):
  116. for rel in item.GetRelations():
  117. relList.append(rel)
  118. data = rel.GetData()
  119. if len(data.GetRelations()) < 2:
  120. relList.append(data)
  121. else:
  122. upList.append(data)
  123. elif isinstance(item, ModelData):
  124. for rel in item.GetRelations():
  125. relList.append(rel)
  126. if rel.GetFrom() == self:
  127. relList.append(rel.GetTo())
  128. else:
  129. relList.append(rel.GetFrom())
  130. elif isinstance(item, ModelLoop):
  131. for rel in item.GetRelations():
  132. relList.append(rel)
  133. for action in self.GetItems():
  134. action.UnSetBlock(item)
  135. return relList, upList
  136. def FindAction(self, aId):
  137. """!Find action by id"""
  138. alist = self.GetItems(objType = ModelAction)
  139. for action in alist:
  140. if action.GetId() == aId:
  141. return action
  142. return None
  143. def GetData(self):
  144. """!Get list of data items"""
  145. result = list()
  146. dataItems = self.GetItems(objType = ModelData)
  147. for action in self.GetItems(objType = ModelAction):
  148. for rel in action.GetRelations():
  149. dataItem = rel.GetData()
  150. if dataItem not in result:
  151. result.append(dataItem)
  152. if dataItem in dataItems:
  153. dataItems.remove(dataItem)
  154. # standalone data
  155. if dataItems:
  156. result += dataItems
  157. return result
  158. def FindData(self, value, prompt):
  159. """!Find data item in the model
  160. @param value value
  161. @param prompt prompt
  162. @return ModelData instance
  163. @return None if not found
  164. """
  165. for data in self.GetData():
  166. if data.GetValue() == value and \
  167. data.GetPrompt() == prompt:
  168. return data
  169. return None
  170. def LoadModel(self, filename):
  171. """!Load model definition stored in GRASS Model XML file (gxm)
  172. @todo Validate against DTD
  173. Raise exception on error.
  174. """
  175. dtdFilename = os.path.join(ETCWXDIR, "xml", "grass-gxm.dtd")
  176. # parse workspace file
  177. try:
  178. gxmXml = ProcessModelFile(etree.parse(filename))
  179. except StandardError, e:
  180. raise GException(e)
  181. if self.canvas:
  182. win = self.canvas.parent
  183. if gxmXml.pos:
  184. win.SetPosition(gxmXml.pos)
  185. if gxmXml.size:
  186. win.SetSize(gxmXml.size)
  187. # load properties
  188. self.properties = gxmXml.properties
  189. self.variables = gxmXml.variables
  190. # load model.GetActions()
  191. for action in gxmXml.actions:
  192. actionItem = ModelAction(parent = self,
  193. x = action['pos'][0],
  194. y = action['pos'][1],
  195. width = action['size'][0],
  196. height = action['size'][1],
  197. task = action['task'],
  198. id = action['id'])
  199. if action['disabled']:
  200. actionItem.Enable(False)
  201. self.AddItem(actionItem)
  202. actionItem.SetValid(actionItem.GetTask().get_options())
  203. actionItem.GetLog() # substitute variables (-> valid/invalid)
  204. # load data & relations
  205. for data in gxmXml.data:
  206. dataItem = ModelData(parent = self,
  207. x = data['pos'][0],
  208. y = data['pos'][1],
  209. width = data['size'][0],
  210. height = data['size'][1],
  211. prompt = data['prompt'],
  212. value = data['value'])
  213. dataItem.SetIntermediate(data['intermediate'])
  214. for rel in data['rels']:
  215. actionItem = self.FindAction(rel['id'])
  216. if rel['dir'] == 'from':
  217. relation = ModelRelation(parent = self, fromShape = dataItem,
  218. toShape = actionItem, param = rel['name'])
  219. else:
  220. relation = ModelRelation(parent = self, fromShape = actionItem,
  221. toShape = dataItem, param = rel['name'])
  222. relation.SetControlPoints(rel['points'])
  223. actionItem.AddRelation(relation)
  224. dataItem.AddRelation(relation)
  225. if self.canvas:
  226. dataItem.Update()
  227. # load loops
  228. for loop in gxmXml.loops:
  229. loopItem = ModelLoop(parent = self,
  230. x = loop['pos'][0],
  231. y = loop['pos'][1],
  232. width = loop['size'][0],
  233. height = loop['size'][1],
  234. text = loop['text'],
  235. id = loop['id'])
  236. self.AddItem(loopItem)
  237. # load conditions
  238. for condition in gxmXml.conditions:
  239. conditionItem = ModelCondition(parent = self,
  240. x = condition['pos'][0],
  241. y = condition['pos'][1],
  242. width = condition['size'][0],
  243. height = condition['size'][1],
  244. text = condition['text'],
  245. id = condition['id'])
  246. self.AddItem(conditionItem)
  247. # define loops & if/else items
  248. for loop in gxmXml.loops:
  249. alist = list()
  250. for aId in loop['items']:
  251. action = self.GetItem(aId)
  252. alist.append(action)
  253. loopItem = self.GetItem(loop['id'])
  254. loopItem.SetItems(alist)
  255. for action in loopItem.GetItems():
  256. action.SetBlock(loopItem)
  257. for condition in gxmXml.conditions:
  258. conditionItem = self.GetItem(condition['id'])
  259. for b in condition['items'].keys():
  260. alist = list()
  261. for aId in condition['items'][b]:
  262. action = self.GetItem(aId)
  263. alist.append(action)
  264. conditionItem.SetItems(alist, branch = b)
  265. items = conditionItem.GetItems()
  266. for b in items.keys():
  267. for action in items[b]:
  268. action.SetBlock(conditionItem)
  269. def AddItem(self, newItem):
  270. """!Add item to the list"""
  271. iId = newItem.GetId()
  272. i = 0
  273. for item in self.items:
  274. if item.GetId() > iId:
  275. self.items.insert(i, newItem)
  276. return
  277. i += 1
  278. self.items.append(newItem)
  279. def IsValid(self):
  280. """Return True if model is valid"""
  281. if self.Validate():
  282. return False
  283. return True
  284. def Validate(self):
  285. """!Validate model, return None if model is valid otherwise
  286. error string"""
  287. errList = list()
  288. variables = self.GetVariables().keys()
  289. pattern = re.compile(r'(.*)(%.+\s?)(.*)')
  290. for action in self.GetItems(objType = ModelAction):
  291. cmd = action.GetLog(string = False)
  292. task = GUI(show = None).ParseCommand(cmd = cmd)
  293. errList += map(lambda x: cmd[0] + ': ' + x, task.get_cmd_error())
  294. # check also variables
  295. for opt in cmd[1:]:
  296. if '=' not in opt:
  297. continue
  298. key, value = opt.split('=', 1)
  299. sval = pattern.search(value)
  300. if sval:
  301. var = sval.group(2).strip()[1:] # ignore '%'
  302. if var not in variables:
  303. report = True
  304. for item in filter(lambda x: isinstance(x, ModelLoop), action.GetBlock()):
  305. if var in item.GetText():
  306. report = False
  307. break
  308. if report:
  309. errList.append(_("%s: undefined variable '%s'") % (cmd[0], var))
  310. ### TODO: check variables in file only optionally
  311. ### errList += self._substituteFile(action, checkOnly = True)
  312. return errList
  313. def _substituteFile(self, item, params = None, checkOnly = False):
  314. """!Subsitute variables in command file inputs
  315. @param checkOnly tuble - True to check variable, don't touch files
  316. @return list of undefined variables
  317. """
  318. errList = list()
  319. self.fileInput = dict()
  320. # collect ascii inputs
  321. for p in item.GetParams()['params']:
  322. if p.get('element', '') == 'file' and \
  323. p.get('prompt', '') == 'input' and \
  324. p.get('age', '') == 'old':
  325. filename = p.get('value', p.get('default', ''))
  326. if filename and \
  327. mimetypes.guess_type(filename)[0] == 'text/plain':
  328. self.fileInput[filename] = None
  329. for finput in self.fileInput:
  330. # read lines
  331. fd = open(finput, "r")
  332. try:
  333. data = self.fileInput[finput] = fd.read()
  334. finally:
  335. fd.close()
  336. # substitute variables
  337. write = False
  338. variables = self.GetVariables()
  339. for variable in variables:
  340. pattern = re.compile('%' + variable)
  341. value = ''
  342. if params and 'variables' in params:
  343. for p in params['variables']['params']:
  344. if variable == p.get('name', ''):
  345. if p.get('type', 'string') == 'string':
  346. value = p.get('value', '')
  347. else:
  348. value = str(p.get('value', ''))
  349. break
  350. if not value:
  351. value = variables[variable].get('value', '')
  352. data = pattern.sub(value, data)
  353. if not checkOnly:
  354. write = True
  355. pattern = re.compile(r'(.*)(%.+\s?)(.*)')
  356. sval = pattern.search(data)
  357. if sval:
  358. var = sval.group(2).strip()[1:] # ignore '%'
  359. cmd = item.GetLog(string = False)[0]
  360. errList.append(_("%s: undefined variable '%s'") % (cmd, var))
  361. if not checkOnly:
  362. if write:
  363. fd = open(finput, "w")
  364. try:
  365. fd.write(data)
  366. finally:
  367. fd.close()
  368. else:
  369. self.fileInput[finput] = None
  370. return errList
  371. def OnPrepare(self, item, params):
  372. self._substituteFile(item, params, checkOnly = False)
  373. def RunAction(self, item, params, log, onDone, onPrepare = None, statusbar = None):
  374. """!Run given action
  375. @param item action item
  376. @param params parameters dict
  377. @param log logging window
  378. @param onDone on-done method
  379. @param onPrepare on-prepare method
  380. @param statusbar wx.StatusBar instance or None
  381. """
  382. name = item.GetName()
  383. if name in params:
  384. paramsOrig = item.GetParams(dcopy = True)
  385. item.MergeParams(params[name])
  386. if statusbar:
  387. statusbar.SetStatusText(_('Running model...'), 0)
  388. data = { 'item' : item,
  389. 'params' : copy.deepcopy(params) }
  390. log.RunCmd(command = item.GetLog(string = False, substitute = params),
  391. onDone = onDone, onPrepare = self.OnPrepare, userData = data)
  392. if name in params:
  393. item.SetParams(paramsOrig)
  394. def Run(self, log, onDone, parent = None):
  395. """!Run model
  396. @param log logging window (see goutput.GMConsole)
  397. @param onDone on-done method
  398. @param parent window for messages or None
  399. """
  400. if self.GetNumItems() < 1:
  401. GMessage(parent = parent,
  402. message = _('Model is empty. Nothing to run.'))
  403. return
  404. statusbar = None
  405. if isinstance(parent, wx.Frame):
  406. statusbar = parent.GetStatusBar()
  407. # validation
  408. if statusbar:
  409. statusbar.SetStatusText(_('Validating model...'), 0)
  410. errList = self.Validate()
  411. if statusbar:
  412. statusbar.SetStatusText('', 0)
  413. if errList:
  414. dlg = wx.MessageDialog(parent = parent,
  415. message = _('Model is not valid. Do you want to '
  416. 'run the model anyway?\n\n%s') % '\n'.join(errList),
  417. caption = _("Run model?"),
  418. style = wx.YES_NO | wx.NO_DEFAULT |
  419. wx.ICON_QUESTION | wx.CENTRE)
  420. ret = dlg.ShowModal()
  421. dlg.Destroy()
  422. if ret != wx.ID_YES:
  423. return
  424. # parametrization
  425. params = self.Parameterize()
  426. if params:
  427. dlg = ModelParamDialog(parent = parent,
  428. params = params)
  429. dlg.CenterOnParent()
  430. ret = dlg.ShowModal()
  431. if ret != wx.ID_OK:
  432. dlg.Destroy()
  433. return
  434. err = dlg.GetErrors()
  435. dlg.Destroy()
  436. if err:
  437. GError(parent = parent, message = unicode('\n'.join(err)))
  438. return
  439. err = list()
  440. for key, item in params.iteritems():
  441. for p in item['params']:
  442. if p.get('value', '') == '':
  443. err.append((key, p.get('name', ''), p.get('description', '')))
  444. if err:
  445. GError(parent = parent,
  446. message = _("Variables below not defined:") + \
  447. "\n\n" + unicode('\n'.join(map(lambda x: "%s: %s (%s)" % (x[0], x[1], x[2]), err))))
  448. return
  449. log.cmdThread.SetId(-1)
  450. for item in self.GetItems():
  451. if not item.IsEnabled():
  452. continue
  453. if isinstance(item, ModelAction):
  454. if item.GetBlockId():
  455. continue
  456. self.RunAction(item, params, log, onDone)
  457. elif isinstance(item, ModelLoop):
  458. cond = item.GetText()
  459. # substitute variables in condition
  460. variables = self.GetVariables()
  461. for variable in variables:
  462. pattern = re.compile('%' + variable)
  463. if pattern.search(cond):
  464. value = ''
  465. if params and 'variables' in params:
  466. for p in params['variables']['params']:
  467. if variable == p.get('name', ''):
  468. value = p.get('value', '')
  469. break
  470. if not value:
  471. value = variables[variable].get('value', '')
  472. if not value:
  473. continue
  474. vtype = variables[variable].get('type', 'string')
  475. if vtype == 'string':
  476. value = '"' + value + '"'
  477. cond = pattern.sub(value, cond)
  478. # split condition
  479. condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
  480. pattern = re.compile('%' + condVar)
  481. ### for vars()[condVar] in eval(condText): ?
  482. vlist = list()
  483. if condText[0] == '`' and condText[-1] == '`':
  484. # run command
  485. cmd, dcmd = utils.CmdToTuple(condText[1:-1].split(' '))
  486. ret = RunCommand(cmd,
  487. read = True,
  488. **dcmd)
  489. if ret:
  490. vlist = ret.splitlines()
  491. else:
  492. vlist = eval(condText)
  493. if 'variables' not in params:
  494. params['variables'] = { 'params' : [] }
  495. varDict = { 'name' : condVar, 'value' : '' }
  496. params['variables']['params'].append(varDict)
  497. for var in vlist:
  498. for action in item.GetItems():
  499. if not isinstance(action, ModelAction) or \
  500. not action.IsEnabled():
  501. continue
  502. varDict['value'] = var
  503. self.RunAction(item = action, params = params,
  504. log = log, onDone = onDone)
  505. params['variables']['params'].remove(varDict)
  506. # discard values
  507. if params:
  508. for item in params.itervalues():
  509. for p in item['params']:
  510. p['value'] = ''
  511. def DeleteIntermediateData(self, log):
  512. """!Detele intermediate data"""
  513. rast, vect, rast3d, msg = self.GetIntermediateData()
  514. if rast:
  515. log.RunCmd(['g.remove', 'rast=%s' %','.join(rast)])
  516. if rast3d:
  517. log.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)])
  518. if vect:
  519. log.RunCmd(['g.remove', 'vect=%s' %','.join(vect)])
  520. def GetIntermediateData(self):
  521. """!Get info about intermediate data"""
  522. rast = list()
  523. rast3d = list()
  524. vect = list()
  525. for data in self.GetData():
  526. if not data.IsIntermediate():
  527. continue
  528. name = data.GetValue()
  529. prompt = data.GetPrompt()
  530. if prompt == 'raster':
  531. rast.append(name)
  532. elif prompt == 'vector':
  533. vect.append(name)
  534. elif prompt == 'rast3d':
  535. rast3d.append(name)
  536. msg = ''
  537. if rast:
  538. msg += '\n\n%s: ' % _('Raster maps')
  539. msg += ', '.join(rast)
  540. if rast3d:
  541. msg += '\n\n%s: ' % _('3D raster maps')
  542. msg += ', '.join(rast3d)
  543. if vect:
  544. msg += '\n\n%s: ' % _('Vector maps')
  545. msg += ', '.join(vect)
  546. return rast, vect, rast3d, msg
  547. def Update(self):
  548. """!Update model"""
  549. for item in self.items:
  550. item.Update()
  551. def IsParameterized(self):
  552. """!Return True if model is parameterized"""
  553. if self.Parameterize():
  554. return True
  555. return False
  556. def Parameterize(self):
  557. """!Return parameterized options"""
  558. result = dict()
  559. idx = 0
  560. if self.variables:
  561. params = list()
  562. result["variables"] = { 'flags' : list(),
  563. 'params' : params,
  564. 'idx' : idx }
  565. for name, values in self.variables.iteritems():
  566. gtype = values.get('type', 'string')
  567. if gtype in ('raster', 'vector', 'mapset', 'file'):
  568. gisprompt = True
  569. prompt = gtype
  570. if gtype == 'raster':
  571. element = 'cell'
  572. else:
  573. element = gtype
  574. ptype = 'string'
  575. else:
  576. gisprompt = False
  577. prompt = None
  578. element = None
  579. ptype = gtype
  580. params.append({ 'gisprompt' : gisprompt,
  581. 'multiple' : False,
  582. 'description' : values.get('description', ''),
  583. 'guidependency' : '',
  584. 'default' : '',
  585. 'age' : None,
  586. 'required' : True,
  587. 'value' : values.get('value', ''),
  588. 'label' : '',
  589. 'guisection' : '',
  590. 'key_desc' : '',
  591. 'values' : list(),
  592. 'parameterized' : False,
  593. 'values_desc' : list(),
  594. 'prompt' : prompt,
  595. 'element' : element,
  596. 'type' : ptype,
  597. 'name' : name })
  598. idx += 1
  599. for action in self.GetItems(objType = ModelAction):
  600. if not action.IsEnabled():
  601. continue
  602. name = action.GetName()
  603. params = action.GetParams()
  604. for f in params['flags']:
  605. if f.get('parameterized', False):
  606. if name not in result:
  607. result[name] = { 'flags' : list(),
  608. 'params': list(),
  609. 'idx' : idx }
  610. result[name]['flags'].append(f)
  611. for p in params['params']:
  612. if p.get('parameterized', False):
  613. if name not in result:
  614. result[name] = { 'flags' : list(),
  615. 'params': list(),
  616. 'idx' : idx }
  617. result[name]['params'].append(p)
  618. if name in result:
  619. idx += 1
  620. self.variablesParams = result # record parameters
  621. return result
  622. class ModelObject:
  623. def __init__(self, id = -1):
  624. self.id = id
  625. self.rels = list() # list of ModelRelations
  626. self.isEnabled = True
  627. self.inBlock = list() # list of related loops/conditions
  628. def __del__(self):
  629. pass
  630. def GetId(self):
  631. """!Get id"""
  632. return self.id
  633. def AddRelation(self, rel):
  634. """!Record new relation
  635. """
  636. self.rels.append(rel)
  637. def GetRelations(self, fdir = None):
  638. """!Get list of relations
  639. @param fdir True for 'from'
  640. """
  641. if fdir is None:
  642. return self.rels
  643. result = list()
  644. for rel in self.rels:
  645. if fdir == 'from':
  646. if rel.GetFrom() == self:
  647. result.append(rel)
  648. else:
  649. if rel.GetTo() == self:
  650. result.append(rel)
  651. return result
  652. def IsEnabled(self):
  653. """!Get True if action is enabled, otherwise False"""
  654. return self.isEnabled
  655. def Enable(self, enabled = True):
  656. """!Enable/disable action"""
  657. self.isEnabled = enabled
  658. self.Update()
  659. def Update(self):
  660. pass
  661. def SetBlock(self, item):
  662. """!Add object to the block (loop/condition)
  663. @param item reference to ModelLoop or ModelCondition which
  664. defines loops/condition
  665. """
  666. if item not in self.inBlock:
  667. self.inBlock.append(item)
  668. def UnSetBlock(self, item):
  669. """!Remove object from the block (loop/consition)
  670. @param item reference to ModelLoop or ModelCondition which
  671. defines loops/codition
  672. """
  673. if item in self.inBlock:
  674. self.inBlock.remove(item)
  675. def GetBlock(self):
  676. """!Get list of related ModelObject(s) which defines block
  677. (loop/condition)
  678. @return list of ModelObjects
  679. """
  680. return self.inBlock
  681. def GetBlockId(self):
  682. """!Get list of related ids which defines block
  683. @return list of ids
  684. """
  685. ret = list()
  686. for mo in self.inBlock:
  687. ret.append(mo.GetId())
  688. return ret
  689. class ModelAction(ModelObject, ogl.RectangleShape):
  690. """!Action class (GRASS module)"""
  691. def __init__(self, parent, x, y, id = -1, cmd = None, task = None, width = None, height = None):
  692. ModelObject.__init__(self, id)
  693. self.parent = parent
  694. self.task = task
  695. if not width:
  696. width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width'))
  697. if not height:
  698. height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height'))
  699. if cmd:
  700. self.task = GUI(show = None).ParseCommand(cmd = cmd)
  701. else:
  702. if task:
  703. self.task = task
  704. else:
  705. self.task = None
  706. self.propWin = None
  707. self.data = list() # list of connected data items
  708. self.isValid = False
  709. self.isParameterized = False
  710. if self.parent.GetCanvas():
  711. ogl.RectangleShape.__init__(self, width, height)
  712. self.SetCanvas(self.parent)
  713. self.SetX(x)
  714. self.SetY(y)
  715. self.SetPen(wx.BLACK_PEN)
  716. self._setPen()
  717. self._setBrush()
  718. self.SetId(id)
  719. if self.task:
  720. self.SetValid(self.task.get_options())
  721. def _setBrush(self, running = False):
  722. """!Set brush"""
  723. if running:
  724. color = UserSettings.Get(group='modeler', key='action',
  725. subkey=('color', 'running'))
  726. elif not self.isEnabled:
  727. color = UserSettings.Get(group='modeler', key='disabled',
  728. subkey='color')
  729. elif self.isValid:
  730. color = UserSettings.Get(group='modeler', key='action',
  731. subkey=('color', 'valid'))
  732. else:
  733. color = UserSettings.Get(group='modeler', key='action',
  734. subkey=('color', 'invalid'))
  735. wxColor = wx.Color(color[0], color[1], color[2])
  736. self.SetBrush(wx.Brush(wxColor))
  737. def _setPen(self):
  738. """!Set pen"""
  739. if self.isParameterized:
  740. width = int(UserSettings.Get(group='modeler', key='action',
  741. subkey=('width', 'parameterized')))
  742. else:
  743. width = int(UserSettings.Get(group='modeler', key='action',
  744. subkey=('width', 'default')))
  745. pen = self.GetPen()
  746. pen.SetWidth(width)
  747. self.SetPen(pen)
  748. def SetId(self, id):
  749. """!Set id"""
  750. self.id = id
  751. cmd = self.task.get_cmd(ignoreErrors = True)
  752. if cmd and len(cmd) > 0:
  753. self.ClearText()
  754. self.AddText('(%d) %s' % (self.id, cmd[0]))
  755. else:
  756. self.AddText('(%d) <<%s>>' % (self.id, _("unknown")))
  757. def SetProperties(self, params, propwin):
  758. """!Record properties dialog"""
  759. self.task.params = params['params']
  760. self.task.flags = params['flags']
  761. self.propWin = propwin
  762. def GetPropDialog(self):
  763. """!Get properties dialog"""
  764. return self.propWin
  765. def GetLog(self, string = True, substitute = None):
  766. """!Get logging info
  767. @param string True to get cmd as a string otherwise a list
  768. @param substitute dictionary of parameter to substitute or None
  769. """
  770. cmd = self.task.get_cmd(ignoreErrors = True, ignoreRequired = True,
  771. ignoreDefault = False)
  772. # substitute variables
  773. if substitute:
  774. variables = []
  775. if 'variables' in substitute:
  776. for p in substitute['variables']['params']:
  777. variables.append(p.get('name', ''))
  778. else:
  779. variables = self.parent.GetVariables()
  780. for variable in variables:
  781. pattern= re.compile('%' + variable)
  782. value = ''
  783. if substitute and 'variables' in substitute:
  784. for p in substitute['variables']['params']:
  785. if variable == p.get('name', ''):
  786. if p.get('type', 'string') == 'string':
  787. value = p.get('value', '')
  788. else:
  789. value = str(p.get('value', ''))
  790. break
  791. if not value:
  792. value = variables[variable].get('value', '')
  793. if not value:
  794. continue
  795. for idx in range(len(cmd)):
  796. if pattern.search(cmd[idx]):
  797. cmd[idx] = pattern.sub(value, cmd[idx])
  798. break
  799. idx += 1
  800. if string:
  801. if cmd is None:
  802. return ''
  803. else:
  804. return ' '.join(cmd)
  805. return cmd
  806. def GetName(self):
  807. """!Get name"""
  808. cmd = self.task.get_cmd(ignoreErrors = True)
  809. if cmd and len(cmd) > 0:
  810. return cmd[0]
  811. return _('unknown')
  812. def GetParams(self, dcopy = False):
  813. """!Get dictionary of parameters"""
  814. if dcopy:
  815. return copy.deepcopy(self.task.get_options())
  816. return self.task.get_options()
  817. def GetTask(self):
  818. """!Get grassTask instance"""
  819. return self.task
  820. def SetParams(self, params):
  821. """!Set dictionary of parameters"""
  822. self.task.params = params['params']
  823. self.task.flags = params['flags']
  824. def MergeParams(self, params):
  825. """!Merge dictionary of parameters"""
  826. if 'flags' in params:
  827. for f in params['flags']:
  828. self.task.set_flag(f['name'],
  829. f.get('value', False))
  830. if 'params' in params:
  831. for p in params['params']:
  832. self.task.set_param(p['name'],
  833. p.get('value', ''))
  834. def SetValid(self, options):
  835. """!Set validity for action
  836. @param options dictionary with flags and params (gtask)
  837. """
  838. self.isValid = True
  839. self.isParameterized = False
  840. for f in options['flags']:
  841. if f.get('parameterized', False):
  842. self.IsParameterized = True
  843. break
  844. for p in options['params']:
  845. if self.isValid and p.get('required', False) and \
  846. p.get('value', '') == '' and \
  847. p.get('default', '') == '':
  848. self.isValid = False
  849. if not self.isParameterized and p.get('parameterized', False):
  850. self.isParameterized = True
  851. if self.parent.GetCanvas():
  852. self._setBrush()
  853. self._setPen()
  854. def IsValid(self):
  855. """!Check validity (all required parameters set)"""
  856. return self.isValid
  857. def IsParameterized(self):
  858. """!Check if action is parameterized"""
  859. return self.isParameterized
  860. def FindData(self, name):
  861. """!Find data item by name"""
  862. for rel in self.GetRelations():
  863. data = rel.GetData()
  864. if name == rel.GetName() and name in data.GetName():
  865. return data
  866. return None
  867. def Update(self, running = False):
  868. """!Update action"""
  869. if running:
  870. self._setBrush(running = True)
  871. else:
  872. self._setBrush()
  873. self._setPen()
  874. def OnDraw(self, dc):
  875. """!Draw action in canvas"""
  876. self._setBrush()
  877. self._setPen()
  878. ogl.RectangleShape.OnDraw(self, dc)
  879. class ModelData(ModelObject, ogl.EllipseShape):
  880. def __init__(self, parent, x, y, value = '', prompt = '', width = None, height = None):
  881. """Data item class
  882. @param parent window parent
  883. @param x, y position of the shape
  884. @param fname, tname list of parameter names from / to
  885. @param value value
  886. @param prompt type of GIS element
  887. @param width,height dimension of the shape
  888. """
  889. ModelObject.__init__(self)
  890. self.parent = parent
  891. self.value = value
  892. self.prompt = prompt
  893. self.intermediate = False
  894. self.propWin = None
  895. if not width:
  896. width = UserSettings.Get(group='modeler', key='data', subkey=('size', 'width'))
  897. if not height:
  898. height = UserSettings.Get(group='modeler', key='data', subkey=('size', 'height'))
  899. if self.parent.GetCanvas():
  900. ogl.EllipseShape.__init__(self, width, height)
  901. self.SetCanvas(self.parent)
  902. self.SetX(x)
  903. self.SetY(y)
  904. self.SetPen(wx.BLACK_PEN)
  905. self._setBrush()
  906. self._setText()
  907. def IsIntermediate(self):
  908. """!Checks if data item is intermediate"""
  909. return self.intermediate
  910. def SetIntermediate(self, im):
  911. """!Set intermediate flag"""
  912. self.intermediate = im
  913. def OnDraw(self, dc):
  914. pen = self.GetPen()
  915. pen.SetWidth(1)
  916. if self.intermediate:
  917. pen.SetStyle(wx.SHORT_DASH)
  918. else:
  919. pen.SetStyle(wx.SOLID)
  920. self.SetPen(pen)
  921. ogl.EllipseShape.OnDraw(self, dc)
  922. def GetLog(self, string = True):
  923. """!Get logging info"""
  924. name = list()
  925. for rel in self.GetRelations():
  926. name.append(rel.GetName())
  927. if name:
  928. return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')'
  929. else:
  930. return self.value + ' (' + self.prompt + ')'
  931. def GetName(self):
  932. """!Get list of names"""
  933. name = list()
  934. for rel in self.GetRelations():
  935. name.append(rel.GetName())
  936. return name
  937. def GetPrompt(self):
  938. """!Get prompt"""
  939. return self.prompt
  940. def SetPrompt(self, prompt):
  941. """!Set prompt
  942. @param prompt
  943. """
  944. self.prompt = prompt
  945. def GetValue(self):
  946. """!Get value"""
  947. return self.value
  948. def SetValue(self, value):
  949. """!Set value
  950. @param value
  951. """
  952. self.value = value
  953. self._setText()
  954. for direction in ('from', 'to'):
  955. for rel in self.GetRelations(direction):
  956. if direction == 'from':
  957. action = rel.GetTo()
  958. else:
  959. action = rel.GetFrom()
  960. task = GUI(show = None).ParseCommand(cmd = action.GetLog(string = False))
  961. task.set_param(rel.GetName(), self.value)
  962. action.SetParams(params = task.get_options())
  963. def GetPropDialog(self):
  964. """!Get properties dialog"""
  965. return self.propWin
  966. def SetPropDialog(self, win):
  967. """!Get properties dialog"""
  968. self.propWin = win
  969. def _setBrush(self):
  970. """!Set brush"""
  971. if self.prompt == 'raster':
  972. color = UserSettings.Get(group = 'modeler', key = 'data',
  973. subkey = ('color', 'raster'))
  974. elif self.prompt == 'raster3d':
  975. color = UserSettings.Get(group = 'modeler', key = 'data',
  976. subkey = ('color', 'raster3d'))
  977. elif self.prompt == 'vector':
  978. color = UserSettings.Get(group = 'modeler', key = 'data',
  979. subkey = ('color', 'vector'))
  980. else:
  981. color = UserSettings.Get(group = 'modeler', key = 'action',
  982. subkey = ('color', 'invalid'))
  983. wxColor = wx.Color(color[0], color[1], color[2])
  984. self.SetBrush(wx.Brush(wxColor))
  985. def _setPen(self):
  986. """!Set pen"""
  987. isParameterized = False
  988. for rel in self.GetRelations('from'):
  989. if rel.GetTo().IsParameterized():
  990. isParameterized = True
  991. break
  992. if not isParameterized:
  993. for rel in self.GetRelations('to'):
  994. if rel.GetFrom().IsParameterized():
  995. isParameterized = True
  996. break
  997. if isParameterized:
  998. width = int(UserSettings.Get(group = 'modeler', key = 'action',
  999. subkey = ('width', 'parameterized')))
  1000. else:
  1001. width = int(UserSettings.Get(group = 'modeler', key = 'action',
  1002. subkey = ('width', 'default')))
  1003. pen = self.GetPen()
  1004. pen.SetWidth(width)
  1005. self.SetPen(pen)
  1006. def _setText(self):
  1007. """!Update text"""
  1008. self.ClearText()
  1009. name = []
  1010. for rel in self.GetRelations():
  1011. name.append(rel.GetName())
  1012. self.AddText('/'.join(name))
  1013. if self.value:
  1014. self.AddText(self.value)
  1015. else:
  1016. self.AddText(_('<not defined>'))
  1017. def Update(self):
  1018. """!Update action"""
  1019. self._setBrush()
  1020. self._setPen()
  1021. self._setText()
  1022. class ModelRelation(ogl.LineShape):
  1023. """!Data - action relation"""
  1024. def __init__(self, parent, fromShape, toShape, param = ''):
  1025. self.fromShape = fromShape
  1026. self.toShape = toShape
  1027. self.param = param
  1028. self.parent = parent
  1029. self._points = None
  1030. if self.parent.GetCanvas():
  1031. ogl.LineShape.__init__(self)
  1032. def __del__(self):
  1033. if self in self.fromShape.rels:
  1034. self.fromShape.rels.remove(self)
  1035. if self in self.toShape.rels:
  1036. self.toShape.rels.remove(self)
  1037. def GetFrom(self):
  1038. """!Get id of 'from' shape"""
  1039. return self.fromShape
  1040. def GetTo(self):
  1041. """!Get id of 'to' shape"""
  1042. return self.toShape
  1043. def GetData(self):
  1044. """!Get related ModelData instance
  1045. @return ModelData instance
  1046. @return None if not found
  1047. """
  1048. if isinstance(self.fromShape, ModelData):
  1049. return self.fromShape
  1050. elif isinstance(self.toShape, ModelData):
  1051. return self.toShape
  1052. return None
  1053. def GetName(self):
  1054. """!Get parameter name"""
  1055. return self.param
  1056. def ResetShapes(self):
  1057. """!Reset related objects"""
  1058. self.fromShape.ResetControlPoints()
  1059. self.toShape.ResetControlPoints()
  1060. self.ResetControlPoints()
  1061. def SetControlPoints(self, points):
  1062. """!Set control points"""
  1063. self._points = points
  1064. def GetControlPoints(self):
  1065. """!Get list of control points"""
  1066. return self._points
  1067. def _setPen(self):
  1068. """!Set pen"""
  1069. pen = self.GetPen()
  1070. pen.SetWidth(1)
  1071. pen.SetStyle(wx.SOLID)
  1072. self.SetPen(pen)
  1073. def OnDraw(self, dc):
  1074. """!Draw relation"""
  1075. self._setPen()
  1076. ogl.LineShape.OnDraw(self, dc)
  1077. def SetName(self, param):
  1078. self.param = param
  1079. class ModelItem(ModelObject):
  1080. def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []):
  1081. """!Abstract class for loops and conditions"""
  1082. ModelObject.__init__(self, id)
  1083. self.parent = parent
  1084. self.text = text
  1085. self.items = items # list of items in the loop
  1086. def GetText(self):
  1087. """!Get loop text"""
  1088. return self.text
  1089. def GetItems(self):
  1090. """!Get items (id)"""
  1091. return self.items
  1092. def SetId(self, id):
  1093. """!Set loop id"""
  1094. self.id = id
  1095. def SetText(self, cond):
  1096. """!Set loop text (condition)"""
  1097. self.text = cond
  1098. self.ClearText()
  1099. self.AddText('(' + str(self.id) + ') ' + self.text)
  1100. def GetLog(self):
  1101. """!Get log info"""
  1102. if self.text:
  1103. return _("Condition: ") + self.text
  1104. else:
  1105. return _("Condition: not defined")
  1106. def AddRelation(self, rel):
  1107. """!Record relation"""
  1108. self.rels.append(rel)
  1109. def Clear(self):
  1110. """!Clear object, remove rels"""
  1111. self.rels = list()
  1112. class ModelLoop(ModelItem, ogl.RectangleShape):
  1113. def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []):
  1114. """!Defines a loop"""
  1115. ModelItem.__init__(self, parent, x, y, id, width, height, text, items)
  1116. if not width:
  1117. width = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'width'))
  1118. if not height:
  1119. height = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'height'))
  1120. if self.parent.GetCanvas():
  1121. ogl.RectangleShape.__init__(self, width, height)
  1122. self.SetCanvas(self.parent)
  1123. self.SetX(x)
  1124. self.SetY(y)
  1125. self.SetPen(wx.BLACK_PEN)
  1126. self.SetCornerRadius(100)
  1127. if text:
  1128. self.AddText('(' + str(self.id) + ') ' + text)
  1129. else:
  1130. self.AddText('(' + str(self.id) + ')')
  1131. self._setBrush()
  1132. def _setBrush(self):
  1133. """!Set brush"""
  1134. if not self.isEnabled:
  1135. color = UserSettings.Get(group='modeler', key='disabled',
  1136. subkey='color')
  1137. else:
  1138. color = UserSettings.Get(group='modeler', key='loop',
  1139. subkey=('color', 'valid'))
  1140. wxColor = wx.Color(color[0], color[1], color[2])
  1141. self.SetBrush(wx.Brush(wxColor))
  1142. def Enable(self, enabled = True):
  1143. """!Enable/disable action"""
  1144. for item in self.items:
  1145. if not isinstance(item, ModelAction):
  1146. continue
  1147. item.Enable(enabled)
  1148. ModelObject.Enable(self, enabled)
  1149. def Update(self):
  1150. self._setBrush()
  1151. def GetName(self):
  1152. """!Get name"""
  1153. return _("loop")
  1154. def SetItems(self, items):
  1155. """!Set items (id)"""
  1156. self.items = items
  1157. class ModelCondition(ModelItem, ogl.PolygonShape):
  1158. def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '',
  1159. items = { 'if' : [], 'else' : [] }):
  1160. """!Defines a if-else condition"""
  1161. ModelItem.__init__(self, parent, x, y, id, width, height, text, items)
  1162. if not width:
  1163. self.width = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'width'))
  1164. else:
  1165. self.width = width
  1166. if not height:
  1167. self.height = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'height'))
  1168. else:
  1169. self.height = height
  1170. if self.parent.GetCanvas():
  1171. ogl.PolygonShape.__init__(self)
  1172. points = [(0, - self.height / 2),
  1173. (self.width / 2, 0),
  1174. (0, self.height / 2),
  1175. (- self.width / 2, 0)]
  1176. self.Create(points)
  1177. self.SetCanvas(self.parent)
  1178. self.SetX(x)
  1179. self.SetY(y)
  1180. self.SetPen(wx.BLACK_PEN)
  1181. if text:
  1182. self.AddText('(' + str(self.id) + ') ' + text)
  1183. else:
  1184. self.AddText('(' + str(self.id) + ')')
  1185. def GetName(self):
  1186. """!Get name"""
  1187. return _("if-else")
  1188. def GetWidth(self):
  1189. """!Get object width"""
  1190. return self.width
  1191. def GetHeight(self):
  1192. """!Get object height"""
  1193. return self.height
  1194. def SetItems(self, items, branch = 'if'):
  1195. """!Set items (id)
  1196. @param items list of items
  1197. @param branch 'if' / 'else'
  1198. """
  1199. if branch in ['if', 'else']:
  1200. self.items[branch] = items
  1201. class ProcessModelFile:
  1202. """!Process GRASS model file (gxm)"""
  1203. def __init__(self, tree):
  1204. """!A ElementTree handler for the GXM XML file, as defined in
  1205. grass-gxm.dtd.
  1206. """
  1207. self.tree = tree
  1208. self.root = self.tree.getroot()
  1209. # list of actions, data
  1210. self.properties = dict()
  1211. self.variables = dict()
  1212. self.actions = list()
  1213. self.data = list()
  1214. self.loops = list()
  1215. self.conditions = list()
  1216. self._processWindow()
  1217. self._processProperties()
  1218. self._processVariables()
  1219. self._processItems()
  1220. self._processData()
  1221. def _filterValue(self, value):
  1222. """!Filter value
  1223. @param value
  1224. """
  1225. value = value.replace('&lt;', '<')
  1226. value = value.replace('&gt;', '>')
  1227. return value
  1228. def _getNodeText(self, node, tag, default = ''):
  1229. """!Get node text"""
  1230. p = node.find(tag)
  1231. if p is not None:
  1232. if p.text:
  1233. return utils.normalize_whitespace(p.text)
  1234. else:
  1235. return ''
  1236. return default
  1237. def _processWindow(self):
  1238. """!Process window properties"""
  1239. node = self.root.find('window')
  1240. if node is None:
  1241. self.pos = self.size = None
  1242. return
  1243. self.pos, self.size = self._getDim(node)
  1244. def _processProperties(self):
  1245. """!Process model properties"""
  1246. node = self.root.find('properties')
  1247. if node is None:
  1248. return
  1249. for key in ('name', 'description', 'author'):
  1250. self._processProperty(node, key)
  1251. for f in node.findall('flag'):
  1252. name = f.get('name', '')
  1253. if name == 'overwrite':
  1254. self.properties['overwrite'] = True
  1255. def _processProperty(self, pnode, name):
  1256. """!Process given property"""
  1257. node = pnode.find(name)
  1258. if node is not None:
  1259. self.properties[name] = node.text
  1260. else:
  1261. self.properties[name] = ''
  1262. def _processVariables(self):
  1263. """!Process model variables"""
  1264. vnode = self.root.find('variables')
  1265. if vnode is None:
  1266. return
  1267. for node in vnode.findall('variable'):
  1268. name = node.get('name', '')
  1269. if not name:
  1270. continue # should not happen
  1271. self.variables[name] = { 'type' : node.get('type', 'string') }
  1272. for key in ('description', 'value'):
  1273. self._processVariable(node, name, key)
  1274. def _processVariable(self, pnode, name, key):
  1275. """!Process given variable"""
  1276. node = pnode.find(key)
  1277. if node is not None:
  1278. if node.text:
  1279. self.variables[name][key] = node.text
  1280. def _processItems(self):
  1281. """!Process model items (actions, loops, conditions)"""
  1282. self._processActions()
  1283. self._processLoops()
  1284. self._processConditions()
  1285. def _processActions(self):
  1286. """!Process model file"""
  1287. for action in self.root.findall('action'):
  1288. pos, size = self._getDim(action)
  1289. disabled = False
  1290. task = action.find('task')
  1291. if task is not None:
  1292. if task.find('disabled') is not None:
  1293. disabled = True
  1294. task = self._processTask(task)
  1295. else:
  1296. task = None
  1297. aId = int(action.get('id', -1))
  1298. self.actions.append({ 'pos' : pos,
  1299. 'size' : size,
  1300. 'task' : task,
  1301. 'id' : aId,
  1302. 'disabled' : disabled })
  1303. def _getDim(self, node):
  1304. """!Get position and size of shape"""
  1305. pos = size = None
  1306. posAttr = node.get('pos', None)
  1307. if posAttr:
  1308. posVal = map(int, posAttr.split(','))
  1309. try:
  1310. pos = (posVal[0], posVal[1])
  1311. except:
  1312. pos = None
  1313. sizeAttr = node.get('size', None)
  1314. if sizeAttr:
  1315. sizeVal = map(int, sizeAttr.split(','))
  1316. try:
  1317. size = (sizeVal[0], sizeVal[1])
  1318. except:
  1319. size = None
  1320. return pos, size
  1321. def _processData(self):
  1322. """!Process model file"""
  1323. for data in self.root.findall('data'):
  1324. pos, size = self._getDim(data)
  1325. param = data.find('data-parameter')
  1326. prompt = value = None
  1327. if param is not None:
  1328. prompt = param.get('prompt', None)
  1329. value = self._filterValue(self._getNodeText(param, 'value'))
  1330. if data.find('intermediate') is None:
  1331. intermediate = False
  1332. else:
  1333. intermediate = True
  1334. rels = list()
  1335. for rel in data.findall('relation'):
  1336. defrel = { 'id' : int(rel.get('id', -1)),
  1337. 'dir' : rel.get('dir', 'to'),
  1338. 'name' : rel.get('name', '') }
  1339. points = list()
  1340. for point in rel.findall('point'):
  1341. x = self._filterValue(self._getNodeText(point, 'x'))
  1342. y = self._filterValue(self._getNodeText(point, 'y'))
  1343. points.append((float(x), float(y)))
  1344. defrel['points'] = points
  1345. rels.append(defrel)
  1346. self.data.append({ 'pos' : pos,
  1347. 'size': size,
  1348. 'prompt' : prompt,
  1349. 'value' : value,
  1350. 'intermediate' : intermediate,
  1351. 'rels' : rels })
  1352. def _processTask(self, node):
  1353. """!Process task
  1354. @return grassTask instance
  1355. @return None on error
  1356. """
  1357. cmd = list()
  1358. parameterized = list()
  1359. name = node.get('name', None)
  1360. if not name:
  1361. return None
  1362. cmd.append(name)
  1363. # flags
  1364. for f in node.findall('flag'):
  1365. flag = f.get('name', '')
  1366. if f.get('parameterized', '0') == '1':
  1367. parameterized.append(('flag', flag))
  1368. if f.get('value', '1') == '0':
  1369. continue
  1370. if len(flag) > 1:
  1371. cmd.append('--' + flag)
  1372. else:
  1373. cmd.append('-' + flag)
  1374. # parameters
  1375. for p in node.findall('parameter'):
  1376. name = p.get('name', '')
  1377. if p.find('parameterized') is not None:
  1378. parameterized.append(('param', name))
  1379. cmd.append('%s=%s' % (name,
  1380. self._filterValue(self._getNodeText(p, 'value'))))
  1381. task, err = GUI(show = None, checkError = True).ParseCommand(cmd = cmd)
  1382. if err:
  1383. GWarning(os.linesep.join(err))
  1384. for opt, name in parameterized:
  1385. if opt == 'flag':
  1386. task.set_flag(name, True, element = 'parameterized')
  1387. else:
  1388. task.set_param(name, True, element = 'parameterized')
  1389. return task
  1390. def _processLoops(self):
  1391. """!Process model loops"""
  1392. for node in self.root.findall('loop'):
  1393. pos, size = self._getDim(node)
  1394. text = self._filterValue(self._getNodeText(node, 'condition')).strip()
  1395. aid = list()
  1396. for anode in node.findall('item'):
  1397. try:
  1398. aid.append(int(anode.text))
  1399. except ValueError:
  1400. pass
  1401. self.loops.append({ 'pos' : pos,
  1402. 'size' : size,
  1403. 'text' : text,
  1404. 'id' : int(node.get('id', -1)),
  1405. 'items' : aid })
  1406. def _processConditions(self):
  1407. """!Process model conditions"""
  1408. for node in self.root.findall('if-else'):
  1409. pos, size = self._getDim(node)
  1410. text = self._filterValue(self._getNodeText(node, 'condition')).strip()
  1411. aid = { 'if' : list(),
  1412. 'else' : list() }
  1413. for b in aid.keys():
  1414. bnode = node.find(b)
  1415. if bnode is None:
  1416. continue
  1417. for anode in bnode.findall('item'):
  1418. try:
  1419. aid[b].append(int(anode.text))
  1420. except ValueError:
  1421. pass
  1422. self.conditions.append({ 'pos' : pos,
  1423. 'size' : size,
  1424. 'text' : text,
  1425. 'id' : int(node.get('id', -1)),
  1426. 'items' : aid })
  1427. class WriteModelFile:
  1428. """!Generic class for writing model file"""
  1429. def __init__(self, fd, model):
  1430. self.fd = fd
  1431. self.model = model
  1432. self.properties = model.GetProperties()
  1433. self.variables = model.GetVariables()
  1434. self.items = model.GetItems()
  1435. self.indent = 0
  1436. self._header()
  1437. self._window()
  1438. self._properties()
  1439. self._variables()
  1440. self._items()
  1441. dataList = list()
  1442. for action in model.GetItems(objType = ModelAction):
  1443. for rel in action.GetRelations():
  1444. dataItem = rel.GetData()
  1445. if dataItem not in dataList:
  1446. dataList.append(dataItem)
  1447. self._data(dataList)
  1448. self._footer()
  1449. def _filterValue(self, value):
  1450. """!Make value XML-valid"""
  1451. value = value.replace('<', '&lt;')
  1452. value = value.replace('>', '&gt;')
  1453. return value
  1454. def _header(self):
  1455. """!Write header"""
  1456. self.fd.write('<?xml version="1.0" encoding="UTF-8"?>\n')
  1457. self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n')
  1458. self.fd.write('%s<gxm>\n' % (' ' * self.indent))
  1459. self.indent += 4
  1460. def _footer(self):
  1461. """!Write footer"""
  1462. self.indent -= 4
  1463. self.fd.write('%s</gxm>\n' % (' ' * self.indent))
  1464. def _window(self):
  1465. """!Write window properties"""
  1466. canvas = self.model.GetCanvas()
  1467. if canvas is None:
  1468. return
  1469. win = canvas.parent
  1470. pos = win.GetPosition()
  1471. size = win.GetSize()
  1472. self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' % \
  1473. (' ' * self.indent, pos[0], pos[1], size[0], size[1]))
  1474. def _properties(self):
  1475. """!Write model properties"""
  1476. self.fd.write('%s<properties>\n' % (' ' * self.indent))
  1477. self.indent += 4
  1478. if self.properties['name']:
  1479. self.fd.write('%s<name>%s</name>\n' % (' ' * self.indent, self.properties['name']))
  1480. if self.properties['description']:
  1481. self.fd.write('%s<description>%s</description>\n' % (' ' * self.indent,
  1482. EncodeString(self.properties['description'])))
  1483. if self.properties['author']:
  1484. self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent,
  1485. EncodeString(self.properties['author'])))
  1486. if 'overwrite' in self.properties and \
  1487. self.properties['overwrite']:
  1488. self.fd.write('%s<flag name="overwrite" />\n' % (' ' * self.indent))
  1489. self.indent -= 4
  1490. self.fd.write('%s</properties>\n' % (' ' * self.indent))
  1491. def _variables(self):
  1492. """!Write model variables"""
  1493. if not self.variables:
  1494. return
  1495. self.fd.write('%s<variables>\n' % (' ' * self.indent))
  1496. self.indent += 4
  1497. for name, values in self.variables.iteritems():
  1498. self.fd.write('%s<variable name="%s" type="%s">\n' % \
  1499. (' ' * self.indent, name, values['type']))
  1500. self.indent += 4
  1501. if 'value' in values:
  1502. self.fd.write('%s<value>%s</value>\n' % \
  1503. (' ' * self.indent, values['value']))
  1504. if 'description' in values:
  1505. self.fd.write('%s<description>%s</description>\n' % \
  1506. (' ' * self.indent, values['description']))
  1507. self.indent -= 4
  1508. self.fd.write('%s</variable>\n' % (' ' * self.indent))
  1509. self.indent -= 4
  1510. self.fd.write('%s</variables>\n' % (' ' * self.indent))
  1511. def _items(self):
  1512. """!Write actions/loops/conditions"""
  1513. for item in self.items:
  1514. if isinstance(item, ModelAction):
  1515. self._action(item)
  1516. elif isinstance(item, ModelLoop):
  1517. self._loop(item)
  1518. elif isinstance(item, ModelCondition):
  1519. self._condition(item)
  1520. def _action(self, action):
  1521. """!Write actions"""
  1522. self.fd.write('%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' % \
  1523. (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(),
  1524. action.GetWidth(), action.GetHeight()))
  1525. self.indent += 4
  1526. self.fd.write('%s<task name="%s">\n' % (' ' * self.indent, action.GetLog(string = False)[0]))
  1527. self.indent += 4
  1528. if not action.IsEnabled():
  1529. self.fd.write('%s<disabled />\n' % (' ' * self.indent))
  1530. for key, val in action.GetParams().iteritems():
  1531. if key == 'flags':
  1532. for f in val:
  1533. if f.get('value', False) or f.get('parameterized', False):
  1534. if f.get('parameterized', False):
  1535. if f.get('value', False) == False:
  1536. self.fd.write('%s<flag name="%s" value="0" parameterized="1" />\n' %
  1537. (' ' * self.indent, f.get('name', '')))
  1538. else:
  1539. self.fd.write('%s<flag name="%s" parameterized="1" />\n' %
  1540. (' ' * self.indent, f.get('name', '')))
  1541. else:
  1542. self.fd.write('%s<flag name="%s" />\n' %
  1543. (' ' * self.indent, f.get('name', '')))
  1544. else: # parameter
  1545. for p in val:
  1546. if not p.get('value', '') and not p.get('parameterized', False):
  1547. continue
  1548. self.fd.write('%s<parameter name="%s">\n' %
  1549. (' ' * self.indent, p.get('name', '')))
  1550. self.indent += 4
  1551. if p.get('parameterized', False):
  1552. self.fd.write('%s<parameterized />\n' % (' ' * self.indent))
  1553. self.fd.write('%s<value>%s</value>\n' %
  1554. (' ' * self.indent, self._filterValue(p.get('value', ''))))
  1555. self.indent -= 4
  1556. self.fd.write('%s</parameter>\n' % (' ' * self.indent))
  1557. self.indent -= 4
  1558. self.fd.write('%s</task>\n' % (' ' * self.indent))
  1559. self.indent -= 4
  1560. self.fd.write('%s</action>\n' % (' ' * self.indent))
  1561. def _data(self, dataList):
  1562. """!Write data"""
  1563. for data in dataList:
  1564. self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' % \
  1565. (' ' * self.indent, data.GetX(), data.GetY(),
  1566. data.GetWidth(), data.GetHeight()))
  1567. self.indent += 4
  1568. self.fd.write('%s<data-parameter prompt="%s">\n' % \
  1569. (' ' * self.indent, data.GetPrompt()))
  1570. self.indent += 4
  1571. self.fd.write('%s<value>%s</value>\n' %
  1572. (' ' * self.indent, self._filterValue(data.GetValue())))
  1573. self.indent -= 4
  1574. self.fd.write('%s</data-parameter>\n' % (' ' * self.indent))
  1575. if data.IsIntermediate():
  1576. self.fd.write('%s<intermediate />\n' % (' ' * self.indent))
  1577. # relations
  1578. for ft in ('from', 'to'):
  1579. for rel in data.GetRelations(ft):
  1580. if ft == 'from':
  1581. aid = rel.GetTo().GetId()
  1582. else:
  1583. aid = rel.GetFrom().GetId()
  1584. self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' % \
  1585. (' ' * self.indent, ft, aid, rel.GetName()))
  1586. self.indent += 4
  1587. for point in rel.GetLineControlPoints()[1:-1]:
  1588. self.fd.write('%s<point>\n' % (' ' * self.indent))
  1589. self.indent += 4
  1590. x, y = point.Get()
  1591. self.fd.write('%s<x>%d</x>\n' % (' ' * self.indent, int(x)))
  1592. self.fd.write('%s<y>%d</y>\n' % (' ' * self.indent, int(y)))
  1593. self.indent -= 4
  1594. self.fd.write('%s</point>\n' % (' ' * self.indent))
  1595. self.indent -= 4
  1596. self.fd.write('%s</relation>\n' % (' ' * self.indent))
  1597. self.indent -= 4
  1598. self.fd.write('%s</data>\n' % (' ' * self.indent))
  1599. def _loop(self, loop):
  1600. """!Write loops"""
  1601. self.fd.write('%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' % \
  1602. (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(),
  1603. loop.GetWidth(), loop.GetHeight()))
  1604. text = loop.GetText()
  1605. self.indent += 4
  1606. if text:
  1607. self.fd.write('%s<condition>%s</condition>\n' %
  1608. (' ' * self.indent, self._filterValue(text)))
  1609. for item in loop.GetItems():
  1610. self.fd.write('%s<item>%d</item>\n' %
  1611. (' ' * self.indent, item.GetId()))
  1612. self.indent -= 4
  1613. self.fd.write('%s</loop>\n' % (' ' * self.indent))
  1614. def _condition(self, condition):
  1615. """!Write conditions"""
  1616. bbox = condition.GetBoundingBoxMin()
  1617. self.fd.write('%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' % \
  1618. (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(),
  1619. bbox[0], bbox[1]))
  1620. text = condition.GetText()
  1621. self.indent += 4
  1622. if text:
  1623. self.fd.write('%s<condition>%s</condition>\n' %
  1624. (' ' * self.indent, self._filterValue(text)))
  1625. items = condition.GetItems()
  1626. for b in items.keys():
  1627. if len(items[b]) < 1:
  1628. continue
  1629. self.fd.write('%s<%s>\n' % (' ' * self.indent, b))
  1630. self.indent += 4
  1631. for item in items[b]:
  1632. self.fd.write('%s<item>%d</item>\n' %
  1633. (' ' * self.indent, item.GetId()))
  1634. self.indent -= 4
  1635. self.fd.write('%s</%s>\n' % (' ' * self.indent, b))
  1636. self.indent -= 4
  1637. self.fd.write('%s</if-else>\n' % (' ' * self.indent))
  1638. class WritePythonFile:
  1639. def __init__(self, fd, model):
  1640. """!Class for exporting model to Python script
  1641. @param fd file desciptor
  1642. """
  1643. self.fd = fd
  1644. self.model = model
  1645. self.indent = 4
  1646. self._writePython()
  1647. def _writePython(self):
  1648. """!Write model to file"""
  1649. properties = self.model.GetProperties()
  1650. self.fd.write(
  1651. r"""#!/usr/bin/env python
  1652. #
  1653. ############################################################################
  1654. #
  1655. # MODULE: %s
  1656. #
  1657. # AUTHOR(S): %s
  1658. #
  1659. # PURPOSE: %s
  1660. #
  1661. # DATE: %s
  1662. #
  1663. #############################################################################
  1664. """ % (properties['name'],
  1665. properties['author'],
  1666. properties['description'],
  1667. time.asctime()))
  1668. self.fd.write(
  1669. r"""
  1670. import sys
  1671. import os
  1672. import atexit
  1673. import grass.script as grass
  1674. """)
  1675. # cleanup()
  1676. rast, vect, rast3d, msg = self.model.GetIntermediateData()
  1677. self.fd.write(
  1678. r"""
  1679. def cleanup():
  1680. """)
  1681. if rast:
  1682. self.fd.write(
  1683. r""" grass.run_command('g.remove',
  1684. rast=%s)
  1685. """ % ','.join(map(lambda x: "'" + x + "'", rast)))
  1686. if vect:
  1687. self.fd.write(
  1688. r""" grass.run_command('g.remove',
  1689. vect = %s)
  1690. """ % ','.join(map(lambda x: "'" + x + "'", vect)))
  1691. if rast3d:
  1692. self.fd.write(
  1693. r""" grass.run_command('g.remove',
  1694. rast3d = %s)
  1695. """ % ','.join(map(lambda x: "'" + x + "'", rast3d)))
  1696. if not rast and not vect and not rast3d:
  1697. self.fd.write(' pass\n')
  1698. self.fd.write("\ndef main():\n")
  1699. for item in self.model.GetItems():
  1700. self._writePythonItem(item)
  1701. self.fd.write("\n return 0\n")
  1702. self.fd.write(
  1703. r"""
  1704. if __name__ == "__main__":
  1705. options, flags = grass.parser()
  1706. atexit.register(cleanup)
  1707. sys.exit(main())
  1708. """)
  1709. def _writePythonItem(self, item, ignoreBlock = True, variables = []):
  1710. """!Write model object to Python file"""
  1711. if isinstance(item, ModelAction):
  1712. if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions
  1713. return
  1714. self._writePythonAction(item, variables = variables)
  1715. elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition):
  1716. # substitute condition
  1717. variables = self.model.GetVariables()
  1718. cond = item.GetText()
  1719. for variable in variables:
  1720. pattern = re.compile('%' + variable)
  1721. if pattern.search(cond):
  1722. value = variables[variable].get('value', '')
  1723. if variables[variable].get('type', 'string') == 'string':
  1724. value = '"' + value + '"'
  1725. cond = pattern.sub(value, cond)
  1726. if isinstance(item, ModelLoop):
  1727. condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
  1728. cond = "%sfor %s in " % (' ' * self.indent, condVar)
  1729. if condText[0] == '`' and condText[-1] == '`':
  1730. task = GUI(show = None).ParseCommand(cmd = utils.split(condText[1:-1]))
  1731. cond += "grass.read_command("
  1732. cond += self._getPythonActionCmd(task, len(cond), variables = [condVar]) + ".splitlines()"
  1733. else:
  1734. cond += condText
  1735. self.fd.write('%s:\n' % cond)
  1736. self.indent += 4
  1737. for action in item.GetItems():
  1738. self._writePythonItem(action, ignoreBlock = False, variables = [condVar])
  1739. self.indent -= 4
  1740. else: # ModelCondition
  1741. self.fd.write('%sif %s:\n' % (' ' * self.indent, cond))
  1742. self.indent += 4
  1743. condItems = item.GetItems()
  1744. for action in condItems['if']:
  1745. self._writePythonItem(action, ignoreBlock = False)
  1746. if condItems['else']:
  1747. self.indent -= 4
  1748. self.fd.write('%selse:\n' % (' ' * self.indent))
  1749. self.indent += 4
  1750. for action in condItems['else']:
  1751. self._writePythonItem(action, ignoreBlock = False)
  1752. self.indent += 4
  1753. def _writePythonAction(self, item, variables = []):
  1754. """!Write model action to Python file"""
  1755. task = GUI(show = None).ParseCommand(cmd = item.GetLog(string = False))
  1756. strcmd = "%sgrass.run_command(" % (' ' * self.indent)
  1757. self.fd.write(strcmd + self._getPythonActionCmd(task, len(strcmd), variables) + '\n')
  1758. def _getPythonActionCmd(self, task, cmdIndent, variables = []):
  1759. opts = task.get_options()
  1760. ret = ''
  1761. flags = ''
  1762. params = list()
  1763. for f in opts['flags']:
  1764. if f.get('value', False):
  1765. name = f.get('name', '')
  1766. if len(name) > 1:
  1767. params.append('%s = True' % name)
  1768. else:
  1769. flags += name
  1770. for p in opts['params']:
  1771. name = p.get('name', None)
  1772. value = p.get('value', None)
  1773. if name and value:
  1774. ptype = p.get('type', 'string')
  1775. if value[0] == '%':
  1776. params.append("%s = %s" % (name, value[1:]))
  1777. elif ptype == 'string':
  1778. params.append('%s = "%s"' % (name, value))
  1779. else:
  1780. params.append("%s = %s" % (name, value))
  1781. ret += '"%s"' % task.get_name()
  1782. if flags:
  1783. ret += ",\n%sflags = '%s'" % (' ' * cmdIndent, flags)
  1784. if len(params) > 0:
  1785. ret += ",\n"
  1786. for opt in params[:-1]:
  1787. ret += "%s%s,\n" % (' ' * cmdIndent, opt)
  1788. ret += "%s%s)" % (' ' * cmdIndent, params[-1])
  1789. else:
  1790. ret += ")"
  1791. return ret