model.py 93 KB

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