dialogs.py 75 KB

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