model.py 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978
  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::ModelComment
  14. - model::ProcessModelFile
  15. - model::WriteModelFile
  16. - model::WritePythonFile
  17. - model::ModelParamDialog
  18. (C) 2010-2018 by the GRASS Development Team
  19. This program is free software under the GNU General Public License
  20. (>=v2). Read the file COPYING that comes with GRASS for details.
  21. @author Martin Landa <landa.martin gmail.com>
  22. @author Python parameterization Ondrej Pesek <pesej.ondrek gmail.com>
  23. """
  24. import os
  25. import getpass
  26. import copy
  27. import re
  28. import mimetypes
  29. import time
  30. import six
  31. try:
  32. import xml.etree.ElementTree as etree
  33. except ImportError:
  34. import elementtree.ElementTree as etree # Python <= 2.4
  35. import xml.sax.saxutils as saxutils
  36. import wx
  37. from wx.lib import ogl
  38. from core import globalvar
  39. from core import utils
  40. from core.gcmd import GMessage, GException, GError, RunCommand, GWarning, GetDefaultEncoding
  41. from core.settings import UserSettings
  42. from gui_core.forms import GUI, CmdPanel
  43. from gui_core.widgets import GNotebook
  44. from gui_core.wrap import Button
  45. from gmodeler.giface import GraphicalModelerGrassInterface
  46. from grass.script import core as grass
  47. from grass.script import task as gtask
  48. class Model(object):
  49. """Class representing the model"""
  50. def __init__(self, canvas=None):
  51. self.items = list() # list of ordered items (action/loop/condition)
  52. # model properties
  53. self.properties = {
  54. 'name': _("model"),
  55. 'description': _("Script generated by wxGUI Graphical Modeler."),
  56. 'author': getpass.getuser()}
  57. # model variables
  58. self.variables = dict()
  59. self.variablesParams = dict()
  60. self.canvas = canvas
  61. def GetCanvas(self):
  62. """Get canvas or None"""
  63. return self.canvas
  64. def GetItems(self, objType=None):
  65. """Get list of model items
  66. :param objType: Object type to filter model objects
  67. """
  68. if not objType:
  69. return self.items
  70. result = list()
  71. for item in self.items:
  72. if isinstance(item, objType):
  73. result.append(item)
  74. return result
  75. def GetItem(self, aId, objType=None):
  76. """Get item of given id
  77. :param aId: item id
  78. :return: Model* instance
  79. :return: None if no item found
  80. """
  81. ilist = self.GetItems(objType)
  82. for item in ilist:
  83. if item.GetId() == aId:
  84. return item
  85. return None
  86. def GetItemIndex(self, item):
  87. """Return list index of given item"""
  88. return self.items.index(item)
  89. def GetNumItems(self, actionOnly=False):
  90. """Get number of items"""
  91. if actionOnly:
  92. return len(self.GetItems(objType=ModelAction))
  93. return len(self.GetItems())
  94. def ReorderItems(self, idxList):
  95. items = list()
  96. for oldIdx, newIdx in six.iteritems(idxList):
  97. item = self.items.pop(oldIdx)
  98. items.append(item)
  99. self.items.insert(newIdx, item)
  100. # try:
  101. # nextItem = self.items[newIdx+1]
  102. # except IndexError:
  103. # continue # newIdx is the last item in the list
  104. # items.append(nextItem)
  105. # x = item.GetX()
  106. # y = item.GetY()
  107. # item.SetX(nextItem.GetX())
  108. # item.SetY(nextItem.GetY())
  109. # nextItem.SetX(x)
  110. # nextItem.SetY(y)
  111. dc = wx.ClientDC(self.canvas)
  112. for item in items:
  113. item.MoveLinks(dc)
  114. for mo in item.GetBlock():
  115. if isinstance(mo, ModelLoop):
  116. self.canvas.parent.DefineLoop(mo)
  117. elif isinstance(mo, ModelCondition):
  118. self.canvas.parent.DefineCondition(mo)
  119. def Normalize(self):
  120. # check for inconsistecies
  121. for idx in range(1, len(self.items)):
  122. if not self.items[idx].GetBlock() and \
  123. isinstance(self.items[idx - 1], ModelLoop):
  124. # swap action not-in-block with previously defined
  125. # loop
  126. itemPrev = self.items[idx - 1]
  127. self.items[idx - 1] = self.items[idx]
  128. self.items[idx] = itemPrev
  129. # update ids
  130. iId = 1
  131. for item in self.items:
  132. item.SetId(iId)
  133. item.SetLabel()
  134. iId += 1
  135. def GetNextId(self):
  136. """Get next id (data ignored)
  137. :return: next id to be used (default: 1)
  138. """
  139. if len(self.items) < 1:
  140. return 1
  141. currId = self.items[-1].GetId()
  142. if currId > 0:
  143. return currId + 1
  144. return 1
  145. def GetProperties(self):
  146. """Get model properties"""
  147. return self.properties
  148. def GetVariables(self, params=False):
  149. """Get model variables"""
  150. if params:
  151. return self.variablesParams
  152. return self.variables
  153. def SetVariables(self, data):
  154. """Set model variables"""
  155. self.variables = data
  156. def Reset(self):
  157. """Reset model"""
  158. self.items = list()
  159. def RemoveItem(self, item, reference=None):
  160. """Remove item from model
  161. :item: item to be removed
  162. :reference: reference item valid for deletion
  163. :return: list of related items to remove/update
  164. """
  165. relList = list()
  166. upList = list()
  167. doRemove = True
  168. if isinstance(item, ModelAction):
  169. for rel in item.GetRelations():
  170. relList.append(rel)
  171. data = rel.GetData()
  172. if len(data.GetRelations()) < 2:
  173. relList.append(data)
  174. else:
  175. upList.append(data)
  176. elif isinstance(item, ModelData):
  177. for rel in item.GetRelations():
  178. otherItem = rel.GetTo() if rel.GetFrom() == item else rel.GetFrom()
  179. if reference and reference != otherItem:
  180. doRemove = False
  181. continue # avoid recursive deletion
  182. relList.append(rel)
  183. if reference and reference != otherItem:
  184. relList.append(otherItem)
  185. if not doRemove:
  186. upList.append(item)
  187. elif isinstance(item, ModelLoop):
  188. for rel in item.GetRelations():
  189. relList.append(rel)
  190. for action in self.GetItems():
  191. action.UnSetBlock(item)
  192. if doRemove and item in self.items:
  193. self.items.remove(item)
  194. return relList, upList
  195. def FindAction(self, aId):
  196. """Find action by id"""
  197. alist = self.GetItems(objType=ModelAction)
  198. for action in alist:
  199. if action.GetId() == aId:
  200. return action
  201. return None
  202. def GetMaps(self, prompt):
  203. """Get list of maps of selected type
  204. :param prompt: to filter maps"""
  205. maps = list()
  206. for data in self.GetData():
  207. if prompt == data.GetPrompt():
  208. mapName = data.GetValue()
  209. if not mapName or mapName[0] == '%':
  210. continue # skip variables
  211. maps.append(mapName)
  212. return maps
  213. def GetData(self):
  214. """Get list of data items"""
  215. result = list()
  216. dataItems = self.GetItems(objType=ModelData)
  217. for action in self.GetItems(objType=ModelAction):
  218. for rel in action.GetRelations():
  219. dataItem = rel.GetData()
  220. if dataItem not in result:
  221. result.append(dataItem)
  222. if dataItem in dataItems:
  223. dataItems.remove(dataItem)
  224. # standalone data
  225. if dataItems:
  226. result += dataItems
  227. return result
  228. def FindData(self, value, prompt):
  229. """Find data item in the model
  230. :param value: value
  231. :param prompt: prompt
  232. :return: ModelData instance
  233. :return: None if not found
  234. """
  235. for data in self.GetData():
  236. if data.GetValue() == value and \
  237. data.GetPrompt() == prompt:
  238. return data
  239. return None
  240. def LoadModel(self, filename):
  241. """Load model definition stored in GRASS Model XML file (gxm)
  242. .. todo::
  243. Validate against DTD
  244. Raise exception on error.
  245. """
  246. dtdFilename = os.path.join(globalvar.WXGUIDIR, "xml", "grass-gxm.dtd")
  247. # parse workspace file
  248. try:
  249. gxmXml = ProcessModelFile(etree.parse(filename))
  250. except Exception as e:
  251. raise GException(unicode(e))
  252. if self.canvas:
  253. win = self.canvas.parent
  254. if gxmXml.pos:
  255. win.SetPosition(gxmXml.pos)
  256. if gxmXml.size:
  257. win.SetSize(gxmXml.size)
  258. # load properties
  259. self.properties = gxmXml.properties
  260. self.variables = gxmXml.variables
  261. # load actions
  262. for action in gxmXml.actions:
  263. actionItem = ModelAction(parent=self,
  264. x=action['pos'][0],
  265. y=action['pos'][1],
  266. width=action['size'][0],
  267. height=action['size'][1],
  268. task=action['task'],
  269. id=action['id'],
  270. label=action['label'],
  271. comment=action['comment'])
  272. if action['disabled']:
  273. actionItem.Enable(False)
  274. self.AddItem(actionItem, pos=actionItem.GetId() - 1)
  275. actionItem.SetValid(actionItem.GetTask().get_options())
  276. actionItem.GetLog() # substitute variables (-> valid/invalid)
  277. # load data & relations
  278. for data in gxmXml.data:
  279. dataItem = ModelData(parent=self,
  280. x=data['pos'][0],
  281. y=data['pos'][1],
  282. width=data['size'][0],
  283. height=data['size'][1],
  284. prompt=data['prompt'],
  285. value=data['value'])
  286. dataItem.SetIntermediate(data['intermediate'])
  287. dataItem.SetHasDisplay(data['display'])
  288. for rel in data['rels']:
  289. actionItem = self.FindAction(rel['id'])
  290. if rel['dir'] == 'from':
  291. relation = ModelRelation(
  292. parent=self,
  293. fromShape=dataItem,
  294. toShape=actionItem,
  295. param=rel['name'])
  296. else:
  297. relation = ModelRelation(
  298. parent=self,
  299. fromShape=actionItem,
  300. toShape=dataItem,
  301. param=rel['name'])
  302. relation.SetControlPoints(rel['points'])
  303. actionItem.AddRelation(relation)
  304. dataItem.AddRelation(relation)
  305. if self.canvas:
  306. dataItem.Update()
  307. # load loops
  308. for loop in gxmXml.loops:
  309. loopItem = ModelLoop(parent=self,
  310. x=loop['pos'][0],
  311. y=loop['pos'][1],
  312. width=loop['size'][0],
  313. height=loop['size'][1],
  314. label=loop['text'],
  315. id=loop['id'])
  316. self.AddItem(loopItem, pos=loopItem.GetId() - 1)
  317. # load conditions
  318. for condition in gxmXml.conditions:
  319. conditionItem = ModelCondition(parent=self,
  320. x=condition['pos'][0],
  321. y=condition['pos'][1],
  322. width=condition['size'][0],
  323. height=condition['size'][1],
  324. label=condition['text'],
  325. id=condition['id'])
  326. self.AddItem(conditionItem, pos=conditionItem.GetId() - 1)
  327. # define loops & if/else items
  328. for loop in gxmXml.loops:
  329. loopItem = self.GetItem(loop['id'], objType=ModelLoop)
  330. loopItem.SetItems(loop['items'])
  331. for idx in loop['items']:
  332. action = self.GetItem(idx, objType=ModelAction)
  333. action.SetBlock(loopItem)
  334. for condition in gxmXml.conditions:
  335. conditionItem = self.GetItem(condition['id'])
  336. for b in condition['items'].keys():
  337. alist = list()
  338. for aId in condition['items'][b]:
  339. action = self.GetItem(aId)
  340. alist.append(action)
  341. conditionItem.SetItems(alist, branch=b)
  342. items = conditionItem.GetItems()
  343. for b in items.keys():
  344. for action in items[b]:
  345. action.SetBlock(conditionItem)
  346. # load comments
  347. for comment in gxmXml.comments:
  348. commentItem = ModelComment(parent=self,
  349. x=comment['pos'][0],
  350. y=comment['pos'][1],
  351. width=comment['size'][0],
  352. height=comment['size'][1],
  353. id=comment['id'],
  354. label=comment['text'])
  355. self.AddItem(commentItem, pos=commentItem.GetId() - 1)
  356. def AddItem(self, newItem, pos=-1):
  357. """Add item to the list"""
  358. if pos != -1:
  359. self.items.insert(pos, newItem)
  360. else:
  361. self.items.append(newItem)
  362. # i = 1
  363. # for item in self.items:
  364. # item.SetId(i)
  365. # i += 1
  366. def IsValid(self):
  367. """Return True if model is valid"""
  368. if self.Validate():
  369. return False
  370. return True
  371. def Validate(self):
  372. """Validate model, return None if model is valid otherwise
  373. error string"""
  374. errList = list()
  375. variables = self.GetVariables().keys()
  376. pattern = re.compile(r'(.*)(%.+\s?)(.*)')
  377. for action in self.GetItems(objType=ModelAction):
  378. cmd = action.GetLog(string=False)
  379. task = GUI(show=None).ParseCommand(cmd=cmd)
  380. errList += map(lambda x: cmd[0] + ': ' + x, task.get_cmd_error())
  381. # check also variables
  382. for opt in cmd[1:]:
  383. if '=' not in opt:
  384. continue
  385. key, value = opt.split('=', 1)
  386. sval = pattern.search(value)
  387. if sval:
  388. var = sval.group(2).strip()[1:] # strip '%' from beginning
  389. found = False
  390. for v in variables:
  391. if var.startswith(v):
  392. found = True
  393. break
  394. if not found:
  395. report = True
  396. for item in filter(
  397. lambda x: isinstance(x, ModelLoop),
  398. action.GetBlock()):
  399. if var in item.GetLabel():
  400. report = False
  401. break
  402. if report:
  403. errList.append(
  404. cmd[0] +
  405. ": " +
  406. _("undefined variable '%s'") %
  407. var)
  408. # TODO: check variables in file only optionally
  409. ### errList += self._substituteFile(action, checkOnly = True)
  410. return errList
  411. def _substituteFile(self, item, params=None, checkOnly=False):
  412. """Subsitute variables in command file inputs
  413. :param bool checkOnly: tuble - True to check variable, don't touch files
  414. :return: list of undefined variables
  415. """
  416. errList = list()
  417. self.fileInput = dict()
  418. # collect ascii inputs
  419. for p in item.GetParams()['params']:
  420. if p.get('element', '') == 'file' and \
  421. p.get('prompt', '') == 'input' and \
  422. p.get('age', '') == 'old':
  423. filename = p.get('value', p.get('default', ''))
  424. if filename and \
  425. mimetypes.guess_type(filename)[0] == 'text/plain':
  426. self.fileInput[filename] = None
  427. for finput in self.fileInput:
  428. # read lines
  429. fd = open(finput, "r")
  430. try:
  431. data = self.fileInput[finput] = fd.read()
  432. finally:
  433. fd.close()
  434. # substitute variables
  435. write = False
  436. variables = self.GetVariables()
  437. for variable in variables:
  438. pattern = re.compile('%' + variable)
  439. value = ''
  440. if params and 'variables' in params:
  441. for p in params['variables']['params']:
  442. if variable == p.get('name', ''):
  443. if p.get('type', 'string') == 'string':
  444. value = p.get('value', '')
  445. else:
  446. value = str(p.get('value', ''))
  447. break
  448. if not value:
  449. value = variables[variable].get('value', '')
  450. data = pattern.sub(value, data)
  451. if not checkOnly:
  452. write = True
  453. pattern = re.compile(r'(.*)(%.+\s?)(.*)')
  454. sval = pattern.search(data)
  455. if sval:
  456. var = sval.group(2).strip()[1:] # ignore '%'
  457. cmd = item.GetLog(string=False)[0]
  458. errList.append(cmd + ": " + _("undefined variable '%s'") % var)
  459. if not checkOnly:
  460. if write:
  461. fd = open(finput, "w")
  462. try:
  463. fd.write(data)
  464. finally:
  465. fd.close()
  466. else:
  467. self.fileInput[finput] = None
  468. return errList
  469. def OnPrepare(self, item, params):
  470. self._substituteFile(item, params, checkOnly=False)
  471. def RunAction(self, item, params, log, onDone=None,
  472. onPrepare=None, statusbar=None):
  473. """Run given action
  474. :param item: action item
  475. :param params: parameters dict
  476. :param log: logging window
  477. :param onDone: on-done method
  478. :param onPrepare: on-prepare method
  479. :param statusbar: wx.StatusBar instance or None
  480. """
  481. name = '({0}) {1}'.format(item.GetId(), item.GetLabel())
  482. if name in params:
  483. paramsOrig = item.GetParams(dcopy=True)
  484. item.MergeParams(params[name])
  485. if statusbar:
  486. statusbar.SetStatusText(_('Running model...'), 0)
  487. data = {'item': item,
  488. 'params': copy.deepcopy(params)}
  489. log.RunCmd(command=item.GetLog(string=False, substitute=params),
  490. onDone=onDone, onPrepare=self.OnPrepare, userData=data)
  491. if name in params:
  492. item.SetParams(paramsOrig)
  493. def Run(self, log, onDone, parent=None):
  494. """Run model
  495. :param log: logging window (see gconsole.GConsole)
  496. :param onDone: on-done method
  497. :param parent: window for messages or None
  498. """
  499. if self.GetNumItems() < 1:
  500. GMessage(parent=parent,
  501. message=_('Model is empty. Nothing to run.'))
  502. return
  503. statusbar = None
  504. if isinstance(parent, wx.Frame):
  505. statusbar = parent.GetStatusBar()
  506. # validation
  507. if statusbar:
  508. statusbar.SetStatusText(_('Validating model...'), 0)
  509. errList = self.Validate()
  510. if statusbar:
  511. statusbar.SetStatusText('', 0)
  512. if errList:
  513. dlg = wx.MessageDialog(
  514. parent=parent,
  515. message=_(
  516. 'Model is not valid. Do you want to '
  517. 'run the model anyway?\n\n%s') %
  518. '\n'.join(errList),
  519. caption=_("Run model?"),
  520. style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  521. ret = dlg.ShowModal()
  522. dlg.Destroy()
  523. if ret != wx.ID_YES:
  524. return
  525. # parametrization
  526. params = self.Parameterize()
  527. delInterData = False
  528. if params:
  529. dlg = ModelParamDialog(parent=parent,
  530. model=self,
  531. params=params)
  532. dlg.CenterOnParent()
  533. ret = dlg.ShowModal()
  534. if ret != wx.ID_OK:
  535. dlg.Destroy()
  536. return
  537. err = dlg.GetErrors()
  538. delInterData = dlg.DeleteIntermediateData()
  539. dlg.Destroy()
  540. if err:
  541. GError(parent=parent, message=unicode('\n'.join(err)))
  542. return
  543. err = list()
  544. for key, item in six.iteritems(params):
  545. for p in item['params']:
  546. if p.get('value', '') == '':
  547. err.append(
  548. (key, p.get(
  549. 'name', ''), p.get(
  550. 'description', '')))
  551. if err:
  552. GError(
  553. parent=parent,
  554. message=_("Variables below not defined:") +
  555. "\n\n" +
  556. unicode(
  557. '\n'.join(
  558. map(
  559. lambda x: "%s: %s (%s)" %
  560. (x[0],
  561. x[1],
  562. x[2]),
  563. err))))
  564. return
  565. log.cmdThread.SetId(-1)
  566. for item in self.GetItems():
  567. if not item.IsEnabled():
  568. continue
  569. if isinstance(item, ModelAction):
  570. if item.GetBlockId():
  571. continue
  572. self.RunAction(item, params, log)
  573. elif isinstance(item, ModelLoop):
  574. cond = item.GetLabel()
  575. # substitute variables in condition
  576. variables = self.GetVariables()
  577. for variable in variables:
  578. pattern = re.compile('%' + variable)
  579. if pattern.search(cond):
  580. value = ''
  581. if params and 'variables' in params:
  582. for p in params['variables']['params']:
  583. if variable == p.get('name', ''):
  584. value = p.get('value', '')
  585. break
  586. if not value:
  587. value = variables[variable].get('value', '')
  588. if not value:
  589. continue
  590. vtype = variables[variable].get('type', 'string')
  591. if vtype == 'string':
  592. value = '"' + value + '"'
  593. cond = pattern.sub(value, cond)
  594. # split condition
  595. # TODO: this part needs some better solution
  596. condVar, condText = map(
  597. lambda x: x.strip(),
  598. re.split('\s* in \s*', cond))
  599. pattern = re.compile('%' + condVar)
  600. # for vars()[condVar] in eval(condText): ?
  601. vlist = list()
  602. if condText[0] == '`' and condText[-1] == '`':
  603. # run command
  604. cmd, dcmd = gtask.cmdlist_to_tuple(
  605. condText[1: -1].split(' '))
  606. ret = RunCommand(cmd,
  607. read=True,
  608. **dcmd)
  609. if ret:
  610. vlist = ret.splitlines()
  611. else:
  612. vlist = eval(condText)
  613. if 'variables' not in params:
  614. params['variables'] = {'params': []}
  615. varDict = {'name': condVar, 'value': ''}
  616. params['variables']['params'].append(varDict)
  617. for var in vlist:
  618. for action in item.GetItems(self.GetItems()):
  619. if not action.IsEnabled():
  620. continue
  621. varDict['value'] = var
  622. self.RunAction(item=action, params=params,
  623. log=log)
  624. params['variables']['params'].remove(varDict)
  625. if delInterData:
  626. self.DeleteIntermediateData(log)
  627. # discard values
  628. if params:
  629. for item in six.itervalues(params):
  630. for p in item['params']:
  631. p['value'] = ''
  632. def DeleteIntermediateData(self, log):
  633. """Detele intermediate data"""
  634. rast, vect, rast3d, msg = self.GetIntermediateData()
  635. if rast:
  636. log.RunCmd(['g.remove', '-f', 'type=raster',
  637. 'name=%s' % ','.join(rast)])
  638. if rast3d:
  639. log.RunCmd(['g.remove', '-f', 'type=raster_3d',
  640. 'name=%s' % ','.join(rast3d)])
  641. if vect:
  642. log.RunCmd(['g.remove', '-f', 'type=vector',
  643. 'name=%s' % ','.join(vect)])
  644. def GetIntermediateData(self):
  645. """Get info about intermediate data"""
  646. rast = list()
  647. rast3d = list()
  648. vect = list()
  649. for data in self.GetData():
  650. if not data.IsIntermediate():
  651. continue
  652. name = data.GetValue()
  653. prompt = data.GetPrompt()
  654. if prompt == 'raster':
  655. rast.append(name)
  656. elif prompt == 'vector':
  657. vect.append(name)
  658. elif prompt == 'raster_3d':
  659. rast3d.append(name)
  660. msg = ''
  661. if rast:
  662. msg += '\n\n%s: ' % _('Raster maps')
  663. msg += ', '.join(rast)
  664. if rast3d:
  665. msg += '\n\n%s: ' % _('3D raster maps')
  666. msg += ', '.join(rast3d)
  667. if vect:
  668. msg += '\n\n%s: ' % _('Vector maps')
  669. msg += ', '.join(vect)
  670. return rast, vect, rast3d, msg
  671. def Update(self):
  672. """Update model"""
  673. for item in self.items:
  674. item.Update()
  675. def IsParameterized(self):
  676. """Return True if model is parameterized"""
  677. if self.Parameterize():
  678. return True
  679. return False
  680. def Parameterize(self):
  681. """Return parameterized options"""
  682. result = dict()
  683. idx = 0
  684. if self.variables:
  685. params = list()
  686. result["variables"] = {'flags': list(),
  687. 'params': params,
  688. 'idx': idx}
  689. for name, values in six.iteritems(self.variables):
  690. gtype = values.get('type', 'string')
  691. if gtype in ('raster', 'vector', 'mapset',
  692. 'file', 'region', 'dir'):
  693. gisprompt = True
  694. prompt = gtype
  695. if gtype == 'raster':
  696. element = 'cell'
  697. else:
  698. element = gtype
  699. ptype = 'string'
  700. else:
  701. gisprompt = False
  702. prompt = None
  703. element = None
  704. ptype = gtype
  705. params.append({'gisprompt': gisprompt,
  706. 'multiple': False,
  707. 'description': values.get('description', ''),
  708. 'guidependency': '',
  709. 'default': '',
  710. 'age': None,
  711. 'required': True,
  712. 'value': values.get('value', ''),
  713. 'label': '',
  714. 'guisection': '',
  715. 'key_desc': '',
  716. 'values': list(),
  717. 'parameterized': False,
  718. 'values_desc': list(),
  719. 'prompt': prompt,
  720. 'element': element,
  721. 'type': ptype,
  722. 'name': name})
  723. idx += 1
  724. for action in self.GetItems(objType=ModelAction):
  725. if not action.IsEnabled():
  726. continue
  727. name = '({0}) {1}'.format(action.GetId(), action.GetLabel())
  728. params = action.GetParams()
  729. increment = False
  730. for f in params['flags']:
  731. if f.get('parameterized', False):
  732. if name not in result:
  733. increment = True
  734. result[name] = {'flags': list(),
  735. 'params': list(),
  736. 'idx': idx}
  737. result[name]['flags'].append(f)
  738. for p in params['params']:
  739. if p.get('parameterized', False):
  740. if name not in result:
  741. increment = True
  742. result[name] = {'flags': list(),
  743. 'params': list(),
  744. 'idx': idx}
  745. result[name]['params'].append(p)
  746. if increment:
  747. idx += 1
  748. self.variablesParams = result # record parameters
  749. return result
  750. class ModelObject(object):
  751. def __init__(self, id=-1, label=''):
  752. self.id = id # internal id, should be not changed
  753. self.label = ''
  754. self.rels = list() # list of ModelRelations
  755. self.isEnabled = True
  756. self.inBlock = list() # list of related loops/conditions
  757. def __del__(self):
  758. pass
  759. def GetLabel(self):
  760. """Get label"""
  761. return self.label
  762. def SetLabel(self, label=''):
  763. """Set label"""
  764. self.label = label
  765. def GetId(self):
  766. """Get id"""
  767. return self.id
  768. def SetId(self, newId):
  769. """Set id"""
  770. if self.inBlock:
  771. for loop in self.inBlock:
  772. # update block item
  773. loop.UpdateItem(self.id, newId)
  774. self.id = newId
  775. def AddRelation(self, rel):
  776. """Record new relation
  777. """
  778. self.rels.append(rel)
  779. def GetRelations(self, fdir=None):
  780. """Get list of relations
  781. :param bool fdir: True for 'from'
  782. """
  783. if fdir is None:
  784. return self.rels
  785. result = list()
  786. for rel in self.rels:
  787. if fdir == 'from':
  788. if rel.GetFrom() == self:
  789. result.append(rel)
  790. else:
  791. if rel.GetTo() == self:
  792. result.append(rel)
  793. return result
  794. def IsEnabled(self):
  795. """Get True if action is enabled, otherwise False"""
  796. return self.isEnabled
  797. def Enable(self, enabled=True):
  798. """Enable/disable action"""
  799. self.isEnabled = enabled
  800. self.Update()
  801. def Update(self):
  802. pass
  803. def SetBlock(self, item):
  804. """Add object to the block (loop/condition)
  805. :param item: reference to ModelLoop or ModelCondition which
  806. defines loops/condition
  807. """
  808. if item not in self.inBlock:
  809. self.inBlock.append(item)
  810. def UnSetBlock(self, item):
  811. """Remove object from the block (loop/consition)
  812. :param item: reference to ModelLoop or ModelCondition which
  813. defines loops/codition
  814. """
  815. if item in self.inBlock:
  816. self.inBlock.remove(item)
  817. def GetBlock(self):
  818. """Get list of related ModelObject(s) which defines block
  819. (loop/condition)
  820. :return: list of ModelObjects
  821. """
  822. return self.inBlock
  823. def GetBlockId(self):
  824. """Get list of related ids which defines block
  825. :return: list of ids
  826. """
  827. ret = list()
  828. for mo in self.inBlock:
  829. ret.append(mo.GetId())
  830. return ret
  831. class ModelAction(ModelObject, ogl.DividedShape):
  832. """Action class (GRASS module)"""
  833. def __init__(self, parent, x, y, id=-1, cmd=None, task=None,
  834. width=None, height=None, label=None, comment=''):
  835. ModelObject.__init__(self, id, label)
  836. self.parent = parent
  837. self.task = task
  838. self.comment = comment
  839. if not width:
  840. width = UserSettings.Get(
  841. group='modeler', key='action', subkey=(
  842. 'size', 'width'))
  843. if not height:
  844. height = UserSettings.Get(
  845. group='modeler', key='action', subkey=(
  846. 'size', 'height'))
  847. if cmd:
  848. self.task = GUI(show=None).ParseCommand(cmd=cmd)
  849. else:
  850. if task:
  851. self.task = task
  852. else:
  853. self.task = None
  854. self.propWin = None
  855. self.data = list() # list of connected data items
  856. self.isValid = False
  857. self.isParameterized = False
  858. if self.parent.GetCanvas():
  859. ogl.DividedShape.__init__(self, width, height)
  860. self.regionLabel = ogl.ShapeRegion()
  861. self.regionLabel.SetFormatMode(
  862. ogl.FORMAT_CENTRE_HORIZ | ogl.FORMAT_CENTRE_VERT)
  863. self.AddRegion(self.regionLabel)
  864. self.regionComment = None
  865. self.SetCanvas(self.parent)
  866. self.SetX(x)
  867. self.SetY(y)
  868. self._setPen()
  869. self._setBrush()
  870. self.SetLabel(label)
  871. if comment:
  872. self.SetComment(comment)
  873. self.SetRegionSizes()
  874. self.ReformatRegions()
  875. if self.task:
  876. self.SetValid(self.task.get_options())
  877. def _setBrush(self, running=False):
  878. """Set brush"""
  879. if running:
  880. color = UserSettings.Get(group='modeler', key='action',
  881. subkey=('color', 'running'))
  882. elif not self.isEnabled:
  883. color = UserSettings.Get(group='modeler', key='disabled',
  884. subkey='color')
  885. elif self.isValid:
  886. color = UserSettings.Get(group='modeler', key='action',
  887. subkey=('color', 'valid'))
  888. else:
  889. color = UserSettings.Get(group='modeler', key='action',
  890. subkey=('color', 'invalid'))
  891. wxColor = wx.Colour(color[0], color[1], color[2])
  892. self.SetBrush(wx.Brush(wxColor))
  893. def _setPen(self):
  894. """Set pen"""
  895. if self.isParameterized:
  896. width = int(UserSettings.Get(group='modeler', key='action',
  897. subkey=('width', 'parameterized')))
  898. else:
  899. width = int(UserSettings.Get(group='modeler', key='action',
  900. subkey=('width', 'default')))
  901. if self.isEnabled:
  902. style = wx.SOLID
  903. else:
  904. style = wx.DOT
  905. pen = wx.Pen(wx.BLACK, width, style)
  906. self.SetPen(pen)
  907. def ReformatRegions(self):
  908. rnum = 0
  909. canvas = self.parent.GetCanvas()
  910. dc = wx.ClientDC(canvas) # used for measuring
  911. for region in self.GetRegions():
  912. text = region.GetText()
  913. self.FormatText(dc, text, rnum)
  914. rnum += 1
  915. def OnSizingEndDragLeft(self, pt, x, y, keys, attch):
  916. ogl.DividedShape.OnSizingEndDragLeft(self, pt, x, y, keys, attch)
  917. self.SetRegionSizes()
  918. self.ReformatRegions()
  919. self.GetCanvas().Refresh()
  920. def SetLabel(self, label=None):
  921. """Set label
  922. :param label: if None use command string instead
  923. """
  924. if label:
  925. self.label = label
  926. elif self.label:
  927. label = self.label
  928. else:
  929. try:
  930. label = self.task.get_cmd(ignoreErrors=True)[0]
  931. except:
  932. label = _("unknown")
  933. idx = self.GetId()
  934. self.regionLabel.SetText('(%d) %s' % (idx, label))
  935. self.SetRegionSizes()
  936. self.ReformatRegions()
  937. def SetComment(self, comment):
  938. """Set comment"""
  939. self.comment = comment
  940. if self.regionComment is None:
  941. self.regionComment = ogl.ShapeRegion()
  942. self.regionComment.SetFormatMode(ogl.FORMAT_CENTRE_HORIZ)
  943. font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
  944. font.SetStyle(wx.ITALIC)
  945. self.regionComment.SetFont(font)
  946. # clear doesn't work
  947. # self.regionComment.ClearText()
  948. self.regionComment.SetText(comment)
  949. self.ClearRegions()
  950. self.AddRegion(self.regionLabel)
  951. self.regionLabel.SetProportions(0.0, 1.0)
  952. if self.comment:
  953. self.AddRegion(self.regionComment)
  954. self.regionLabel.SetProportions(0.0, 0.4)
  955. self.SetRegionSizes()
  956. self.ReformatRegions()
  957. def GetComment(self):
  958. """Get comment"""
  959. return self.comment
  960. def SetProperties(self, params, propwin):
  961. """Record properties dialog"""
  962. self.task.params = params['params']
  963. self.task.flags = params['flags']
  964. self.propWin = propwin
  965. def GetPropDialog(self):
  966. """Get properties dialog"""
  967. return self.propWin
  968. def GetLog(self, string=True, substitute=None):
  969. """Get logging info
  970. :param string: True to get cmd as a string otherwise a list
  971. :param substitute: dictionary of parameter to substitute or None
  972. """
  973. cmd = self.task.get_cmd(ignoreErrors=True, ignoreRequired=True,
  974. ignoreDefault=False)
  975. # substitute variables
  976. if substitute:
  977. variables = []
  978. if 'variables' in substitute:
  979. for p in substitute['variables']['params']:
  980. variables.append(p.get('name', ''))
  981. else:
  982. variables = self.parent.GetVariables()
  983. # order variables by length
  984. for variable in sorted(variables, key=len, reverse=True):
  985. pattern = re.compile('%' + variable)
  986. value = ''
  987. if substitute and 'variables' in substitute:
  988. for p in substitute['variables']['params']:
  989. if variable == p.get('name', ''):
  990. if p.get('type', 'string') == 'string':
  991. value = p.get('value', '')
  992. else:
  993. value = str(p.get('value', ''))
  994. break
  995. if not value:
  996. value = variables[variable].get('value', '')
  997. if not value:
  998. continue
  999. for idx in range(len(cmd)):
  1000. if pattern.search(cmd[idx]):
  1001. cmd[idx] = pattern.sub(value, cmd[idx])
  1002. idx += 1
  1003. if string:
  1004. if cmd is None:
  1005. return ''
  1006. else:
  1007. return ' '.join(cmd)
  1008. return cmd
  1009. def GetLabel(self):
  1010. """Get name"""
  1011. if self.label:
  1012. return self.label
  1013. cmd = self.task.get_cmd(ignoreErrors=True)
  1014. if cmd and len(cmd) > 0:
  1015. return cmd[0]
  1016. return _('unknown')
  1017. def GetParams(self, dcopy=False):
  1018. """Get dictionary of parameters"""
  1019. if dcopy:
  1020. return copy.deepcopy(self.task.get_options())
  1021. return self.task.get_options()
  1022. def GetTask(self):
  1023. """Get grassTask instance"""
  1024. return self.task
  1025. def SetParams(self, params):
  1026. """Set dictionary of parameters"""
  1027. self.task.params = params['params']
  1028. self.task.flags = params['flags']
  1029. def MergeParams(self, params):
  1030. """Merge dictionary of parameters"""
  1031. if 'flags' in params:
  1032. for f in params['flags']:
  1033. self.task.set_flag(f['name'],
  1034. f.get('value', False))
  1035. if 'params' in params:
  1036. for p in params['params']:
  1037. self.task.set_param(p['name'],
  1038. p.get('value', ''))
  1039. def SetValid(self, options):
  1040. """Set validity for action
  1041. :param options: dictionary with flags and params (gtask)
  1042. """
  1043. self.isValid = True
  1044. for f in options['flags']:
  1045. if f.get('parameterized', False):
  1046. self.isParameterized = True
  1047. break
  1048. for p in options['params']:
  1049. if self.isValid and p.get('required', False) and \
  1050. p.get('value', '') == '' and \
  1051. p.get('default', '') == '':
  1052. self.isValid = False
  1053. if not self.isParameterized and p.get('parameterized', False):
  1054. self.isParameterized = True
  1055. if self.parent.GetCanvas():
  1056. self._setBrush()
  1057. self._setPen()
  1058. def IsValid(self):
  1059. """Check validity (all required parameters set)"""
  1060. return self.isValid
  1061. def IsParameterized(self):
  1062. """Check if action is parameterized"""
  1063. return self.isParameterized
  1064. def GetParameterizedParams(self):
  1065. """Return parameterized flags and options"""
  1066. param = {'flags': [], 'params': []}
  1067. options = self.GetParams()
  1068. for f in options['flags']:
  1069. if f.get('parameterized', False):
  1070. param['flags'].append(f)
  1071. for p in options['params']:
  1072. if p.get('parameterized', False):
  1073. param['params'].append(p)
  1074. return param
  1075. def FindData(self, name):
  1076. """Find data item by name"""
  1077. for rel in self.GetRelations():
  1078. data = rel.GetData()
  1079. if name == rel.GetLabel() and name in data.GetLabel():
  1080. return data
  1081. return None
  1082. def Update(self, running=False):
  1083. """Update action"""
  1084. if running:
  1085. self._setBrush(running=True)
  1086. else:
  1087. self._setBrush()
  1088. self._setPen()
  1089. def OnDraw(self, dc):
  1090. """Draw action in canvas"""
  1091. self._setBrush()
  1092. self._setPen()
  1093. ogl.RectangleShape.Recentre(self, dc) # re-center text
  1094. ogl.RectangleShape.OnDraw(self, dc)
  1095. class ModelData(ModelObject, ogl.EllipseShape):
  1096. def __init__(self, parent, x, y, value='',
  1097. prompt='', width=None, height=None):
  1098. """Data item class
  1099. :param parent: window parent
  1100. :param x, y: position of the shape
  1101. :param fname, tname: list of parameter names from / to
  1102. :param value: value
  1103. :param prompt: type of GIS element
  1104. :param width, height: dimension of the shape
  1105. """
  1106. ModelObject.__init__(self)
  1107. self.parent = parent
  1108. self.value = value
  1109. self.prompt = prompt
  1110. self.intermediate = False
  1111. self.display = False
  1112. self.propWin = None
  1113. if not width:
  1114. width = UserSettings.Get(
  1115. group='modeler', key='data', subkey=(
  1116. 'size', 'width'))
  1117. if not height:
  1118. height = UserSettings.Get(
  1119. group='modeler', key='data', subkey=(
  1120. 'size', 'height'))
  1121. if self.parent.GetCanvas():
  1122. ogl.EllipseShape.__init__(self, width, height)
  1123. self.SetCanvas(self.parent)
  1124. self.SetX(x)
  1125. self.SetY(y)
  1126. self._setPen()
  1127. self._setBrush()
  1128. self.SetLabel()
  1129. def IsIntermediate(self):
  1130. """Checks if data item is intermediate"""
  1131. return self.intermediate
  1132. def SetIntermediate(self, im):
  1133. """Set intermediate flag"""
  1134. self.intermediate = im
  1135. def HasDisplay(self):
  1136. """Checks if data item is marked to be displayed"""
  1137. return self.display
  1138. def SetHasDisplay(self, tbd):
  1139. """Set to-be-displayed flag"""
  1140. self.display = tbd
  1141. def OnDraw(self, dc):
  1142. self._setPen()
  1143. ogl.EllipseShape.OnDraw(self, dc)
  1144. def GetLog(self, string=True):
  1145. """Get logging info"""
  1146. name = list()
  1147. for rel in self.GetRelations():
  1148. name.append(rel.GetLabel())
  1149. if name:
  1150. return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')'
  1151. else:
  1152. return self.value + ' (' + self.prompt + ')'
  1153. def GetLabel(self):
  1154. """Get list of names"""
  1155. name = list()
  1156. for rel in self.GetRelations():
  1157. name.append(rel.GetLabel())
  1158. return name
  1159. def GetPrompt(self):
  1160. """Get prompt"""
  1161. return self.prompt
  1162. def SetPrompt(self, prompt):
  1163. """Set prompt
  1164. :param prompt:
  1165. """
  1166. self.prompt = prompt
  1167. def GetValue(self):
  1168. """Get value"""
  1169. return self.value
  1170. def SetValue(self, value):
  1171. """Set value
  1172. :param value:
  1173. """
  1174. self.value = value
  1175. self.SetLabel()
  1176. for direction in ('from', 'to'):
  1177. for rel in self.GetRelations(direction):
  1178. if direction == 'from':
  1179. action = rel.GetTo()
  1180. else:
  1181. action = rel.GetFrom()
  1182. task = GUI(
  1183. show=None).ParseCommand(
  1184. cmd=action.GetLog(
  1185. string=False))
  1186. task.set_param(rel.GetLabel(), self.value)
  1187. action.MergeParams(task.get_options())
  1188. def GetPropDialog(self):
  1189. """Get properties dialog"""
  1190. return self.propWin
  1191. def SetPropDialog(self, win):
  1192. """Get properties dialog"""
  1193. self.propWin = win
  1194. def _setBrush(self):
  1195. """Set brush"""
  1196. if self.prompt == 'raster':
  1197. color = UserSettings.Get(group='modeler', key='data',
  1198. subkey=('color', 'raster'))
  1199. elif self.prompt == 'raster_3d':
  1200. color = UserSettings.Get(group='modeler', key='data',
  1201. subkey=('color', 'raster3d'))
  1202. elif self.prompt == 'vector':
  1203. color = UserSettings.Get(group='modeler', key='data',
  1204. subkey=('color', 'vector'))
  1205. elif self.prompt == 'dbtable':
  1206. color = UserSettings.Get(group='modeler', key='data',
  1207. subkey=('color', 'dbtable'))
  1208. else:
  1209. color = UserSettings.Get(group='modeler', key='action',
  1210. subkey=('color', 'invalid'))
  1211. wxColor = wx.Colour(color[0], color[1], color[2])
  1212. self.SetBrush(wx.Brush(wxColor))
  1213. def _setPen(self):
  1214. """Set pen"""
  1215. isParameterized = False
  1216. for rel in self.GetRelations('from'):
  1217. if rel.GetTo().IsParameterized():
  1218. isParameterized = True
  1219. break
  1220. if not isParameterized:
  1221. for rel in self.GetRelations('to'):
  1222. if rel.GetFrom().IsParameterized():
  1223. isParameterized = True
  1224. break
  1225. if isParameterized:
  1226. width = int(UserSettings.Get(group='modeler', key='action',
  1227. subkey=('width', 'parameterized')))
  1228. else:
  1229. width = int(UserSettings.Get(group='modeler', key='action',
  1230. subkey=('width', 'default')))
  1231. if self.intermediate:
  1232. style = wx.DOT
  1233. else:
  1234. style = wx.SOLID
  1235. pen = wx.Pen(wx.BLACK, width, style)
  1236. self.SetPen(pen)
  1237. def SetLabel(self):
  1238. """Update text"""
  1239. self.ClearText()
  1240. name = []
  1241. for rel in self.GetRelations():
  1242. name.append(rel.GetLabel())
  1243. self.AddText('/'.join(name))
  1244. if self.value:
  1245. self.AddText(self.value)
  1246. else:
  1247. self.AddText(_('<not defined>'))
  1248. def Update(self):
  1249. """Update action"""
  1250. self._setBrush()
  1251. self._setPen()
  1252. self.SetLabel()
  1253. def GetDisplayCmd(self):
  1254. """Get display command as list"""
  1255. cmd = []
  1256. if self.prompt == 'raster':
  1257. cmd.append('d.rast')
  1258. elif self.prompt == 'vector':
  1259. cmd.append('d.vect')
  1260. else:
  1261. raise GException("Unsupported display prompt: {}".format(
  1262. self.prompt))
  1263. cmd.append('map=' + self.value)
  1264. return cmd
  1265. class ModelRelation(ogl.LineShape):
  1266. """Data - action relation"""
  1267. def __init__(self, parent, fromShape, toShape, param=''):
  1268. self.fromShape = fromShape
  1269. self.toShape = toShape
  1270. self.param = param
  1271. self.parent = parent
  1272. self._points = None
  1273. if self.parent.GetCanvas():
  1274. ogl.LineShape.__init__(self)
  1275. def __del__(self):
  1276. if self in self.fromShape.rels:
  1277. self.fromShape.rels.remove(self)
  1278. if self in self.toShape.rels:
  1279. self.toShape.rels.remove(self)
  1280. def GetFrom(self):
  1281. """Get id of 'from' shape"""
  1282. return self.fromShape
  1283. def GetTo(self):
  1284. """Get id of 'to' shape"""
  1285. return self.toShape
  1286. def GetData(self):
  1287. """Get related ModelData instance
  1288. :return: ModelData instance
  1289. :return: None if not found
  1290. """
  1291. if isinstance(self.fromShape, ModelData):
  1292. return self.fromShape
  1293. elif isinstance(self.toShape, ModelData):
  1294. return self.toShape
  1295. return None
  1296. def GetLabel(self):
  1297. """Get parameter name"""
  1298. return self.param
  1299. def ResetShapes(self):
  1300. """Reset related objects"""
  1301. self.fromShape.ResetControlPoints()
  1302. self.toShape.ResetControlPoints()
  1303. self.ResetControlPoints()
  1304. def SetControlPoints(self, points):
  1305. """Set control points"""
  1306. self._points = points
  1307. def GetControlPoints(self):
  1308. """Get list of control points"""
  1309. return self._points
  1310. def _setPen(self):
  1311. """Set pen"""
  1312. pen = wx.Pen(wx.BLACK, 1, wx.SOLID)
  1313. self.SetPen(pen)
  1314. def OnDraw(self, dc):
  1315. """Draw relation"""
  1316. self._setPen()
  1317. ogl.LineShape.OnDraw(self, dc)
  1318. def SetName(self, param):
  1319. self.param = param
  1320. class ModelItem(ModelObject):
  1321. def __init__(self, parent, x, y, id=-1, width=None,
  1322. height=None, label='', items=[]):
  1323. """Abstract class for loops and conditions"""
  1324. ModelObject.__init__(self, id, label)
  1325. self.parent = parent
  1326. def _setPen(self):
  1327. """Set pen"""
  1328. if self.isEnabled:
  1329. style = wx.SOLID
  1330. else:
  1331. style = wx.DOT
  1332. pen = wx.Pen(wx.BLACK, 1, style)
  1333. self.SetPen(pen)
  1334. def SetId(self, id):
  1335. """Set loop id"""
  1336. self.id = id
  1337. def SetLabel(self, label=''):
  1338. """Set loop text (condition)"""
  1339. if label:
  1340. self.label = label
  1341. self.ClearText()
  1342. self.AddText('(' + str(self.id) + ') ' + self.label)
  1343. def GetLog(self):
  1344. """Get log info"""
  1345. if self.label:
  1346. return _("Condition: ") + self.label
  1347. else:
  1348. return _("Condition: not defined")
  1349. def AddRelation(self, rel):
  1350. """Record relation"""
  1351. self.rels.append(rel)
  1352. def Clear(self):
  1353. """Clear object, remove rels"""
  1354. self.rels = list()
  1355. class ModelLoop(ModelItem, ogl.RectangleShape):
  1356. def __init__(self, parent, x, y, id=-1, idx=-1,
  1357. width=None, height=None, label='', items=[]):
  1358. """Defines a loop"""
  1359. ModelItem.__init__(self, parent, x, y, id, width, height, label, items)
  1360. self.itemIds = list() # unordered
  1361. if not width:
  1362. width = UserSettings.Get(
  1363. group='modeler', key='loop', subkey=(
  1364. 'size', 'width'))
  1365. if not height:
  1366. height = UserSettings.Get(
  1367. group='modeler', key='loop', subkey=(
  1368. 'size', 'height'))
  1369. if self.parent.GetCanvas():
  1370. ogl.RectangleShape.__init__(self, width, height)
  1371. self.SetCanvas(self.parent)
  1372. self.SetX(x)
  1373. self.SetY(y)
  1374. self._setPen()
  1375. self._setBrush()
  1376. self.SetCornerRadius(100)
  1377. self.SetLabel(label)
  1378. def _setBrush(self):
  1379. """Set brush"""
  1380. if not self.isEnabled:
  1381. color = UserSettings.Get(group='modeler', key='disabled',
  1382. subkey='color')
  1383. else:
  1384. color = UserSettings.Get(group='modeler', key='loop',
  1385. subkey=('color', 'valid'))
  1386. wxColor = wx.Colour(color[0], color[1], color[2])
  1387. self.SetBrush(wx.Brush(wxColor))
  1388. def Enable(self, enabled=True):
  1389. """Enable/disable action"""
  1390. for idx in self.itemIds:
  1391. item = self.parent.FindAction(idx)
  1392. if item:
  1393. item.Enable(enabled)
  1394. ModelObject.Enable(self, enabled)
  1395. self.Update()
  1396. def Update(self):
  1397. self._setPen()
  1398. self._setBrush()
  1399. def GetItems(self, items):
  1400. """Get sorted items by id"""
  1401. result = list()
  1402. for item in items:
  1403. if item.GetId() in self.itemIds:
  1404. result.append(item)
  1405. return result
  1406. def SetItems(self, items):
  1407. """Set items (id)"""
  1408. self.itemIds = items
  1409. def UpdateItem(self, oldId, newId):
  1410. """Update item in the list"""
  1411. idx = self.itemIds.index(oldId)
  1412. if idx != -1:
  1413. self.itemIds[idx] = newId
  1414. def OnDraw(self, dc):
  1415. """Draw loop in canvas"""
  1416. self._setBrush()
  1417. ogl.RectangleShape.Recentre(self, dc) # re-center text
  1418. ogl.RectangleShape.OnDraw(self, dc)
  1419. class ModelCondition(ModelItem, ogl.PolygonShape):
  1420. def __init__(self, parent, x, y, id=-1, width=None, height=None, label='',
  1421. items={'if': [], 'else': []}):
  1422. """Defines a if-else condition"""
  1423. ModelItem.__init__(self, parent, x, y, id, width, height, label, items)
  1424. self.itemIds = {'if': [], 'else': []}
  1425. if not width:
  1426. self.width = UserSettings.Get(
  1427. group='modeler',
  1428. key='if-else',
  1429. subkey=(
  1430. 'size',
  1431. 'width'))
  1432. else:
  1433. self.width = width
  1434. if not height:
  1435. self.height = UserSettings.Get(
  1436. group='modeler',
  1437. key='if-else',
  1438. subkey=(
  1439. 'size',
  1440. 'height'))
  1441. else:
  1442. self.height = height
  1443. if self.parent.GetCanvas():
  1444. ogl.PolygonShape.__init__(self)
  1445. points = [(0, - self.height / 2),
  1446. (self.width / 2, 0),
  1447. (0, self.height / 2),
  1448. (- self.width / 2, 0)]
  1449. self.Create(points)
  1450. self.SetCanvas(self.parent)
  1451. self.SetX(x)
  1452. self.SetY(y)
  1453. self.SetPen(wx.BLACK_PEN)
  1454. if label:
  1455. self.AddText('(' + str(self.id) + ') ' + label)
  1456. else:
  1457. self.AddText('(' + str(self.id) + ')')
  1458. def GetLabel(self):
  1459. """Get name"""
  1460. return _("if-else")
  1461. def GetWidth(self):
  1462. """Get object width"""
  1463. return self.width
  1464. def GetHeight(self):
  1465. """Get object height"""
  1466. return self.height
  1467. def GetItems(self, items):
  1468. """Get sorted items by id"""
  1469. result = {'if': [], 'else': []}
  1470. for item in items:
  1471. if item.GetId() in self.itemIds['if']:
  1472. result['if'].append(item)
  1473. elif item.GetId() in self.itemIds['else']:
  1474. result['else'].append(item)
  1475. return result
  1476. def SetItems(self, items, branch='if'):
  1477. """Set items (id)
  1478. :param items: list of items
  1479. :param branch: 'if' / 'else'
  1480. """
  1481. if branch in ['if', 'else']:
  1482. self.itemIds[branch] = items
  1483. class ModelComment(ModelObject, ogl.RectangleShape):
  1484. def __init__(self, parent, x, y, id=-1, width=None, height=None, label=''):
  1485. """Defines a model comment"""
  1486. ModelObject.__init__(self, id, label)
  1487. if not width:
  1488. width = UserSettings.Get(
  1489. group='modeler', key='comment', subkey=(
  1490. 'size', 'width'))
  1491. if not height:
  1492. height = UserSettings.Get(
  1493. group='modeler', key='comment', subkey=(
  1494. 'size', 'height'))
  1495. if parent.GetCanvas():
  1496. ogl.RectangleShape.__init__(self, width, height)
  1497. self.SetCanvas(parent)
  1498. self.SetX(x)
  1499. self.SetY(y)
  1500. font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
  1501. font.SetStyle(wx.ITALIC)
  1502. self.SetFont(font)
  1503. self._setPen()
  1504. self._setBrush()
  1505. self.SetLabel(label)
  1506. def _setBrush(self, running=False):
  1507. """Set brush"""
  1508. color = UserSettings.Get(group='modeler', key='comment',
  1509. subkey='color')
  1510. wxColor = wx.Colour(color[0], color[1], color[2])
  1511. self.SetBrush(wx.Brush(wxColor))
  1512. def _setPen(self):
  1513. """Set pen"""
  1514. pen = wx.Pen(wx.BLACK, 1, wx.DOT)
  1515. self.SetPen(pen)
  1516. def SetLabel(self, label=None):
  1517. """Set label
  1518. :param label: if None use command string instead
  1519. """
  1520. if label:
  1521. self.label = label
  1522. elif self.label:
  1523. label = self.label
  1524. else:
  1525. label = ''
  1526. idx = self.GetId()
  1527. self.ClearText()
  1528. self.AddText('(%d) %s' % (idx, label))
  1529. def GetComment(self):
  1530. return self.GetLabel()
  1531. def SetComment(self, comment):
  1532. self.SetLabel(comment)
  1533. self.GetCanvas().Refresh()
  1534. class ProcessModelFile:
  1535. """Process GRASS model file (gxm)"""
  1536. def __init__(self, tree):
  1537. """A ElementTree handler for the GXM XML file, as defined in
  1538. grass-gxm.dtd.
  1539. """
  1540. self.tree = tree
  1541. self.root = self.tree.getroot()
  1542. # check if input is a valid GXM file
  1543. if self.root is None or self.root.tag != 'gxm':
  1544. if self.root is not None:
  1545. tagName = self.root.tag
  1546. else:
  1547. tabName = _("empty")
  1548. raise GException(
  1549. _("Details: unsupported tag name '{0}'.").format(tagName))
  1550. # list of actions, data
  1551. self.properties = dict()
  1552. self.variables = dict()
  1553. self.actions = list()
  1554. self.data = list()
  1555. self.loops = list()
  1556. self.conditions = list()
  1557. self.comments = list()
  1558. self._processWindow()
  1559. self._processProperties()
  1560. self._processVariables()
  1561. self._processItems()
  1562. self._processData()
  1563. def _filterValue(self, value):
  1564. """Filter value
  1565. :param value:
  1566. """
  1567. value = value.replace('&lt;', '<')
  1568. value = value.replace('&gt;', '>')
  1569. return value
  1570. def _getNodeText(self, node, tag, default=''):
  1571. """Get node text"""
  1572. p = node.find(tag)
  1573. if p is not None:
  1574. if p.text:
  1575. return utils.normalize_whitespace(p.text)
  1576. else:
  1577. return ''
  1578. return default
  1579. def _processWindow(self):
  1580. """Process window properties"""
  1581. node = self.root.find('window')
  1582. if node is None:
  1583. self.pos = self.size = None
  1584. return
  1585. self.pos, self.size = self._getDim(node)
  1586. def _processProperties(self):
  1587. """Process model properties"""
  1588. node = self.root.find('properties')
  1589. if node is None:
  1590. return
  1591. for key in ('name', 'description', 'author'):
  1592. self._processProperty(node, key)
  1593. for f in node.findall('flag'):
  1594. name = f.get('name', '')
  1595. if name == 'overwrite':
  1596. self.properties['overwrite'] = True
  1597. def _processProperty(self, pnode, name):
  1598. """Process given property"""
  1599. node = pnode.find(name)
  1600. if node is not None:
  1601. self.properties[name] = node.text
  1602. else:
  1603. self.properties[name] = ''
  1604. def _processVariables(self):
  1605. """Process model variables"""
  1606. vnode = self.root.find('variables')
  1607. if vnode is None:
  1608. return
  1609. for node in vnode.findall('variable'):
  1610. name = node.get('name', '')
  1611. if not name:
  1612. continue # should not happen
  1613. self.variables[name] = {'type': node.get('type', 'string')}
  1614. for key in ('description', 'value'):
  1615. self._processVariable(node, name, key)
  1616. def _processVariable(self, pnode, name, key):
  1617. """Process given variable"""
  1618. node = pnode.find(key)
  1619. if node is not None:
  1620. if node.text:
  1621. self.variables[name][key] = node.text
  1622. def _processItems(self):
  1623. """Process model items (actions, loops, conditions)"""
  1624. self._processActions()
  1625. self._processLoops()
  1626. self._processConditions()
  1627. self._processComments()
  1628. def _processActions(self):
  1629. """Process model file"""
  1630. for action in self.root.findall('action'):
  1631. pos, size = self._getDim(action)
  1632. disabled = False
  1633. task = action.find('task')
  1634. if task is not None:
  1635. if task.find('disabled') is not None:
  1636. disabled = True
  1637. task = self._processTask(task)
  1638. else:
  1639. task = None
  1640. aId = int(action.get('id', -1))
  1641. label = action.get('name')
  1642. comment = action.find('comment')
  1643. if comment is not None:
  1644. commentString = comment.text
  1645. else:
  1646. commentString = ''
  1647. self.actions.append({'pos': pos,
  1648. 'size': size,
  1649. 'task': task,
  1650. 'id': aId,
  1651. 'disabled': disabled,
  1652. 'label': label,
  1653. 'comment': commentString})
  1654. def _getDim(self, node):
  1655. """Get position and size of shape"""
  1656. pos = size = None
  1657. posAttr = node.get('pos', None)
  1658. if posAttr:
  1659. posVal = list(map(int, posAttr.split(',')))
  1660. try:
  1661. pos = (posVal[0], posVal[1])
  1662. except:
  1663. pos = None
  1664. sizeAttr = node.get('size', None)
  1665. if sizeAttr:
  1666. sizeVal = list(map(int, sizeAttr.split(',')))
  1667. try:
  1668. size = (sizeVal[0], sizeVal[1])
  1669. except:
  1670. size = None
  1671. return pos, size
  1672. def _processData(self):
  1673. """Process model file"""
  1674. for data in self.root.findall('data'):
  1675. pos, size = self._getDim(data)
  1676. param = data.find('data-parameter')
  1677. prompt = value = None
  1678. if param is not None:
  1679. prompt = param.get('prompt', None)
  1680. value = self._filterValue(self._getNodeText(param, 'value'))
  1681. intermediate = False if data.find('intermediate') is None else True
  1682. display = False if data.find('display') is None else True
  1683. rels = list()
  1684. for rel in data.findall('relation'):
  1685. defrel = {'id': int(rel.get('id', -1)),
  1686. 'dir': rel.get('dir', 'to'),
  1687. 'name': rel.get('name', '')}
  1688. points = list()
  1689. for point in rel.findall('point'):
  1690. x = self._filterValue(self._getNodeText(point, 'x'))
  1691. y = self._filterValue(self._getNodeText(point, 'y'))
  1692. points.append((float(x), float(y)))
  1693. defrel['points'] = points
  1694. rels.append(defrel)
  1695. self.data.append({'pos': pos,
  1696. 'size': size,
  1697. 'prompt': prompt,
  1698. 'value': value,
  1699. 'intermediate': intermediate,
  1700. 'display': display,
  1701. 'rels': rels})
  1702. def _processTask(self, node):
  1703. """Process task
  1704. :return: grassTask instance
  1705. :return: None on error
  1706. """
  1707. cmd = list()
  1708. parameterized = list()
  1709. name = node.get('name', None)
  1710. if not name:
  1711. return None
  1712. cmd.append(name)
  1713. # flags
  1714. for f in node.findall('flag'):
  1715. flag = f.get('name', '')
  1716. if f.get('parameterized', '0') == '1':
  1717. parameterized.append(('flag', flag))
  1718. if f.get('value', '1') == '0':
  1719. continue
  1720. if len(flag) > 1:
  1721. cmd.append('--' + flag)
  1722. else:
  1723. cmd.append('-' + flag)
  1724. # parameters
  1725. for p in node.findall('parameter'):
  1726. name = p.get('name', '')
  1727. if p.find('parameterized') is not None:
  1728. parameterized.append(('param', name))
  1729. cmd.append(
  1730. '%s=%s' %
  1731. (name, self._filterValue(
  1732. self._getNodeText(
  1733. p, 'value'))))
  1734. task, err = GUI(show=None, checkError=True).ParseCommand(cmd=cmd)
  1735. if err:
  1736. GWarning(os.linesep.join(err))
  1737. for opt, name in parameterized:
  1738. if opt == 'flag':
  1739. task.set_flag(name, True, element='parameterized')
  1740. else:
  1741. task.set_param(name, True, element='parameterized')
  1742. return task
  1743. def _processLoops(self):
  1744. """Process model loops"""
  1745. for node in self.root.findall('loop'):
  1746. pos, size = self._getDim(node)
  1747. text = self._filterValue(
  1748. self._getNodeText(
  1749. node, 'condition')).strip()
  1750. aid = list()
  1751. for anode in node.findall('item'):
  1752. try:
  1753. aid.append(int(anode.text))
  1754. except ValueError:
  1755. pass
  1756. self.loops.append({'pos': pos,
  1757. 'size': size,
  1758. 'text': text,
  1759. 'id': int(node.get('id', -1)),
  1760. 'items': aid})
  1761. def _processConditions(self):
  1762. """Process model conditions"""
  1763. for node in self.root.findall('if-else'):
  1764. pos, size = self._getDim(node)
  1765. text = self._filterValue(
  1766. self._getNodeText(
  1767. node, 'condition')).strip()
  1768. aid = {'if': list(),
  1769. 'else': list()}
  1770. for b in aid.keys():
  1771. bnode = node.find(b)
  1772. if bnode is None:
  1773. continue
  1774. for anode in bnode.findall('item'):
  1775. try:
  1776. aid[b].append(int(anode.text))
  1777. except ValueError:
  1778. pass
  1779. self.conditions.append({'pos': pos,
  1780. 'size': size,
  1781. 'text': text,
  1782. 'id': int(node.get('id', -1)),
  1783. 'items': aid})
  1784. def _processComments(self):
  1785. """Process model comments"""
  1786. for node in self.root.findall('comment'):
  1787. pos, size = self._getDim(node)
  1788. text = self._filterValue(node.text)
  1789. self.comments.append({'pos': pos,
  1790. 'size': size,
  1791. 'text': text,
  1792. 'id': int(node.get('id', -1)),
  1793. 'text': text})
  1794. class WriteModelFile:
  1795. """Generic class for writing model file"""
  1796. def __init__(self, fd, model):
  1797. self.fd = fd
  1798. self.model = model
  1799. self.properties = model.GetProperties()
  1800. self.variables = model.GetVariables()
  1801. self.items = model.GetItems()
  1802. self.indent = 0
  1803. self._header()
  1804. self._window()
  1805. self._properties()
  1806. self._variables()
  1807. self._items()
  1808. dataList = list()
  1809. for action in model.GetItems(objType=ModelAction):
  1810. for rel in action.GetRelations():
  1811. dataItem = rel.GetData()
  1812. if dataItem not in dataList:
  1813. dataList.append(dataItem)
  1814. self._data(dataList)
  1815. self._footer()
  1816. def _filterValue(self, value):
  1817. """Escapes value to be stored in XML.
  1818. :param value: string to be escaped as XML
  1819. :return: a XML-valid string
  1820. """
  1821. value = saxutils.escape(value)
  1822. return value
  1823. def _header(self):
  1824. """Write header"""
  1825. self.fd.write(
  1826. '<?xml version="1.0" encoding="%s"?>\n' %
  1827. GetDefaultEncoding(
  1828. forceUTF8=True))
  1829. self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n')
  1830. self.fd.write('%s<gxm>\n' % (' ' * self.indent))
  1831. self.indent += 4
  1832. def _footer(self):
  1833. """Write footer"""
  1834. self.indent -= 4
  1835. self.fd.write('%s</gxm>\n' % (' ' * self.indent))
  1836. def _window(self):
  1837. """Write window properties"""
  1838. canvas = self.model.GetCanvas()
  1839. if canvas is None:
  1840. return
  1841. win = canvas.parent
  1842. pos = win.GetPosition()
  1843. size = win.GetSize()
  1844. self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' %
  1845. (' ' * self.indent, pos[0], pos[1], size[0], size[1]))
  1846. def _properties(self):
  1847. """Write model properties"""
  1848. self.fd.write('%s<properties>\n' % (' ' * self.indent))
  1849. self.indent += 4
  1850. if self.properties['name']:
  1851. self.fd.write(
  1852. '%s<name>%s</name>\n' %
  1853. (' ' *
  1854. self.indent,
  1855. self.properties['name']))
  1856. if self.properties['description']:
  1857. self.fd.write(
  1858. '%s<description>%s</description>\n' %
  1859. (' ' *
  1860. self.indent,
  1861. self.properties['description']))
  1862. if self.properties['author']:
  1863. self.fd.write(
  1864. '%s<author>%s</author>\n' %
  1865. (' ' *
  1866. self.indent,
  1867. self.properties['author']))
  1868. if 'overwrite' in self.properties and \
  1869. self.properties['overwrite']:
  1870. self.fd.write(
  1871. '%s<flag name="overwrite" />\n' %
  1872. (' ' * self.indent))
  1873. self.indent -= 4
  1874. self.fd.write('%s</properties>\n' % (' ' * self.indent))
  1875. def _variables(self):
  1876. """Write model variables"""
  1877. if not self.variables:
  1878. return
  1879. self.fd.write('%s<variables>\n' % (' ' * self.indent))
  1880. self.indent += 4
  1881. for name, values in six.iteritems(self.variables):
  1882. self.fd.write(
  1883. '%s<variable name="%s" type="%s">\n' %
  1884. (' ' * self.indent, name, values['type']))
  1885. self.indent += 4
  1886. if 'value' in values:
  1887. self.fd.write('%s<value>%s</value>\n' %
  1888. (' ' * self.indent, values['value']))
  1889. if 'description' in values:
  1890. self.fd.write(
  1891. '%s<description>%s</description>\n' %
  1892. (' ' * self.indent, values['description']))
  1893. self.indent -= 4
  1894. self.fd.write('%s</variable>\n' % (' ' * self.indent))
  1895. self.indent -= 4
  1896. self.fd.write('%s</variables>\n' % (' ' * self.indent))
  1897. def _items(self):
  1898. """Write actions/loops/conditions"""
  1899. for item in self.items:
  1900. if isinstance(item, ModelAction):
  1901. self._action(item)
  1902. elif isinstance(item, ModelLoop):
  1903. self._loop(item)
  1904. elif isinstance(item, ModelCondition):
  1905. self._condition(item)
  1906. elif isinstance(item, ModelComment):
  1907. self._comment(item)
  1908. def _action(self, action):
  1909. """Write actions"""
  1910. self.fd.write(
  1911. '%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' %
  1912. (' ' * self.indent,
  1913. action.GetId(),
  1914. action.GetLabel(),
  1915. action.GetX(),
  1916. action.GetY(),
  1917. action.GetWidth(),
  1918. action.GetHeight()))
  1919. self.indent += 4
  1920. comment = action.GetComment()
  1921. if comment:
  1922. self.fd.write(
  1923. '%s<comment>%s</comment>\n' %
  1924. (' ' * self.indent, comment))
  1925. self.fd.write('%s<task name="%s">\n' %
  1926. (' ' * self.indent, action.GetLog(string=False)[0]))
  1927. self.indent += 4
  1928. if not action.IsEnabled():
  1929. self.fd.write('%s<disabled />\n' % (' ' * self.indent))
  1930. for key, val in six.iteritems(action.GetParams()):
  1931. if key == 'flags':
  1932. for f in val:
  1933. if f.get('value', False) or f.get('parameterized', False):
  1934. if f.get('parameterized', False):
  1935. if f.get('value', False) == False:
  1936. self.fd.write(
  1937. '%s<flag name="%s" value="0" parameterized="1" />\n' %
  1938. (' ' *
  1939. self.indent,
  1940. f.get(
  1941. 'name',
  1942. '')))
  1943. else:
  1944. self.fd.write(
  1945. '%s<flag name="%s" parameterized="1" />\n' %
  1946. (' ' *
  1947. self.indent,
  1948. f.get(
  1949. 'name',
  1950. '')))
  1951. else:
  1952. self.fd.write(
  1953. '%s<flag name="%s" />\n' %
  1954. (' ' * self.indent, f.get('name', '')))
  1955. else: # parameter
  1956. for p in val:
  1957. if not p.get(
  1958. 'value', '') and not p.get(
  1959. 'parameterized', False):
  1960. continue
  1961. self.fd.write('%s<parameter name="%s">\n' %
  1962. (' ' * self.indent, p.get('name', '')))
  1963. self.indent += 4
  1964. if p.get('parameterized', False):
  1965. self.fd.write(
  1966. '%s<parameterized />\n' %
  1967. (' ' * self.indent))
  1968. self.fd.write(
  1969. '%s<value>%s</value>\n' %
  1970. (' ' *
  1971. self.indent,
  1972. self._filterValue(
  1973. p.get(
  1974. 'value',
  1975. ''))))
  1976. self.indent -= 4
  1977. self.fd.write('%s</parameter>\n' % (' ' * self.indent))
  1978. self.indent -= 4
  1979. self.fd.write('%s</task>\n' % (' ' * self.indent))
  1980. self.indent -= 4
  1981. self.fd.write('%s</action>\n' % (' ' * self.indent))
  1982. def _data(self, dataList):
  1983. """Write data"""
  1984. for data in dataList:
  1985. self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' %
  1986. (' ' * self.indent, data.GetX(), data.GetY(),
  1987. data.GetWidth(), data.GetHeight()))
  1988. self.indent += 4
  1989. self.fd.write('%s<data-parameter prompt="%s">\n' %
  1990. (' ' * self.indent, data.GetPrompt()))
  1991. self.indent += 4
  1992. self.fd.write(
  1993. '%s<value>%s</value>\n' %
  1994. (' ' *
  1995. self.indent,
  1996. self._filterValue(
  1997. data.GetValue())))
  1998. self.indent -= 4
  1999. self.fd.write('%s</data-parameter>\n' % (' ' * self.indent))
  2000. if data.IsIntermediate():
  2001. self.fd.write('%s<intermediate />\n' % (' ' * self.indent))
  2002. if data.HasDisplay():
  2003. self.fd.write('%s<display />\n' % (' ' * self.indent))
  2004. # relations
  2005. for ft in ('from', 'to'):
  2006. for rel in data.GetRelations(ft):
  2007. if ft == 'from':
  2008. aid = rel.GetTo().GetId()
  2009. else:
  2010. aid = rel.GetFrom().GetId()
  2011. self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' %
  2012. (' ' * self.indent, ft, aid, rel.GetLabel()))
  2013. self.indent += 4
  2014. for point in rel.GetLineControlPoints()[1:-1]:
  2015. self.fd.write('%s<point>\n' % (' ' * self.indent))
  2016. self.indent += 4
  2017. x, y = point.Get()
  2018. self.fd.write(
  2019. '%s<x>%d</x>\n' %
  2020. (' ' * self.indent, int(x)))
  2021. self.fd.write(
  2022. '%s<y>%d</y>\n' %
  2023. (' ' * self.indent, int(y)))
  2024. self.indent -= 4
  2025. self.fd.write('%s</point>\n' % (' ' * self.indent))
  2026. self.indent -= 4
  2027. self.fd.write('%s</relation>\n' % (' ' * self.indent))
  2028. self.indent -= 4
  2029. self.fd.write('%s</data>\n' % (' ' * self.indent))
  2030. def _loop(self, loop):
  2031. """Write loops"""
  2032. self.fd.write(
  2033. '%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' %
  2034. (' ' *
  2035. self.indent,
  2036. loop.GetId(),
  2037. loop.GetX(),
  2038. loop.GetY(),
  2039. loop.GetWidth(),
  2040. loop.GetHeight()))
  2041. self.indent += 4
  2042. cond = loop.GetLabel()
  2043. if cond:
  2044. self.fd.write('%s<condition>%s</condition>\n' %
  2045. (' ' * self.indent, self._filterValue(cond)))
  2046. for item in loop.GetItems(self.model.GetItems(objType=ModelAction)):
  2047. self.fd.write('%s<item>%d</item>\n' %
  2048. (' ' * self.indent, item.GetId()))
  2049. self.indent -= 4
  2050. self.fd.write('%s</loop>\n' % (' ' * self.indent))
  2051. def _condition(self, condition):
  2052. """Write conditions"""
  2053. bbox = condition.GetBoundingBoxMin()
  2054. self.fd.write(
  2055. '%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' %
  2056. (' ' *
  2057. self.indent,
  2058. condition.GetId(),
  2059. condition.GetX(),
  2060. condition.GetY(),
  2061. bbox[0],
  2062. bbox[1]))
  2063. text = condition.GetLabel()
  2064. self.indent += 4
  2065. if text:
  2066. self.fd.write('%s<condition>%s</condition>\n' %
  2067. (' ' * self.indent, self._filterValue(text)))
  2068. items = condition.GetItems()
  2069. for b in items.keys():
  2070. if len(items[b]) < 1:
  2071. continue
  2072. self.fd.write('%s<%s>\n' % (' ' * self.indent, b))
  2073. self.indent += 4
  2074. for item in items[b]:
  2075. self.fd.write('%s<item>%d</item>\n' %
  2076. (' ' * self.indent, item.GetId()))
  2077. self.indent -= 4
  2078. self.fd.write('%s</%s>\n' % (' ' * self.indent, b))
  2079. self.indent -= 4
  2080. self.fd.write('%s</if-else>\n' % (' ' * self.indent))
  2081. def _comment(self, comment):
  2082. """Write comment"""
  2083. self.fd.write(
  2084. '%s<comment id="%d" pos="%d,%d" size="%d,%d">%s</comment>\n' %
  2085. (' ' *
  2086. self.indent,
  2087. comment.GetId(),
  2088. comment.GetX(),
  2089. comment.GetY(),
  2090. comment.GetWidth(),
  2091. comment.GetHeight(),
  2092. comment.GetLabel()))
  2093. class WritePythonFile:
  2094. def __init__(self, fd, model):
  2095. """Class for exporting model to Python script
  2096. :param fd: file descriptor
  2097. """
  2098. self.fd = fd
  2099. self.model = model
  2100. self.indent = 4
  2101. self._writePython()
  2102. def _getStandardizedOption(self, string):
  2103. if string == 'raster':
  2104. return 'G_OPT_R_MAP'
  2105. elif string == 'vector':
  2106. return 'G_OPT_V_MAP'
  2107. elif string == 'mapset':
  2108. return 'G_OPT_M_MAPSET'
  2109. elif string == 'file':
  2110. return 'G_OPT_F_INPUT'
  2111. elif string == 'dir':
  2112. return 'G_OPT_M_DIR'
  2113. elif string == 'region':
  2114. return 'G_OPT_M_REGION'
  2115. return ''
  2116. def _writePython(self):
  2117. """Write model to file"""
  2118. properties = self.model.GetProperties()
  2119. # header
  2120. self.fd.write(
  2121. r"""#!/usr/bin/env python3
  2122. #
  2123. #{header_begin}
  2124. #
  2125. # MODULE: {module_name}
  2126. #
  2127. # AUTHOR(S): {author}
  2128. #
  2129. # PURPOSE: {purpose}
  2130. #
  2131. # DATE: {date}
  2132. #
  2133. #{header_end}
  2134. """.format(header_begin='#' * 77,
  2135. module_name=properties['name'],
  2136. author=properties['author'],
  2137. purpose='\n# '.join(properties['description'].splitlines()),
  2138. date=time.asctime(),
  2139. header_end='#' * 77))
  2140. # UI
  2141. self.fd.write(
  2142. r"""
  2143. #%module
  2144. #% description: {description}
  2145. #%end
  2146. """.format(description=' '.join(properties['description'].splitlines())))
  2147. modelItems = self.model.GetItems()
  2148. for item in modelItems:
  2149. for flag in item.GetParameterizedParams()['flags']:
  2150. if flag['label']:
  2151. desc = flag['label']
  2152. else:
  2153. desc = flag['description']
  2154. self.fd.write(
  2155. r"""#%option
  2156. #% key: {flag_name}
  2157. #% description: {description}
  2158. #% required: yes
  2159. #% type: string
  2160. #% options: True, False
  2161. #% guisection: Flags
  2162. """.format(flag_name=self._getParamName(flag['name'], item),
  2163. description=desc))
  2164. if flag['value']:
  2165. self.fd.write("#% answer: {}\n".format(flag['value']))
  2166. else:
  2167. self.fd.write("#% answer: False\n")
  2168. self.fd.write("#%end\n")
  2169. for param in item.GetParameterizedParams()['params']:
  2170. if param['label']:
  2171. desc = param['label']
  2172. else:
  2173. desc = param['description']
  2174. self.fd.write(
  2175. r"""#%option
  2176. #% key: {param_name}
  2177. #% description: {description}
  2178. #% required: yes
  2179. """.format(param_name=self._getParamName(param['name'], item),
  2180. description=desc))
  2181. if param['type'] != 'float':
  2182. self.fd.write('#% type: {}\n'.format(param['type']))
  2183. else:
  2184. self.fd.write('#% type: double\n')
  2185. if param['key_desc']:
  2186. self.fd.write("#% key_desc: ")
  2187. self.fd.write(', '.join(param['key_desc']))
  2188. self.fd.write("\n")
  2189. if param['value']:
  2190. self.fd.write("#% answer: {}\n".format(param['value']))
  2191. self.fd.write("#%end\n")
  2192. # import modules
  2193. self.fd.write(
  2194. r"""
  2195. import sys
  2196. import os
  2197. import atexit
  2198. from grass.script import parser, run_command
  2199. """)
  2200. # cleanup()
  2201. rast, vect, rast3d, msg = self.model.GetIntermediateData()
  2202. self.fd.write(
  2203. r"""
  2204. def cleanup():
  2205. """)
  2206. if rast:
  2207. self.fd.write(
  2208. r""" run_command('g.remove', flags='f', type='raster',
  2209. name=%s)
  2210. """ % ','.join(map(lambda x: "'" + x + "'", rast)))
  2211. if vect:
  2212. self.fd.write(
  2213. r""" run_command('g.remove', flags='f', type='vector',
  2214. name=%s)
  2215. """ % ','.join(map(lambda x: "'" + x + "'", vect)))
  2216. if rast3d:
  2217. self.fd.write(
  2218. r""" run_command('g.remove', flags='f', type='raster_3d',
  2219. name=%s)
  2220. """ % ','.join(map(lambda x: "'" + x + "'", rast3d)))
  2221. if not rast and not vect and not rast3d:
  2222. self.fd.write(' pass\n')
  2223. self.fd.write("\ndef main(options, flags):\n")
  2224. for item in self.model.GetItems():
  2225. self._writePythonItem(item,
  2226. variables=item.GetParameterizedParams())
  2227. self.fd.write(" return 0\n")
  2228. for item in modelItems:
  2229. if item.GetParameterizedParams()['flags']:
  2230. self.fd.write(r"""
  2231. def getParameterizedFlags(paramFlags, itemFlags):
  2232. fl = ''
  2233. """)
  2234. self.fd.write(""" for i in [key for key, value in paramFlags.iteritems() if value == 'True']:
  2235. if i in itemFlags:
  2236. fl += i[-1]
  2237. return fl
  2238. """)
  2239. break
  2240. self.fd.write(
  2241. r"""
  2242. if __name__ == "__main__":
  2243. options, flags = parser()
  2244. atexit.register(cleanup)
  2245. """)
  2246. if properties.get('overwrite'):
  2247. self.fd.write(' os.environ["GRASS_OVERWRITE"] = "1"\n')
  2248. self.fd.write(' sys.exit(main())\n')
  2249. def _writePythonItem(self, item, ignoreBlock=True, variables={}):
  2250. """Write model object to Python file"""
  2251. if isinstance(item, ModelAction):
  2252. if ignoreBlock and item.GetBlockId():
  2253. # ignore items in loops of conditions
  2254. return
  2255. self._writePythonAction(item, variables=variables)
  2256. elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition):
  2257. # substitute condition
  2258. cond = item.GetLabel()
  2259. for variable in self.model.GetVariables():
  2260. pattern = re.compile('%' + variable)
  2261. if pattern.search(cond):
  2262. value = variables[variable].get('value', '')
  2263. if variables[variable].get('type', 'string') == 'string':
  2264. value = '"' + value + '"'
  2265. cond = pattern.sub(value, cond)
  2266. if isinstance(item, ModelLoop):
  2267. condVar, condText = map(
  2268. lambda x: x.strip(),
  2269. re.split('\s* in \s*', cond))
  2270. cond = "%sfor %s in " % (' ' * self.indent, condVar)
  2271. if condText[0] == '`' and condText[-1] == '`':
  2272. task = GUI(
  2273. show=None).ParseCommand(
  2274. cmd=utils.split(
  2275. condText[
  2276. 1:-
  2277. 1]))
  2278. cond += "grass.read_command("
  2279. cond += self._getPythonActionCmd(
  2280. task, len(cond), variables=[condVar]) + ".splitlines()"
  2281. else:
  2282. cond += condText
  2283. self.fd.write('%s:\n' % cond)
  2284. self.indent += 4
  2285. variablesLoop = variables.copy()
  2286. variablesLoop[condVar] = None
  2287. for action in item.GetItems(
  2288. self.model.GetItems(objType=ModelAction)):
  2289. self._writePythonItem(
  2290. action, ignoreBlock=False, variables=variablesLoop)
  2291. self.indent -= 4
  2292. if isinstance(item, ModelCondition):
  2293. self.fd.write('%sif %s:\n' % (' ' * self.indent, cond))
  2294. self.indent += 4
  2295. condItems = item.GetItems()
  2296. for action in condItems['if']:
  2297. self._writePythonItem(action, ignoreBlock=False)
  2298. if condItems['else']:
  2299. self.indent -= 4
  2300. self.fd.write('%selse:\n' % (' ' * self.indent))
  2301. self.indent += 4
  2302. for action in condItems['else']:
  2303. self._writePythonItem(action, ignoreBlock=False)
  2304. self.indent += 4
  2305. self.fd.write('\n')
  2306. if isinstance(item, ModelComment):
  2307. self._writePythonComment(item)
  2308. def _writePythonAction(self, item, variables={}):
  2309. """Write model action to Python file"""
  2310. task = GUI(show=None).ParseCommand(cmd=item.GetLog(string=False))
  2311. strcmd = "%srun_command(" % (' ' * self.indent)
  2312. self.fd.write(
  2313. strcmd +
  2314. self._getPythonActionCmd(
  2315. item,
  2316. task,
  2317. len(strcmd),
  2318. variables) +
  2319. '\n')
  2320. def _getPythonActionCmd(self, item, task, cmdIndent, variables={}):
  2321. opts = task.get_options()
  2322. ret = ''
  2323. flags = ''
  2324. params = list()
  2325. itemParameterizedFlags = list()
  2326. parameterizedParams = [v['name'] for v in variables['params']]
  2327. parameterizedFlags = [v['name'] for v in variables['flags']]
  2328. for f in opts['flags']:
  2329. if f.get('name') in parameterizedFlags and len(f.get('name')) == 1:
  2330. itemParameterizedFlags.append(
  2331. '"{}"'.format(self._getParamName(f.get('name'), item)))
  2332. if f.get('value', False):
  2333. name = f.get('name', '')
  2334. if len(name) > 1:
  2335. params.append('%s = True' % name)
  2336. else:
  2337. flags += name
  2338. itemParameterizedFlags = ', '.join(itemParameterizedFlags)
  2339. for p in opts['params']:
  2340. name = p.get('name', None)
  2341. value = p.get('value', None)
  2342. if (name and value) or (name in parameterizedParams):
  2343. ptype = p.get('type', 'string')
  2344. foundVar = False
  2345. if name in parameterizedParams:
  2346. foundVar = True
  2347. value = 'options["{}"]'.format(self._getParamName(name,
  2348. item))
  2349. if foundVar or ptype != 'string':
  2350. params.append("{}={}".format(name, value))
  2351. else:
  2352. params.append('{}="{}"'.format(name, value))
  2353. ret += '"%s"' % task.get_name()
  2354. if flags:
  2355. ret += ",\n{indent}flags='{fl}'".format(indent=' ' * cmdIndent,
  2356. fl=flags)
  2357. if itemParameterizedFlags:
  2358. ret += ' + getParameterizedFlags(options, [{}])'.format(
  2359. itemParameterizedFlags)
  2360. elif itemParameterizedFlags:
  2361. ret += ',\n{}flags=getParameterizedFlags(options, [{}])'.format(
  2362. ' ' * cmdIndent,
  2363. itemParameterizedFlags)
  2364. if len(params) > 0:
  2365. ret += ",\n"
  2366. for opt in params[:-1]:
  2367. ret += "%s%s,\n" % (' ' * cmdIndent, opt)
  2368. ret += "%s%s)" % (' ' * cmdIndent, params[-1])
  2369. else:
  2370. ret += ")"
  2371. return ret
  2372. def _writePythonComment(self, item):
  2373. """Write model comment to Python file"""
  2374. for line in item.GetLabel().splitlines():
  2375. self.fd.write('#' + line + '\n')
  2376. def _substituteVariable(self, string, variable, data):
  2377. """Substitute variable in the string
  2378. :param string: string to be modified
  2379. :param variable: variable to be substituted
  2380. :param data: data related to the variable
  2381. :return: modified string
  2382. """
  2383. result = ''
  2384. ss = re.split("\w*(%" + variable + ")w*", string)
  2385. if not ss[0] and not ss[-1]:
  2386. if data:
  2387. return "options['%s']" % variable
  2388. else:
  2389. return variable
  2390. for s in ss:
  2391. if not s or s == '"':
  2392. continue
  2393. if s == '%' + variable:
  2394. if data:
  2395. result += "+options['%s']+" % variable
  2396. else:
  2397. result += '+%s+' % variable
  2398. else:
  2399. result += '"' + s
  2400. if not s.endswith(']'): # options
  2401. result += '"'
  2402. return result.strip('+')
  2403. def _getParamName(self, parameter_name, item):
  2404. return '{module_name}{module_id}_{param_name}'.format(
  2405. module_name=re.sub('[^a-zA-Z]+', '', item.GetLabel()),
  2406. module_id=item.GetId(),
  2407. param_name=parameter_name)
  2408. class ModelParamDialog(wx.Dialog):
  2409. def __init__(
  2410. self, parent, model, params, id=wx.ID_ANY,
  2411. title=_("Model parameters"),
  2412. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
  2413. """Model parameters dialog
  2414. """
  2415. self.parent = parent
  2416. self._model = model
  2417. self.params = params
  2418. self.tasks = list() # list of tasks/pages
  2419. wx.Dialog.__init__(
  2420. self,
  2421. parent=parent,
  2422. id=id,
  2423. title=title,
  2424. style=style,
  2425. **kwargs)
  2426. self.notebook = GNotebook(parent=self,
  2427. style=globalvar.FNPageDStyle)
  2428. panel = self._createPages()
  2429. wx.CallAfter(self.notebook.SetSelection, 0)
  2430. # intermediate data?
  2431. self.interData = wx.CheckBox(parent=self, label=_(
  2432. "Delete intermediate data when finish"))
  2433. self.interData.SetValue(True)
  2434. rast, vect, rast3d, msg = self._model.GetIntermediateData()
  2435. if not rast and not vect and not rast3d:
  2436. self.interData.Hide()
  2437. self.btnCancel = Button(parent=self, id=wx.ID_CANCEL)
  2438. self.btnRun = Button(parent=self, id=wx.ID_OK,
  2439. label=_("&Run"))
  2440. self.btnRun.SetDefault()
  2441. self._layout()
  2442. size = self.GetBestSize()
  2443. self.SetMinSize(size)
  2444. self.SetSize((size.width, size.height +
  2445. panel.constrained_size[1] -
  2446. panel.panelMinHeight))
  2447. def _layout(self):
  2448. btnSizer = wx.StdDialogButtonSizer()
  2449. btnSizer.AddButton(self.btnCancel)
  2450. btnSizer.AddButton(self.btnRun)
  2451. btnSizer.Realize()
  2452. mainSizer = wx.BoxSizer(wx.VERTICAL)
  2453. mainSizer.Add(self.notebook, proportion=1,
  2454. flag=wx.EXPAND)
  2455. if self.interData.IsShown():
  2456. mainSizer.Add(self.interData, proportion=0,
  2457. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  2458. mainSizer.Add(wx.StaticLine(parent=self, id=wx.ID_ANY,
  2459. style=wx.LI_HORIZONTAL),
  2460. proportion=0,
  2461. flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
  2462. mainSizer.Add(btnSizer, proportion=0,
  2463. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  2464. self.SetSizer(mainSizer)
  2465. mainSizer.Fit(self)
  2466. def _createPages(self):
  2467. """Create for each parameterized module its own page"""
  2468. nameOrdered = [''] * len(self.params.keys())
  2469. for name, params in six.iteritems(self.params):
  2470. nameOrdered[params['idx']] = name
  2471. for name in nameOrdered:
  2472. params = self.params[name]
  2473. panel = self._createPage(name, params)
  2474. if name == 'variables':
  2475. name = _('Variables')
  2476. self.notebook.AddPage(page=panel, text=name)
  2477. return panel
  2478. def _createPage(self, name, params):
  2479. """Define notebook page"""
  2480. if name in globalvar.grassCmd:
  2481. task = gtask.grassTask(name)
  2482. else:
  2483. task = gtask.grassTask()
  2484. task.flags = params['flags']
  2485. task.params = params['params']
  2486. panel = CmdPanel(parent=self, id=wx.ID_ANY, task=task,
  2487. giface=GraphicalModelerGrassInterface(self._model))
  2488. self.tasks.append(task)
  2489. return panel
  2490. def GetErrors(self):
  2491. """Check for errors, get list of messages"""
  2492. errList = list()
  2493. for task in self.tasks:
  2494. errList += task.get_cmd_error()
  2495. return errList
  2496. def DeleteIntermediateData(self):
  2497. """Check if to detele intermediate data"""
  2498. if self.interData.IsShown() and self.interData.IsChecked():
  2499. return True
  2500. return False