dialogs.py 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625
  1. """
  2. @package gui_core.dialogs
  3. @brief Various dialogs used in wxGUI.
  4. List of classes:
  5. - :class:`SimpleDialog`
  6. - :class:`LocationDialog`
  7. - :class:`MapsetDialog`
  8. - :class:`VectorDialog`
  9. - :class:`NewVectorDialog`
  10. - :class:`SavedRegion`
  11. - :class:`GroupDialog`
  12. - :class:`MapLayersDialog`
  13. - :class:`SetOpacityDialog`
  14. - :class:`ImageSizeDialog`
  15. - :class:`SqlQueryFrame`
  16. - :class:`SymbolDialog`
  17. - :class:`QuitDialog`
  18. - :class:`DefaultFontDialog`
  19. (C) 2008-2016 by the GRASS Development Team
  20. This program is free software under the GNU General Public License
  21. (>=v2). Read the file COPYING that comes with GRASS for details.
  22. @author Martin Landa <landa.martin gmail.com>
  23. @author Anna Kratochvilova <kratochanna gmail.com> (GroupDialog, SymbolDialog)
  24. """
  25. import os
  26. import re
  27. import six
  28. import wx
  29. from grass.script import core as grass
  30. from grass.script.utils import naturally_sorted, try_remove
  31. from grass.pydispatch.signal import Signal
  32. from core import globalvar
  33. from core.gcmd import GError, RunCommand, GMessage
  34. from gui_core.gselect import (
  35. LocationSelect,
  36. MapsetSelect,
  37. Select,
  38. OgrTypeSelect,
  39. SubGroupSelect,
  40. )
  41. from gui_core.widgets import SingleSymbolPanel, SimpleValidator, MapValidator
  42. from core.settings import UserSettings
  43. from core.debug import Debug
  44. from core.utils import is_shell_running
  45. from gui_core.wrap import (
  46. Button,
  47. CheckListBox,
  48. EmptyBitmap,
  49. HyperlinkCtrl,
  50. Menu,
  51. NewId,
  52. SpinCtrl,
  53. StaticBox,
  54. StaticText,
  55. TextCtrl,
  56. )
  57. class SimpleDialog(wx.Dialog):
  58. def __init__(
  59. self,
  60. parent,
  61. title,
  62. id=wx.ID_ANY,
  63. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  64. **kwargs,
  65. ):
  66. """General dialog to choose given element (location, mapset, vector map, etc.)
  67. :param parent: window
  68. :param title: window title
  69. """
  70. wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)
  71. self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY)
  72. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  73. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  74. self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
  75. self.btnOK.SetDefault()
  76. self.__layout()
  77. self.warning = _("Required item is not set.")
  78. def __layout(self):
  79. """Do layout"""
  80. self.sizer = wx.BoxSizer(wx.VERTICAL)
  81. self.dataSizer = wx.BoxSizer(wx.VERTICAL)
  82. # self.informLabel = wx.StaticText(self.panel, id = wx.ID_ANY)
  83. # buttons
  84. btnSizer = wx.StdDialogButtonSizer()
  85. btnSizer.AddButton(self.btnCancel)
  86. btnSizer.AddButton(self.btnOK)
  87. btnSizer.Realize()
  88. self.sizer.Add(self.dataSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  89. # self.sizer.Add(item = self.informLabel, proportion = 0, flag = wx.ALL, border = 5)
  90. self.sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  91. def ValidatorCallback(self, win):
  92. GMessage(parent=self, message=self.warning)
  93. # self.informLabel.SetForegroundColour(wx.Colour(255, 0, 0))
  94. # self.informLabel.SetLabel(self.warning)
  95. class LocationDialog(SimpleDialog):
  96. """Dialog used to select location"""
  97. def __init__(self, parent, title=_("Select GRASS location and mapset")):
  98. SimpleDialog.__init__(self, parent, title)
  99. self.element1 = LocationSelect(
  100. parent=self.panel,
  101. id=wx.ID_ANY,
  102. size=globalvar.DIALOG_GSELECT_SIZE,
  103. validator=SimpleValidator(callback=self.ValidatorCallback),
  104. )
  105. self.element1.Bind(wx.EVT_TEXT, self.OnLocation)
  106. self.element1.Bind(wx.EVT_COMBOBOX, self.OnLocation)
  107. self.element2 = MapsetSelect(
  108. parent=self.panel,
  109. id=wx.ID_ANY,
  110. size=globalvar.DIALOG_GSELECT_SIZE,
  111. setItems=False,
  112. skipCurrent=True,
  113. validator=SimpleValidator(callback=self.ValidatorCallback),
  114. )
  115. self.element1.SetFocus()
  116. self.warning = _("Location or mapset is not defined.")
  117. self._layout()
  118. self.SetMinSize(self.GetSize())
  119. def _layout(self):
  120. """Do layout"""
  121. self.dataSizer.Add(
  122. StaticText(
  123. parent=self.panel, id=wx.ID_ANY, label=_("Name of GRASS location:")
  124. ),
  125. proportion=0,
  126. flag=wx.ALL,
  127. border=1,
  128. )
  129. self.dataSizer.Add(
  130. self.element1, proportion=0, flag=wx.EXPAND | wx.ALL, border=1
  131. )
  132. self.dataSizer.Add(
  133. StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Name of mapset:")),
  134. proportion=0,
  135. flag=wx.EXPAND | wx.ALL,
  136. border=1,
  137. )
  138. self.dataSizer.Add(
  139. self.element2, proportion=0, flag=wx.EXPAND | wx.ALL, border=1
  140. )
  141. self.panel.SetSizer(self.sizer)
  142. self.sizer.Fit(self)
  143. def OnLocation(self, event):
  144. """Select mapset given location name"""
  145. location = event.GetString()
  146. if location:
  147. dbase = grass.gisenv()["GISDBASE"]
  148. self.element2.UpdateItems(dbase=dbase, location=location)
  149. self.element2.SetSelection(0)
  150. mapset = self.element2.GetStringSelection()
  151. def GetValues(self):
  152. """Get location, mapset"""
  153. return (self.element1.GetValue(), self.element2.GetValue())
  154. class MapsetDialog(SimpleDialog):
  155. """Dialog used to select mapset"""
  156. def __init__(
  157. self, parent, title=_("Select mapset in GRASS location"), location=None
  158. ):
  159. SimpleDialog.__init__(self, parent, title)
  160. if location:
  161. self.SetTitle(self.GetTitle() + " <%s>" % location)
  162. else:
  163. self.SetTitle(self.GetTitle() + " <%s>" % grass.gisenv()["LOCATION_NAME"])
  164. self.element = MapsetSelect(
  165. parent=self.panel,
  166. id=wx.ID_ANY,
  167. skipCurrent=True,
  168. size=globalvar.DIALOG_GSELECT_SIZE,
  169. validator=SimpleValidator(callback=self.ValidatorCallback),
  170. )
  171. self.element.SetFocus()
  172. self.warning = _("Name of mapset is missing.")
  173. self._layout()
  174. self.SetMinSize(self.GetSize())
  175. def _layout(self):
  176. """Do layout"""
  177. self.dataSizer.Add(
  178. StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Name of mapset:")),
  179. proportion=0,
  180. flag=wx.ALL,
  181. border=1,
  182. )
  183. self.dataSizer.Add(
  184. self.element, proportion=0, flag=wx.EXPAND | wx.ALL, border=1
  185. )
  186. self.panel.SetSizer(self.sizer)
  187. self.sizer.Fit(self)
  188. def GetMapset(self):
  189. return self.element.GetValue()
  190. class VectorDialog(SimpleDialog):
  191. def __init__(self, parent, title=_("Select vector map"), layerTree=None):
  192. """Dialog for selecting existing vector map
  193. :param parent: parent window
  194. :param title: window title
  195. :param layerTree: show only vector maps in given layer tree if not None
  196. :return: dialog instance
  197. """
  198. SimpleDialog.__init__(self, parent, title)
  199. self.element = self._selection_widget(layerTree)
  200. self.element.SetFocus()
  201. self.warning = _("Name of vector map is missing.")
  202. wx.CallAfter(self._layout)
  203. def _selection_widget(self, layerTree):
  204. return Select(
  205. parent=self.panel,
  206. id=wx.ID_ANY,
  207. size=globalvar.DIALOG_GSELECT_SIZE,
  208. type="vector",
  209. layerTree=layerTree,
  210. fullyQualified=True,
  211. )
  212. def _layout(self):
  213. """Do layout"""
  214. self.dataSizer.Add(
  215. StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Name of vector map:")),
  216. proportion=0,
  217. flag=wx.ALL,
  218. border=1,
  219. )
  220. self.dataSizer.Add(
  221. self.element, proportion=0, flag=wx.EXPAND | wx.ALL, border=1
  222. )
  223. self.panel.SetSizer(self.sizer)
  224. self.sizer.Fit(self)
  225. def GetName(self, full=False):
  226. """Get name of vector map to be created
  227. :param full: True to get fully qualified name
  228. """
  229. name = self.element.GetValue()
  230. if full:
  231. if "@" in name:
  232. return name
  233. else:
  234. return name + "@" + grass.gisenv()["MAPSET"]
  235. return name.split("@", 1)[0]
  236. class NewVectorDialog(VectorDialog):
  237. def __init__(
  238. self,
  239. parent,
  240. title=_("Create new vector map"),
  241. disableAdd=False,
  242. disableTable=False,
  243. showType=False,
  244. ):
  245. """Dialog for creating new vector map
  246. :param parent: parent window
  247. :param title: window title
  248. :param disableAdd: disable 'add layer' checkbox
  249. :param disableTable: disable 'create table' checkbox
  250. :param showType: True to show feature type selector (used for creating new empty OGR layers)
  251. :return: dialog instance
  252. """
  253. VectorDialog.__init__(self, parent, title)
  254. # determine output format
  255. if showType:
  256. self.ftype = OgrTypeSelect(parent=self, panel=self.panel)
  257. else:
  258. self.ftype = None
  259. # create attribute table
  260. self.table = wx.CheckBox(
  261. parent=self.panel, id=wx.ID_ANY, label=_("Create attribute table")
  262. )
  263. self.table.SetValue(True)
  264. if disableTable:
  265. self.table.Enable(False)
  266. if showType:
  267. self.keycol = None
  268. else:
  269. self.keycol = TextCtrl(
  270. parent=self.panel, id=wx.ID_ANY, size=globalvar.DIALOG_SPIN_SIZE
  271. )
  272. self.keycol.SetValue(
  273. UserSettings.Get(group="atm", key="keycolumn", subkey="value")
  274. )
  275. if disableTable:
  276. self.keycol.Enable(False)
  277. self.addbox = wx.CheckBox(
  278. parent=self.panel,
  279. label=_("Add created map into layer tree"),
  280. style=wx.NO_BORDER,
  281. )
  282. if disableAdd:
  283. self.addbox.SetValue(True)
  284. self.addbox.Enable(False)
  285. else:
  286. self.addbox.SetValue(
  287. UserSettings.Get(group="cmd", key="addNewLayer", subkey="enabled")
  288. )
  289. self.table.Bind(wx.EVT_CHECKBOX, self.OnTable)
  290. self.warning = _("Name of new vector map is missing.")
  291. def _selection_widget(self, layerTree):
  292. return Select(
  293. parent=self.panel,
  294. id=wx.ID_ANY,
  295. size=globalvar.DIALOG_GSELECT_SIZE,
  296. type="vector",
  297. layerTree=layerTree,
  298. fullyQualified=False,
  299. validator=MapValidator(),
  300. )
  301. def OnTable(self, event):
  302. if self.keycol:
  303. self.keycol.Enable(event.IsChecked())
  304. def _layout(self):
  305. """Do layout"""
  306. self.dataSizer.Add(
  307. StaticText(
  308. parent=self.panel, id=wx.ID_ANY, label=_("Name for new vector map:")
  309. ),
  310. proportion=0,
  311. flag=wx.ALL,
  312. border=1,
  313. )
  314. self.dataSizer.Add(
  315. self.element, proportion=0, flag=wx.EXPAND | wx.ALL, border=1
  316. )
  317. if self.ftype:
  318. self.dataSizer.AddSpacer(1)
  319. self.dataSizer.Add(
  320. self.ftype, proportion=0, flag=wx.EXPAND | wx.ALL, border=1
  321. )
  322. self.dataSizer.Add(self.table, proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
  323. if self.keycol:
  324. keySizer = wx.BoxSizer(wx.HORIZONTAL)
  325. keySizer.Add(
  326. StaticText(parent=self.panel, label=_("Key column:")),
  327. proportion=0,
  328. flag=wx.ALIGN_CENTER_VERTICAL,
  329. )
  330. keySizer.AddSpacer(10)
  331. keySizer.Add(self.keycol, proportion=0)
  332. self.dataSizer.Add(
  333. keySizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=1
  334. )
  335. self.dataSizer.AddSpacer(5)
  336. self.dataSizer.Add(self.addbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
  337. self.panel.SetSizer(self.sizer)
  338. self.sizer.Fit(self)
  339. self.SetMinSize(self.GetSize())
  340. def GetKey(self):
  341. """Get key column name"""
  342. if self.keycol:
  343. return self.keycol.GetValue()
  344. return UserSettings.Get(group="atm", key="keycolumn", subkey="value")
  345. def IsChecked(self, key):
  346. """Get dialog properties
  347. :param key: window key ('add', 'table')
  348. :return: True/False
  349. :return: None on error
  350. """
  351. if key == "add":
  352. return self.addbox.IsChecked()
  353. elif key == "table":
  354. return self.table.IsChecked()
  355. return None
  356. def GetFeatureType(self):
  357. """Get feature type for OGR
  358. :return: feature type as string
  359. :return: None for native format
  360. """
  361. if self.ftype:
  362. return self.ftype.GetType()
  363. return None
  364. def CreateNewVector(
  365. parent,
  366. cmd,
  367. title=_("Create new vector map"),
  368. exceptMap=None,
  369. giface=None,
  370. disableAdd=False,
  371. disableTable=False,
  372. ):
  373. """Create new vector map layer
  374. :param cmd: (prog, \*\*kwargs)
  375. :param title: window title
  376. :param exceptMap: list of maps to be excepted
  377. :param log:
  378. :param disableAdd: disable 'add layer' checkbox
  379. :param disableTable: disable 'create table' checkbox
  380. :return: dialog instance
  381. :return: None on error
  382. """
  383. vExternalOut = grass.parse_command("v.external.out", flags="g")
  384. isNative = vExternalOut["format"] == "native"
  385. if cmd[0] == "v.edit" and not isNative:
  386. showType = True
  387. else:
  388. showType = False
  389. dlg = NewVectorDialog(
  390. parent,
  391. title=title,
  392. disableAdd=disableAdd,
  393. disableTable=disableTable,
  394. showType=showType,
  395. )
  396. if dlg.ShowModal() != wx.ID_OK:
  397. dlg.Destroy()
  398. return None
  399. outmap = dlg.GetName()
  400. key = dlg.GetKey()
  401. if outmap == exceptMap:
  402. GError(parent=parent, message=_("Unable to create vector map <%s>.") % outmap)
  403. dlg.Destroy()
  404. return None
  405. if dlg.table.IsEnabled() and not key:
  406. GError(
  407. parent=parent,
  408. message=_(
  409. "Invalid or empty key column.\n" "Unable to create vector map <%s>."
  410. )
  411. % outmap,
  412. )
  413. dlg.Destroy()
  414. return
  415. if outmap == "": # should not happen
  416. dlg.Destroy()
  417. return None
  418. # update cmd -> output name defined
  419. cmd[1][cmd[2]] = outmap
  420. if showType:
  421. cmd[1]["type"] = dlg.GetFeatureType()
  422. curMapset = grass.gisenv()["MAPSET"]
  423. if isNative:
  424. listOfVectors = grass.list_grouped("vector")[curMapset]
  425. else:
  426. listOfVectors = RunCommand(
  427. "v.external",
  428. quiet=True,
  429. parent=parent,
  430. read=True,
  431. flags="l",
  432. input=vExternalOut["dsn"],
  433. ).splitlines()
  434. overwrite = False
  435. if (
  436. not UserSettings.Get(group="cmd", key="overwrite", subkey="enabled")
  437. and outmap in listOfVectors
  438. ):
  439. dlgOw = wx.MessageDialog(
  440. parent,
  441. message=_(
  442. "Vector map <%s> already exists "
  443. "in the current mapset. "
  444. "Do you want to overwrite it?"
  445. )
  446. % outmap,
  447. caption=_("Overwrite?"),
  448. style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION,
  449. )
  450. if dlgOw.ShowModal() == wx.ID_YES:
  451. overwrite = True
  452. else:
  453. dlgOw.Destroy()
  454. dlg.Destroy()
  455. return None
  456. if UserSettings.Get(group="cmd", key="overwrite", subkey="enabled"):
  457. overwrite = True
  458. ret = RunCommand(prog=cmd[0], parent=parent, overwrite=overwrite, **cmd[1])
  459. if ret != 0:
  460. dlg.Destroy()
  461. return None
  462. if (
  463. not isNative
  464. and not grass.find_file(outmap, element="vector", mapset=curMapset)["fullname"]
  465. ):
  466. # create link for OGR layers
  467. RunCommand(
  468. "v.external",
  469. overwrite=overwrite,
  470. parent=parent,
  471. input=vExternalOut["dsn"],
  472. layer=outmap,
  473. )
  474. # create attribute table
  475. if dlg.table.IsEnabled() and dlg.table.IsChecked():
  476. if isNative:
  477. sql = "CREATE TABLE %s (%s INTEGER)" % (outmap, key)
  478. RunCommand("db.connect", flags="c")
  479. Debug.msg(1, "SQL: %s" % sql)
  480. RunCommand("db.execute", quiet=True, parent=parent, input="-", stdin=sql)
  481. RunCommand(
  482. "v.db.connect",
  483. quiet=True,
  484. parent=parent,
  485. map=outmap,
  486. table=outmap,
  487. key=key,
  488. layer="1",
  489. )
  490. # TODO: how to deal with attribute tables for OGR layers?
  491. # return fully qualified map name
  492. if "@" not in outmap:
  493. outmap += "@" + grass.gisenv()["MAPSET"]
  494. # if giface:
  495. # giface.WriteLog(_("New vector map <%s> created") % outmap)
  496. return dlg
  497. class SavedRegion(wx.Dialog):
  498. def __init__(self, parent, title, id=wx.ID_ANY, loadsave="load", **kwargs):
  499. """Loading or saving of display extents to saved region file
  500. :param loadsave: load or save region?
  501. """
  502. wx.Dialog.__init__(self, parent, id, title, **kwargs)
  503. self.loadsave = loadsave
  504. self.wind = ""
  505. sizer = wx.BoxSizer(wx.VERTICAL)
  506. box = wx.BoxSizer(wx.HORIZONTAL)
  507. label = StaticText(parent=self, id=wx.ID_ANY)
  508. box.Add(label, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
  509. if loadsave == "load":
  510. label.SetLabel(_("Load region:"))
  511. self._selection = Select(
  512. parent=self, size=globalvar.DIALOG_GSELECT_SIZE, type="windows"
  513. )
  514. elif loadsave == "save":
  515. label.SetLabel(_("Save region:"))
  516. self._selection = Select(
  517. parent=self,
  518. size=globalvar.DIALOG_GSELECT_SIZE,
  519. type="windows",
  520. mapsets=[grass.gisenv()["MAPSET"]],
  521. fullyQualified=False,
  522. )
  523. box.Add(self._selection, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
  524. self._selection.SetFocus()
  525. self._selection.Bind(wx.EVT_TEXT, self.OnRegion)
  526. sizer.Add(
  527. box,
  528. proportion=0,
  529. flag=wx.GROW | wx.ALL,
  530. border=5,
  531. )
  532. line = wx.StaticLine(
  533. parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL
  534. )
  535. sizer.Add(
  536. line,
  537. proportion=0,
  538. flag=wx.GROW | wx.LEFT | wx.RIGHT,
  539. border=5,
  540. )
  541. btnsizer = wx.StdDialogButtonSizer()
  542. btn = Button(parent=self, id=wx.ID_OK)
  543. btn.SetDefault()
  544. btnsizer.AddButton(btn)
  545. btn = Button(parent=self, id=wx.ID_CANCEL)
  546. btnsizer.AddButton(btn)
  547. btnsizer.Realize()
  548. sizer.Add(btnsizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
  549. self.SetSizer(sizer)
  550. sizer.Fit(self)
  551. self.Layout()
  552. def OnRegion(self, event):
  553. value = self._selection.GetValue()
  554. if "@" in value:
  555. value = value.rsplit("@", 1)[0]
  556. if not grass.legal_name(value):
  557. GMessage(
  558. parent=self,
  559. message=_(
  560. "Name cannot begin with '.' "
  561. "and must not contain space, quotes, "
  562. "'/', ''', '@', ',', '=', '*', "
  563. "and all other non-alphanumeric characters."
  564. ),
  565. )
  566. else:
  567. self.wind = value
  568. def GetName(self):
  569. """Return region name"""
  570. return self.wind
  571. class GroupDialog(wx.Dialog):
  572. """Dialog for creating/editing groups"""
  573. def __init__(
  574. self,
  575. parent=None,
  576. defaultGroup=None,
  577. defaultSubgroup=None,
  578. title=_("Create or edit imagery groups"),
  579. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  580. **kwargs,
  581. ):
  582. wx.Dialog.__init__(
  583. self, parent=parent, id=wx.ID_ANY, title=title, style=style, **kwargs
  584. )
  585. self.parent = parent
  586. self.defaultGroup = defaultGroup
  587. self.defaultSubgroup = defaultSubgroup
  588. self.currentGroup = self.defaultGroup
  589. self.currentSubgroup = self.defaultGroup
  590. self.dataChanged = False
  591. # signaling edit subgroup / group mode
  592. self.edit_subg = False
  593. # sungroup maps dict value - ischecked
  594. self.subgmaps = {}
  595. # list of group maps
  596. self.gmaps = []
  597. # pattern chosen for filtering
  598. self.flt_pattern = ""
  599. self.bodySizer = self._createDialogBody()
  600. # buttons
  601. btnOk = Button(parent=self, id=wx.ID_OK)
  602. btnApply = Button(parent=self, id=wx.ID_APPLY)
  603. btnClose = Button(parent=self, id=wx.ID_CANCEL)
  604. btnOk.SetToolTip(_("Apply changes to selected group and close dialog"))
  605. btnApply.SetToolTip(_("Apply changes to selected group"))
  606. btnClose.SetToolTip(_("Close dialog, changes are not applied"))
  607. # btnOk.SetDefault()
  608. # sizers & do layout
  609. # btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  610. # btnSizer.Add(item = btnClose, proportion = 0,
  611. # flag = wx.RIGHT | wx.ALIGN_RIGHT | wx.EXPAND, border = 5)
  612. # btnSizer.Add(item = btnApply, proportion = 0,
  613. # flag = wx.LEFT, border = 5)
  614. btnSizer = wx.StdDialogButtonSizer()
  615. btnSizer.AddButton(btnOk)
  616. btnSizer.AddButton(btnApply)
  617. btnSizer.AddButton(btnClose)
  618. btnSizer.Realize()
  619. mainSizer = wx.BoxSizer(wx.VERTICAL)
  620. mainSizer.Add(
  621. self.bodySizer, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10
  622. )
  623. mainSizer.Add(
  624. wx.StaticLine(parent=self, id=wx.ID_ANY, style=wx.LI_HORIZONTAL),
  625. proportion=0,
  626. flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
  627. border=10,
  628. )
  629. mainSizer.Add(
  630. btnSizer,
  631. proportion=0,
  632. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT,
  633. border=10,
  634. )
  635. self.SetSizer(mainSizer)
  636. mainSizer.Fit(self)
  637. btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
  638. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  639. btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
  640. # set dialog min size
  641. self.SetMinSize(self.GetSize())
  642. self.SetSize((-1, 400))
  643. def _createDialogBody(self):
  644. bodySizer = wx.BoxSizer(wx.VERTICAL)
  645. # TODO same text in MapLayersDialogBase
  646. filter_tooltip = _(
  647. "Put here a regular expression."
  648. " Characters '.*' stand for anything,"
  649. " character '^' stands for the beginning"
  650. " and '$' for the end."
  651. )
  652. # group selection
  653. bodySizer.Add(
  654. StaticText(
  655. parent=self,
  656. id=wx.ID_ANY,
  657. label=_("Select existing group or " "enter name of new group:"),
  658. ),
  659. flag=wx.TOP,
  660. border=10,
  661. )
  662. self.groupSelect = Select(
  663. parent=self,
  664. type="group",
  665. mapsets=[grass.gisenv()["MAPSET"]],
  666. size=globalvar.DIALOG_GSELECT_SIZE,
  667. fullyQualified=False,
  668. ) # searchpath?
  669. bodySizer.Add(self.groupSelect, flag=wx.TOP | wx.EXPAND, border=5)
  670. self.subg_chbox = wx.CheckBox(
  671. parent=self, id=wx.ID_ANY, label=_("Edit/create subgroup")
  672. )
  673. bodySizer.Add(self.subg_chbox, flag=wx.TOP, border=10)
  674. self.subg_panel = wx.Panel(self)
  675. subg_sizer = wx.BoxSizer(wx.VERTICAL)
  676. subg_sizer.Add(
  677. StaticText(
  678. parent=self.subg_panel,
  679. id=wx.ID_ANY,
  680. label=_("Select existing subgroup or " "enter name of new subgroup:"),
  681. )
  682. )
  683. self.subGroupSelect = SubGroupSelect(parent=self.subg_panel)
  684. subg_sizer.Add(self.subGroupSelect, flag=wx.EXPAND | wx.TOP, border=5)
  685. self.subg_panel.SetSizer(subg_sizer)
  686. bodySizer.Add(self.subg_panel, flag=wx.TOP | wx.EXPAND, border=5)
  687. bodySizer.AddSpacer(10)
  688. buttonSizer = wx.BoxSizer(wx.VERTICAL)
  689. # layers in group
  690. self.gListPanel = wx.Panel(self)
  691. gListSizer = wx.GridBagSizer(vgap=3, hgap=2)
  692. self.g_sel_all = wx.CheckBox(
  693. parent=self.gListPanel, id=wx.ID_ANY, label=_("Select all")
  694. )
  695. gListSizer.Add(self.g_sel_all, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0, 1))
  696. gListSizer.Add(
  697. StaticText(parent=self.gListPanel, label=_("Pattern:")),
  698. flag=wx.ALIGN_CENTER_VERTICAL,
  699. pos=(1, 0),
  700. )
  701. self.gfilter = TextCtrl(
  702. parent=self.gListPanel, id=wx.ID_ANY, value="", size=(250, -1)
  703. )
  704. self.gfilter.SetToolTip(filter_tooltip)
  705. gListSizer.Add(self.gfilter, flag=wx.EXPAND, pos=(1, 1))
  706. gListSizer.Add(
  707. StaticText(parent=self.gListPanel, label=_("List of maps:")),
  708. flag=wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM,
  709. border=5,
  710. pos=(2, 0),
  711. )
  712. sizer = wx.BoxSizer(wx.HORIZONTAL)
  713. self.gLayerBox = wx.ListBox(
  714. parent=self.gListPanel,
  715. id=wx.ID_ANY,
  716. size=(-1, 150),
  717. style=wx.LB_MULTIPLE | wx.LB_NEEDED_SB,
  718. )
  719. sizer.Add(self.gLayerBox, proportion=1, flag=wx.EXPAND)
  720. self.addLayer = Button(self.gListPanel, id=wx.ID_ADD)
  721. self.addLayer.SetToolTip(_("Select map layers and add them to the list."))
  722. buttonSizer.Add(self.addLayer, flag=wx.BOTTOM, border=10)
  723. self.removeLayer = Button(self.gListPanel, id=wx.ID_REMOVE)
  724. self.removeLayer.SetToolTip(_("Remove selected layer(s) from list."))
  725. buttonSizer.Add(self.removeLayer)
  726. sizer.Add(buttonSizer, flag=wx.LEFT, border=5)
  727. gListSizer.Add(sizer, flag=wx.EXPAND, pos=(2, 1))
  728. gListSizer.AddGrowableCol(1)
  729. gListSizer.AddGrowableRow(2)
  730. self.gListPanel.SetSizer(gListSizer)
  731. bodySizer.Add(self.gListPanel, proportion=1, flag=wx.EXPAND)
  732. # layers in subgroup
  733. self.subgListPanel = wx.Panel(self)
  734. subgListSizer = wx.GridBagSizer(vgap=3, hgap=2)
  735. # select toggle
  736. self.subg_sel_all = wx.CheckBox(
  737. parent=self.subgListPanel, id=wx.ID_ANY, label=_("Select all")
  738. )
  739. subgListSizer.Add(self.subg_sel_all, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0, 1))
  740. subgListSizer.Add(
  741. StaticText(parent=self.subgListPanel, label=_("Pattern:")),
  742. flag=wx.ALIGN_CENTER_VERTICAL,
  743. pos=(1, 0),
  744. )
  745. self.subgfilter = TextCtrl(
  746. parent=self.subgListPanel, id=wx.ID_ANY, value="", size=(250, -1)
  747. )
  748. self.subgfilter.SetToolTip(filter_tooltip)
  749. subgListSizer.Add(self.subgfilter, flag=wx.EXPAND, pos=(1, 1))
  750. subgListSizer.Add(
  751. StaticText(parent=self.subgListPanel, label=_("List of maps:")),
  752. flag=wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM,
  753. border=5,
  754. pos=(2, 0),
  755. )
  756. self.subgListBox = CheckListBox(
  757. parent=self.subgListPanel, id=wx.ID_ANY, size=(250, 100)
  758. )
  759. self.subgListBox.SetToolTip(
  760. _("Check maps from group to be included into subgroup.")
  761. )
  762. subgListSizer.Add(self.subgListBox, flag=wx.EXPAND, pos=(2, 1))
  763. subgListSizer.AddGrowableCol(1)
  764. subgListSizer.AddGrowableRow(2)
  765. self.subgListPanel.SetSizer(subgListSizer)
  766. bodySizer.Add(self.subgListPanel, proportion=1, flag=wx.EXPAND)
  767. self.infoLabel = StaticText(parent=self, id=wx.ID_ANY)
  768. bodySizer.Add(self.infoLabel, flag=wx.TOP | wx.BOTTOM, border=5)
  769. # bindings
  770. self.gfilter.Bind(wx.EVT_TEXT, self.OnGroupFilter)
  771. self.subgfilter.Bind(wx.EVT_TEXT, self.OnSubgroupFilter)
  772. self.gLayerBox.Bind(wx.EVT_LISTBOX, self.OnGLayerCheck)
  773. self.subgListBox.Bind(wx.EVT_CHECKLISTBOX, self.OnSubgLayerCheck)
  774. self.groupSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnGroupSelected)
  775. self.addLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
  776. self.removeLayer.Bind(wx.EVT_BUTTON, self.OnRemoveLayer)
  777. self.subg_chbox.Bind(wx.EVT_CHECKBOX, self.OnSubgChbox)
  778. self.subGroupSelect.Bind(wx.EVT_TEXT, lambda event: self.SubGroupSelected())
  779. self.subg_sel_all.Bind(wx.EVT_CHECKBOX, self.OnSubgSelAll)
  780. self.g_sel_all.Bind(wx.EVT_CHECKBOX, self.OnGSelAll)
  781. if self.defaultGroup:
  782. self.groupSelect.SetValue(self.defaultGroup)
  783. if self.defaultSubgroup is not None:
  784. self.subGroupSelect.SetValue(self.defaultSubgroup)
  785. self.subg_chbox.SetValue(1)
  786. self.SubgChbox(True)
  787. else:
  788. self.subg_chbox.SetValue(0)
  789. self.SubgChbox(False)
  790. return bodySizer
  791. def OnGLayerCheck(self, event):
  792. self._checkGSellAll()
  793. def OnSubgSelAll(self, event):
  794. check = event.IsChecked()
  795. for item in range(self.subgListBox.GetCount()):
  796. self.CheckSubgItem(item, check)
  797. self.dataChanged = True
  798. event.Skip()
  799. def OnGSelAll(self, event):
  800. check = event.IsChecked()
  801. if not check:
  802. self.gLayerBox.DeselectAll()
  803. else:
  804. for item in range(self.subgListBox.GetCount()):
  805. self.gLayerBox.Select(item)
  806. event.Skip()
  807. def _checkGSellAll(self):
  808. check = False
  809. nsel = len(self.gLayerBox.GetSelections())
  810. if self.gLayerBox.GetCount() == nsel and self.gLayerBox.GetCount() != 0:
  811. check = True
  812. self.g_sel_all.SetValue(check)
  813. def _checkSubGSellAll(self):
  814. not_all_checked = False
  815. if self.subgListBox.GetCount() == 0:
  816. not_all_checked = True
  817. else:
  818. for item in range(self.subgListBox.GetCount()):
  819. if not self.subgListBox.IsChecked(item):
  820. not_all_checked = True
  821. self.subg_sel_all.SetValue(not not_all_checked)
  822. def OnSubgroupFilter(self, event):
  823. text = event.GetString()
  824. self.gfilter.ChangeValue(text)
  825. self.flt_pattern = text
  826. self.FilterGroup()
  827. self.FilterSubgroup()
  828. event.Skip()
  829. def OnGroupFilter(self, event):
  830. text = event.GetString()
  831. self.subgfilter.ChangeValue(text)
  832. self.flt_pattern = text
  833. self.FilterGroup()
  834. self.FilterSubgroup()
  835. event.Skip()
  836. def OnSubgLayerCheck(self, event):
  837. idx = event.GetInt()
  838. m = self.subgListBox.GetString(idx)
  839. self.subgmaps[m] = self.subgListBox.IsChecked(idx)
  840. self.dataChanged = True
  841. self._checkSubGSellAll()
  842. def CheckSubgItem(self, idx, val):
  843. m = self.subgListBox.GetString(idx)
  844. self.subgListBox.Check(idx, val)
  845. self.subgmaps[m] = val
  846. self.dataChanged = val
  847. def DisableSubgroupEdit(self):
  848. """Disable editation of subgroups in the dialog
  849. .. todo::
  850. used by gcp manager, maybe the gcp m should also support subgroups
  851. """
  852. self.edit_subg = False
  853. self.subg_panel.Hide()
  854. self.subg_chbox.Hide()
  855. self.subgListBox.Hide()
  856. self.Layout()
  857. def OnSubgChbox(self, event):
  858. edit_subg = self.subg_chbox.GetValue()
  859. self.SubgChbox(edit_subg)
  860. def SubgChbox(self, edit_subg):
  861. self._checkChange()
  862. if edit_subg:
  863. self.edit_subg = edit_subg
  864. self.SubGroupSelected()
  865. self._subgroupLayout()
  866. else:
  867. self.edit_subg = edit_subg
  868. self.GroupSelected()
  869. self._groupLayout()
  870. self.SetMinSize(self.GetBestSize())
  871. def _groupLayout(self):
  872. self.subg_panel.Hide()
  873. self.subgListPanel.Hide()
  874. self.gListPanel.Show()
  875. self.Layout()
  876. def _subgroupLayout(self):
  877. self.subg_panel.Show()
  878. self.subgListPanel.Show()
  879. self.gListPanel.Hide()
  880. self.Layout()
  881. def OnAddLayer(self, event):
  882. """Add new layer to listbox"""
  883. dlg = MapLayersDialogForGroups(
  884. parent=self, title=_("Add selected map layers into group")
  885. )
  886. if dlg.ShowModal() != wx.ID_OK:
  887. dlg.Destroy()
  888. return
  889. layers = dlg.GetMapLayers()
  890. for layer in layers:
  891. if layer not in self.gmaps:
  892. self.gLayerBox.Append(layer)
  893. self.gmaps.append(layer)
  894. self.dataChanged = True
  895. def OnRemoveLayer(self, event):
  896. """Remove layer from listbox"""
  897. while self.gLayerBox.GetSelections():
  898. sel = self.gLayerBox.GetSelections()[0]
  899. m = self.gLayerBox.GetString(sel)
  900. self.gLayerBox.Delete(sel)
  901. self.gmaps.remove(m)
  902. self.dataChanged = True
  903. def GetLayers(self):
  904. """Get layers"""
  905. if self.edit_subg:
  906. layers = []
  907. for maps, sel in six.iteritems(self.subgmaps):
  908. if sel:
  909. layers.append(maps)
  910. else:
  911. layers = self.gmaps[:]
  912. return layers
  913. def OnGroupSelected(self, event):
  914. """Text changed in group selector"""
  915. # callAfter must be called to close popup before other actions
  916. wx.CallAfter(self.GroupSelected)
  917. def GroupSelected(self):
  918. """Group was selected, check if changes were apllied"""
  919. self._checkChange()
  920. group, s = self.GetSelectedGroup()
  921. maps = list()
  922. groups = self.GetExistGroups()
  923. if group in groups:
  924. maps = self.GetGroupLayers(group)
  925. self.subGroupSelect.Insert(group)
  926. self.gmaps = maps
  927. maps = self._filter(maps)
  928. self.ShowGroupLayers(maps)
  929. self.currentGroup = group
  930. self.SubGroupSelected()
  931. self.ClearNotification()
  932. self._checkGSellAll()
  933. def FilterGroup(self):
  934. maps = self._filter(self.gmaps)
  935. self.ShowGroupLayers(maps)
  936. self._checkGSellAll()
  937. def FilterSubgroup(self):
  938. maps = self._filter(self.gmaps)
  939. self.subgListBox.Set(maps)
  940. for i, m in enumerate(maps):
  941. if m in six.iterkeys(self.subgmaps) and self.subgmaps[m]:
  942. self.subgListBox.Check(i)
  943. self._checkSubGSellAll()
  944. def SubGroupSelected(self):
  945. """Subgroup was selected, check if changes were apllied"""
  946. self._checkChange()
  947. subgroup = self.subGroupSelect.GetValue().strip()
  948. group = self.currentGroup
  949. gmaps = list()
  950. groups = self.GetExistGroups()
  951. self.subgmaps = {}
  952. if group in groups:
  953. gmaps = self.GetGroupLayers(group)
  954. if subgroup:
  955. maps = self.GetGroupLayers(group, subgroup)
  956. for m in maps:
  957. if m in gmaps:
  958. self.subgmaps[m] = True
  959. else:
  960. self.subgmaps[m] = False
  961. gmaps = self._filter(gmaps)
  962. self.subgListBox.Set(gmaps)
  963. for i, m in enumerate(gmaps):
  964. if m in self.subgmaps:
  965. self.subgListBox.Check(i)
  966. else:
  967. self.subgListBox.Check(i, False)
  968. self._checkSubGSellAll()
  969. self.currentSubgroup = subgroup
  970. self.ClearNotification()
  971. def _filter(self, data):
  972. """Apply filter for strings in data list"""
  973. flt_data = []
  974. if len(self.flt_pattern) == 0:
  975. flt_data = data[:]
  976. return flt_data
  977. for dt in data:
  978. try:
  979. if re.compile(self.flt_pattern).search(dt):
  980. flt_data.append(dt)
  981. except:
  982. pass
  983. return flt_data
  984. def _checkChange(self):
  985. if self.edit_subg:
  986. self._checkSubgroupChange()
  987. else:
  988. self._checkGroupChange()
  989. def _checkGroupChange(self):
  990. if self.currentGroup and self.dataChanged:
  991. dlg = wx.MessageDialog(
  992. self,
  993. message=_("Group <%s> was changed, " "do you want to apply changes?")
  994. % self.currentGroup,
  995. caption=_("Unapplied changes"),
  996. style=wx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULT,
  997. )
  998. if dlg.ShowModal() == wx.ID_YES:
  999. self.ApplyChanges()
  1000. dlg.Destroy()
  1001. self.dataChanged = False
  1002. def _checkSubgroupChange(self):
  1003. if self.currentSubgroup and self.dataChanged:
  1004. dlg = wx.MessageDialog(
  1005. self,
  1006. message=_("Subgroup <%s> was changed, " "do you want to apply changes?")
  1007. % self.currentSubgroup,
  1008. caption=_("Unapplied changes"),
  1009. style=wx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULT,
  1010. )
  1011. if dlg.ShowModal() == wx.ID_YES:
  1012. self.ApplyChanges()
  1013. dlg.Destroy()
  1014. self.dataChanged = False
  1015. def ShowGroupLayers(self, mapList):
  1016. """Show map layers in currently selected group"""
  1017. self.gLayerBox.Set(mapList)
  1018. def EditGroup(self, group, subgroup=None):
  1019. """Edit selected group"""
  1020. layersNew = self.GetLayers()
  1021. layersOld = self.GetGroupLayers(group, subgroup)
  1022. add = []
  1023. remove = []
  1024. for layerNew in layersNew:
  1025. if layerNew not in layersOld:
  1026. add.append(layerNew)
  1027. for layerOld in layersOld:
  1028. if layerOld not in layersNew:
  1029. remove.append(layerOld)
  1030. kwargs = {}
  1031. if subgroup:
  1032. kwargs["subgroup"] = subgroup
  1033. ret = None
  1034. if remove:
  1035. ret = RunCommand(
  1036. "i.group",
  1037. parent=self,
  1038. group=group,
  1039. flags="r",
  1040. input=",".join(remove),
  1041. **kwargs,
  1042. )
  1043. if add:
  1044. ret = RunCommand(
  1045. "i.group", parent=self, group=group, input=",".join(add), **kwargs
  1046. )
  1047. return ret
  1048. def CreateNewGroup(self, group, subgroup):
  1049. """Create new group"""
  1050. layers = self.GetLayers()
  1051. if not layers:
  1052. GMessage(parent=self, message=_("No raster maps selected."))
  1053. return 1
  1054. kwargs = {}
  1055. if subgroup:
  1056. kwargs["subgroup"] = subgroup
  1057. ret = RunCommand("i.group", parent=self, group=group, input=layers, **kwargs)
  1058. # update subgroup select
  1059. self.SubGroupSelected()
  1060. return ret
  1061. def GetExistGroups(self):
  1062. """Returns existing groups in current mapset"""
  1063. return grass.list_grouped("group")[grass.gisenv()["MAPSET"]]
  1064. def GetExistSubgroups(self, group):
  1065. """Returns existing subgroups in a group"""
  1066. return RunCommand("i.group", group=group, read=True, flags="sg").splitlines()
  1067. def ShowResult(self, group, returnCode, create):
  1068. """Show if operation was successful."""
  1069. group += "@" + grass.gisenv()["MAPSET"]
  1070. if returnCode is None:
  1071. label = _("No changes to apply in group <%s>.") % group
  1072. elif returnCode == 0:
  1073. if create:
  1074. label = _("Group <%s> was successfully created.") % group
  1075. else:
  1076. label = _("Group <%s> was successfully changed.") % group
  1077. else:
  1078. if create:
  1079. label = _("Creating of new group <%s> failed.") % group
  1080. else:
  1081. label = _("Changing of group <%s> failed.") % group
  1082. self.infoLabel.SetLabel(label)
  1083. wx.CallLater(4000, self.ClearNotification)
  1084. def GetSelectedGroup(self):
  1085. """Return currently selected group (without mapset)"""
  1086. g = self.groupSelect.GetValue().split("@")[0]
  1087. if self.edit_subg:
  1088. s = self.subGroupSelect.GetValue()
  1089. else:
  1090. s = None
  1091. return g, s
  1092. def GetGroupLayers(self, group, subgroup=None):
  1093. """Get layers in group"""
  1094. kwargs = dict()
  1095. kwargs["group"] = group
  1096. if subgroup:
  1097. kwargs["subgroup"] = subgroup
  1098. res = RunCommand("i.group", parent=self, flags="g", read=True, **kwargs)
  1099. if not res:
  1100. return []
  1101. return res.splitlines()
  1102. def ClearNotification(self):
  1103. """Clear notification string"""
  1104. if self.infoLabel:
  1105. self.infoLabel.SetLabel("")
  1106. def ApplyChanges(self):
  1107. """Create or edit group"""
  1108. group = self.currentGroup
  1109. if not group:
  1110. GMessage(parent=self, message=_("No group selected."))
  1111. return False
  1112. if self.edit_subg and not self.currentSubgroup:
  1113. GMessage(parent=self, message=_("No subgroup selected."))
  1114. return 0
  1115. if self.edit_subg:
  1116. subgroup = self.currentSubgroup
  1117. else:
  1118. subgroup = None
  1119. groups = self.GetExistGroups()
  1120. if group in groups:
  1121. ret = self.EditGroup(group, subgroup)
  1122. self.ShowResult(group=group, returnCode=ret, create=False)
  1123. else:
  1124. ret = self.CreateNewGroup(group, subgroup)
  1125. self.ShowResult(group=group, returnCode=ret, create=True)
  1126. self.dataChanged = False
  1127. return True
  1128. def OnApply(self, event):
  1129. """Apply changes"""
  1130. self.ApplyChanges()
  1131. def OnOk(self, event):
  1132. """Apply changes and close dialog"""
  1133. if self.ApplyChanges():
  1134. self.OnClose(event)
  1135. def OnClose(self, event):
  1136. """Close dialog"""
  1137. if not self.IsModal():
  1138. self.Destroy()
  1139. event.Skip()
  1140. class MapLayersDialogBase(wx.Dialog):
  1141. """Base dialog for selecting map layers (raster, vector).
  1142. There are 3 subclasses: MapLayersDialogForGroups, MapLayersDialogForModeler,
  1143. MapLayersDialog. Base class contains core functionality.
  1144. """
  1145. def __init__(
  1146. self, parent, title, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs
  1147. ):
  1148. wx.Dialog.__init__(
  1149. self, parent=parent, id=wx.ID_ANY, title=title, style=style, **kwargs
  1150. )
  1151. self.parent = parent # GMFrame or ?
  1152. self.applyAddingMapLayers = Signal("MapLayersDialogBase.applyAddingMapLayers")
  1153. self.mainSizer = wx.BoxSizer(wx.VERTICAL)
  1154. # dialog body
  1155. self.bodySizer = self._createDialogBody()
  1156. self.mainSizer.Add(
  1157. self.bodySizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5
  1158. )
  1159. # update list of layer to be loaded
  1160. self.map_layers = [] # list of map layers (full list type/mapset)
  1161. self.LoadMapLayers(
  1162. self.GetLayerType(cmd=True), self.mapset.GetStringSelection()
  1163. )
  1164. self._fullyQualifiedNames()
  1165. self._modelerDSeries()
  1166. # buttons
  1167. btnCancel = Button(parent=self, id=wx.ID_CANCEL)
  1168. btnOk = Button(parent=self, id=wx.ID_OK)
  1169. btnOk.SetDefault()
  1170. # sizers & do layout
  1171. self.btnSizer = wx.StdDialogButtonSizer()
  1172. self.btnSizer.AddButton(btnCancel)
  1173. self.btnSizer.AddButton(btnOk)
  1174. self._addApplyButton()
  1175. self.btnSizer.Realize()
  1176. self.mainSizer.Add(
  1177. self.btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5
  1178. )
  1179. self.SetSizer(self.mainSizer)
  1180. self.mainSizer.Fit(self)
  1181. # set dialog min size
  1182. self.SetMinSize(self.GetSize())
  1183. def _modelerDSeries(self):
  1184. """Method used only by MapLayersDialogForModeler,
  1185. for other subclasses does nothing.
  1186. """
  1187. pass
  1188. def _addApplyButton(self):
  1189. """Method used only by MapLayersDialog,
  1190. for other subclasses does nothing.
  1191. """
  1192. pass
  1193. def _fullyQualifiedNames(self):
  1194. """Adds CheckBox which determines is fully qualified names are retuned."""
  1195. self.fullyQualified = wx.CheckBox(
  1196. parent=self, id=wx.ID_ANY, label=_("Use fully-qualified map names")
  1197. )
  1198. self.fullyQualified.SetValue(True)
  1199. self.mainSizer.Add(
  1200. self.fullyQualified,
  1201. proportion=0,
  1202. flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
  1203. border=5,
  1204. )
  1205. def _useFullyQualifiedNames(self):
  1206. return self.fullyQualified.IsChecked()
  1207. def _layerTypes(self):
  1208. """Determines which layer types can be chosen.
  1209. Valid values:
  1210. - raster
  1211. - raster3d
  1212. - vector
  1213. """
  1214. return [_("raster"), _("3D raster"), _("vector")]
  1215. def _selectAll(self):
  1216. """Check all layers by default"""
  1217. return True
  1218. def _createDialogBody(self):
  1219. bodySizer = wx.GridBagSizer(vgap=3, hgap=3)
  1220. # layer type
  1221. bodySizer.Add(
  1222. StaticText(parent=self, label=_("Map type:")),
  1223. flag=wx.ALIGN_CENTER_VERTICAL,
  1224. pos=(0, 0),
  1225. )
  1226. self.layerType = wx.Choice(
  1227. parent=self, id=wx.ID_ANY, choices=self._layerTypes(), size=(100, -1)
  1228. )
  1229. self.layerType.SetSelection(0)
  1230. bodySizer.Add(self.layerType, pos=(0, 1))
  1231. self.layerType.Bind(wx.EVT_CHOICE, self.OnChangeParams)
  1232. # select toggle
  1233. self.toggle = wx.CheckBox(parent=self, id=wx.ID_ANY, label=_("Select toggle"))
  1234. self.toggle.SetValue(self._selectAll())
  1235. bodySizer.Add(self.toggle, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0, 2))
  1236. # mapset filter
  1237. bodySizer.Add(
  1238. StaticText(parent=self, label=_("Mapset:")),
  1239. flag=wx.ALIGN_CENTER_VERTICAL,
  1240. pos=(1, 0),
  1241. )
  1242. self.mapset = MapsetSelect(parent=self, searchPath=True)
  1243. self.mapset.SetStringSelection(grass.gisenv()["MAPSET"])
  1244. bodySizer.Add(self.mapset, pos=(1, 1), span=(1, 2))
  1245. # map name filter
  1246. bodySizer.Add(
  1247. StaticText(parent=self, label=_("Pattern:")),
  1248. flag=wx.ALIGN_CENTER_VERTICAL,
  1249. pos=(2, 0),
  1250. )
  1251. self.filter = TextCtrl(parent=self, id=wx.ID_ANY, value="", size=(250, -1))
  1252. bodySizer.Add(self.filter, flag=wx.EXPAND, pos=(2, 1), span=(1, 2))
  1253. self.filter.SetFocus()
  1254. # TODO same text in GroupDialog
  1255. self.filter.SetToolTip(
  1256. _(
  1257. "Put here a regular expression."
  1258. " Characters '.*' stand for anything,"
  1259. " character '^' stands for the beginning"
  1260. " and '$' for the end."
  1261. )
  1262. )
  1263. # layer list
  1264. bodySizer.Add(
  1265. StaticText(parent=self, label=_("List of maps:")),
  1266. flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_TOP,
  1267. pos=(3, 0),
  1268. )
  1269. self.layers = CheckListBox(
  1270. parent=self, id=wx.ID_ANY, size=(250, 100), choices=[]
  1271. )
  1272. bodySizer.Add(self.layers, flag=wx.EXPAND, pos=(3, 1), span=(1, 2))
  1273. bodySizer.AddGrowableCol(1)
  1274. bodySizer.AddGrowableRow(3)
  1275. # bindings
  1276. self.mapset.Bind(wx.EVT_TEXT, self.OnChangeParams)
  1277. self.mapset.Bind(wx.EVT_COMBOBOX, self.OnChangeParams)
  1278. self.layers.Bind(wx.EVT_RIGHT_DOWN, self.OnMenu)
  1279. self.filter.Bind(wx.EVT_TEXT, self.OnFilter)
  1280. self.toggle.Bind(wx.EVT_CHECKBOX, self.OnToggle)
  1281. return bodySizer
  1282. def LoadMapLayers(self, type, mapset):
  1283. """Load list of map layers
  1284. :param str type: layer type ('raster' or 'vector')
  1285. :param str mapset: mapset name
  1286. """
  1287. self.map_layers = grass.list_grouped(type=type)[mapset]
  1288. self.layers.Set(naturally_sorted(self.map_layers))
  1289. # check all items by default
  1290. for item in range(self.layers.GetCount()):
  1291. self.layers.Check(item, check=self._selectAll())
  1292. def OnChangeParams(self, event):
  1293. """Filter parameters changed by user"""
  1294. # update list of layer to be loaded
  1295. self.LoadMapLayers(
  1296. self.GetLayerType(cmd=True), self.mapset.GetStringSelection()
  1297. )
  1298. event.Skip()
  1299. def OnMenu(self, event):
  1300. """Table description area, context menu"""
  1301. if not hasattr(self, "popupID1"):
  1302. self.popupDataID1 = NewId()
  1303. self.popupDataID2 = NewId()
  1304. self.popupDataID3 = NewId()
  1305. self.Bind(wx.EVT_MENU, self.OnSelectAll, id=self.popupDataID1)
  1306. self.Bind(wx.EVT_MENU, self.OnSelectInvert, id=self.popupDataID2)
  1307. self.Bind(wx.EVT_MENU, self.OnDeselectAll, id=self.popupDataID3)
  1308. # generate popup-menu
  1309. menu = Menu()
  1310. menu.Append(self.popupDataID1, _("Select all"))
  1311. menu.Append(self.popupDataID2, _("Invert selection"))
  1312. menu.Append(self.popupDataID3, _("Deselect all"))
  1313. self.PopupMenu(menu)
  1314. menu.Destroy()
  1315. def OnSelectAll(self, event):
  1316. """Select all map layer from list"""
  1317. for item in range(self.layers.GetCount()):
  1318. self.layers.Check(item, True)
  1319. def OnSelectInvert(self, event):
  1320. """Invert current selection"""
  1321. for item in range(self.layers.GetCount()):
  1322. if self.layers.IsChecked(item):
  1323. self.layers.Check(item, False)
  1324. else:
  1325. self.layers.Check(item, True)
  1326. def OnDeselectAll(self, event):
  1327. """Select all map layer from list"""
  1328. for item in range(self.layers.GetCount()):
  1329. self.layers.Check(item, False)
  1330. def OnFilter(self, event):
  1331. """Apply filter for map names"""
  1332. if len(event.GetString()) == 0:
  1333. self.layers.Set(self.map_layers)
  1334. return
  1335. list = []
  1336. for layer in self.map_layers:
  1337. try:
  1338. if re.compile(event.GetString()).search(layer):
  1339. list.append(layer)
  1340. except:
  1341. pass
  1342. list = naturally_sorted(list)
  1343. self.layers.Set(list)
  1344. self.OnSelectAll(None)
  1345. event.Skip()
  1346. def OnToggle(self, event):
  1347. """Select toggle (check or uncheck all layers)"""
  1348. check = event.IsChecked()
  1349. for item in range(self.layers.GetCount()):
  1350. self.layers.Check(item, check)
  1351. event.Skip()
  1352. def GetMapLayers(self):
  1353. """Return list of checked map layers"""
  1354. layerNames = []
  1355. for indx in self.layers.GetSelections():
  1356. # layers.append(self.layers.GetStringSelec(indx))
  1357. pass
  1358. mapset = self.mapset.GetStringSelection()
  1359. for item in range(self.layers.GetCount()):
  1360. if not self.layers.IsChecked(item):
  1361. continue
  1362. if self._useFullyQualifiedNames():
  1363. layerNames.append(self.layers.GetString(item) + "@" + mapset)
  1364. else:
  1365. layerNames.append(self.layers.GetString(item))
  1366. return layerNames
  1367. def GetLayerType(self, cmd=False):
  1368. """Get selected layer type
  1369. :param bool cmd: True for g.list
  1370. """
  1371. if not cmd:
  1372. return self.layerType.GetStringSelection()
  1373. sel = self.layerType.GetSelection()
  1374. if sel == 0:
  1375. ltype = "raster"
  1376. elif sel == 1:
  1377. ltype = "raster_3d"
  1378. else:
  1379. ltype = "vector"
  1380. return ltype
  1381. class MapLayersDialog(MapLayersDialogBase):
  1382. """Subclass of MapLayersDialogBase used in Layer Manager.
  1383. Contains apply button, which sends wxApplyMapLayers event.
  1384. """
  1385. def __init__(self, parent, title, **kwargs):
  1386. MapLayersDialogBase.__init__(self, parent=parent, title=title, **kwargs)
  1387. def _addApplyButton(self):
  1388. btnApply = Button(parent=self, id=wx.ID_APPLY)
  1389. self.btnSizer.AddButton(btnApply)
  1390. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  1391. def OnApply(self, event):
  1392. self.applyAddingMapLayers.emit(
  1393. mapLayers=self.GetMapLayers(), ltype=self.GetLayerType(cmd=True)
  1394. )
  1395. class MapLayersDialogForGroups(MapLayersDialogBase):
  1396. """Subclass of MapLayersDialogBase used for specyfying maps in an imagery group.
  1397. Shows only raster maps.
  1398. """
  1399. def __init__(self, parent, title, **kwargs):
  1400. MapLayersDialogBase.__init__(self, parent=parent, title=title, **kwargs)
  1401. def _layerTypes(self):
  1402. return [
  1403. _("raster"),
  1404. ]
  1405. def _selectAll(self):
  1406. """Could be overridden"""
  1407. return False
  1408. def _fullyQualifiedNames(self):
  1409. pass
  1410. def _useFullyQualifiedNames(self):
  1411. return True
  1412. class MapLayersDialogForModeler(MapLayersDialogBase):
  1413. """Subclass of MapLayersDialogBase used in Modeler."""
  1414. def __init__(self, parent, title, **kwargs):
  1415. MapLayersDialogBase.__init__(self, parent=parent, title=title, **kwargs)
  1416. def _modelerDSeries(self):
  1417. self.dseries = wx.CheckBox(
  1418. parent=self, id=wx.ID_ANY, label=_("Dynamic series (%s)") % "g.list"
  1419. )
  1420. self.dseries.SetValue(False)
  1421. self.mainSizer.Add(
  1422. self.dseries, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5
  1423. )
  1424. def GetDSeries(self):
  1425. """Used by modeler only
  1426. :return: g.list command
  1427. """
  1428. if not self.dseries or not self.dseries.IsChecked():
  1429. return ""
  1430. cond = "map in `g.list type=%s " % self.GetLayerType(cmd=True)
  1431. patt = self.filter.GetValue()
  1432. if patt:
  1433. cond += "pattern=%s " % patt
  1434. cond += "mapset=%s`" % self.mapset.GetStringSelection()
  1435. return cond
  1436. class SetOpacityDialog(wx.Dialog):
  1437. """Set opacity of map layers.
  1438. Dialog expects opacity between 0 and 1 and returns this range, too.
  1439. """
  1440. def __init__(
  1441. self,
  1442. parent,
  1443. id=wx.ID_ANY,
  1444. title=_("Set Map Layer Opacity"),
  1445. size=wx.DefaultSize,
  1446. pos=wx.DefaultPosition,
  1447. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  1448. opacity=1,
  1449. ):
  1450. self.parent = parent # GMFrame
  1451. self.opacity = opacity # current opacity
  1452. super(SetOpacityDialog, self).__init__(
  1453. parent, id=id, pos=pos, size=size, style=style, title=title
  1454. )
  1455. self.applyOpacity = Signal("SetOpacityDialog.applyOpacity")
  1456. panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1457. sizer = wx.BoxSizer(wx.VERTICAL)
  1458. box = wx.GridBagSizer(vgap=5, hgap=5)
  1459. self.value = wx.Slider(
  1460. panel,
  1461. id=wx.ID_ANY,
  1462. value=int(self.opacity * 100),
  1463. style=wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | wx.SL_TOP | wx.SL_LABELS,
  1464. minValue=0,
  1465. maxValue=100,
  1466. size=(350, -1),
  1467. )
  1468. box.Add(self.value, flag=wx.ALIGN_CENTRE, pos=(0, 0), span=(1, 2))
  1469. box.Add(
  1470. StaticText(parent=panel, id=wx.ID_ANY, label=_("transparent")), pos=(1, 0)
  1471. )
  1472. box.Add(
  1473. StaticText(parent=panel, id=wx.ID_ANY, label=_("opaque")),
  1474. flag=wx.ALIGN_RIGHT,
  1475. pos=(1, 1),
  1476. )
  1477. sizer.Add(box, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  1478. line = wx.StaticLine(parent=panel, id=wx.ID_ANY, style=wx.LI_HORIZONTAL)
  1479. sizer.Add(line, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  1480. # buttons
  1481. btnsizer = wx.StdDialogButtonSizer()
  1482. btnOK = Button(parent=panel, id=wx.ID_OK)
  1483. btnOK.SetDefault()
  1484. btnsizer.AddButton(btnOK)
  1485. btnCancel = Button(parent=panel, id=wx.ID_CANCEL)
  1486. btnsizer.AddButton(btnCancel)
  1487. btnApply = Button(parent=panel, id=wx.ID_APPLY)
  1488. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  1489. btnsizer.AddButton(btnApply)
  1490. btnsizer.Realize()
  1491. sizer.Add(btnsizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  1492. panel.SetSizer(sizer)
  1493. sizer.Fit(panel)
  1494. self.SetSize(self.GetBestSize())
  1495. self.Layout()
  1496. def GetOpacity(self):
  1497. """Button 'OK' pressed"""
  1498. # return opacity value
  1499. opacity = float(self.value.GetValue()) / 100
  1500. return opacity
  1501. def OnApply(self, event):
  1502. self.applyOpacity.emit(value=self.GetOpacity())
  1503. def GetImageHandlers(image):
  1504. """Get list of supported image handlers"""
  1505. lext = list()
  1506. ltype = list()
  1507. try:
  1508. for h in image.GetHandlers():
  1509. lext.append(h.GetExtension())
  1510. except AttributeError:
  1511. lext = {"png", "gif", "jpg", "pcx", "pnm", "tif", "xpm"}
  1512. filetype = ""
  1513. if "png" in lext:
  1514. filetype += "PNG file (*.png)|*.png|"
  1515. ltype.append({"type": wx.BITMAP_TYPE_PNG, "ext": "png"})
  1516. filetype += "BMP file (*.bmp)|*.bmp|"
  1517. ltype.append({"type": wx.BITMAP_TYPE_BMP, "ext": "bmp"})
  1518. if "gif" in lext:
  1519. filetype += "GIF file (*.gif)|*.gif|"
  1520. ltype.append({"type": wx.BITMAP_TYPE_GIF, "ext": "gif"})
  1521. if "jpg" in lext:
  1522. filetype += "JPG file (*.jpg)|*.jpg|"
  1523. ltype.append({"type": wx.BITMAP_TYPE_JPEG, "ext": "jpg"})
  1524. if "pcx" in lext:
  1525. filetype += "PCX file (*.pcx)|*.pcx|"
  1526. ltype.append({"type": wx.BITMAP_TYPE_PCX, "ext": "pcx"})
  1527. if "pnm" in lext:
  1528. filetype += "PNM file (*.pnm)|*.pnm|"
  1529. ltype.append({"type": wx.BITMAP_TYPE_PNM, "ext": "pnm"})
  1530. if "tif" in lext:
  1531. filetype += "TIF file (*.tif)|*.tif|"
  1532. ltype.append({"type": wx.BITMAP_TYPE_TIF, "ext": "tif"})
  1533. if "xpm" in lext:
  1534. filetype += "XPM file (*.xpm)|*.xpm"
  1535. ltype.append({"type": wx.BITMAP_TYPE_XPM, "ext": "xpm"})
  1536. return filetype, ltype
  1537. class ImageSizeDialog(wx.Dialog):
  1538. """Set size for saved graphic file"""
  1539. def __init__(
  1540. self,
  1541. parent,
  1542. id=wx.ID_ANY,
  1543. title=_("Set image size"),
  1544. style=wx.DEFAULT_DIALOG_STYLE,
  1545. **kwargs,
  1546. ):
  1547. self.parent = parent
  1548. wx.Dialog.__init__(self, parent, id=id, style=style, title=title, **kwargs)
  1549. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1550. self.box = StaticBox(
  1551. parent=self.panel, id=wx.ID_ANY, label=" % s" % _("Image size")
  1552. )
  1553. size = self.parent.GetWindow().GetClientSize()
  1554. self.width = SpinCtrl(parent=self.panel, id=wx.ID_ANY, style=wx.SP_ARROW_KEYS)
  1555. self.width.SetRange(20, 1e6)
  1556. self.width.SetValue(size.width)
  1557. wx.CallAfter(self.width.SetFocus)
  1558. self.height = SpinCtrl(parent=self.panel, id=wx.ID_ANY, style=wx.SP_ARROW_KEYS)
  1559. self.height.SetRange(20, 1e6)
  1560. self.height.SetValue(size.height)
  1561. self.template = wx.Choice(
  1562. parent=self.panel,
  1563. id=wx.ID_ANY,
  1564. size=(125, -1),
  1565. choices=[
  1566. "",
  1567. "640x480",
  1568. "800x600",
  1569. "1024x768",
  1570. "1280x960",
  1571. "1600x1200",
  1572. "1920x1440",
  1573. ],
  1574. )
  1575. self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
  1576. self.btnOK.SetDefault()
  1577. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  1578. self.template.Bind(wx.EVT_CHOICE, self.OnTemplate)
  1579. self._layout()
  1580. self.SetSize(self.GetBestSize())
  1581. def _layout(self):
  1582. """Do layout"""
  1583. sizer = wx.BoxSizer(wx.VERTICAL)
  1584. # body
  1585. box = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
  1586. fbox = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
  1587. fbox.Add(
  1588. StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Width:")),
  1589. flag=wx.ALIGN_CENTER_VERTICAL,
  1590. )
  1591. fbox.Add(self.width)
  1592. fbox.Add(
  1593. StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Height:")),
  1594. flag=wx.ALIGN_CENTER_VERTICAL,
  1595. )
  1596. fbox.Add(self.height)
  1597. fbox.Add(
  1598. StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Template:")),
  1599. flag=wx.ALIGN_CENTER_VERTICAL,
  1600. )
  1601. fbox.Add(self.template)
  1602. box.Add(fbox, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  1603. sizer.Add(box, proportion=1, flag=wx.EXPAND | wx.ALL, border=3)
  1604. # buttons
  1605. btnsizer = wx.StdDialogButtonSizer()
  1606. btnsizer.AddButton(self.btnOK)
  1607. btnsizer.AddButton(self.btnCancel)
  1608. btnsizer.Realize()
  1609. sizer.Add(btnsizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  1610. self.panel.SetSizer(sizer)
  1611. sizer.Fit(self.panel)
  1612. self.Layout()
  1613. def GetValues(self):
  1614. """Get width/height values"""
  1615. return self.width.GetValue(), self.height.GetValue()
  1616. def OnTemplate(self, event):
  1617. """Template selected"""
  1618. sel = event.GetString()
  1619. if not sel:
  1620. width, height = self.parent.GetWindow().GetClientSize()
  1621. else:
  1622. width, height = map(int, sel.split("x"))
  1623. self.width.SetValue(width)
  1624. self.height.SetValue(height)
  1625. class SqlQueryFrame(wx.Frame):
  1626. def __init__(self, parent, id=wx.ID_ANY, title=_("SQL Query Utility"), *kwargs):
  1627. """SQL Query Utility window"""
  1628. self.parent = parent
  1629. wx.Frame.__init__(self, parent=parent, id=id, title=title, *kwargs)
  1630. self.SetIcon(
  1631. wx.Icon(
  1632. os.path.join(globalvar.ICONDIR, "grass_sql.ico"), wx.BITMAP_TYPE_ICO
  1633. )
  1634. )
  1635. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1636. self.sqlBox = StaticBox(
  1637. parent=self.panel, id=wx.ID_ANY, label=_(" SQL statement ")
  1638. )
  1639. self.sql = TextCtrl(parent=self.panel, id=wx.ID_ANY, style=wx.TE_MULTILINE)
  1640. self.btnApply = Button(parent=self.panel, id=wx.ID_APPLY)
  1641. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  1642. self.Bind(wx.EVT_BUTTON, self.OnCloseWindow, self.btnCancel)
  1643. self._layout()
  1644. self.SetMinSize(wx.Size(300, 150))
  1645. self.SetSize(wx.Size(500, 200))
  1646. def _layout(self):
  1647. """Do layout"""
  1648. sizer = wx.BoxSizer(wx.VERTICAL)
  1649. sqlSizer = wx.StaticBoxSizer(self.sqlBox, wx.HORIZONTAL)
  1650. sqlSizer.Add(self.sql, proportion=1, flag=wx.EXPAND)
  1651. btnSizer = wx.StdDialogButtonSizer()
  1652. btnSizer.AddButton(self.btnApply)
  1653. btnSizer.AddButton(self.btnCancel)
  1654. btnSizer.Realize()
  1655. sizer.Add(sqlSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  1656. sizer.Add(
  1657. btnSizer,
  1658. proportion=0,
  1659. flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
  1660. border=5,
  1661. )
  1662. self.panel.SetSizer(sizer)
  1663. self.Layout()
  1664. def OnCloseWindow(self, event):
  1665. """Close window"""
  1666. self.Close()
  1667. class SymbolDialog(wx.Dialog):
  1668. """Dialog for GRASS symbols selection.
  1669. Dialog is called in gui_core::forms module.
  1670. """
  1671. def __init__(self, parent, symbolPath, currentSymbol=None, title=_("Symbols")):
  1672. """Dialog constructor.
  1673. It is assumed that symbolPath contains folders with symbols.
  1674. :param parent: dialog parent
  1675. :param symbolPath: absolute path to symbols
  1676. :param currentSymbol: currently selected symbol (e.g. 'basic/x')
  1677. :param title: dialog title
  1678. """
  1679. wx.Dialog.__init__(self, parent=parent, title=title, id=wx.ID_ANY)
  1680. self.symbolPath = symbolPath
  1681. self.currentSymbol = currentSymbol # default basic/x
  1682. self.selected = None
  1683. self.selectedDir = None
  1684. self._layout()
  1685. def _layout(self):
  1686. mainPanel = wx.Panel(self, id=wx.ID_ANY)
  1687. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1688. vSizer = wx.BoxSizer(wx.VERTICAL)
  1689. fgSizer = wx.FlexGridSizer(rows=2, cols=2, vgap=5, hgap=5)
  1690. self.folderChoice = wx.Choice(
  1691. mainPanel, id=wx.ID_ANY, choices=os.listdir(self.symbolPath)
  1692. )
  1693. self.folderChoice.Bind(wx.EVT_CHOICE, self.OnFolderSelect)
  1694. fgSizer.Add(
  1695. StaticText(mainPanel, id=wx.ID_ANY, label=_("Symbol directory:")),
  1696. proportion=0,
  1697. flag=wx.ALIGN_CENTER_VERTICAL,
  1698. )
  1699. fgSizer.Add(self.folderChoice, proportion=0, flag=wx.ALIGN_CENTER, border=0)
  1700. self.infoLabel = StaticText(mainPanel, id=wx.ID_ANY)
  1701. fgSizer.Add(
  1702. StaticText(mainPanel, id=wx.ID_ANY, label=_("Symbol name:")),
  1703. flag=wx.ALIGN_CENTRE_VERTICAL,
  1704. )
  1705. fgSizer.Add(self.infoLabel, proportion=0, flag=wx.ALIGN_CENTRE_VERTICAL)
  1706. vSizer.Add(fgSizer, proportion=0, flag=wx.ALL, border=5)
  1707. self.panels = self._createSymbolPanels(mainPanel)
  1708. for panel in self.panels:
  1709. vSizer.Add(panel, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  1710. mainSizer.Add(vSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  1711. self.btnCancel = Button(parent=mainPanel, id=wx.ID_CANCEL)
  1712. self.btnOK = Button(parent=mainPanel, id=wx.ID_OK)
  1713. self.btnOK.SetDefault()
  1714. self.btnOK.Enable(False)
  1715. # buttons
  1716. btnSizer = wx.StdDialogButtonSizer()
  1717. btnSizer.AddButton(self.btnCancel)
  1718. btnSizer.AddButton(self.btnOK)
  1719. btnSizer.Realize()
  1720. mainSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  1721. # show panel with the largest number of images and fit size
  1722. count = []
  1723. for folder in os.listdir(self.symbolPath):
  1724. count.append(len(os.listdir(os.path.join(self.symbolPath, folder))))
  1725. index = count.index(max(count))
  1726. self.folderChoice.SetSelection(index)
  1727. self.OnFolderSelect(None)
  1728. self.infoLabel.Show()
  1729. mainPanel.SetSizerAndFit(mainSizer)
  1730. self.SetSize(self.GetBestSize())
  1731. # show currently selected symbol
  1732. if self.currentSymbol:
  1733. # set directory
  1734. self.selectedDir, self.selected = os.path.split(self.currentSymbol)
  1735. self.folderChoice.SetStringSelection(self.selectedDir)
  1736. # select symbol
  1737. panelIdx = self.folderChoice.GetSelection()
  1738. for panel in self.symbolPanels[panelIdx]:
  1739. if panel.GetName() == self.selected:
  1740. panel.Select()
  1741. else:
  1742. self.folderChoice.SetSelection(0)
  1743. self.OnFolderSelect(None)
  1744. def _createSymbolPanels(self, parent):
  1745. """Creates multiple panels with symbols.
  1746. Panels are shown/hidden according to selected folder."""
  1747. folders = os.listdir(self.symbolPath)
  1748. panels = []
  1749. self.symbolPanels = []
  1750. for folder in folders:
  1751. panel = wx.Panel(parent, style=wx.BORDER_RAISED)
  1752. sizer = wx.GridSizer(cols=6, vgap=3, hgap=3)
  1753. images = self._getSymbols(path=os.path.join(self.symbolPath, folder))
  1754. symbolPanels = []
  1755. for img in images:
  1756. iP = SingleSymbolPanel(parent=panel, symbolPath=img)
  1757. iP.symbolSelectionChanged.connect(self.SelectionChanged)
  1758. sizer.Add(iP, proportion=0, flag=wx.ALIGN_CENTER)
  1759. symbolPanels.append(iP)
  1760. panel.SetSizerAndFit(sizer)
  1761. panel.Hide()
  1762. panels.append(panel)
  1763. self.symbolPanels.append(symbolPanels)
  1764. return panels
  1765. def _getSymbols(self, path):
  1766. # we assume that images are in subfolders (1 level only)
  1767. imageList = []
  1768. for image in os.listdir(path):
  1769. imageList.append(os.path.join(path, image))
  1770. return sorted(imageList)
  1771. def OnFolderSelect(self, event):
  1772. """Selected folder with symbols changed."""
  1773. idx = self.folderChoice.GetSelection()
  1774. for i in range(len(self.panels)):
  1775. sizer = self.panels[i].GetContainingSizer()
  1776. sizer.Show(self.panels[i], i == idx, recursive=True)
  1777. sizer.Layout()
  1778. if self.selectedDir == self.folderChoice.GetStringSelection():
  1779. self.btnOK.Enable()
  1780. self.infoLabel.SetLabel(self.selected)
  1781. else:
  1782. self.btnOK.Disable()
  1783. self.infoLabel.SetLabel("")
  1784. def SelectionChanged(self, name, doubleClick):
  1785. """Selected symbol changed."""
  1786. if doubleClick:
  1787. self.EndModal(wx.ID_OK)
  1788. # deselect all
  1789. for i in range(len(self.panels)):
  1790. for panel in self.symbolPanels[i]:
  1791. if panel.GetName() != name:
  1792. panel.Deselect()
  1793. self.btnOK.Enable()
  1794. self.selected = name
  1795. self.selectedDir = self.folderChoice.GetStringSelection()
  1796. self.infoLabel.SetLabel(name)
  1797. def GetSelectedSymbolName(self):
  1798. """Returns currently selected symbol name (e.g. 'basic/x')."""
  1799. # separator must be '/' and not dependent on OS
  1800. return self.selectedDir + "/" + self.selected
  1801. def GetSelectedSymbolPath(self):
  1802. """Returns currently selected symbol full path."""
  1803. return os.path.join(self.symbolPath, self.selectedDir, self.selected)
  1804. class TextEntryDialog(wx.Dialog):
  1805. """Simple dialog with text field.
  1806. It differs from wx.TextEntryDialog because it allows adding validator.
  1807. """
  1808. def __init__(
  1809. self,
  1810. parent,
  1811. message,
  1812. caption="",
  1813. defaultValue="",
  1814. validator=wx.DefaultValidator,
  1815. style=wx.OK | wx.CANCEL | wx.CENTRE,
  1816. textStyle=0,
  1817. textSize=(300, -1),
  1818. **kwargs,
  1819. ):
  1820. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=caption, **kwargs)
  1821. vbox = wx.BoxSizer(wx.VERTICAL)
  1822. stline = StaticText(self, id=wx.ID_ANY, label=message)
  1823. vbox.Add(stline, proportion=0, flag=wx.EXPAND | wx.ALL, border=10)
  1824. self._textCtrl = TextCtrl(
  1825. self, id=wx.ID_ANY, value=defaultValue, validator=validator, style=textStyle
  1826. )
  1827. self._textCtrl.SetInitialSize(textSize)
  1828. wx.CallAfter(self._textCtrl.SetFocus)
  1829. vbox.Add(
  1830. self._textCtrl, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10
  1831. )
  1832. self._textCtrl.SetFocus()
  1833. sizer = self.CreateSeparatedButtonSizer(style)
  1834. vbox.Add(sizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
  1835. self.SetSizerAndFit(vbox)
  1836. def GetValue(self):
  1837. return self._textCtrl.GetValue()
  1838. def SetValue(self, value):
  1839. self._textCtrl.SetValue(value)
  1840. class HyperlinkDialog(wx.Dialog):
  1841. """Dialog for displaying message with hyperlink."""
  1842. def __init__(
  1843. self, parent, title, message, hyperlink, hyperlinkLabel=None, style=wx.OK
  1844. ):
  1845. """Constructor
  1846. :param parent: gui parent
  1847. :param title: dialog title
  1848. :param message: message
  1849. :param hyperlink: url
  1850. :param hyperlinkLabel: label shown instead of url
  1851. :param style: button style
  1852. """
  1853. wx.Dialog.__init__(
  1854. self,
  1855. parent=parent,
  1856. id=wx.ID_ANY,
  1857. title=title,
  1858. style=wx.DEFAULT_DIALOG_STYLE,
  1859. )
  1860. sizer = wx.BoxSizer(wx.VERTICAL)
  1861. label = StaticText(self, label=message)
  1862. sizer.Add(label, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=10)
  1863. hyperlinkLabel = hyperlinkLabel if hyperlinkLabel else hyperlink
  1864. hyperlinkCtrl = HyperlinkCtrl(
  1865. self,
  1866. id=wx.ID_ANY,
  1867. label=hyperlinkLabel,
  1868. url=hyperlink,
  1869. style=HyperlinkCtrl.HL_ALIGN_LEFT | HyperlinkCtrl.HL_CONTEXTMENU,
  1870. )
  1871. sizer.Add(hyperlinkCtrl, proportion=0, flag=wx.EXPAND | wx.ALL, border=10)
  1872. btnsizer = self.CreateSeparatedButtonSizer(style)
  1873. sizer.Add(btnsizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
  1874. self.SetSizer(sizer)
  1875. sizer.Fit(self)
  1876. class QuitDialog(wx.Dialog):
  1877. def __init__(
  1878. self,
  1879. parent,
  1880. title=_("Quit GRASS GIS"),
  1881. id=wx.ID_ANY,
  1882. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  1883. **kwargs,
  1884. ):
  1885. """Dialog to quit GRASS
  1886. :param parent: window
  1887. """
  1888. wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)
  1889. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1890. self._icon = wx.StaticBitmap(
  1891. self.panel,
  1892. wx.ID_ANY,
  1893. wx.ArtProvider().GetBitmap(wx.ART_QUESTION, client=wx.ART_MESSAGE_BOX),
  1894. )
  1895. self._shell_running = is_shell_running()
  1896. if self._shell_running:
  1897. text = _(
  1898. "Do you want to quit GRASS GIS including shell "
  1899. "or just close the GUI?"
  1900. )
  1901. else:
  1902. text = _("Do you want to quit GRASS GIS?")
  1903. self.informLabel = StaticText(parent=self.panel, id=wx.ID_ANY, label=text)
  1904. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  1905. if self._shell_running:
  1906. self.btnClose = Button(parent=self.panel, id=wx.ID_NO, label=_("Close GUI"))
  1907. self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
  1908. self.btnQuit = Button(
  1909. parent=self.panel, id=wx.ID_YES, label=_("Quit GRASS GIS")
  1910. )
  1911. self.btnQuit.SetFocus()
  1912. self.btnQuit.Bind(wx.EVT_BUTTON, self.OnQuit)
  1913. self.__layout()
  1914. def __layout(self):
  1915. """Do layout"""
  1916. sizer = wx.BoxSizer(wx.VERTICAL)
  1917. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  1918. btnSizer.Add(self.btnCancel, flag=wx.RIGHT, border=5)
  1919. if self._shell_running:
  1920. btnSizer.Add(self.btnClose, flag=wx.RIGHT, border=5)
  1921. btnSizer.Add(self.btnQuit, flag=wx.RIGHT, border=5)
  1922. bodySizer = wx.BoxSizer(wx.HORIZONTAL)
  1923. bodySizer.Add(self._icon, flag=wx.RIGHT, border=10)
  1924. bodySizer.Add(self.informLabel, proportion=1, flag=wx.EXPAND)
  1925. sizer.Add(bodySizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=15)
  1926. sizer.Add(btnSizer, proportion=0, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
  1927. self.panel.SetSizer(sizer)
  1928. sizer.Fit(self)
  1929. self.Layout()
  1930. def OnClose(self, event):
  1931. self.EndModal(wx.ID_NO)
  1932. def OnQuit(self, event):
  1933. self.EndModal(wx.ID_YES)
  1934. class DefaultFontDialog(wx.Dialog):
  1935. """
  1936. Opens a file selection dialog to select default font
  1937. to use in all GRASS displays
  1938. """
  1939. def __init__(
  1940. self,
  1941. parent,
  1942. title,
  1943. id=wx.ID_ANY,
  1944. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  1945. settings=UserSettings,
  1946. type="font",
  1947. ):
  1948. self.settings = settings
  1949. self.type = type
  1950. wx.Dialog.__init__(self, parent, id, title, style=style)
  1951. panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1952. self.tmp_file = grass.tempfile(False) + ".png"
  1953. self.fontdict, fontdict_reverse, self.fontlist = self.GetFonts()
  1954. border = wx.BoxSizer(wx.VERTICAL)
  1955. box = StaticBox(parent=panel, id=wx.ID_ANY, label=" %s " % _("Font settings"))
  1956. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1957. gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  1958. label = StaticText(parent=panel, id=wx.ID_ANY, label=_("Select font:"))
  1959. gridSizer.Add(label, flag=wx.ALIGN_TOP, pos=(0, 0))
  1960. self.fontlb = wx.ListBox(
  1961. parent=panel,
  1962. id=wx.ID_ANY,
  1963. pos=wx.DefaultPosition,
  1964. choices=self.fontlist,
  1965. style=wx.LB_SINGLE,
  1966. )
  1967. self.Bind(wx.EVT_LISTBOX, self.EvtListBox, self.fontlb)
  1968. self.Bind(wx.EVT_LISTBOX_DCLICK, self.EvtListBoxDClick, self.fontlb)
  1969. gridSizer.Add(self.fontlb, flag=wx.EXPAND, pos=(1, 0))
  1970. self.renderfont = wx.StaticBitmap(
  1971. panel, -1, wx.Bitmap.FromRGBA(100, 50, 255, 255, 255)
  1972. )
  1973. gridSizer.Add(self.renderfont, flag=wx.EXPAND, pos=(2, 0))
  1974. if self.type == "font":
  1975. if "GRASS_FONT" in os.environ:
  1976. self.font = os.environ["GRASS_FONT"]
  1977. else:
  1978. self.font = self.settings.Get(
  1979. group="display", key="font", subkey="type"
  1980. )
  1981. self.encoding = self.settings.Get(
  1982. group="display", key="font", subkey="encoding"
  1983. )
  1984. label = StaticText(
  1985. parent=panel, id=wx.ID_ANY, label=_("Character encoding:")
  1986. )
  1987. gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(3, 0))
  1988. self.textentry = TextCtrl(parent=panel, id=wx.ID_ANY, value=self.encoding)
  1989. gridSizer.Add(self.textentry, flag=wx.EXPAND, pos=(4, 0))
  1990. self.textentry.Bind(wx.EVT_TEXT, self.OnEncoding)
  1991. elif self.type == "outputfont":
  1992. self.font = self.settings.Get(
  1993. group="appearance", key="outputfont", subkey="type"
  1994. )
  1995. self.fontsize = self.settings.Get(
  1996. group="appearance", key="outputfont", subkey="size"
  1997. )
  1998. label = StaticText(parent=panel, id=wx.ID_ANY, label=_("Font size:"))
  1999. gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(3, 0))
  2000. self.spin = SpinCtrl(parent=panel, id=wx.ID_ANY)
  2001. if self.fontsize:
  2002. self.spin.SetValue(int(self.fontsize))
  2003. self.spin.Bind(wx.EVT_SPINCTRL, self.OnSizeSpin)
  2004. self.spin.Bind(wx.EVT_TEXT, self.OnSizeSpin)
  2005. gridSizer.Add(self.spin, flag=wx.ALIGN_CENTER_VERTICAL, pos=(4, 0))
  2006. else:
  2007. return
  2008. if self.font:
  2009. long_name = fontdict_reverse.get(self.font, None)
  2010. if long_name:
  2011. self.fontlb.SetStringSelection(long_name, True)
  2012. else:
  2013. # font is not in the list of GRASS recognized fonts
  2014. self.font = None
  2015. gridSizer.AddGrowableCol(0)
  2016. sizer.Add(gridSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  2017. border.Add(sizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=3)
  2018. btnsizer = wx.StdDialogButtonSizer()
  2019. btn = Button(parent=panel, id=wx.ID_OK)
  2020. btn.SetDefault()
  2021. btnsizer.AddButton(btn)
  2022. btn = Button(parent=panel, id=wx.ID_CANCEL)
  2023. btnsizer.AddButton(btn)
  2024. btnsizer.Realize()
  2025. border.Add(btnsizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  2026. panel.SetAutoLayout(True)
  2027. panel.SetSizer(border)
  2028. border.Fit(self)
  2029. row, col = gridSizer.GetItemPosition(self.renderfont)
  2030. self.renderfont.SetSize(gridSizer.GetCellSize(row, col))
  2031. if self.font:
  2032. self.RenderText(self.font, _("Example"), size=self.renderfont.GetSize())
  2033. self.Layout()
  2034. def OnEncoding(self, event):
  2035. self.encoding = event.GetString()
  2036. def EvtListBox(self, event):
  2037. self.font = self.fontdict[event.GetString()]
  2038. self.RenderText(self.font, "Example", size=self.renderfont.GetSize())
  2039. event.Skip()
  2040. def EvtListBoxDClick(self, event):
  2041. self.font = self.fontdict[event.GetString()]
  2042. event.Skip()
  2043. def OnSizeSpin(self, event):
  2044. self.fontsize = self.spin.GetValue()
  2045. event.Skip()
  2046. def GetFonts(self):
  2047. """
  2048. parses fonts directory or fretypecap file to get a list of fonts
  2049. for the listbox
  2050. """
  2051. fontlist = []
  2052. fontdict = {}
  2053. fontdict_reverse = {}
  2054. env = os.environ.copy()
  2055. driver = UserSettings.Get(group="display", key="driver", subkey="type")
  2056. if driver == "png":
  2057. env["GRASS_RENDER_IMMEDIATE"] = "png"
  2058. else:
  2059. env["GRASS_RENDER_IMMEDIATE"] = "cairo"
  2060. ret = RunCommand("d.fontlist", flags="v", read=True, env=env)
  2061. if not ret:
  2062. return fontlist
  2063. dfonts = ret.splitlines()
  2064. for line in dfonts:
  2065. shortname = line.split("|")[0]
  2066. longname = line.split("|")[1]
  2067. # not sure when this happens?
  2068. if shortname.startswith("#"):
  2069. continue
  2070. fontlist.append(longname)
  2071. fontdict[longname] = shortname
  2072. fontdict_reverse[shortname] = longname
  2073. fontlist = naturally_sorted(list(set(fontlist)))
  2074. return fontdict, fontdict_reverse, fontlist
  2075. def RenderText(self, font, text, size):
  2076. """Renders an example text with the selected font and resets the bitmap widget"""
  2077. env = os.environ.copy()
  2078. driver = UserSettings.Get(group="display", key="driver", subkey="type")
  2079. if driver == "png":
  2080. env["GRASS_RENDER_IMMEDIATE"] = "png"
  2081. else:
  2082. env["GRASS_RENDER_IMMEDIATE"] = "cairo"
  2083. env["GRASS_RENDER_WIDTH"] = str(size[0])
  2084. env["GRASS_RENDER_HEIGHT"] = str(size[1])
  2085. env["GRASS_RENDER_FILE"] = self.tmp_file
  2086. env["GRASS_REGION"] = grass.region_env(s=0, n=size[1], w=0, e=size[0])
  2087. ret = RunCommand(
  2088. "d.text",
  2089. text=text,
  2090. font=font,
  2091. align="cc",
  2092. at="50,60",
  2093. size=80,
  2094. color="black",
  2095. env=env,
  2096. )
  2097. if ret == 0:
  2098. self.renderfont.SetBitmap(wx.Bitmap(self.tmp_file))
  2099. else:
  2100. self.renderfont.SetBitmap(EmptyBitmap(size[0], size[1]))
  2101. try_remove(self.tmp_file)