forms.py 129 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423
  1. """
  2. @package gui_core.forms
  3. @brief Construct simple wxPython GUI from a GRASS command interface
  4. description.
  5. Classes:
  6. - forms::UpdateThread
  7. - forms::UpdateQThread
  8. - forms::TaskFrame
  9. - forms::CmdPanel
  10. - forms::GUI
  11. - forms::GrassGUIApp
  12. This program is just a coarse approach to automatically build a GUI
  13. from a xml-based GRASS user interface description.
  14. You need to have Python 2.4, wxPython 2.8 and python-xml.
  15. The XML stream is read from executing the command given in the
  16. command line, thus you may call it for instance this way:
  17. python <this file.py> r.basins.fill
  18. Or you set an alias or wrap the call up in a nice shell script, GUI
  19. environment ... please contribute your idea.
  20. Updated to wxPython 2.8 syntax and contrib widgets. Methods added to
  21. make it callable by gui. Method added to automatically re-run with
  22. pythonw on a Mac.
  23. .. todo::
  24. verify option value types
  25. Copyright(C) 2000-2015 by the GRASS Development Team
  26. This program is free software under the GPL(>=v2) Read the file
  27. COPYING coming with GRASS for details.
  28. @author Jan-Oliver Wagner <jan@intevation.de>
  29. @author Bernhard Reiter <bernhard@intevation.de>
  30. @author Michael Barton, Arizona State University
  31. @author Daniel Calvelo <dca.gis@gmail.com>
  32. @author Martin Landa <landa.martin@gmail.com>
  33. @author Luca Delucchi <lucadeluge@gmail.com>
  34. @author Stepan Turek <stepan.turek seznam.cz> (CoordinatesSelect)
  35. """
  36. from __future__ import print_function
  37. import sys
  38. import textwrap
  39. import os
  40. import copy
  41. import locale
  42. import six
  43. if sys.version_info.major == 2:
  44. import Queue
  45. else:
  46. import queue as Queue
  47. unicode = str
  48. import codecs
  49. from threading import Thread
  50. import wx
  51. try:
  52. import wx.lib.agw.flatnotebook as FN
  53. except ImportError:
  54. import wx.lib.flatnotebook as FN
  55. import wx.lib.colourselect as csel
  56. import wx.lib.filebrowsebutton as filebrowse
  57. from wx.lib.newevent import NewEvent
  58. try:
  59. import xml.etree.ElementTree as etree
  60. except ImportError:
  61. import elementtree.ElementTree as etree # Python <= 2.4
  62. # needed when started from command line and for testing
  63. if __name__ == "__main__":
  64. if os.getenv("GISBASE") is None:
  65. # intentionally not translatable
  66. sys.exit(
  67. "Failed to start. GRASS GIS is not running"
  68. " or the installation is broken."
  69. )
  70. from grass.script.setup import set_gui_path
  71. set_gui_path()
  72. from grass.pydispatch.signal import Signal
  73. from grass.script import core as grass
  74. from grass.script import task as gtask
  75. from core import globalvar
  76. from gui_core.widgets import (
  77. StaticWrapText,
  78. ScrolledPanel,
  79. ColorTablesComboBox,
  80. BarscalesComboBox,
  81. NArrowsComboBox,
  82. )
  83. from gui_core.ghelp import HelpPanel
  84. from gui_core import gselect
  85. from core import gcmd
  86. from core import utils
  87. from core.settings import UserSettings
  88. from gui_core.widgets import (
  89. FloatValidator,
  90. FormListbook,
  91. FormNotebook,
  92. GNotebook,
  93. PlacementValidator,
  94. )
  95. from core.giface import Notification, StandaloneGrassInterface
  96. from gui_core.widgets import LayersList
  97. from gui_core.wrap import (
  98. BitmapFromImage,
  99. Button,
  100. CloseButton,
  101. StaticText,
  102. StaticBox,
  103. SpinCtrl,
  104. CheckBox,
  105. BitmapButton,
  106. TextCtrl,
  107. NewId,
  108. )
  109. from core.debug import Debug
  110. wxUpdateDialog, EVT_DIALOG_UPDATE = NewEvent()
  111. """Hide some options in the GUI"""
  112. # _blackList = { 'enabled' : False,
  113. # 'items' : { 'r.buffer' : {'params' : ['input', 'output'],
  114. # 'flags' : ['z', 'overwrite']}}}
  115. _blackList = {"enabled": False, "items": {}}
  116. def text_beautify(someString, width=70):
  117. """Make really long texts shorter, clean up whitespace and remove
  118. trailing punctuation.
  119. """
  120. if width > 0:
  121. return escape_ampersand(
  122. os.linesep.join(
  123. textwrap.wrap(utils.normalize_whitespace(someString), width)
  124. ).strip(".,;:")
  125. )
  126. else:
  127. return escape_ampersand(utils.normalize_whitespace(someString).strip(".,;:"))
  128. def escape_ampersand(text):
  129. """Escapes ampersands with additional ampersand for GUI"""
  130. return text.replace("&", "&&")
  131. class UpdateThread(Thread):
  132. """Update dialog widgets in the thread"""
  133. def __init__(self, parent, event, eventId, task):
  134. Thread.__init__(self)
  135. self.parent = parent
  136. self.event = event
  137. self.eventId = eventId
  138. self.task = task
  139. self.setDaemon(True)
  140. # list of functions which updates the dialog
  141. self.data = {}
  142. def run(self):
  143. # get widget id
  144. if not self.eventId:
  145. for p in self.task.params:
  146. if p.get("gisprompt", False) is False:
  147. continue
  148. prompt = p.get("element", "")
  149. if prompt == "vector":
  150. name = p.get("name", "")
  151. if name in ("map", "input"):
  152. self.eventId = p["wxId"][0]
  153. if self.eventId is None:
  154. return
  155. p = self.task.get_param(self.eventId, element="wxId", raiseError=False)
  156. if not p or "wxId-bind" not in p:
  157. return
  158. # is this check necessary?
  159. # get widget prompt
  160. # pType = p.get('prompt', '')
  161. # if not pType:
  162. # return
  163. # check for map/input parameter
  164. pMap = self.task.get_param("map", raiseError=False)
  165. if not pMap:
  166. pMap = self.task.get_param("input", raiseError=False)
  167. if pMap:
  168. map = pMap.get("value", "")
  169. else:
  170. map = None
  171. # avoid running db.describe several times
  172. cparams = dict()
  173. cparams[map] = {
  174. "dbInfo": None,
  175. "layers": None,
  176. }
  177. # update reference widgets
  178. for uid in p["wxId-bind"]:
  179. win = self.parent.FindWindowById(uid)
  180. if not win:
  181. continue
  182. name = win.GetName()
  183. # @todo: replace name by isinstance() and signals
  184. pBind = self.task.get_param(uid, element="wxId", raiseError=False)
  185. if pBind:
  186. pBind["value"] = ""
  187. # set appropriate types in t.* modules and g.list/remove element
  188. # selections
  189. if name == "Select":
  190. type_param = self.task.get_param(
  191. "type", element="name", raiseError=False
  192. )
  193. if "all" in type_param.get("value"):
  194. etype = type_param.get("values")[:]
  195. if "all" in etype:
  196. etype.remove("all")
  197. etype = ",".join(etype)
  198. else:
  199. etype = type_param.get("value")
  200. if globalvar.CheckWxVersion([3]):
  201. self.data[win.SetElementList] = {"type": etype}
  202. else:
  203. self.data[win.GetParent().SetElementList] = {"type": etype}
  204. # t.(un)register has one type for 'input', 'maps'
  205. maps_param = self.task.get_param(
  206. "maps", element="name", raiseError=False
  207. )
  208. if self.task.get_name().startswith("t") and maps_param is not None:
  209. if maps_param["wxId"][0] != uid:
  210. element_dict = {
  211. "raster": "strds",
  212. "vector": "stvds",
  213. "raster_3d": "str3ds",
  214. }
  215. self.data[win.GetParent().SetType] = {
  216. "etype": element_dict[type_param.get("value")]
  217. }
  218. map = layer = None
  219. driver = db = None
  220. if name in ("LayerSelect", "ColumnSelect", "SqlWhereSelect"):
  221. if p.get("element", "") == "vector": # -> vector
  222. # get map name
  223. map = p.get("value", "")
  224. # get layer
  225. for bid in p["wxId-bind"]:
  226. p = self.task.get_param(bid, element="wxId", raiseError=False)
  227. if not p:
  228. continue
  229. if p.get("element", "") in ["layer", "layer_all"]:
  230. layer = p.get("value", "")
  231. if layer != "":
  232. layer = p.get("value", "")
  233. else:
  234. layer = p.get("default", "")
  235. break
  236. elif p.get("element", "") in ["layer", "layer_all"]: # -> layer
  237. # get layer
  238. layer = p.get("value", "")
  239. if layer != "":
  240. layer = p.get("value", "")
  241. else:
  242. layer = p.get("default", "")
  243. # get map name
  244. pMapL = self.task.get_param(
  245. p["wxId"][0], element="wxId-bind", raiseError=False
  246. )
  247. if pMapL:
  248. gui_deps = pMapL.get("guidependency", None)
  249. if gui_deps:
  250. gui_deps = gui_deps.split(",")
  251. if not gui_deps or (gui_deps and p.get("name", "") in gui_deps):
  252. map = pMapL.get("value", "")
  253. if name == "TableSelect" or (name == "ColumnSelect" and not map):
  254. pDriver = self.task.get_param(
  255. "dbdriver", element="prompt", raiseError=False
  256. )
  257. if pDriver:
  258. driver = pDriver.get("value", "")
  259. pDb = self.task.get_param("dbname", element="prompt", raiseError=False)
  260. if pDb:
  261. db = pDb.get("value", "")
  262. if name == "ColumnSelect":
  263. pTable = self.task.get_param(
  264. "dbtable", element="element", raiseError=False
  265. )
  266. if pTable:
  267. table = pTable.get("value", "")
  268. if name == "LayerSelect":
  269. # determine format
  270. native = True
  271. if pMap:
  272. for id in pMap["wxId"]:
  273. winVec = self.parent.FindWindowById(id)
  274. if (
  275. winVec.GetName() == "VectorFormat"
  276. and winVec.GetSelection() != 0
  277. ):
  278. native = False
  279. break
  280. # TODO: update only if needed
  281. if native:
  282. if map:
  283. self.data[win.InsertLayers] = {"vector": map}
  284. else:
  285. self.data[win.InsertLayers] = {}
  286. else:
  287. if map:
  288. self.data[win.InsertLayers] = {"dsn": map.rstrip("@OGR")}
  289. else:
  290. self.data[win.InsertLayers] = {}
  291. elif name == "TableSelect":
  292. self.data[win.InsertTables] = {"driver": driver, "database": db}
  293. elif name == "ColumnSelect":
  294. if map:
  295. if map not in cparams:
  296. cparams[map] = {
  297. "dbInfo": None,
  298. "layers": None,
  299. }
  300. if not cparams[map]["dbInfo"]:
  301. cparams[map]["dbInfo"] = gselect.VectorDBInfo(map)
  302. self.data[win.GetParent().InsertColumns] = {
  303. "vector": map,
  304. "layer": layer,
  305. "dbInfo": cparams[map]["dbInfo"],
  306. }
  307. else: # table
  308. if driver and db:
  309. self.data[win.GetParent().InsertTableColumns] = {
  310. "table": pTable.get("value"),
  311. "driver": driver,
  312. "database": db,
  313. }
  314. elif pTable:
  315. self.data[win.GetParent().InsertTableColumns] = {
  316. "table": pTable.get("value")
  317. }
  318. elif name == "SubGroupSelect":
  319. self.data[win.Insert] = {"group": p.get("value", "")}
  320. elif name == "SignatureSelect":
  321. if p.get("prompt", "group") == "group":
  322. group = p.get("value", "")
  323. pSubGroup = self.task.get_param(
  324. "subgroup", element="prompt", raiseError=False
  325. )
  326. if pSubGroup:
  327. subgroup = pSubGroup.get("value", "")
  328. else:
  329. subgroup = None
  330. else:
  331. subgroup = p.get("value", "")
  332. pGroup = self.task.get_param(
  333. "group", element="prompt", raiseError=False
  334. )
  335. if pGroup:
  336. group = pGroup.get("value", "")
  337. else:
  338. group = None
  339. self.data[win.Insert] = {"group": group, "subgroup": subgroup}
  340. elif name == "LocationSelect":
  341. pDbase = self.task.get_param(
  342. "dbase", element="element", raiseError=False
  343. )
  344. if pDbase:
  345. self.data[win.UpdateItems] = {"dbase": pDbase.get("value", "")}
  346. elif name == "MapsetSelect":
  347. pDbase = self.task.get_param(
  348. "dbase", element="element", raiseError=False
  349. )
  350. pLocation = self.task.get_param(
  351. "location", element="element", raiseError=False
  352. )
  353. if pDbase and pLocation:
  354. self.data[win.UpdateItems] = {
  355. "dbase": pDbase.get("value", ""),
  356. "location": pLocation.get("value", ""),
  357. }
  358. elif name == "ProjSelect":
  359. pDbase = self.task.get_param(
  360. "dbase", element="element", raiseError=False
  361. )
  362. pLocation = self.task.get_param(
  363. "location", element="element", raiseError=False
  364. )
  365. pMapset = self.task.get_param(
  366. "mapset", element="element", raiseError=False
  367. )
  368. if pDbase and pLocation and pMapset:
  369. self.data[win.UpdateItems] = {
  370. "dbase": pDbase.get("value", ""),
  371. "location": pLocation.get("value", ""),
  372. "mapset": pMapset.get("value", ""),
  373. }
  374. elif name == "SqlWhereSelect":
  375. if map:
  376. self.data[win.GetParent().SetData] = {"vector": map, "layer": layer}
  377. # TODO: table?
  378. def UpdateDialog(parent, event, eventId, task):
  379. return UpdateThread(parent, event, eventId, task)
  380. class UpdateQThread(Thread):
  381. """Update dialog widgets in the thread"""
  382. requestId = 0
  383. def __init__(self, parent, requestQ, resultQ, **kwds):
  384. Thread.__init__(self, **kwds)
  385. self.parent = parent # cmdPanel
  386. self.setDaemon(True)
  387. self.requestQ = requestQ
  388. self.resultQ = resultQ
  389. self.start()
  390. def Update(self, callable, *args, **kwds):
  391. UpdateQThread.requestId += 1
  392. self.request = None
  393. self.requestQ.put((UpdateQThread.requestId, callable, args, kwds))
  394. return UpdateQThread.requestId
  395. def run(self):
  396. while True:
  397. requestId, callable, args, kwds = self.requestQ.get()
  398. self.request = callable(*args, **kwds)
  399. self.resultQ.put((requestId, self.request.run()))
  400. if self.request:
  401. event = wxUpdateDialog(data=self.request.data)
  402. wx.PostEvent(self.parent, event)
  403. class TaskFrame(wx.Frame):
  404. """This is the Frame containing the dialog for options input.
  405. The dialog is organized in a notebook according to the guisections
  406. defined by each GRASS command.
  407. If run with a parent, it may Apply, Ok or Cancel; the latter two
  408. close the dialog. The former two trigger a callback.
  409. If run standalone, it will allow execution of the command.
  410. The command is checked and sent to the clipboard when clicking
  411. 'Copy'.
  412. """
  413. def __init__(
  414. self,
  415. parent,
  416. giface,
  417. task_description,
  418. id=wx.ID_ANY,
  419. get_dcmd=None,
  420. layer=None,
  421. title=None,
  422. style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
  423. **kwargs,
  424. ):
  425. self.get_dcmd = get_dcmd
  426. self.layer = layer
  427. self.task = task_description
  428. self.parent = parent # LayerTree | Modeler | None | ...
  429. self._giface = giface
  430. self.dialogClosing = Signal("TaskFrame.dialogClosing")
  431. # Module name as title by default
  432. if not title:
  433. title = self.task.get_name()
  434. wx.Frame.__init__(
  435. self,
  436. parent=parent,
  437. id=id,
  438. title=title,
  439. name="MainFrame",
  440. style=style,
  441. **kwargs,
  442. )
  443. self.locale = wx.Locale(language=wx.LANGUAGE_DEFAULT)
  444. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  445. # statusbar
  446. self.CreateStatusBar()
  447. # icon
  448. self.SetIcon(
  449. wx.Icon(
  450. os.path.join(globalvar.ICONDIR, "grass_dialog.ico"), wx.BITMAP_TYPE_ICO
  451. )
  452. )
  453. guisizer = wx.BoxSizer(wx.VERTICAL)
  454. # set appropriate output window
  455. if self.parent:
  456. self.standalone = False
  457. else:
  458. self.standalone = True
  459. # logo + description
  460. topsizer = wx.BoxSizer(wx.HORIZONTAL)
  461. # GRASS logo
  462. self.logo = wx.StaticBitmap(
  463. self.panel,
  464. -1,
  465. wx.Bitmap(
  466. name=os.path.join(globalvar.IMGDIR, "grass_form.png"),
  467. type=wx.BITMAP_TYPE_PNG,
  468. ),
  469. )
  470. topsizer.Add(
  471. self.logo, proportion=0, border=3, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL
  472. )
  473. # add module description
  474. if self.task.label:
  475. module_desc = self.task.label + " " + self.task.description
  476. else:
  477. module_desc = self.task.description
  478. self.description = StaticWrapText(parent=self.panel, label=module_desc)
  479. topsizer.Add(self.description, proportion=1, border=5, flag=wx.ALL | wx.EXPAND)
  480. guisizer.Add(topsizer, proportion=0, flag=wx.EXPAND)
  481. self.panel.SetSizerAndFit(guisizer)
  482. self.Layout()
  483. # notebooks
  484. self.notebookpanel = CmdPanel(
  485. parent=self.panel, giface=self._giface, task=self.task, frame=self
  486. )
  487. self._gconsole = self.notebookpanel._gconsole
  488. if self._gconsole:
  489. self._gconsole.mapCreated.connect(self.OnMapCreated)
  490. self._gconsole.updateMap.connect(lambda: self._giface.updateMap.emit())
  491. self.goutput = self.notebookpanel.goutput
  492. if self.goutput:
  493. self.goutput.showNotification.connect(
  494. lambda message: self.SetStatusText(message)
  495. )
  496. self.notebookpanel.OnUpdateValues = self.updateValuesHook
  497. guisizer.Add(self.notebookpanel, proportion=1, flag=wx.EXPAND)
  498. # status bar
  499. status_text = _("Enter parameters for '") + self.task.name + "'"
  500. try:
  501. self.task.get_cmd()
  502. self.updateValuesHook()
  503. except ValueError:
  504. self.SetStatusText(status_text)
  505. # buttons
  506. btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
  507. # cancel
  508. self.btn_cancel = CloseButton(parent=self.panel)
  509. self.btn_cancel.SetToolTip(
  510. _("Close this window without executing the command (Ctrl+Q)")
  511. )
  512. btnsizer.Add(
  513. self.btn_cancel, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10
  514. )
  515. self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  516. # bind closing to ESC and CTRL+Q
  517. self.Bind(wx.EVT_MENU, self.OnCancel, id=wx.ID_CANCEL)
  518. accelTableList = [(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, wx.ID_CANCEL)]
  519. accelTableList.append((wx.ACCEL_CTRL, ord("Q"), wx.ID_CANCEL))
  520. # TODO: bind Ctrl-t for tile windows here (trac #2004)
  521. if self.get_dcmd is not None: # A callback has been set up
  522. btn_apply = Button(parent=self.panel, id=wx.ID_APPLY)
  523. btn_ok = Button(parent=self.panel, id=wx.ID_OK)
  524. btn_ok.SetDefault()
  525. btnsizer.Add(
  526. btn_apply, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10
  527. )
  528. btnsizer.Add(btn_ok, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10)
  529. btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
  530. btn_ok.Bind(wx.EVT_BUTTON, self.OnOK)
  531. else: # We're standalone
  532. # run
  533. self.btn_run = Button(parent=self.panel, id=wx.ID_OK, label=_("&Run"))
  534. self.btn_run.SetToolTip(_("Run the command (Ctrl+R)"))
  535. self.btn_run.SetDefault()
  536. btnsizer.Add(
  537. self.btn_run, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10
  538. )
  539. self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
  540. self.Bind(wx.EVT_MENU, self.OnRun, id=wx.ID_OK)
  541. accelTableList.append((wx.ACCEL_CTRL, ord("R"), wx.ID_OK))
  542. # copy
  543. self.btn_clipboard = Button(parent=self.panel, id=wx.ID_ANY, label=_("Copy"))
  544. self.btn_clipboard.SetToolTip(
  545. _("Copy the current command string to the clipboard")
  546. )
  547. btnsizer.Add(
  548. self.btn_clipboard, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10
  549. )
  550. self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopyCommand)
  551. # help
  552. self.btn_help = Button(parent=self.panel, id=wx.ID_HELP)
  553. self.btn_help.SetToolTip(_("Show manual page of the command (Ctrl+H)"))
  554. self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
  555. self.Bind(wx.EVT_MENU, self.OnHelp, id=wx.ID_HELP)
  556. accelTableList.append((wx.ACCEL_CTRL, ord("H"), wx.ID_HELP))
  557. if self.notebookpanel.notebook.GetPageIndexByName("manual") < 0:
  558. self.btn_help.Hide()
  559. # add help button
  560. btnsizer.Add(
  561. self.btn_help, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10
  562. )
  563. guisizer.Add(
  564. btnsizer, proportion=0, flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, border=30
  565. )
  566. # abort key bindings
  567. abortId = NewId()
  568. self.Bind(wx.EVT_MENU, self.OnAbort, id=abortId)
  569. accelTableList.append((wx.ACCEL_CTRL, ord("S"), abortId))
  570. # set accelerator table
  571. accelTable = wx.AcceleratorTable(accelTableList)
  572. self.SetAcceleratorTable(accelTable)
  573. if self._giface and self._giface.GetLayerTree():
  574. addLayer = False
  575. for p in self.task.params:
  576. if p.get("age", "old") == "new" and p.get("prompt", "") in (
  577. "raster",
  578. "vector",
  579. "raster_3d",
  580. ):
  581. addLayer = True
  582. if addLayer:
  583. # add newly created map into layer tree
  584. self.addbox = wx.CheckBox(
  585. parent=self.panel,
  586. label=_("Add created map(s) into layer tree"),
  587. style=wx.NO_BORDER,
  588. )
  589. self.addbox.SetValue(
  590. UserSettings.Get(group="cmd", key="addNewLayer", subkey="enabled")
  591. )
  592. guisizer.Add(
  593. self.addbox,
  594. proportion=0,
  595. flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
  596. border=5,
  597. )
  598. hasNew = False
  599. for p in self.task.params:
  600. if p.get("age", "old") == "new":
  601. hasNew = True
  602. break
  603. if self.get_dcmd is None and hasNew:
  604. # close dialog when command is terminated
  605. self.closebox = CheckBox(
  606. parent=self.panel, label=_("Close dialog on finish"), style=wx.NO_BORDER
  607. )
  608. self.closebox.SetValue(
  609. UserSettings.Get(group="cmd", key="closeDlg", subkey="enabled")
  610. )
  611. self.closebox.SetToolTip(
  612. _(
  613. "Close dialog when command is successfully finished. "
  614. "Change this settings in Preferences dialog ('Command' tab)."
  615. )
  616. )
  617. guisizer.Add(
  618. self.closebox,
  619. proportion=0,
  620. flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
  621. border=5,
  622. )
  623. # bindings
  624. self.Bind(wx.EVT_CLOSE, self.OnCancel)
  625. # do layout
  626. # called automatically by SetSizer()
  627. self.panel.SetAutoLayout(True)
  628. self.panel.SetSizerAndFit(guisizer)
  629. sizeFrame = self.GetBestSize()
  630. self.SetMinSize(sizeFrame)
  631. if hasattr(self, "closebox"):
  632. scale = 0.33
  633. else:
  634. scale = 0.50
  635. self.SetSize(
  636. wx.Size(
  637. round(sizeFrame[0]),
  638. round(
  639. sizeFrame[1]
  640. + scale
  641. * max(
  642. self.notebookpanel.panelMinHeight,
  643. self.notebookpanel.constrained_size[1],
  644. )
  645. ),
  646. )
  647. )
  648. # thread to update dialog
  649. # create queues
  650. self.requestQ = Queue.Queue()
  651. self.resultQ = Queue.Queue()
  652. self.updateThread = UpdateQThread(
  653. self.notebookpanel, self.requestQ, self.resultQ
  654. )
  655. self.Layout()
  656. # keep initial window size limited for small screens
  657. width, height = self.GetSize()
  658. self.SetSize(wx.Size(min(width, 650), min(height, 500)))
  659. # fix goutput's pane size (required for Mac OSX)
  660. if self.goutput:
  661. self.goutput.SetSashPosition(int(self.GetSize()[1] * 0.75))
  662. def MakeModal(self, modal=True):
  663. if globalvar.wxPythonPhoenix:
  664. if modal and not hasattr(self, "_disabler"):
  665. self._disabler = wx.WindowDisabler(self)
  666. if not modal and hasattr(self, "_disabler"):
  667. del self._disabler
  668. else:
  669. super(TaskFrame, self).MakeModal(modal)
  670. def updateValuesHook(self, event=None):
  671. """Update status bar data"""
  672. self.SetStatusText(
  673. " ".join(
  674. [
  675. gcmd.DecodeString(each) if isinstance(each, str) else each
  676. for each in self.notebookpanel.createCmd(ignoreErrors=True)
  677. ]
  678. )
  679. )
  680. if event:
  681. event.Skip()
  682. def OnDone(self, event):
  683. """This function is launched from OnRun() when command is
  684. finished
  685. """
  686. if hasattr(self, "btn_cancel"):
  687. self.btn_cancel.Enable(True)
  688. if hasattr(self, "btn_clipboard"):
  689. self.btn_clipboard.Enable(True)
  690. if hasattr(self, "btn_help"):
  691. self.btn_help.Enable(True)
  692. if hasattr(self, "btn_run"):
  693. self.btn_run.Enable(True)
  694. if (
  695. hasattr(self, "get_dcmd")
  696. and self.get_dcmd is None
  697. and hasattr(self, "closebox")
  698. and self.closebox.IsChecked()
  699. and (event.returncode == 0)
  700. ):
  701. # was closed also when aborted but better is leave it open
  702. wx.CallLater(2000, self.Close)
  703. def OnMapCreated(self, name, ltype):
  704. """Map created or changed
  705. :param name: map name
  706. :param ltype: layer type (prompt value)
  707. """
  708. if hasattr(self, "addbox") and self.addbox.IsChecked():
  709. add = True
  710. else:
  711. add = False
  712. if self._giface:
  713. self._giface.mapCreated.emit(name=name, ltype=ltype, add=add)
  714. def OnOK(self, event):
  715. """OK button pressed"""
  716. cmd = self.OnApply(event)
  717. if cmd is not None and self.get_dcmd is not None:
  718. self.OnCancel(event)
  719. def OnApply(self, event):
  720. """Apply the command"""
  721. if self._giface and hasattr(self._giface, "_model"):
  722. cmd = self.createCmd(ignoreErrors=True, ignoreRequired=True)
  723. else:
  724. cmd = self.createCmd()
  725. if cmd is not None and self.get_dcmd is not None:
  726. # return d.* command to layer tree for rendering
  727. self.get_dcmd(
  728. cmd,
  729. self.layer,
  730. {"params": self.task.params, "flags": self.task.flags},
  731. self,
  732. )
  733. # echo d.* command to output console
  734. # self.parent.writeDCommand(cmd)
  735. return cmd
  736. def OnRun(self, event):
  737. """Run the command"""
  738. cmd = self.createCmd()
  739. if not cmd or len(cmd) < 1:
  740. return
  741. ret = 0
  742. if self.standalone or cmd[0][0:2] != "d.":
  743. # Send any non-display command to parent window (probably wxgui.py)
  744. # put to parents switch to 'Command output'
  745. self.notebookpanel.notebook.SetSelectionByName("output")
  746. try:
  747. if self.task.path:
  748. cmd[0] = self.task.path # full path
  749. ret = self._gconsole.RunCmd(cmd, onDone=self.OnDone)
  750. except AttributeError as e:
  751. print(
  752. "%s: Probably not running in wxgui.py session?" % (e),
  753. file=sys.stderr,
  754. )
  755. print("parent window is: %s" % (str(self.parent)), file=sys.stderr)
  756. else:
  757. gcmd.Command(cmd)
  758. if ret != 0:
  759. self.notebookpanel.notebook.SetSelection(0)
  760. return
  761. # update buttons status
  762. for btn in (self.btn_run, self.btn_cancel, self.btn_clipboard, self.btn_help):
  763. btn.Enable(False)
  764. def OnAbort(self, event):
  765. """Abort running command"""
  766. from core.gconsole import wxCmdAbort
  767. event = wxCmdAbort(aborted=True)
  768. wx.PostEvent(self._gconsole, event)
  769. def OnCopyCommand(self, event):
  770. """Copy the command"""
  771. cmddata = wx.TextDataObject()
  772. # list -> string
  773. cmdlist = self.createCmd(ignoreErrors=True)
  774. # TODO: better protect whitespace with quotes
  775. for i in range(1, len(cmdlist)):
  776. if " " in cmdlist[i]:
  777. optname, val = cmdlist[i].split("=", 1)
  778. cmdlist[i] = '%s="%s"' % (optname, val)
  779. cmdstring = " ".join(cmdlist)
  780. cmddata.SetText(cmdstring)
  781. if wx.TheClipboard.Open():
  782. # wx.TheClipboard.UsePrimarySelection(True)
  783. wx.TheClipboard.SetData(cmddata)
  784. wx.TheClipboard.Close()
  785. self.SetStatusText(_("'%s' copied to clipboard") % (cmdstring))
  786. def OnCancel(self, event):
  787. """Cancel button pressed"""
  788. self.MakeModal(False)
  789. self.dialogClosing.emit()
  790. if (
  791. self.get_dcmd
  792. and self.parent
  793. and self.parent.GetName() in ("LayerTree", "MapWindow")
  794. ):
  795. Debug.msg(1, "TaskFrame.OnCancel(): known parent")
  796. # display decorations and
  797. # pressing OK or cancel after setting layer properties
  798. if (
  799. self.task.name
  800. in [
  801. "d.barscale",
  802. "d.legend",
  803. "d.northarrow",
  804. "d.histogram",
  805. "d.text",
  806. "d.legend.vect",
  807. ]
  808. or len(self.parent.GetLayerInfo(self.layer, key="cmd")) >= 1
  809. ):
  810. # TODO: do this through policy
  811. self.Hide()
  812. # canceled layer with nothing set
  813. elif len(self.parent.GetLayerInfo(self.layer, key="cmd")) < 1:
  814. # TODO: do this through callback or signal
  815. try:
  816. self.parent.Delete(self.layer)
  817. except ValueError:
  818. # happens when closing dialog of a new layer which was
  819. # removed from tree
  820. pass
  821. self._Destroy()
  822. else:
  823. Debug.msg(1, "TaskFrame.OnCancel(): no parent")
  824. # cancel for non-display commands
  825. self._Destroy()
  826. def OnHelp(self, event):
  827. """Show manual page (switch to the 'Manual' notebook page)"""
  828. if self.notebookpanel.notebook.GetPageIndexByName("manual") > -1:
  829. self.notebookpanel.notebook.SetSelectionByName("manual")
  830. self.notebookpanel.OnPageChange(None)
  831. if event:
  832. event.Skip()
  833. def createCmd(self, ignoreErrors=False, ignoreRequired=False):
  834. """Create command string (python list)"""
  835. return self.notebookpanel.createCmd(
  836. ignoreErrors=ignoreErrors, ignoreRequired=ignoreRequired
  837. )
  838. def _Destroy(self):
  839. """Destroy Frame"""
  840. self.notebookpanel.notebook.Unbind(wx.EVT_NOTEBOOK_PAGE_CHANGED)
  841. self.notebookpanel.notebook.widget.Unbind(wx.EVT_NOTEBOOK_PAGE_CHANGED)
  842. self.Destroy()
  843. class CmdPanel(wx.Panel):
  844. """A panel containing a notebook dividing in tabs the different
  845. guisections of the GRASS cmd.
  846. """
  847. def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwargs):
  848. if frame:
  849. self.parent = frame
  850. else:
  851. self.parent = parent
  852. self.task = task
  853. self._giface = giface
  854. wx.Panel.__init__(self, parent, id=id, *args, **kwargs)
  855. self.mapCreated = Signal
  856. self.updateMap = Signal
  857. # Determine tab layout
  858. sections = []
  859. is_section = {}
  860. not_hidden = [
  861. p
  862. for p in self.task.params + self.task.flags
  863. if not p.get("hidden", False) is True
  864. ]
  865. self.label_id = [] # wrap titles on resize
  866. self.Bind(wx.EVT_SIZE, self.OnSize)
  867. for task in not_hidden:
  868. if task.get("required", False) and not task.get("guisection", ""):
  869. # All required go into Main, even if they had defined another
  870. # guisection
  871. task["guisection"] = _("Required")
  872. if task.get("guisection", "") == "":
  873. # Undefined guisections end up into Options
  874. task["guisection"] = _("Optional")
  875. if task["guisection"] not in is_section:
  876. # We do it like this to keep the original order, except for
  877. # Main which goes first
  878. is_section[task["guisection"]] = 1
  879. sections.append(task["guisection"])
  880. else:
  881. is_section[task["guisection"]] += 1
  882. del is_section
  883. # 'Required' tab goes first, 'Optional' as the last one
  884. for (newidx, content) in [
  885. (0, _("Required")),
  886. (len(sections) - 1, _("Optional")),
  887. ]:
  888. if content in sections:
  889. idx = sections.index(content)
  890. sections[idx : idx + 1] = []
  891. sections[newidx:newidx] = [content]
  892. panelsizer = wx.BoxSizer(orient=wx.VERTICAL)
  893. # build notebook
  894. style = UserSettings.Get(
  895. group="appearance", key="commandNotebook", subkey="selection"
  896. )
  897. if style == 0: # basic top
  898. self.notebook = FormNotebook(self, style=wx.BK_TOP)
  899. self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChange)
  900. elif style == 1: # basic left
  901. self.notebook = FormNotebook(self, style=wx.BK_LEFT)
  902. self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChange)
  903. elif style == 2: # fancy green
  904. self.notebook = GNotebook(
  905. self, style=globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON
  906. )
  907. self.notebook.SetTabAreaColour(globalvar.FNPageColor)
  908. self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChange)
  909. elif style == 3:
  910. self.notebook = FormListbook(self, style=wx.BK_LEFT)
  911. self.notebook.Bind(wx.EVT_LISTBOOK_PAGE_CHANGED, self.OnPageChange)
  912. self.notebook.Refresh()
  913. tab = {}
  914. tabsizer = {}
  915. for section in sections:
  916. tab[section] = ScrolledPanel(parent=self.notebook)
  917. tab[section].SetScrollRate(10, 10)
  918. tabsizer[section] = wx.BoxSizer(orient=wx.VERTICAL)
  919. #
  920. # flags
  921. #
  922. visible_flags = [
  923. f for f in self.task.flags if not f.get("hidden", False) is True
  924. ]
  925. for f in visible_flags:
  926. # we don't want another help (checkbox appeared in r58783)
  927. if f["name"] == "help":
  928. continue
  929. which_sizer = tabsizer[f["guisection"]]
  930. which_panel = tab[f["guisection"]]
  931. # if label is given: description -> tooltip
  932. if f.get("label", "") != "":
  933. title = text_beautify(f["label"])
  934. tooltip = text_beautify(f["description"], width=-1)
  935. else:
  936. title = text_beautify(f["description"])
  937. tooltip = None
  938. title_sizer = wx.BoxSizer(wx.HORIZONTAL)
  939. rtitle_txt = StaticText(parent=which_panel, label="(" + f["name"] + ")")
  940. chk = CheckBox(parent=which_panel, label=title, style=wx.NO_BORDER)
  941. self.label_id.append(chk.GetId())
  942. if tooltip:
  943. chk.SetToolTip(tooltip)
  944. chk.SetValue(f.get("value", False))
  945. title_sizer.Add(chk, proportion=1, flag=wx.EXPAND)
  946. title_sizer.Add(rtitle_txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  947. which_sizer.Add(
  948. title_sizer,
  949. proportion=0,
  950. flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
  951. border=5,
  952. )
  953. f["wxId"] = [
  954. chk.GetId(),
  955. ]
  956. chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
  957. if self.parent.GetName() == "MainFrame" and (
  958. self._giface and hasattr(self._giface, "_model")
  959. ):
  960. parChk = wx.CheckBox(
  961. parent=which_panel, id=wx.ID_ANY, label=_("Parameterized in model")
  962. )
  963. parChk.SetName("ModelParam")
  964. parChk.SetValue(f.get("parameterized", False))
  965. if "wxId" in f:
  966. f["wxId"].append(parChk.GetId())
  967. else:
  968. f["wxId"] = [parChk.GetId()]
  969. parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
  970. which_sizer.Add(parChk, proportion=0, flag=wx.LEFT, border=20)
  971. if f["name"] in ("verbose", "quiet"):
  972. chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
  973. vq = UserSettings.Get(group="cmd", key="verbosity", subkey="selection")
  974. if f["name"] == vq:
  975. chk.SetValue(True)
  976. f["value"] = True
  977. if f["name"] == "overwrite":
  978. value = UserSettings.Get(group="cmd", key="overwrite", subkey="enabled")
  979. if value: # override only when enabled
  980. f["value"] = value
  981. chk.SetValue(f["value"])
  982. #
  983. # parameters
  984. #
  985. visible_params = [
  986. p for p in self.task.params if not p.get("hidden", False) is True
  987. ]
  988. try:
  989. first_param = visible_params[0]
  990. except IndexError:
  991. first_param = None
  992. for p in visible_params:
  993. which_sizer = tabsizer[p["guisection"]]
  994. which_panel = tab[p["guisection"]]
  995. # if label is given -> label and description -> tooltip
  996. # otherwise description -> lavel
  997. if p.get("label", "") != "":
  998. title = text_beautify(p["label"])
  999. tooltip = text_beautify(p["description"], width=-1)
  1000. else:
  1001. title = text_beautify(p["description"])
  1002. tooltip = None
  1003. prompt = p.get("prompt", "")
  1004. # title sizer (description, name, type)
  1005. if (
  1006. (len(p.get("values", [])) > 0)
  1007. and p.get("multiple", False)
  1008. and p.get("gisprompt", False) is False
  1009. and p.get("type", "") == "string"
  1010. ):
  1011. title_txt = StaticBox(parent=which_panel, id=wx.ID_ANY)
  1012. else:
  1013. title_sizer = wx.BoxSizer(wx.HORIZONTAL)
  1014. title_txt = StaticText(parent=which_panel)
  1015. if p["key_desc"]:
  1016. ltype = ",".join(p["key_desc"])
  1017. else:
  1018. ltype = p["type"]
  1019. # red star for required options
  1020. if p.get("required", False):
  1021. required_txt = StaticText(parent=which_panel, label="*")
  1022. required_txt.SetForegroundColour(wx.RED)
  1023. required_txt.SetToolTip(_("This option is required"))
  1024. else:
  1025. required_txt = StaticText(parent=which_panel, label="")
  1026. rtitle_txt = StaticText(
  1027. parent=which_panel, label="(" + p["name"] + "=" + ltype + ")"
  1028. )
  1029. title_sizer.Add(
  1030. title_txt, proportion=0, flag=wx.LEFT | wx.TOP | wx.EXPAND, border=5
  1031. )
  1032. title_sizer.Add(required_txt, proportion=1, flag=wx.EXPAND, border=0)
  1033. title_sizer.Add(
  1034. rtitle_txt, proportion=0, flag=wx.RIGHT | wx.TOP, border=5
  1035. )
  1036. which_sizer.Add(title_sizer, proportion=0, flag=wx.EXPAND)
  1037. self.label_id.append(title_txt.GetId())
  1038. # title expansion
  1039. if p.get("multiple", False) and len(p.get("values", "")) == 0:
  1040. title = _("[multiple]") + " " + title
  1041. if p.get("value", "") == "":
  1042. p["value"] = p.get("default", "")
  1043. if len(p.get("values", [])) > 0:
  1044. valuelist = list(map(str, p.get("values", [])))
  1045. valuelist_desc = list(map(unicode, p.get("values_desc", [])))
  1046. required_text = "*" if p.get("required", False) else ""
  1047. if (
  1048. p.get("multiple", False)
  1049. and p.get("gisprompt", False) is False
  1050. and p.get("type", "") == "string"
  1051. ):
  1052. title_txt.SetLabel(
  1053. " %s:%s (%s=%s) "
  1054. % (title, required_text, p["name"], p["type"])
  1055. )
  1056. stSizer = wx.StaticBoxSizer(box=title_txt, orient=wx.VERTICAL)
  1057. if valuelist_desc:
  1058. hSizer = wx.FlexGridSizer(cols=1, vgap=1, hgap=1)
  1059. else:
  1060. hSizer = wx.FlexGridSizer(cols=6, vgap=1, hgap=1)
  1061. isEnabled = {}
  1062. # copy default values
  1063. if p["value"] == "":
  1064. p["value"] = p.get("default", "")
  1065. for defval in p.get("value", "").split(","):
  1066. isEnabled[defval] = "yes"
  1067. # for multi checkboxes, this is an array of all wx IDs
  1068. # for each individual checkbox
  1069. p["wxId"] = list()
  1070. idx = 0
  1071. for val in valuelist:
  1072. try:
  1073. label = valuelist_desc[idx]
  1074. except IndexError:
  1075. label = val
  1076. chkbox = wx.CheckBox(
  1077. parent=which_panel, label=text_beautify(label)
  1078. )
  1079. p["wxId"].append(chkbox.GetId())
  1080. if val in isEnabled:
  1081. chkbox.SetValue(True)
  1082. hSizer.Add(chkbox, proportion=0)
  1083. chkbox.Bind(wx.EVT_CHECKBOX, self.OnUpdateSelection)
  1084. chkbox.Bind(wx.EVT_CHECKBOX, self.OnCheckBoxMulti)
  1085. idx += 1
  1086. stSizer.Add(
  1087. hSizer, proportion=0, flag=wx.ADJUST_MINSIZE | wx.ALL, border=1
  1088. )
  1089. which_sizer.Add(
  1090. stSizer,
  1091. proportion=0,
  1092. flag=wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT,
  1093. border=5,
  1094. )
  1095. elif p.get("gisprompt", False) is False:
  1096. if len(valuelist) == 1: # -> textctrl
  1097. title_txt.SetLabel(
  1098. "%s (%s %s):" % (title, _("valid range"), str(valuelist[0]))
  1099. )
  1100. if p.get("type", "") == "integer" and not p.get(
  1101. "multiple", False
  1102. ):
  1103. # for multiple integers use textctrl instead of
  1104. # spinsctrl
  1105. try:
  1106. minValue, maxValue = list(
  1107. map(int, valuelist[0].rsplit("-", 1))
  1108. )
  1109. except ValueError:
  1110. minValue = -1e6
  1111. maxValue = 1e6
  1112. txt2 = SpinCtrl(
  1113. parent=which_panel,
  1114. id=wx.ID_ANY,
  1115. size=globalvar.DIALOG_SPIN_SIZE,
  1116. min=minValue,
  1117. max=maxValue,
  1118. )
  1119. style = wx.BOTTOM | wx.LEFT
  1120. else:
  1121. if p["name"] in ("at"):
  1122. txt2 = TextCtrl(
  1123. parent=which_panel,
  1124. value=p.get("default", ""),
  1125. validator=PlacementValidator(
  1126. num_of_params=len(p["key_desc"])
  1127. ),
  1128. )
  1129. else:
  1130. txt2 = TextCtrl(
  1131. parent=which_panel, value=p.get("default", "")
  1132. )
  1133. style = wx.EXPAND | wx.BOTTOM | wx.LEFT
  1134. value = self._getValue(p)
  1135. # parameter previously set
  1136. if value:
  1137. if isinstance(txt2, SpinCtrl):
  1138. txt2.SetValue(int(value))
  1139. else:
  1140. txt2.SetValue(value)
  1141. which_sizer.Add(txt2, proportion=0, flag=style, border=5)
  1142. p["wxId"] = [
  1143. txt2.GetId(),
  1144. ]
  1145. txt2.Bind(wx.EVT_TEXT, self.OnSetValue)
  1146. else:
  1147. title_txt.SetLabel(title + ":")
  1148. value = self._getValue(p)
  1149. if p["name"] in ("icon", "icon_area", "icon_line"): # symbols
  1150. bitmap = wx.Bitmap(
  1151. os.path.join(globalvar.SYMBDIR, value) + ".png"
  1152. )
  1153. bb = BitmapButton(
  1154. parent=which_panel, id=wx.ID_ANY, bitmap=bitmap
  1155. )
  1156. iconLabel = StaticText(parent=which_panel, id=wx.ID_ANY)
  1157. iconLabel.SetLabel(value)
  1158. p["value"] = value
  1159. p["wxId"] = [bb.GetId(), iconLabel.GetId()]
  1160. bb.Bind(wx.EVT_BUTTON, self.OnSetSymbol)
  1161. this_sizer = wx.BoxSizer(wx.HORIZONTAL)
  1162. this_sizer.Add(
  1163. bb,
  1164. proportion=0,
  1165. flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT,
  1166. border=5,
  1167. )
  1168. this_sizer.Add(
  1169. iconLabel,
  1170. proportion=0,
  1171. flag=wx.ADJUST_MINSIZE
  1172. | wx.BOTTOM
  1173. | wx.LEFT
  1174. | wx.ALIGN_CENTER_VERTICAL,
  1175. border=5,
  1176. )
  1177. which_sizer.Add(
  1178. this_sizer,
  1179. proportion=0,
  1180. flag=wx.ADJUST_MINSIZE,
  1181. border=0,
  1182. )
  1183. else:
  1184. # list of values (combo)
  1185. cb = wx.ComboBox(
  1186. parent=which_panel,
  1187. id=wx.ID_ANY,
  1188. value=p.get("default", ""),
  1189. size=globalvar.DIALOG_COMBOBOX_SIZE,
  1190. choices=valuelist,
  1191. style=wx.CB_DROPDOWN,
  1192. )
  1193. if value:
  1194. cb.SetValue(value) # parameter previously set
  1195. which_sizer.Add(
  1196. cb,
  1197. proportion=0,
  1198. flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT,
  1199. border=5,
  1200. )
  1201. p["wxId"] = [
  1202. cb.GetId(),
  1203. ]
  1204. cb.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1205. cb.Bind(wx.EVT_TEXT, self.OnSetValue)
  1206. if p.get("guidependency", ""):
  1207. cb.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
  1208. # text entry
  1209. if (
  1210. p.get("type", "string") in ("string", "integer", "float")
  1211. and len(p.get("values", [])) == 0
  1212. and p.get("gisprompt", False) is False
  1213. and p.get("prompt", "") != "color"
  1214. ):
  1215. title_txt.SetLabel(title + ":")
  1216. p["wxId"] = []
  1217. if (
  1218. p.get("multiple", False)
  1219. or p.get("type", "string") == "string"
  1220. or len(p.get("key_desc", [])) > 1
  1221. ):
  1222. if p["name"] in ("at"):
  1223. win = TextCtrl(
  1224. parent=which_panel,
  1225. value=p.get("default", ""),
  1226. validator=PlacementValidator(
  1227. num_of_params=len(p["key_desc"])
  1228. ),
  1229. )
  1230. else:
  1231. win = TextCtrl(parent=which_panel, value=p.get("default", ""))
  1232. value = self._getValue(p)
  1233. if value:
  1234. # parameter previously set
  1235. win.SetValue(
  1236. value if p.get("type", "string") == "string" else str(value)
  1237. )
  1238. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1239. style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
  1240. if p.get("name", "") == "font":
  1241. font_btn = Button(parent=which_panel, label=_("Select font"))
  1242. font_btn.Bind(wx.EVT_BUTTON, self.OnSelectFont)
  1243. font_sizer = wx.BoxSizer(wx.HORIZONTAL)
  1244. font_sizer.Add(win, proportion=1, flag=style, border=5)
  1245. font_sizer.Add(font_btn, proportion=0, flag=style, border=5)
  1246. which_sizer.Add(font_sizer, proportion=0, flag=style, border=5)
  1247. p["wxId"].append(font_btn.GetId())
  1248. else:
  1249. which_sizer.Add(win, proportion=0, flag=style, border=5)
  1250. elif p.get("type", "") == "integer":
  1251. minValue = -1e9
  1252. maxValue = 1e9
  1253. value = self._getValue(p)
  1254. win = SpinCtrl(
  1255. parent=which_panel,
  1256. value=p.get("default", ""),
  1257. size=globalvar.DIALOG_SPIN_SIZE,
  1258. min=minValue,
  1259. max=maxValue,
  1260. )
  1261. if value:
  1262. win.SetValue(int(value)) # parameter previously set
  1263. win.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
  1264. style = wx.BOTTOM | wx.LEFT | wx.RIGHT
  1265. which_sizer.Add(win, proportion=0, flag=style, border=5)
  1266. else: # float
  1267. win = TextCtrl(
  1268. parent=which_panel,
  1269. value=p.get("default", ""),
  1270. validator=FloatValidator(),
  1271. )
  1272. style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
  1273. which_sizer.Add(win, proportion=0, flag=style, border=5)
  1274. value = self._getValue(p)
  1275. if value:
  1276. win.SetValue(str(value)) # parameter previously set
  1277. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1278. p["wxId"].append(win.GetId())
  1279. #
  1280. # element selection tree combobox (maps, icons, regions, etc.)
  1281. #
  1282. if p.get("gisprompt", False):
  1283. title_txt.SetLabel(title + ":")
  1284. # GIS element entry
  1285. if p.get("prompt", "") not in (
  1286. "color",
  1287. "cat",
  1288. "cats",
  1289. "subgroup",
  1290. "sigfile",
  1291. "separator",
  1292. "dbdriver",
  1293. "dbname",
  1294. "dbtable",
  1295. "dbcolumn",
  1296. "layer",
  1297. "location",
  1298. "mapset",
  1299. "dbase",
  1300. "coords",
  1301. "file",
  1302. "dir",
  1303. "colortable",
  1304. "barscale",
  1305. "northarrow",
  1306. "datasource",
  1307. "datasource_layer",
  1308. "sql_query",
  1309. ):
  1310. multiple = p.get("multiple", False)
  1311. if p.get("age", "") == "new":
  1312. mapsets = [
  1313. grass.gisenv()["MAPSET"],
  1314. ]
  1315. else:
  1316. mapsets = None
  1317. if (
  1318. self.task.name in ("r.proj", "v.proj")
  1319. and p.get("name", "") == "input"
  1320. ):
  1321. selection = gselect.ProjSelect(
  1322. parent=which_panel, isRaster=self.task.name == "r.proj"
  1323. )
  1324. p["wxId"] = [
  1325. selection.GetId(),
  1326. ]
  1327. selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1328. selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1329. else:
  1330. elem = p.get("element", None)
  1331. # hack for t.* modules
  1332. if elem in ("stds", "map"):
  1333. orig_elem = elem
  1334. type_param = self.task.get_param(
  1335. "type", element="name", raiseError=False
  1336. )
  1337. if type_param:
  1338. elem = type_param.get("default", None)
  1339. # for t.(un)register:
  1340. maps_param = self.task.get_param(
  1341. "maps", element="name", raiseError=False
  1342. )
  1343. if maps_param and orig_elem == "stds":
  1344. element_dict = {
  1345. "raster": "strds",
  1346. "vector": "stvds",
  1347. "raster_3d": "str3ds",
  1348. }
  1349. elem = element_dict[type_param.get("default")]
  1350. extraItems = None
  1351. if self._giface:
  1352. if hasattr(self._giface, "_model"):
  1353. extraItems = {
  1354. _("Graphical Modeler"): self._giface.GetLayerList(
  1355. p.get("prompt")
  1356. )
  1357. }
  1358. else:
  1359. layers = self._giface.GetLayerList()
  1360. if len(layers) > 0:
  1361. mapList = []
  1362. extraItems = {_("Map Display"): mapList}
  1363. for layer in layers:
  1364. if layer.type != p.get("prompt"):
  1365. continue
  1366. if str(layer):
  1367. mapList.append(str(layer))
  1368. selection = gselect.Select(
  1369. parent=which_panel,
  1370. id=wx.ID_ANY,
  1371. size=globalvar.DIALOG_GSELECT_SIZE,
  1372. type=elem,
  1373. multiple=multiple,
  1374. nmaps=len(p.get("key_desc", [])),
  1375. mapsets=mapsets,
  1376. fullyQualified=p.get("age", "old") == "old",
  1377. extraItems=extraItems,
  1378. )
  1379. value = self._getValue(p)
  1380. if value:
  1381. selection.SetValue(value)
  1382. formatSelector = True
  1383. # A gselect.Select is a combobox with two children: a textctl and a popupwindow;
  1384. # we target the textctl here
  1385. textWin = selection.GetTextCtrl()
  1386. if globalvar.CheckWxVersion([3]):
  1387. p["wxId"] = [
  1388. selection.GetId(),
  1389. ]
  1390. else:
  1391. p["wxId"] = [
  1392. textWin.GetId(),
  1393. ]
  1394. if prompt != "vector":
  1395. self.FindWindowById(p["wxId"][0]).Bind(
  1396. wx.EVT_TEXT, self.OnSetValue
  1397. )
  1398. if prompt == "vector":
  1399. win = self.FindWindowById(p["wxId"][0])
  1400. # handlers should be bound in this order
  1401. # OnUpdateSelection depends on calling OnSetValue first
  1402. # which is bad
  1403. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1404. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1405. # if formatSelector and p.get('age', 'old') == 'old':
  1406. # # OGR supported (read-only)
  1407. # self.hsizer = wx.BoxSizer(wx.HORIZONTAL)
  1408. # self.hsizer.Add(item = selection,
  1409. # flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_TOP,
  1410. # border = 5)
  1411. # # format (native / ogr)
  1412. # rbox = wx.RadioBox(parent = which_panel, id = wx.ID_ANY,
  1413. # label = " %s " % _("Format"),
  1414. # style = wx.RA_SPECIFY_ROWS,
  1415. # choices = [_("Native / Linked OGR"), _("Direct OGR")])
  1416. # if p.get('value', '').lower().rfind('@ogr') > -1:
  1417. # rbox.SetSelection(1)
  1418. # rbox.SetName('VectorFormat')
  1419. # rbox.Bind(wx.EVT_RADIOBOX, self.OnVectorFormat)
  1420. # self.hsizer.Add(item = rbox,
  1421. # flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT |
  1422. # wx.RIGHT | wx.ALIGN_TOP,
  1423. # border = 5)
  1424. # ogrSelection = gselect.GdalSelect(parent = self, panel = which_panel, ogr = True,
  1425. # default = 'dir',
  1426. # exclude = ['file'])
  1427. # self.Bind(gselect.EVT_GDALSELECT, self.OnUpdateSelection)
  1428. # self.Bind(gselect.EVT_GDALSELECT, self.OnSetValue)
  1429. # ogrSelection.SetName('OgrSelect')
  1430. # ogrSelection.Hide()
  1431. # which_sizer.Add(item = self.hsizer, proportion = 0)
  1432. # p['wxId'].append(rbox.GetId())
  1433. # p['wxId'].append(ogrSelection.GetId())
  1434. # for win in ogrSelection.GetDsnWin():
  1435. # p['wxId'].append(win.GetId())
  1436. # else:
  1437. which_sizer.Add(
  1438. selection,
  1439. proportion=0,
  1440. flag=wx.ADJUST_MINSIZE
  1441. | wx.BOTTOM
  1442. | wx.LEFT
  1443. | wx.RIGHT
  1444. | wx.TOP,
  1445. border=5,
  1446. )
  1447. elif prompt == "group":
  1448. win = self.FindWindowById(p["wxId"][0])
  1449. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1450. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1451. which_sizer.Add(
  1452. selection,
  1453. proportion=0,
  1454. flag=wx.ADJUST_MINSIZE
  1455. | wx.BOTTOM
  1456. | wx.LEFT
  1457. | wx.RIGHT
  1458. | wx.TOP,
  1459. border=5,
  1460. )
  1461. else:
  1462. if prompt in ("stds", "strds", "stvds", "str3ds"):
  1463. showButton = True
  1464. try:
  1465. # if matplotlib is there
  1466. from timeline import frame
  1467. showButton = True
  1468. except ImportError:
  1469. showButton = False
  1470. else:
  1471. showButton = False
  1472. if showButton:
  1473. iconTheme = UserSettings.Get(
  1474. group="appearance", key="iconTheme", subkey="type"
  1475. )
  1476. bitmap = wx.Bitmap(
  1477. os.path.join(
  1478. globalvar.ICONDIR, iconTheme, "map-info.png"
  1479. )
  1480. )
  1481. bb = BitmapButton(parent=which_panel, bitmap=bitmap)
  1482. bb.Bind(wx.EVT_BUTTON, self.OnTimelineTool)
  1483. bb.SetToolTip(
  1484. _(
  1485. "Show graphical representation of temporal extent of dataset(s) ."
  1486. )
  1487. )
  1488. p["wxId"].append(bb.GetId())
  1489. hSizer = wx.BoxSizer(wx.HORIZONTAL)
  1490. hSizer.Add(
  1491. selection,
  1492. proportion=0,
  1493. flag=wx.ADJUST_MINSIZE
  1494. | wx.BOTTOM
  1495. | wx.LEFT
  1496. | wx.RIGHT
  1497. | wx.TOP,
  1498. border=5,
  1499. )
  1500. hSizer.Add(
  1501. bb,
  1502. proportion=0,
  1503. flag=wx.EXPAND | wx.BOTTOM | wx.RIGHT | wx.TOP,
  1504. border=5,
  1505. )
  1506. which_sizer.Add(hSizer)
  1507. else:
  1508. which_sizer.Add(
  1509. selection,
  1510. proportion=0,
  1511. flag=wx.ADJUST_MINSIZE
  1512. | wx.BOTTOM
  1513. | wx.LEFT
  1514. | wx.RIGHT
  1515. | wx.TOP,
  1516. border=5,
  1517. )
  1518. # subgroup
  1519. elif prompt == "subgroup":
  1520. selection = gselect.SubGroupSelect(parent=which_panel)
  1521. p["wxId"] = [selection.GetId()]
  1522. selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1523. selection.Bind(wx.EVT_TEXT, self.OnSetValue)
  1524. which_sizer.Add(
  1525. selection,
  1526. proportion=0,
  1527. flag=wx.ADJUST_MINSIZE
  1528. | wx.BOTTOM
  1529. | wx.LEFT
  1530. | wx.RIGHT
  1531. | wx.TOP,
  1532. border=5,
  1533. )
  1534. # sigrature file
  1535. elif prompt == "sigfile":
  1536. selection = gselect.SignatureSelect(
  1537. parent=which_panel, element=p.get("element", "sig")
  1538. )
  1539. p["wxId"] = [selection.GetId()]
  1540. selection.Bind(wx.EVT_TEXT, self.OnSetValue)
  1541. selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1542. which_sizer.Add(
  1543. selection,
  1544. proportion=0,
  1545. flag=wx.ADJUST_MINSIZE
  1546. | wx.BOTTOM
  1547. | wx.LEFT
  1548. | wx.RIGHT
  1549. | wx.TOP,
  1550. border=5,
  1551. )
  1552. # separator
  1553. elif prompt == "separator":
  1554. win = gselect.SeparatorSelect(parent=which_panel)
  1555. value = self._getValue(p)
  1556. win.SetValue(value)
  1557. p["wxId"] = [win.GetId()]
  1558. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1559. win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1560. which_sizer.Add(
  1561. win,
  1562. proportion=0,
  1563. flag=wx.ADJUST_MINSIZE
  1564. | wx.BOTTOM
  1565. | wx.LEFT
  1566. | wx.RIGHT
  1567. | wx.TOP,
  1568. border=5,
  1569. )
  1570. # layer, dbdriver, dbname, dbcolumn, dbtable entry
  1571. elif prompt in (
  1572. "dbdriver",
  1573. "dbname",
  1574. "dbtable",
  1575. "dbcolumn",
  1576. "layer",
  1577. "location",
  1578. "mapset",
  1579. "dbase",
  1580. ):
  1581. if p.get("multiple", "no") == "yes":
  1582. win = TextCtrl(
  1583. parent=which_panel,
  1584. value=p.get("default", ""),
  1585. size=globalvar.DIALOG_TEXTCTRL_SIZE,
  1586. )
  1587. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1588. else:
  1589. value = self._getValue(p)
  1590. if prompt == "layer":
  1591. if p.get("element", "layer") == "layer_all":
  1592. all = True
  1593. else:
  1594. all = False
  1595. if p.get("age", "old") == "old":
  1596. win = gselect.LayerSelect(
  1597. parent=which_panel, all=all, default=p["default"]
  1598. )
  1599. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1600. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1601. win.SetValue(
  1602. str(value)
  1603. ) # default or previously set value
  1604. else:
  1605. win = SpinCtrl(
  1606. parent=which_panel,
  1607. id=wx.ID_ANY,
  1608. min=1,
  1609. max=100,
  1610. initial=int(p["default"]),
  1611. )
  1612. win.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
  1613. win.SetValue(
  1614. int(value)
  1615. ) # default or previously set value
  1616. p["wxId"] = [win.GetId()]
  1617. elif prompt == "dbdriver":
  1618. win = gselect.DriverSelect(
  1619. parent=which_panel,
  1620. choices=p.get("values", []),
  1621. value=value,
  1622. )
  1623. win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
  1624. win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1625. elif prompt == "dbname":
  1626. win = gselect.DatabaseSelect(
  1627. parent=which_panel, value=value
  1628. )
  1629. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1630. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1631. elif prompt == "dbtable":
  1632. if p.get("age", "old") == "old":
  1633. win = gselect.TableSelect(parent=which_panel)
  1634. win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
  1635. win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1636. else:
  1637. win = TextCtrl(
  1638. parent=which_panel,
  1639. value=p.get("default", ""),
  1640. size=globalvar.DIALOG_TEXTCTRL_SIZE,
  1641. )
  1642. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1643. elif prompt == "dbcolumn":
  1644. win = gselect.ColumnSelect(
  1645. parent=which_panel,
  1646. value=value,
  1647. param=p,
  1648. multiple=p.get("multiple", False),
  1649. )
  1650. # A gselect.ColumnSelect is a combobox
  1651. # with two children: a textctl and a
  1652. # popupwindow; we target the textctl here
  1653. textWin = win.GetTextCtrl()
  1654. p["wxId"] = [
  1655. textWin.GetId(),
  1656. ]
  1657. textWin.Bind(wx.EVT_TEXT, self.OnSetValue)
  1658. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1659. elif prompt == "location":
  1660. win = gselect.LocationSelect(
  1661. parent=which_panel, value=value
  1662. )
  1663. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1664. win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
  1665. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1666. win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1667. elif prompt == "mapset":
  1668. if p.get("age", "old") == "old":
  1669. new = False
  1670. else:
  1671. new = True
  1672. win = gselect.MapsetSelect(
  1673. parent=which_panel,
  1674. value=value,
  1675. new=new,
  1676. multiple=p.get("multiple", False),
  1677. )
  1678. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1679. win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
  1680. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1681. win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1682. elif prompt == "dbase":
  1683. win = gselect.DbaseSelect(
  1684. parent=which_panel, changeCallback=self.OnSetValue
  1685. )
  1686. win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
  1687. p["wxId"] = [win.GetChildren()[1].GetId()]
  1688. if "wxId" not in p:
  1689. try:
  1690. p["wxId"] = [
  1691. win.GetId(),
  1692. ]
  1693. except AttributeError:
  1694. pass
  1695. flags = wx.BOTTOM | wx.LEFT | wx.RIGHT
  1696. if prompt == "dbname":
  1697. flags |= wx.EXPAND
  1698. which_sizer.Add(win, proportion=0, flag=flags, border=5)
  1699. # color entry
  1700. elif prompt == "color":
  1701. default_color = (200, 200, 200)
  1702. label_color = _("Select Color")
  1703. if p.get("default", "") != "":
  1704. default_color, label_color = utils.color_resolve(p["default"])
  1705. if (
  1706. p.get("value", "") != "" and p.get("value", "") != "none"
  1707. ): # parameter previously set
  1708. if not p.get("multiple", False):
  1709. default_color, label_color = utils.color_resolve(p["value"])
  1710. if p.get("element", "") == "color_none" or p.get("multiple", False):
  1711. this_sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
  1712. else:
  1713. this_sizer = which_sizer
  1714. colorSize = 150
  1715. # For color selectors, this is a three-member array, holding the IDs of
  1716. # the color picker, the text control for multiple colors (or None),
  1717. # and either a "transparent" checkbox or None
  1718. p["wxId"] = [None] * 3
  1719. if p.get("multiple", False):
  1720. txt = TextCtrl(parent=which_panel, id=wx.ID_ANY)
  1721. this_sizer.Add(
  1722. txt,
  1723. proportion=1,
  1724. flag=wx.ADJUST_MINSIZE | wx.LEFT | wx.TOP,
  1725. border=5,
  1726. )
  1727. txt.Bind(wx.EVT_TEXT, self.OnSetValue)
  1728. if p.get("value", ""):
  1729. txt.SetValue(p["value"])
  1730. colorSize = 40
  1731. label_color = ""
  1732. p["wxId"][1] = txt.GetId()
  1733. which_sizer.Add(this_sizer, flag=wx.EXPAND | wx.RIGHT, border=5)
  1734. btn_colour = csel.ColourSelect(
  1735. parent=which_panel,
  1736. id=wx.ID_ANY,
  1737. label=label_color,
  1738. colour=default_color,
  1739. pos=wx.DefaultPosition,
  1740. size=(colorSize, 32),
  1741. )
  1742. this_sizer.Add(
  1743. btn_colour,
  1744. proportion=0,
  1745. flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT,
  1746. border=5,
  1747. )
  1748. btn_colour.Bind(csel.EVT_COLOURSELECT, self.OnColorChange)
  1749. p["wxId"][0] = btn_colour.GetId()
  1750. if p.get("element", "") == "color_none":
  1751. none_check = wx.CheckBox(
  1752. which_panel, wx.ID_ANY, _("Transparent")
  1753. )
  1754. if p.get("value", "") == "none":
  1755. none_check.SetValue(True)
  1756. else:
  1757. none_check.SetValue(False)
  1758. this_sizer.Add(
  1759. none_check,
  1760. proportion=0,
  1761. flag=wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP,
  1762. border=5,
  1763. )
  1764. which_sizer.Add(this_sizer)
  1765. none_check.Bind(wx.EVT_CHECKBOX, self.OnColorChange)
  1766. p["wxId"][2] = none_check.GetId()
  1767. # file selector
  1768. elif p.get("prompt", "") != "color" and p.get("prompt", "") == "file":
  1769. if p.get("age", "new") == "new":
  1770. fmode = wx.FD_SAVE
  1771. else:
  1772. fmode = wx.FD_OPEN
  1773. # check wildcard
  1774. try:
  1775. fExt = os.path.splitext(p.get("key_desc", ["*.*"])[0])[1]
  1776. except:
  1777. fExt = None
  1778. if not fExt:
  1779. fMask = "*"
  1780. else:
  1781. fMask = "%s files (*%s)|*%s|Files (*)|*" % (
  1782. fExt[1:].upper(),
  1783. fExt,
  1784. fExt,
  1785. )
  1786. fbb = filebrowse.FileBrowseButton(
  1787. parent=which_panel,
  1788. id=wx.ID_ANY,
  1789. fileMask=fMask,
  1790. size=globalvar.DIALOG_GSELECT_SIZE,
  1791. labelText="",
  1792. dialogTitle=_("Choose %s")
  1793. % p.get("description", _("file")).lower(),
  1794. buttonText=_("Browse"),
  1795. startDirectory=os.getcwd(),
  1796. fileMode=fmode,
  1797. changeCallback=self.OnSetValue,
  1798. )
  1799. value = self._getValue(p)
  1800. if value:
  1801. fbb.SetValue(value) # parameter previously set
  1802. which_sizer.Add(
  1803. fbb, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5
  1804. )
  1805. # A file browse button is a combobox with two children:
  1806. # a textctl and a button;
  1807. # we have to target the button here
  1808. p["wxId"] = [fbb.GetChildren()[1].GetId()]
  1809. if (
  1810. p.get("age", "new") == "old"
  1811. and p.get("prompt", "") == "file"
  1812. and p.get("element", "") == "file"
  1813. and UserSettings.Get(
  1814. group="cmd", key="interactiveInput", subkey="enabled"
  1815. )
  1816. ):
  1817. # widget for interactive input
  1818. ifbb = TextCtrl(
  1819. parent=which_panel,
  1820. id=wx.ID_ANY,
  1821. style=wx.TE_MULTILINE,
  1822. size=(-1, 75),
  1823. )
  1824. if p.get("value", "") and os.path.isfile(p["value"]):
  1825. ifbb.Clear()
  1826. enc = locale.getdefaultlocale()[1]
  1827. with codecs.open(
  1828. p["value"], encoding=enc, errors="ignore"
  1829. ) as f:
  1830. nonascii = bytearray(range(0x80, 0x100))
  1831. for line in f.readlines():
  1832. try:
  1833. ifbb.AppendText(line)
  1834. except UnicodeDecodeError:
  1835. # remove non-ascii characters on encoding mismatch (file vs OS)
  1836. ifbb.AppendText(line.translate(None, nonascii))
  1837. ifbb.SetInsertionPoint(0)
  1838. ifbb.Bind(wx.EVT_TEXT, self.OnFileText)
  1839. btnLoad = Button(
  1840. parent=which_panel, id=wx.ID_ANY, label=_("&Load")
  1841. )
  1842. btnLoad.SetToolTip(_("Load and edit content of a file"))
  1843. btnLoad.Bind(wx.EVT_BUTTON, self.OnFileLoad)
  1844. btnSave = Button(
  1845. parent=which_panel, id=wx.ID_ANY, label=_("&Save as")
  1846. )
  1847. btnSave.SetToolTip(_("Save content to a file for further use"))
  1848. btnSave.Bind(wx.EVT_BUTTON, self.OnFileSave)
  1849. fileContentLabel = StaticText(
  1850. parent=which_panel,
  1851. id=wx.ID_ANY,
  1852. label=_("or enter values directly:"),
  1853. )
  1854. fileContentLabel.SetToolTip(
  1855. _(
  1856. "Enter file content directly instead of specifying"
  1857. " a file."
  1858. " Temporary file will be automatically created."
  1859. )
  1860. )
  1861. which_sizer.Add(
  1862. fileContentLabel,
  1863. proportion=0,
  1864. flag=wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM,
  1865. border=5,
  1866. )
  1867. which_sizer.Add(
  1868. ifbb,
  1869. proportion=1,
  1870. flag=wx.EXPAND | wx.RIGHT | wx.LEFT,
  1871. border=5,
  1872. )
  1873. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  1874. btnSizer.Add(btnLoad, proportion=0, flag=wx.RIGHT, border=10)
  1875. btnSizer.Add(btnSave, proportion=0)
  1876. which_sizer.Add(
  1877. btnSizer,
  1878. proportion=0,
  1879. flag=wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP,
  1880. border=5,
  1881. )
  1882. p["wxId"].append(ifbb.GetId())
  1883. p["wxId"].append(btnLoad.GetId())
  1884. p["wxId"].append(btnSave.GetId())
  1885. # directory selector
  1886. elif p.get("prompt", "") != "color" and p.get("prompt", "") == "dir":
  1887. fbb = filebrowse.DirBrowseButton(
  1888. parent=which_panel,
  1889. id=wx.ID_ANY,
  1890. size=globalvar.DIALOG_GSELECT_SIZE,
  1891. labelText="",
  1892. dialogTitle=_("Choose %s")
  1893. % p.get("description", _("Directory")),
  1894. buttonText=_("Browse"),
  1895. startDirectory=os.getcwd(),
  1896. newDirectory=True,
  1897. changeCallback=self.OnSetValue,
  1898. )
  1899. value = self._getValue(p)
  1900. if value:
  1901. fbb.SetValue(value) # parameter previously set
  1902. which_sizer.Add(
  1903. fbb, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5
  1904. )
  1905. # A file browse button is a combobox with two children:
  1906. # a textctl and a button;
  1907. # we have to target the button here
  1908. p["wxId"] = [fbb.GetChildren()[1].GetId()]
  1909. # interactive inserting of coordinates from map window
  1910. elif prompt == "coords":
  1911. # interactive inserting if layer manager is accessible
  1912. if self._giface:
  1913. win = gselect.CoordinatesSelect(
  1914. parent=which_panel,
  1915. giface=self._giface,
  1916. multiple=p.get("multiple", False),
  1917. param=p,
  1918. )
  1919. p["wxId"] = [win.GetTextWin().GetId()]
  1920. win.GetTextWin().Bind(wx.EVT_TEXT, self.OnSetValue)
  1921. # bind closing event because destructor is not working
  1922. # properly
  1923. if hasattr(self.parent, "dialogClosing"):
  1924. self.parent.dialogClosing.connect(win.OnClose)
  1925. # normal text field
  1926. else:
  1927. win = TextCtrl(parent=which_panel)
  1928. p["wxId"] = [win.GetId()]
  1929. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1930. which_sizer.Add(
  1931. win,
  1932. proportion=0,
  1933. flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
  1934. border=5,
  1935. )
  1936. elif prompt in ("cat", "cats"):
  1937. # interactive selection of vector categories if layer
  1938. # manager is accessible
  1939. if self._giface:
  1940. win = gselect.VectorCategorySelect(
  1941. parent=which_panel, giface=self._giface, task=self.task
  1942. )
  1943. p["wxId"] = [win.GetTextWin().GetId()]
  1944. win.GetTextWin().Bind(wx.EVT_TEXT, self.OnSetValue)
  1945. # bind closing event because destructor is not working
  1946. # properly
  1947. if hasattr(self.parent, "dialogClosing"):
  1948. self.parent.dialogClosing.connect(win.OnClose)
  1949. # normal text field
  1950. else:
  1951. win = TextCtrl(parent=which_panel)
  1952. value = self._getValue(p)
  1953. win.SetValue(value)
  1954. p["wxId"] = [win.GetId()]
  1955. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  1956. which_sizer.Add(
  1957. win,
  1958. proportion=0,
  1959. flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
  1960. border=5,
  1961. )
  1962. elif prompt in ("colortable", "barscale", "northarrow"):
  1963. if prompt == "colortable":
  1964. cb = ColorTablesComboBox(
  1965. parent=which_panel,
  1966. value=p.get("default", ""),
  1967. size=globalvar.DIALOG_COMBOBOX_SIZE,
  1968. choices=valuelist,
  1969. )
  1970. elif prompt == "barscale":
  1971. cb = BarscalesComboBox(
  1972. parent=which_panel,
  1973. value=p.get("default", ""),
  1974. size=globalvar.DIALOG_COMBOBOX_SIZE,
  1975. choices=valuelist,
  1976. )
  1977. elif prompt == "northarrow":
  1978. cb = NArrowsComboBox(
  1979. parent=which_panel,
  1980. value=p.get("default", ""),
  1981. size=globalvar.DIALOG_COMBOBOX_SIZE,
  1982. choices=valuelist,
  1983. )
  1984. value = self._getValue(p)
  1985. if value:
  1986. cb.SetValue(value) # parameter previously set
  1987. which_sizer.Add(
  1988. cb,
  1989. proportion=0,
  1990. flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT,
  1991. border=5,
  1992. )
  1993. p["wxId"] = [cb.GetId(), cb.GetTextCtrl().GetId()]
  1994. cb.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
  1995. cb.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnSetValue)
  1996. if p.get("guidependency", ""):
  1997. cb.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
  1998. elif prompt == "datasource":
  1999. win = gselect.GdalSelect(parent=parent, panel=which_panel, ogr=True)
  2000. win.Bind(wx.EVT_TEXT, self.OnSetValue)
  2001. win.Bind(wx.EVT_CHOICE, self.OnSetValue)
  2002. p["wxId"] = [
  2003. win.GetId(),
  2004. win.fileWidgets["browse"].GetChildren()[1].GetId(),
  2005. win.dirWidgets["browse"].GetChildren()[1].GetId(),
  2006. win.dbWidgets["choice"].GetId(),
  2007. ]
  2008. value = self._getValue(p)
  2009. if value:
  2010. win.fileWidgets["browse"].GetChildren()[1].SetValue(
  2011. value
  2012. ) # parameter previously set
  2013. which_sizer.Add(win, proportion=0, flag=wx.EXPAND)
  2014. elif prompt == "datasource_layer":
  2015. self.win1 = LayersList(
  2016. parent=which_panel,
  2017. columns=[
  2018. _("Layer id"),
  2019. _("Layer name"),
  2020. _("Feature type"),
  2021. _("Projection match"),
  2022. ],
  2023. )
  2024. which_sizer.Add(
  2025. self.win1, proportion=0, flag=wx.EXPAND | wx.ALL, border=3
  2026. )
  2027. porf = self.task.get_param(
  2028. "input", element="name", raiseError=False
  2029. )
  2030. if porf and "wxId" in porf:
  2031. winDataSource = self.FindWindowById(porf["wxId"][0])
  2032. winDataSource.reloadDataRequired.connect(
  2033. lambda listData: self.win1.LoadData(listData, False)
  2034. )
  2035. p["wxId"] = [self.win1.GetId()]
  2036. def OnCheckItem(index, flag):
  2037. layers = list()
  2038. geometry = None
  2039. for layer, match, listId in self.win1.GetLayers():
  2040. if "|" in layer:
  2041. layer, geometry = layer.split("|", 1)
  2042. layers.append(layer)
  2043. porf = self.task.get_param(
  2044. "layer", element="name", raiseError=False
  2045. )
  2046. porf["value"] = ",".join(layers)
  2047. # geometry is currently discarded
  2048. # TODO: v.import has no geometry option
  2049. self.OnUpdateValues() # TODO: replace by signal
  2050. self.win1.OnCheckItem = OnCheckItem
  2051. elif prompt == "sql_query":
  2052. win = gselect.SqlWhereSelect(parent=which_panel, param=p)
  2053. p["wxId"] = [win.GetTextWin().GetId()]
  2054. win.GetTextWin().Bind(wx.EVT_TEXT, self.OnSetValue)
  2055. which_sizer.Add(
  2056. win,
  2057. proportion=0,
  2058. flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
  2059. border=5,
  2060. )
  2061. if self.parent.GetName() == "MainFrame" and (
  2062. self._giface and hasattr(self._giface, "_model")
  2063. ):
  2064. parChk = wx.CheckBox(
  2065. parent=which_panel, id=wx.ID_ANY, label=_("Parameterized in model")
  2066. )
  2067. parChk.SetName("ModelParam")
  2068. parChk.SetValue(p.get("parameterized", False))
  2069. if "wxId" in p:
  2070. p["wxId"].append(parChk.GetId())
  2071. else:
  2072. p["wxId"] = [parChk.GetId()]
  2073. parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
  2074. which_sizer.Add(parChk, proportion=0, flag=wx.LEFT, border=20)
  2075. if title_txt is not None:
  2076. # create tooltip if given
  2077. if len(p["values_desc"]) > 0:
  2078. if tooltip:
  2079. tooltip += 2 * os.linesep
  2080. else:
  2081. tooltip = ""
  2082. if len(p["values"]) == len(p["values_desc"]):
  2083. for i in range(len(p["values"])):
  2084. tooltip += (
  2085. p["values"][i] + ": " + p["values_desc"][i] + os.linesep
  2086. )
  2087. tooltip.strip(os.linesep)
  2088. if tooltip:
  2089. title_txt.SetToolTip(tooltip)
  2090. if p == first_param:
  2091. if "wxId" in p and len(p["wxId"]) > 0:
  2092. win = self.FindWindowById(p["wxId"][0])
  2093. win.SetFocus()
  2094. #
  2095. # set widget relations for OnUpdateSelection
  2096. #
  2097. pMap = None
  2098. pLayer = []
  2099. pDriver = None
  2100. pDatabase = None
  2101. pTable = None
  2102. pColumn = []
  2103. pGroup = None
  2104. pSubGroup = None
  2105. pSigFile = []
  2106. pDbase = None
  2107. pLocation = None
  2108. pMapset = None
  2109. pSqlWhere = []
  2110. for p in self.task.params:
  2111. if (
  2112. self.task.blackList["enabled"]
  2113. and self.task.get_name() in self.task.blackList["items"]
  2114. and p.get("name", "")
  2115. in self.task.blackList["items"][self.task.get_name()]["params"]
  2116. ):
  2117. continue
  2118. guidep = p.get("guidependency", "")
  2119. if guidep:
  2120. # fixed options dependency defined
  2121. options = guidep.split(",")
  2122. for opt in options:
  2123. pOpt = self.task.get_param(opt, element="name", raiseError=False)
  2124. if pOpt and id:
  2125. if "wxId-bind" not in p:
  2126. p["wxId-bind"] = list()
  2127. p["wxId-bind"] += pOpt["wxId"]
  2128. continue
  2129. if p.get("gisprompt", False) is False:
  2130. continue
  2131. prompt = p.get("prompt", "")
  2132. if prompt in ("raster", "vector"):
  2133. name = p.get("name", "")
  2134. if name in ("map", "input"):
  2135. pMap = p
  2136. elif prompt == "layer":
  2137. pLayer.append(p)
  2138. elif prompt == "dbcolumn":
  2139. pColumn.append(p)
  2140. elif prompt == "dbdriver":
  2141. pDriver = p
  2142. elif prompt == "dbname":
  2143. pDatabase = p
  2144. elif prompt == "dbtable":
  2145. pTable = p
  2146. elif prompt == "group":
  2147. pGroup = p
  2148. elif prompt == "subgroup":
  2149. pSubGroup = p
  2150. elif prompt == "sigfile":
  2151. pSigFile.append(p)
  2152. elif prompt == "dbase":
  2153. pDbase = p
  2154. elif prompt == "location":
  2155. pLocation = p
  2156. elif prompt == "mapset":
  2157. pMapset = p
  2158. elif prompt == "sql_query":
  2159. pSqlWhere.append(p)
  2160. # collect ids
  2161. pColumnIds = []
  2162. for p in pColumn:
  2163. pColumnIds += p["wxId"]
  2164. pLayerIds = []
  2165. for p in pLayer:
  2166. pLayerIds += p["wxId"]
  2167. pSigFileIds = []
  2168. for p in pSigFile:
  2169. pSigFileIds += p["wxId"]
  2170. pSqlWhereIds = []
  2171. for p in pSqlWhere:
  2172. pSqlWhereIds += p["wxId"]
  2173. # set wxId-bindings
  2174. if pMap:
  2175. pMap["wxId-bind"] = []
  2176. if pLayer:
  2177. pMap["wxId-bind"] += pLayerIds
  2178. pMap["wxId-bind"] += copy.copy(pColumnIds)
  2179. pMap["wxId-bind"] += copy.copy(pSqlWhereIds)
  2180. if pLayer:
  2181. for p in pLayer:
  2182. p["wxId-bind"] = copy.copy(pColumnIds)
  2183. p["wxId-bind"] += copy.copy(pSqlWhereIds)
  2184. if pDriver and pTable:
  2185. pDriver["wxId-bind"] = pTable["wxId"]
  2186. if pDatabase and pTable:
  2187. pDatabase["wxId-bind"] = pTable["wxId"]
  2188. if pTable and pColumnIds:
  2189. pTable["wxId-bind"] = pColumnIds
  2190. if pGroup and pSubGroup:
  2191. if pSigFile:
  2192. pGroup["wxId-bind"] = pSigFileIds + pSubGroup["wxId"]
  2193. pSubGroup["wxId-bind"] = pSigFileIds
  2194. else:
  2195. pGroup["wxId-bind"] = pSubGroup["wxId"]
  2196. if pDbase and pLocation:
  2197. pDbase["wxId-bind"] = pLocation["wxId"]
  2198. if pLocation and pMapset:
  2199. pLocation["wxId-bind"] = pMapset["wxId"]
  2200. if pLocation and pMapset and pMap:
  2201. # pLocation['wxId-bind'] += pMap['wxId']
  2202. pMapset["wxId-bind"] = pMap["wxId"]
  2203. #
  2204. # determine panel size
  2205. #
  2206. maxsizes = (0, 0)
  2207. for section in sections:
  2208. tab[section].SetSizer(tabsizer[section])
  2209. tab[section].SetupScrolling(True, True, 10, 10)
  2210. tab[section].Layout()
  2211. minsecsizes = tabsizer[section].GetSize()
  2212. maxsizes = list(map(lambda x: max(maxsizes[x], minsecsizes[x]), (0, 1)))
  2213. # TODO: be less arbitrary with these 600
  2214. self.panelMinHeight = 100
  2215. self.constrained_size = (min(600, maxsizes[0]) + 25, min(400, maxsizes[1]) + 25)
  2216. for section in sections:
  2217. tab[section].SetMinSize((self.constrained_size[0], self.panelMinHeight))
  2218. # add pages to notebook
  2219. imageList = wx.ImageList(16, 16)
  2220. self.notebook.AssignImageList(imageList)
  2221. for section in sections:
  2222. self.notebook.AddPage(page=tab[section], text=section, name=section)
  2223. index = self.AddBitmapToImageList(section, imageList)
  2224. if index >= 0:
  2225. self.notebook.SetPageImage(section, index)
  2226. # are we running from command line?
  2227. # add 'command output' tab regardless standalone dialog
  2228. if self.parent.GetName() == "MainFrame" and self.parent.get_dcmd is None:
  2229. from core.gconsole import GConsole, EVT_CMD_RUN, EVT_CMD_DONE
  2230. from gui_core.goutput import GConsoleWindow
  2231. self._gconsole = GConsole(guiparent=self.notebook, giface=self._giface)
  2232. self.goutput = GConsoleWindow(
  2233. parent=self.notebook,
  2234. giface=self._giface,
  2235. gconsole=self._gconsole,
  2236. margin=False,
  2237. )
  2238. self._gconsole.Bind(
  2239. EVT_CMD_RUN,
  2240. lambda event: self._switchPageHandler(
  2241. event=event, notification=Notification.MAKE_VISIBLE
  2242. ),
  2243. )
  2244. self._gconsole.Bind(
  2245. EVT_CMD_DONE,
  2246. lambda event: self._switchPageHandler(
  2247. event=event, notification=Notification.RAISE_WINDOW
  2248. ),
  2249. )
  2250. self.outpage = self.notebook.AddPage(
  2251. page=self.goutput, text=_("Command output"), name="output"
  2252. )
  2253. else:
  2254. self.goutput = None
  2255. self._gconsole = None
  2256. self.manualTab = HelpPanel(parent=self.notebook, command=self.task.get_name())
  2257. if not self.manualTab.GetFile():
  2258. self.manualTab.Hide()
  2259. else:
  2260. self.notebook.AddPage(page=self.manualTab, text=_("Manual"), name="manual")
  2261. index = self.AddBitmapToImageList(section="manual", imageList=imageList)
  2262. if index >= 0:
  2263. self.notebook.SetPageImage("manual", index)
  2264. if self.manualTab.IsLoaded():
  2265. self.manualTab.SetMinSize((self.constrained_size[0], self.panelMinHeight))
  2266. self.notebook.SetSelection(0)
  2267. panelsizer.Add(self.notebook, proportion=1, flag=wx.EXPAND)
  2268. self.SetSizer(panelsizer)
  2269. panelsizer.Fit(self.notebook)
  2270. self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
  2271. def _getValue(self, p):
  2272. """Get value or default value of given parameter
  2273. :param p: parameter directory
  2274. """
  2275. if p.get("value", "") != "":
  2276. return p["value"]
  2277. return p.get("default", "")
  2278. def OnFileLoad(self, event):
  2279. """Load file to interactive input"""
  2280. me = event.GetId()
  2281. win = dict()
  2282. for p in self.task.params:
  2283. if "wxId" in p and me in p["wxId"]:
  2284. win["file"] = self.FindWindowById(p["wxId"][0])
  2285. win["text"] = self.FindWindowById(p["wxId"][1])
  2286. break
  2287. if not win:
  2288. return
  2289. path = win["file"].GetValue()
  2290. if not path:
  2291. gcmd.GMessage(parent=self, message=_("Nothing to load."))
  2292. return
  2293. data = ""
  2294. try:
  2295. f = open(path, "r")
  2296. except IOError as e:
  2297. gcmd.GError(
  2298. parent=self,
  2299. showTraceback=False,
  2300. message=_("Unable to load file.\n\nReason: %s") % e,
  2301. )
  2302. return
  2303. try:
  2304. data = f.read()
  2305. finally:
  2306. f.close()
  2307. win["text"].SetValue(data)
  2308. def OnFileSave(self, event):
  2309. """Save interactive input to the file"""
  2310. wId = event.GetId()
  2311. win = {}
  2312. for p in self.task.params:
  2313. if wId in p.get("wxId", []):
  2314. win["file"] = self.FindWindowById(p["wxId"][0])
  2315. win["text"] = self.FindWindowById(p["wxId"][1])
  2316. break
  2317. if not win:
  2318. return
  2319. text = win["text"].GetValue()
  2320. if not text:
  2321. gcmd.GMessage(parent=self, message=_("Nothing to save."))
  2322. return
  2323. dlg = wx.FileDialog(
  2324. parent=self,
  2325. message=_("Save input as..."),
  2326. defaultDir=os.getcwd(),
  2327. style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
  2328. )
  2329. if dlg.ShowModal() == wx.ID_OK:
  2330. path = dlg.GetPath()
  2331. enc = locale.getdefaultlocale()[1]
  2332. f = codecs.open(path, encoding=enc, mode="w", errors="replace")
  2333. try:
  2334. f.write(text + os.linesep)
  2335. finally:
  2336. f.close()
  2337. win["file"].SetValue(path)
  2338. dlg.Destroy()
  2339. def OnFileText(self, event):
  2340. """File input interactively entered"""
  2341. text = event.GetString()
  2342. p = self.task.get_param(value=event.GetId(), element="wxId", raiseError=False)
  2343. if not p:
  2344. return # should not happen
  2345. win = self.FindWindowById(p["wxId"][0])
  2346. if text:
  2347. filename = win.GetValue()
  2348. if not filename or filename == p["default"]: # m.proj has - as default
  2349. filename = grass.tempfile()
  2350. win.SetValue(filename)
  2351. enc = locale.getdefaultlocale()[1]
  2352. f = codecs.open(filename, encoding=enc, mode="w", errors="replace")
  2353. try:
  2354. f.write(text)
  2355. if text[-1] != os.linesep:
  2356. f.write(os.linesep)
  2357. finally:
  2358. f.close()
  2359. else:
  2360. win.SetValue("")
  2361. def OnVectorFormat(self, event):
  2362. """Change vector format (native / ogr).
  2363. Currently unused.
  2364. """
  2365. sel = event.GetSelection()
  2366. idEvent = event.GetId()
  2367. p = self.task.get_param(value=idEvent, element="wxId", raiseError=False)
  2368. if not p:
  2369. return # should not happen
  2370. # detect windows
  2371. winNative = None
  2372. winOgr = None
  2373. for id in p["wxId"]:
  2374. if id == idEvent:
  2375. continue
  2376. name = self.FindWindowById(id).GetName()
  2377. if name == "Select":
  2378. # fix the mystery (also in nviz_tools.py)
  2379. winNative = self.FindWindowById(id + 1)
  2380. elif name == "OgrSelect":
  2381. winOgr = self.FindWindowById(id)
  2382. # enable / disable widgets & update values
  2383. rbox = self.FindWindowByName("VectorFormat")
  2384. self.hsizer.Remove(rbox)
  2385. if sel == 0: # -> native
  2386. winOgr.Hide()
  2387. self.hsizer.Remove(winOgr)
  2388. self.hsizer.Add(
  2389. winNative,
  2390. flag=wx.ADJUST_MINSIZE
  2391. | wx.BOTTOM
  2392. | wx.LEFT
  2393. | wx.RIGHT
  2394. | wx.TOP
  2395. | wx.ALIGN_TOP,
  2396. border=5,
  2397. )
  2398. winNative.Show()
  2399. p["value"] = winNative.GetValue()
  2400. elif sel == 1: # -> OGR
  2401. sizer = wx.BoxSizer(wx.VERTICAL)
  2402. winNative.Hide()
  2403. self.hsizer.Remove(winNative)
  2404. sizer.Add(winOgr)
  2405. winOgr.Show()
  2406. p["value"] = winOgr.GetDsn()
  2407. self.hsizer.Add(
  2408. sizer,
  2409. flag=wx.ADJUST_MINSIZE
  2410. | wx.BOTTOM
  2411. | wx.LEFT
  2412. | wx.RIGHT
  2413. | wx.TOP
  2414. | wx.ALIGN_TOP,
  2415. border=5,
  2416. )
  2417. self.hsizer.Add(
  2418. rbox,
  2419. flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.ALIGN_TOP,
  2420. border=5,
  2421. )
  2422. self.hsizer.Layout()
  2423. self.Layout()
  2424. self.OnUpdateValues()
  2425. self.OnUpdateSelection(event)
  2426. def OnUpdateDialog(self, event):
  2427. for fn, kwargs in six.iteritems(event.data):
  2428. fn(**kwargs)
  2429. self.parent.updateValuesHook()
  2430. def OnVerbosity(self, event):
  2431. """Verbosity level changed"""
  2432. verbose = self.FindWindowById(self.task.get_flag("verbose")["wxId"][0])
  2433. quiet = self.FindWindowById(self.task.get_flag("quiet")["wxId"][0])
  2434. if event.IsChecked():
  2435. if event.GetId() == verbose.GetId():
  2436. if quiet.IsChecked():
  2437. quiet.SetValue(False)
  2438. self.task.get_flag("quiet")["value"] = False
  2439. else:
  2440. if verbose.IsChecked():
  2441. verbose.SetValue(False)
  2442. self.task.get_flag("verbose")["value"] = False
  2443. event.Skip()
  2444. def OnPageChange(self, event):
  2445. if not event:
  2446. sel = self.notebook.GetSelection()
  2447. else:
  2448. sel = event.GetSelection()
  2449. idx = self.notebook.GetPageIndexByName("manual")
  2450. if idx > -1 and sel == idx:
  2451. # calling LoadPage() is strangely time-consuming (only first call)
  2452. # FIXME: move to helpPage.__init__()
  2453. if not self.manualTab.IsLoaded():
  2454. wx.GetApp().Yield()
  2455. self.manualTab.LoadPage()
  2456. self.Layout()
  2457. if event:
  2458. # skip is needed for wx.Notebook on Windows
  2459. event.Skip()
  2460. # this is needed for dialogs launched from layer manager
  2461. # event is somehow propagated?
  2462. event.StopPropagation()
  2463. def _switchPageHandler(self, event, notification):
  2464. self._switchPage(notification=notification)
  2465. event.Skip()
  2466. def _switchPage(self, notification):
  2467. """Manages @c 'output' notebook page according to event notification."""
  2468. if notification == Notification.HIGHLIGHT:
  2469. self.notebook.HighlightPageByName("output")
  2470. if notification == Notification.MAKE_VISIBLE:
  2471. self.notebook.SetSelectionByName("output")
  2472. if notification == Notification.RAISE_WINDOW:
  2473. self.notebook.SetSelectionByName("output")
  2474. self.SetFocus()
  2475. self.Raise()
  2476. def OnColorChange(self, event):
  2477. myId = event.GetId()
  2478. for p in self.task.params:
  2479. if "wxId" in p and myId in p["wxId"]:
  2480. multiple = p["wxId"][1] is not None # multiple colors
  2481. hasTansp = p["wxId"][2] is not None
  2482. if multiple:
  2483. # selected color is added at the end of textCtrl
  2484. colorchooser = wx.FindWindowById(p["wxId"][0])
  2485. new_color = colorchooser.GetValue()[:]
  2486. new_label = utils.rgb2str.get(
  2487. new_color, ":".join(list(map(str, new_color)))
  2488. )
  2489. textCtrl = wx.FindWindowById(p["wxId"][1])
  2490. val = textCtrl.GetValue()
  2491. sep = ","
  2492. if val and val[-1] != sep:
  2493. val += sep
  2494. val += new_label
  2495. textCtrl.SetValue(val)
  2496. p["value"] = val
  2497. elif hasTansp and wx.FindWindowById(p["wxId"][2]).GetValue():
  2498. p["value"] = "none"
  2499. else:
  2500. colorchooser = wx.FindWindowById(p["wxId"][0])
  2501. new_color = colorchooser.GetValue()[:]
  2502. # This is weird: new_color is a 4-tuple and new_color[:] is a 3-tuple
  2503. # under wx2.8.1
  2504. new_label = utils.rgb2str.get(
  2505. new_color, ":".join(list(map(str, new_color)))
  2506. )
  2507. colorchooser.SetLabel(new_label)
  2508. colorchooser.SetColour(new_color)
  2509. colorchooser.Refresh()
  2510. p["value"] = colorchooser.GetLabel()
  2511. self.OnUpdateValues()
  2512. def OnUpdateValues(self, event=None):
  2513. """If we were part of a richer interface, report back the
  2514. current command being built.
  2515. This method should be set by the parent of this panel if
  2516. needed. It's a hook, actually. Beware of what is 'self' in
  2517. the method def, though. It will be called with no arguments.
  2518. """
  2519. pass
  2520. def OnCheckBoxMulti(self, event):
  2521. """Fill the values as a ','-separated string according to
  2522. current status of the checkboxes.
  2523. """
  2524. me = event.GetId()
  2525. theParam = None
  2526. for p in self.task.params:
  2527. if "wxId" in p and me in p["wxId"]:
  2528. theParam = p
  2529. myIndex = p["wxId"].index(me)
  2530. # Unpack current value list
  2531. currentValues = {}
  2532. for isThere in theParam.get("value", "").split(","):
  2533. currentValues[isThere] = 1
  2534. theValue = theParam["values"][myIndex]
  2535. if event.IsChecked():
  2536. currentValues[theValue] = 1
  2537. else:
  2538. del currentValues[theValue]
  2539. # Keep the original order, so that some defaults may be recovered
  2540. currentValueList = []
  2541. for v in theParam["values"]:
  2542. if v in currentValues:
  2543. currentValueList.append(v)
  2544. # Pack it back
  2545. theParam["value"] = ",".join(currentValueList)
  2546. self.OnUpdateValues()
  2547. event.Skip()
  2548. def OnSetValue(self, event):
  2549. """Retrieve the widget value and set the task value field
  2550. accordingly.
  2551. Use for widgets that have a proper GetValue() method, i.e. not
  2552. for selectors.
  2553. """
  2554. myId = event.GetId()
  2555. me = wx.FindWindowById(myId)
  2556. name = me.GetName()
  2557. found = False
  2558. for porf in self.task.params + self.task.flags:
  2559. if "wxId" not in porf:
  2560. continue
  2561. if myId in porf["wxId"]:
  2562. found = True
  2563. break
  2564. if not found:
  2565. return
  2566. if name == "GdalSelect":
  2567. porf["value"] = event.dsn
  2568. elif name == "ModelParam":
  2569. porf["parameterized"] = me.IsChecked()
  2570. elif name == "GdalSelectDataSource":
  2571. win = self.FindWindowById(porf["wxId"][0])
  2572. porf["value"] = win.GetDsn()
  2573. pLayer = self.task.get_param("layer", element="name", raiseError=False)
  2574. if pLayer:
  2575. pLayer["value"] = ""
  2576. else:
  2577. if isinstance(me, SpinCtrl):
  2578. porf["value"] = str(me.GetValue())
  2579. elif isinstance(me, wx.ComboBox):
  2580. porf["value"] = me.GetValue()
  2581. elif isinstance(me, wx.Choice):
  2582. porf["value"] = me.GetStringSelection()
  2583. else:
  2584. porf["value"] = me.GetValue()
  2585. self.OnUpdateValues(event)
  2586. event.Skip()
  2587. def OnSetSymbol(self, event):
  2588. """Shows dialog for symbol selection"""
  2589. myId = event.GetId()
  2590. for p in self.task.params:
  2591. if "wxId" in p and myId in p["wxId"]:
  2592. from gui_core.dialogs import SymbolDialog
  2593. dlg = SymbolDialog(
  2594. self, symbolPath=globalvar.SYMBDIR, currentSymbol=p["value"]
  2595. )
  2596. if dlg.ShowModal() == wx.ID_OK:
  2597. img = dlg.GetSelectedSymbolPath()
  2598. p["value"] = dlg.GetSelectedSymbolName()
  2599. bitmapButton = wx.FindWindowById(p["wxId"][0])
  2600. label = wx.FindWindowById(p["wxId"][1])
  2601. bitmapButton.SetBitmapLabel(wx.Bitmap(img + ".png"))
  2602. label.SetLabel(p["value"])
  2603. self.OnUpdateValues(event)
  2604. dlg.Destroy()
  2605. def OnTimelineTool(self, event):
  2606. """Show Timeline Tool with dataset(s) from gselect.
  2607. .. todo::
  2608. update from gselect automatically
  2609. """
  2610. myId = event.GetId()
  2611. for p in self.task.params:
  2612. if "wxId" in p and myId in p["wxId"]:
  2613. select = self.FindWindowById(p["wxId"][0])
  2614. if not select.GetValue():
  2615. gcmd.GMessage(parent=self, message=_("No dataset given."))
  2616. return
  2617. datasets = select.GetValue().split(",")
  2618. from timeline import frame
  2619. frame.run(parent=self, datasets=datasets)
  2620. def OnSelectFont(self, event):
  2621. """Select font using font dialog"""
  2622. myId = event.GetId()
  2623. for p in self.task.params:
  2624. if "wxId" in p and myId in p["wxId"]:
  2625. from gui_core.dialogs import DefaultFontDialog
  2626. dlg = DefaultFontDialog(
  2627. parent=self,
  2628. title=_("Select font"),
  2629. style=wx.DEFAULT_DIALOG_STYLE,
  2630. type="font",
  2631. )
  2632. if dlg.ShowModal() == wx.ID_OK:
  2633. if dlg.font:
  2634. p["value"] = dlg.font
  2635. self.FindWindowById(p["wxId"][1]).SetValue(dlg.font)
  2636. self.OnUpdateValues(event)
  2637. dlg.Destroy()
  2638. def OnUpdateSelection(self, event):
  2639. """Update dialog (layers, tables, columns, etc.)"""
  2640. if not hasattr(self.parent, "updateThread"):
  2641. if event:
  2642. event.Skip()
  2643. return
  2644. if event:
  2645. self.parent.updateThread.Update(
  2646. UpdateDialog, self, event, event.GetId(), self.task
  2647. )
  2648. else:
  2649. self.parent.updateThread.Update(UpdateDialog, self, None, None, self.task)
  2650. def createCmd(self, ignoreErrors=False, ignoreRequired=False):
  2651. """Produce a command line string (list) or feeding into GRASS.
  2652. :param ignoreErrors: True then it will return whatever has been
  2653. built so far, even though it would not be
  2654. a correct command for GRASS
  2655. """
  2656. try:
  2657. cmd = self.task.get_cmd(
  2658. ignoreErrors=ignoreErrors, ignoreRequired=ignoreRequired
  2659. )
  2660. except ValueError as err:
  2661. dlg = wx.MessageDialog(
  2662. parent=self,
  2663. message=gcmd.DecodeString(str(err)),
  2664. caption=_("Error in %s") % self.task.name,
  2665. style=wx.OK | wx.ICON_ERROR | wx.CENTRE,
  2666. )
  2667. dlg.ShowModal()
  2668. dlg.Destroy()
  2669. cmd = None
  2670. return cmd
  2671. def OnSize(self, event):
  2672. width = event.GetSize()[0]
  2673. fontsize = self.GetFont().GetPointSize()
  2674. text_width = max(width / (fontsize - 3), 70)
  2675. for id in self.label_id:
  2676. win = self.FindWindowById(id)
  2677. label = win.GetLabel()
  2678. label_new = "\n".join(textwrap.wrap(label, text_width))
  2679. win.SetLabel(label_new)
  2680. event.Skip()
  2681. def AddBitmapToImageList(self, section, imageList):
  2682. iconTheme = UserSettings.Get(group="appearance", key="iconTheme", subkey="type")
  2683. iconSectionDict = {
  2684. "manual": os.path.join(globalvar.ICONDIR, iconTheme, "help.png")
  2685. }
  2686. if section in iconSectionDict.keys():
  2687. image = wx.Image(iconSectionDict[section]).Scale(
  2688. 16, 16, wx.IMAGE_QUALITY_HIGH
  2689. )
  2690. idx = imageList.Add(BitmapFromImage(image))
  2691. return idx
  2692. return -1
  2693. class GUI:
  2694. def __init__(
  2695. self,
  2696. parent=None,
  2697. giface=None,
  2698. show=True,
  2699. modal=False,
  2700. centreOnParent=False,
  2701. checkError=False,
  2702. ):
  2703. """Parses GRASS commands when module is imported and used from
  2704. Layer Manager.
  2705. """
  2706. self.parent = parent
  2707. self.show = show
  2708. self.modal = modal
  2709. self._giface = giface
  2710. self.centreOnParent = centreOnParent
  2711. self.checkError = checkError
  2712. self.grass_task = None
  2713. self.cmd = list()
  2714. global _blackList
  2715. if self.parent:
  2716. _blackList["enabled"] = True
  2717. else:
  2718. _blackList["enabled"] = False
  2719. def GetCmd(self):
  2720. """Get validated command"""
  2721. return self.cmd
  2722. def ParseCommand(self, cmd, completed=None):
  2723. """Parse command
  2724. Note: cmd is given as list
  2725. If command is given with options, return validated cmd list:
  2726. - add key name for first parameter if not given
  2727. - change mapname to mapname@mapset
  2728. """
  2729. dcmd_params = {}
  2730. if completed is None:
  2731. get_dcmd = None
  2732. layer = None
  2733. dcmd_params = None
  2734. else:
  2735. get_dcmd = completed[0]
  2736. layer = completed[1]
  2737. if completed[2]:
  2738. dcmd_params.update(completed[2])
  2739. # parse the interface decription
  2740. try:
  2741. global _blackList
  2742. self.grass_task = gtask.parse_interface(cmd[0], blackList=_blackList)
  2743. except (grass.ScriptError, ValueError) as e:
  2744. raise gcmd.GException(e.value)
  2745. # if layer parameters previously set, re-insert them into dialog
  2746. if completed is not None:
  2747. if "params" in dcmd_params:
  2748. self.grass_task.params = dcmd_params["params"]
  2749. if "flags" in dcmd_params:
  2750. self.grass_task.flags = dcmd_params["flags"]
  2751. err = list()
  2752. # update parameters if needed && validate command
  2753. if len(cmd) > 1:
  2754. i = 0
  2755. cmd_validated = [cmd[0]]
  2756. for option in cmd[1:]:
  2757. if option[0] == "-": # flag
  2758. if len(option) == 1: # catch typo like 'g.proj - w'
  2759. raise gcmd.GException(
  2760. _("Unable to parse command '%s'") % " ".join(cmd)
  2761. )
  2762. if option[1] == "-":
  2763. self.grass_task.set_flag(option[2:], True)
  2764. else:
  2765. self.grass_task.set_flag(option[1], True)
  2766. cmd_validated.append(option)
  2767. else: # parameter
  2768. try:
  2769. key, value = option.split("=", 1)
  2770. except ValueError:
  2771. if self.grass_task.firstParam:
  2772. if i == 0: # add key name of first parameter if not given
  2773. key = self.grass_task.firstParam
  2774. value = option
  2775. else:
  2776. raise gcmd.GException(
  2777. _("Unable to parse command '%s'") % " ".join(cmd)
  2778. )
  2779. else:
  2780. continue
  2781. task = self.grass_task.get_param(key, raiseError=False)
  2782. if not task:
  2783. err.append(
  2784. _("%(cmd)s: parameter '%(key)s' not available")
  2785. % {"cmd": cmd[0], "key": key}
  2786. )
  2787. continue
  2788. self.grass_task.set_param(key, value)
  2789. cmd_validated.append(key + "=" + value)
  2790. i += 1
  2791. # update original command list
  2792. cmd = cmd_validated
  2793. if self.show is not None:
  2794. self.mf = TaskFrame(
  2795. parent=self.parent,
  2796. giface=self._giface,
  2797. task_description=self.grass_task,
  2798. get_dcmd=get_dcmd,
  2799. layer=layer,
  2800. )
  2801. else:
  2802. self.mf = None
  2803. if get_dcmd is not None:
  2804. # update only propwin reference
  2805. get_dcmd(dcmd=None, layer=layer, params=None, propwin=self.mf)
  2806. if self.show is not None:
  2807. self.mf.notebookpanel.OnUpdateSelection(None)
  2808. if self.show is True:
  2809. if self.parent and self.centreOnParent:
  2810. self.mf.CentreOnParent()
  2811. else:
  2812. self.mf.CenterOnScreen()
  2813. self.mf.Show(self.show)
  2814. self.mf.MakeModal(self.modal)
  2815. else:
  2816. self.mf.OnApply(None)
  2817. self.cmd = cmd
  2818. if self.checkError:
  2819. return self.grass_task, err
  2820. else:
  2821. return self.grass_task
  2822. def GetCommandInputMapParamKey(self, cmd):
  2823. """Get parameter key for input raster/vector map
  2824. :param cmd: module name
  2825. :return: parameter key
  2826. :return: None on failure
  2827. """
  2828. # parse the interface decription
  2829. if not self.grass_task:
  2830. tree = etree.fromstring(gtask.get_interface_description(cmd))
  2831. self.grass_task = gtask.processTask(tree).get_task()
  2832. for p in self.grass_task.params:
  2833. if p.get("name", "") in ("input", "map"):
  2834. age = p.get("age", "")
  2835. prompt = p.get("prompt", "")
  2836. element = p.get("element", "")
  2837. if (
  2838. age == "old"
  2839. and element in ("cell", "grid3", "vector")
  2840. and prompt in ("raster", "raster_3d", "vector")
  2841. ):
  2842. return p.get("name", None)
  2843. return None
  2844. class GrassGUIApp(wx.App):
  2845. """Stand-alone GRASS command GUI"""
  2846. def __init__(self, grass_task):
  2847. self.grass_task = grass_task
  2848. wx.App.__init__(self, False)
  2849. def OnInit(self):
  2850. msg = self.grass_task.get_error_msg()
  2851. if msg:
  2852. gcmd.GError(
  2853. msg
  2854. + "\n\n"
  2855. + _("Try to set up GRASS_ADDON_PATH or GRASS_ADDON_BASE variable.")
  2856. )
  2857. return True
  2858. self.mf = TaskFrame(
  2859. parent=None,
  2860. giface=StandaloneGrassInterface(),
  2861. task_description=self.grass_task,
  2862. )
  2863. self.mf.CentreOnScreen()
  2864. self.mf.Show(True)
  2865. self.SetTopWindow(self.mf)
  2866. return True
  2867. USAGE_MESSAGE = """Usage:
  2868. {name} <grass module>
  2869. {name} <full path to file>
  2870. python {name} <grass module>
  2871. Test:
  2872. python {name} test
  2873. python {name} g.region
  2874. python {name} "g.region -p"
  2875. python {name} temporal/t.list/t.list.py"""
  2876. if __name__ == "__main__":
  2877. if len(sys.argv) == 1:
  2878. sys.exit(_(USAGE_MESSAGE).format(name=sys.argv[0]))
  2879. if sys.argv[1] != "test":
  2880. q = wx.LogNull()
  2881. Debug.msg(1, "forms.py called using command: %s" % sys.argv[1])
  2882. cmd = utils.split(sys.argv[1])
  2883. task = gtask.grassTask(cmd[0])
  2884. task.set_options(cmd[1:])
  2885. Debug.msg(
  2886. 1,
  2887. "forms.py opening form for: %s"
  2888. % task.get_cmd(ignoreErrors=True, ignoreRequired=True),
  2889. )
  2890. app = GrassGUIApp(task)
  2891. app.MainLoop()
  2892. else: # Test
  2893. # Test grassTask from within a GRASS session
  2894. if os.getenv("GISBASE") is not None:
  2895. task = gtask.grassTask("d.vect")
  2896. task.get_param("map")["value"] = "map_name"
  2897. task.get_flag("i")["value"] = True
  2898. task.get_param("layer")["value"] = 1
  2899. task.get_param("label_bcolor")["value"] = "red"
  2900. # the default parameter display is added automatically
  2901. assert (
  2902. " ".join(task.get_cmd())
  2903. == "d.vect -i map=map_name layer=1 display=shape label_bcolor=red"
  2904. )
  2905. print("Creation of task successful")
  2906. # Test interface building with handmade grassTask,
  2907. # possibly outside of a GRASS session.
  2908. print("Now creating a module dialog (task frame)")
  2909. task = gtask.grassTask()
  2910. task.name = "TestTask"
  2911. task.description = (
  2912. "This is an artificial grassTask() object intended for testing purposes."
  2913. )
  2914. task.keywords = ["grass", "test", "task"]
  2915. task.params = [
  2916. {
  2917. "name": "text",
  2918. "description": "Descriptions go into tooltips if labels are present, like this one",
  2919. "label": "Enter some text",
  2920. "key_desc": ["value"],
  2921. "values_desc": [],
  2922. },
  2923. {
  2924. "name": "hidden_text",
  2925. "description": "This text should not appear in the form",
  2926. "hidden": True,
  2927. "key_desc": ["value"],
  2928. "values_desc": [],
  2929. },
  2930. {
  2931. "name": "text_default",
  2932. "description": "Enter text to override the default",
  2933. "default": "default text",
  2934. "key_desc": ["value"],
  2935. "values_desc": [],
  2936. },
  2937. {
  2938. "name": "text_prefilled",
  2939. "description": "You should see a friendly welcome message here",
  2940. "value": "hello, world",
  2941. "key_desc": ["value"],
  2942. "values_desc": [],
  2943. },
  2944. {
  2945. "name": "plain_color",
  2946. "description": "This is a plain color, and it is a compulsory parameter",
  2947. "required": False,
  2948. "gisprompt": True,
  2949. "prompt": "color",
  2950. "key_desc": ["value"],
  2951. "values_desc": [],
  2952. },
  2953. {
  2954. "name": "transparent_color",
  2955. "description": "This color becomes transparent when set to none",
  2956. "guisection": "tab",
  2957. "gisprompt": True,
  2958. "prompt": "color",
  2959. "key_desc": ["value"],
  2960. "values_desc": [],
  2961. },
  2962. {
  2963. "name": "multi",
  2964. "description": "A multiple selection",
  2965. "default": "red,green,blue",
  2966. "gisprompt": False,
  2967. "guisection": "tab",
  2968. "multiple": "yes",
  2969. "type": "string",
  2970. "value": "",
  2971. "values": ["red", "green", "yellow", "blue", "purple", "other"],
  2972. "key_desc": ["value"],
  2973. "values_desc": [],
  2974. },
  2975. {
  2976. "name": "single",
  2977. "description": "A single multiple-choice selection",
  2978. "values": ["red", "green", "yellow", "blue", "purple", "other"],
  2979. "guisection": "tab",
  2980. "key_desc": ["value"],
  2981. "values_desc": [],
  2982. },
  2983. {
  2984. "name": "large_multi",
  2985. "description": "A large multiple selection",
  2986. "gisprompt": False,
  2987. "multiple": "yes",
  2988. # values must be an array of strings
  2989. "values": utils.str2rgb.keys() + list(map(str, utils.str2rgb.values())),
  2990. "key_desc": ["value"],
  2991. "values_desc": [],
  2992. },
  2993. {
  2994. "name": "a_file",
  2995. "description": "A file selector",
  2996. "gisprompt": True,
  2997. "element": "file",
  2998. "key_desc": ["value"],
  2999. "values_desc": [],
  3000. },
  3001. ]
  3002. task.flags = [
  3003. {
  3004. "name": "a",
  3005. "description": "Some flag, will appear in Main since it is required",
  3006. "required": True,
  3007. "value": False,
  3008. "suppress_required": False,
  3009. },
  3010. {
  3011. "name": "b",
  3012. "description": "pre-filled flag, will appear in options since it is not required",
  3013. "value": True,
  3014. "suppress_required": False,
  3015. },
  3016. {
  3017. "name": "hidden_flag",
  3018. "description": "hidden flag, should not be changeable",
  3019. "hidden": "yes",
  3020. "value": True,
  3021. "suppress_required": False,
  3022. },
  3023. ]
  3024. q = wx.LogNull()
  3025. GrassGUIApp(task).MainLoop()