model.py 96 KB

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