dialogs.py 75 KB

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