gselect.py 88 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289
  1. """!
  2. @package gui_core.gselect
  3. @brief Custom control that selects elements
  4. Classes:
  5. - gselect::Select
  6. - gselect::VectorSelect
  7. - gselect::ListCtrlComboPopup
  8. - gselect::TreeCrtlComboPopup
  9. - gselect::VectorDBInfo
  10. - gselect::LayerSelect
  11. - gselect::DriverSelect
  12. - gselect::DatabaseSelect
  13. - gselect::TableSelect
  14. - gselect::ColumnSelect
  15. - gselect::DbaseSelect
  16. - gselect::LocationSelect
  17. - gselect::MapsetSelect
  18. - gselect::SubGroupSelect
  19. - gselect::FormatSelect
  20. - gselect::GdalSelect
  21. - gselect::ProjSelect
  22. - gselect::ElementSelect
  23. - gselect::OgrTypeSelect
  24. - gselect::CoordinatesSelect
  25. - gselect::SignatureSelect
  26. (C) 2007-2014 by the GRASS Development Team
  27. This program is free software under the GNU General Public License
  28. (>=v2). Read the file COPYING that comes with GRASS for details.
  29. @author Michael Barton
  30. @author Martin Landa <landa.martin gmail.com>
  31. @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
  32. @author Stepan Turek <stepan.turek seznam.cz> (CoordinatesSelect, ListCtrlComboPopup)
  33. """
  34. import os
  35. import sys
  36. import glob
  37. import copy
  38. import wx
  39. import wx.combo
  40. import wx.lib.buttons as buttons
  41. import wx.lib.filebrowsebutton as filebrowse
  42. from core import globalvar
  43. import grass.script as grass
  44. from grass.script import task as gtask
  45. try:
  46. from grass.pygrass import messages
  47. except ImportError, e:
  48. print >> sys.stderr, _("Unable to import pyGRASS: %s\n"
  49. "Some functionality will be not accessible") % e
  50. from gui_core.widgets import ManageSettingsWidget, CoordinatesValidator
  51. from core.gcmd import RunCommand, GError, GMessage
  52. from core.utils import GetListOfLocations, GetListOfMapsets, \
  53. GetFormats, rasterFormatExtension, vectorFormatExtension
  54. from core.utils import GetSettingsPath, GetValidLayerName, ListSortLower
  55. from core.utils import GetVectorNumberOfLayers, _
  56. from core.settings import UserSettings
  57. from core.debug import Debug
  58. from grass.pydispatch.signal import Signal
  59. class Select(wx.combo.ComboCtrl):
  60. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
  61. type = None, multiple = False, nmaps = 1,
  62. mapsets = None, updateOnPopup = True, onPopup = None,
  63. fullyQualified = True, extraItems = {}, layerTree = None,
  64. validator = wx.DefaultValidator):
  65. """!Custom control to create a ComboBox with a tree control to
  66. display and select GIS elements within acessible mapsets.
  67. Elements can be selected with mouse. Can allow multiple
  68. selections, when argument <em>multiple</em> is True. Multiple
  69. selections are separated by commas.
  70. @param type type of GIS elements ('raster, 'vector', ...)
  71. @param multiple True for multiple input
  72. @param nmaps number of maps to be entered
  73. @param mapsets force list of mapsets (otherwise search path)
  74. @param updateOnPopup True for updating list of elements on popup
  75. @param onPopup function to be called on Popup
  76. @param fullyQualified True to provide fully qualified names (map@mapset)
  77. @param extraItems extra items to add (given as dictionary) - see gmodeler for usage
  78. @param layerTree show only elements from given layer tree if not None
  79. @param validator validator for TextCtrl
  80. """
  81. wx.combo.ComboCtrl.__init__(self, parent=parent, id=id, size=size, validator=validator)
  82. self.GetChildren()[0].SetName("Select")
  83. self.GetChildren()[0].type = type
  84. self.tcp = TreeCtrlComboPopup()
  85. self.SetPopupControl(self.tcp)
  86. self.SetPopupExtents(0, 100)
  87. if type:
  88. self.tcp.SetData(type = type, mapsets = mapsets,
  89. multiple = multiple, nmaps = nmaps,
  90. updateOnPopup = updateOnPopup, onPopup = onPopup,
  91. fullyQualified = fullyQualified, extraItems = extraItems, layerTree = layerTree)
  92. self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  93. def OnKeyDown(self, event):
  94. """!Open popup and send key events to the tree."""
  95. if self.IsPopupShown():
  96. self.tcp.OnKeyDown(event)
  97. else:
  98. if event.GetKeyCode() == wx.WXK_DOWN:
  99. self.ShowPopup()
  100. event.Skip()
  101. def SetElementList(self, type, mapsets = None):
  102. """!Set element list
  103. @param type GIS element type
  104. @param mapsets list of acceptable mapsets (None for all in search path)
  105. """
  106. self.tcp.SetData(type = type, mapsets = mapsets)
  107. def GetElementList(self):
  108. """!Load elements"""
  109. self.tcp.GetElementList()
  110. def SetType(self, etype, multiple = False, nmaps = 1,
  111. mapsets = None, updateOnPopup = True, onPopup = None):
  112. """!Param set element type for widget
  113. @param etype element type, see gselect.ElementSelect
  114. """
  115. self.tcp.SetData(type = etype, mapsets = mapsets,
  116. multiple = multiple, nmaps = nmaps,
  117. updateOnPopup = updateOnPopup, onPopup = onPopup)
  118. class VectorSelect(Select):
  119. def __init__(self, parent, ftype, **kwargs):
  120. """!Custom to create a ComboBox with a tree control to display and
  121. select vector maps. You can filter the vector maps. If you
  122. don't need this feature use Select class instead
  123. @param ftype filter vector maps based on feature type
  124. """
  125. Select.__init__(self, parent = parent, id = wx.ID_ANY,
  126. type = 'vector', **kwargs)
  127. self.ftype = ftype
  128. # remove vector maps which do not contain given feature type
  129. self.tcp.SetFilter(self._isElement)
  130. def _isElement(self, vectorName):
  131. """!Check if element should be filtered out"""
  132. try:
  133. if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1:
  134. return False
  135. except KeyError:
  136. return False
  137. return True
  138. class ListCtrlComboPopup(wx.combo.ComboPopup):
  139. """!Create a list ComboBox using TreeCtrl with hidden root.
  140. @todo: use event.EventObject instead of hardcoding (see forms.py)
  141. https://groups.google.com/forum/#!topic/wxpython-users/pRz6bi0k0XY
  142. """
  143. # overridden ComboPopup methods
  144. def Init(self):
  145. self.value = [] # for multiple is False ->
  146. # len(self.value) in [0,1]
  147. self.curitem = None
  148. self.multiple = False
  149. self.updateOnPopup = True
  150. self.filterItems = [] # limit items based on this list,
  151. # see layerTree parameter
  152. def Create(self, parent):
  153. self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
  154. |wx.TR_HAS_BUTTONS
  155. |wx.TR_SINGLE
  156. |wx.TR_LINES_AT_ROOT
  157. |wx.SIMPLE_BORDER
  158. |wx.TR_FULL_ROW_HIGHLIGHT)
  159. self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
  160. self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
  161. # the following dummy handler are needed to keep tree events
  162. # from propagating up to the parent GIS Manager layer tree
  163. self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, lambda x: None)
  164. self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, lambda x: None)
  165. self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, lambda x: None)
  166. self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, lambda x: None)
  167. self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
  168. self.seltree.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda x: None)
  169. self.seltree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, lambda x: None)
  170. def GetControl(self):
  171. return self.seltree
  172. def GetStringValue(self):
  173. """!Get value as a string separated by commas
  174. """
  175. return ','.join(self.value)
  176. def SetStringValue(self, value):
  177. # this assumes that item strings are unique...
  178. root = self.seltree.GetRootItem()
  179. if not root:
  180. return
  181. winValue = self.GetCombo().GetValue().strip(',')
  182. self.value = []
  183. if winValue:
  184. self.value = winValue.split(',')
  185. def OnPopup(self, force = False):
  186. """!Limited only for first selected
  187. """
  188. if not force and not self.updateOnPopup:
  189. return
  190. # selects map starting according to written text
  191. inputText = self.GetCombo().GetValue().strip()
  192. if inputText:
  193. root = self.seltree.GetRootItem()
  194. match = self.FindItem(root, inputText, startLetters = True)
  195. self.seltree.EnsureVisible(match)
  196. self.seltree.SelectItem(match)
  197. def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
  198. """!Reads UserSettings to get height (which was 200 in old implementation).
  199. """
  200. height = UserSettings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
  201. return wx.Size(minWidth, min(height, maxHeight))
  202. def FindItem(self, parentItem, text, startLetters = False):
  203. """!Finds item with given name or starting with given text
  204. """
  205. startletters = startLetters
  206. item, cookie = self.seltree.GetFirstChild(parentItem)
  207. while wx.TreeItemId.IsOk(item):
  208. if self.seltree.GetItemText(item) == text:
  209. return item
  210. if self.seltree.ItemHasChildren(item):
  211. item = self.FindItem(item, text, startLetters = startletters)
  212. if wx.TreeItemId.IsOk(item):
  213. return item
  214. elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]):
  215. return item
  216. item, cookie = self.seltree.GetNextChild(parentItem, cookie)
  217. return wx.TreeItemId()
  218. def AddItem(self, value):
  219. root = self.seltree.GetRootItem()
  220. if not root:
  221. root = self.seltree.AddRoot("<hidden root>")
  222. self.seltree.AppendItem(root, text = value)
  223. def SetItems(self, items):
  224. root = self.seltree.GetRootItem()
  225. if not root:
  226. root = self.seltree.AddRoot("<hidden root>")
  227. for item in items:
  228. self.seltree.AppendItem(root, text = item)
  229. def OnKeyUp(self, event):
  230. """!Enable to select items using keyboard
  231. """
  232. item = self.seltree.GetSelection()
  233. if event.GetKeyCode() == wx.WXK_DOWN:
  234. self.seltree.SelectItem(self.seltree.GetNextVisible(item))
  235. elif event.GetKeyCode() == wx.WXK_UP:
  236. itemPrev = self.seltree.GetPrevSibling(item)
  237. self.seltree.SelectItem(itemPrev)
  238. elif event.GetKeyCode() == wx.WXK_ESCAPE:
  239. self.Dismiss()
  240. elif event.GetKeyCode() == wx.WXK_RETURN:
  241. self.seltree.SelectItem(item)
  242. self.curitem = item
  243. item_str = self.seltree.GetItemText(self.curitem)
  244. if self.multiple:
  245. if item_str not in self.value:
  246. self.value.append(item_str)
  247. else:
  248. self.value = [item_str]
  249. self.Dismiss()
  250. def OnMotion(self, evt):
  251. """!Have the selection follow the mouse, like in a real combobox
  252. """
  253. item, flags = self.seltree.HitTest(evt.GetPosition())
  254. if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
  255. self.seltree.SelectItem(item)
  256. self.curitem = item
  257. evt.Skip()
  258. def OnLeftDown(self, evt):
  259. """!Do the combobox selection
  260. """
  261. if self.curitem is None:
  262. return
  263. item_str = self.seltree.GetItemText(self.curitem)
  264. if self.multiple:
  265. if item_str not in self.value:
  266. self.value.append(item_str)
  267. else:
  268. self.value = [item_str]
  269. self.Dismiss()
  270. evt.Skip()
  271. def SetData(self, **kargs):
  272. """!Set object properties"""
  273. if 'multiple' in kargs:
  274. self.multiple = kargs['multiple']
  275. if 'onPopup' in kargs:
  276. self.onPopup = kargs['onPopup']
  277. if kargs.get('layerTree', None):
  278. self.filterItems = [] # reset
  279. ltype = kargs['type']
  280. for layer in kargs['layerTree'].GetVisibleLayers(skipDigitized = True):
  281. if layer.GetType() != ltype:
  282. continue
  283. self.filterItems.append(layer.GetName())
  284. def DeleteAllItems(self):
  285. """!Delete all items in popup"""
  286. self.seltree.DeleteAllItems()
  287. class TreeCtrlComboPopup(ListCtrlComboPopup):
  288. """!Create a tree ComboBox for selecting maps and other GIS elements
  289. in accessible mapsets within the current location
  290. """
  291. # overridden ComboPopup methods
  292. def Init(self):
  293. ListCtrlComboPopup.Init(self)
  294. self.nmaps = 1
  295. self.type = None
  296. self.mapsets = None
  297. self.onPopup = None
  298. self.fullyQualified = True
  299. self.extraItems = dict()
  300. self.SetFilter(None)
  301. self.tgis_error = False
  302. def SetFilter(self, filter):
  303. """!Set filter for GIS elements, see e.g. VectorSelect"""
  304. self.filterElements = filter
  305. def OnPopup(self, force = False):
  306. """!Limited only for first selected"""
  307. if not force and not self.updateOnPopup:
  308. return
  309. if self.onPopup:
  310. selected, exclude = self.onPopup(self.type)
  311. else:
  312. selected = None
  313. exclude = False
  314. self.GetElementList(selected, exclude)
  315. ListCtrlComboPopup.OnPopup(self, force)
  316. def GetElementList(self, elements = None, exclude = False):
  317. """!Get filtered list of GIS elements in accessible mapsets
  318. and display as tree with all relevant elements displayed
  319. beneath each mapset branch
  320. """
  321. # update list
  322. self.seltree.DeleteAllItems()
  323. self._getElementList(self.type, self.mapsets, elements, exclude)
  324. if len(self.value) > 0:
  325. root = self.seltree.GetRootItem()
  326. if not root:
  327. return
  328. item = self.FindItem(root, self.value[0])
  329. try:
  330. self.seltree.EnsureVisible(item)
  331. self.seltree.SelectItem(item)
  332. except:
  333. pass
  334. def _getElementList(self, element, mapsets = None, elements = None, exclude = False):
  335. """!Get list of GIS elements in accessible mapsets and display as tree
  336. with all relevant elements displayed beneath each mapset branch
  337. @param element GIS element
  338. @param mapsets list of acceptable mapsets (None for all mapsets in search path)
  339. @param elements list of forced GIS elements
  340. @param exclude True to exclude, False for forcing the list (elements)
  341. """
  342. # get current mapset
  343. curr_mapset = grass.gisenv()['MAPSET']
  344. # map element types to g.mlist types
  345. elementdict = {'cell':'rast',
  346. 'raster':'rast',
  347. 'rast':'rast',
  348. 'raster files':'rast',
  349. 'grid3':'rast3d',
  350. 'rast3d':'rast3d',
  351. '3d-raster':'rast3d',
  352. 'raster3D':'rast3d',
  353. 'raster3D files':'rast3d',
  354. 'vector':'vect',
  355. 'vect':'vect',
  356. 'binary vector files':'vect',
  357. 'dig':'oldvect',
  358. 'oldvect':'oldvect',
  359. 'old vector':'oldvect',
  360. 'dig_ascii':'asciivect',
  361. 'asciivect':'asciivect',
  362. 'asciivector':'asciivect',
  363. 'ascii vector files':'asciivect',
  364. 'icons':'icon',
  365. 'icon':'icon',
  366. 'paint icon files':'icon',
  367. 'paint/labels':'labels',
  368. 'labels':'labels',
  369. 'label':'labels',
  370. 'paint label files':'labels',
  371. 'site_lists':'sites',
  372. 'sites':'sites',
  373. 'site list':'sites',
  374. 'site list files':'sites',
  375. 'windows':'region',
  376. 'region':'region',
  377. 'region definition':'region',
  378. 'region definition files':'region',
  379. 'windows3d':'region3d',
  380. 'region3d':'region3d',
  381. 'region3D definition':'region3d',
  382. 'region3D definition files':'region3d',
  383. 'group':'group',
  384. 'imagery group':'group',
  385. 'imagery group files':'group',
  386. '3d.view':'3dview',
  387. '3dview':'3dview',
  388. '3D viewing parameters':'3dview',
  389. '3D view parameters':'3dview',
  390. 'stds':'stds',
  391. 'strds':'strds',
  392. 'str3ds':'str3ds',
  393. 'stvds':'stvds'}
  394. if element not in elementdict:
  395. self.AddItem(_('Not selectable element'), node = False)
  396. return
  397. if element in ('stds', 'strds', 'str3ds', 'stvds'):
  398. if self.tgis_error is False:
  399. import grass.temporal as tgis
  400. filesdict = tgis.tlist_grouped(elementdict[element], element == 'stds')
  401. else:
  402. filesdict = None
  403. else:
  404. if globalvar.have_mlist:
  405. filesdict = grass.mlist_grouped(elementdict[element],
  406. check_search_path = False)
  407. else:
  408. filesdict = grass.list_grouped(elementdict[element],
  409. check_search_path = False)
  410. # add extra items first
  411. if self.extraItems:
  412. for group, items in self.extraItems.iteritems():
  413. node = self.AddItem(group, node = True)
  414. self.seltree.SetItemTextColour(node, wx.Colour(50, 50, 200))
  415. for item in items:
  416. self.AddItem(item, node = False, parent = node)
  417. self.seltree.ExpandAllChildren(node)
  418. # list of mapsets in current location
  419. if mapsets is None:
  420. mapsets = grass.mapsets(search_path = True)
  421. # current mapset first
  422. if curr_mapset in mapsets and mapsets[0] != curr_mapset:
  423. mapsets.remove(curr_mapset)
  424. mapsets.insert(0, curr_mapset)
  425. first_mapset = None
  426. for mapset in mapsets:
  427. mapset_node = self.AddItem(_('Mapset') + ': ' + mapset, node = True, mapset = mapset)
  428. node = mapset_node
  429. if not first_mapset:
  430. first_mapset = mapset_node
  431. self.seltree.SetItemTextColour(mapset_node, wx.Colour(50, 50, 200))
  432. if mapset not in filesdict:
  433. continue
  434. try:
  435. if type(filesdict[mapset]) == dict:
  436. for elementType in filesdict[mapset].keys():
  437. node = self.AddItem(_('Type: ') + elementType, mapset = mapset,
  438. node = True, parent = mapset_node)
  439. self.seltree.SetItemTextColour(node, wx.Colour(50, 50, 200))
  440. elem_list = filesdict[mapset][elementType]
  441. self._addItems(elist = elem_list, elements = elements,
  442. mapset = mapset, exclude = exclude, node = node)
  443. else:
  444. elem_list = filesdict[mapset]
  445. self._addItems(elist = elem_list, elements = elements,
  446. mapset = mapset, exclude = exclude, node = node)
  447. except StandardError, e:
  448. sys.stderr.write(_("GSelect: invalid item: %s") % e)
  449. continue
  450. if self.seltree.ItemHasChildren(mapset_node):
  451. sel = UserSettings.Get(group='appearance', key='elementListExpand',
  452. subkey='selection')
  453. collapse = True
  454. if sel == 0: # collapse all except PERMANENT and current
  455. if mapset in ('PERMANENT', curr_mapset):
  456. collapse = False
  457. elif sel == 1: # collapse all except PERMANENT
  458. if mapset == 'PERMANENT':
  459. collapse = False
  460. elif sel == 2: # collapse all except current
  461. if mapset == curr_mapset:
  462. collapse = False
  463. elif sel == 3: # collapse all
  464. pass
  465. elif sel == 4: # expand all
  466. collapse = False
  467. if collapse:
  468. self.seltree.CollapseAllChildren(mapset_node)
  469. else:
  470. self.seltree.ExpandAllChildren(mapset_node)
  471. if first_mapset:
  472. # select first mapset (MSW hack)
  473. self.seltree.SelectItem(first_mapset)
  474. # helpers
  475. def _addItems(self, elist, elements, mapset, exclude, node):
  476. """!Helper function for adding multiple items (maps, stds).
  477. @param elist list of map/stds names
  478. @param elements list of forced elements
  479. @param mapset mapset name
  480. @param exclude True to exclude, False for forcing the list
  481. @param node parent node
  482. """
  483. elist.sort()
  484. for elem in elist:
  485. if elem != '':
  486. fullqElem = elem + '@' + mapset
  487. if self.filterItems and fullqElem not in self.filterItems:
  488. continue # skip items missed in self.filterItems
  489. if elements is not None:
  490. if (exclude and fullqElem in elements) or \
  491. (not exclude and fullqElem not in elements):
  492. continue
  493. if self.filterElements:
  494. if self.filterElements(fullqElem):
  495. self.AddItem(elem, mapset = mapset, node = False, parent = node)
  496. else:
  497. self.AddItem(elem, mapset = mapset, node = False, parent = node)
  498. def AddItem(self, value, mapset = None, node = True, parent = None):
  499. if not parent:
  500. root = self.seltree.GetRootItem()
  501. if not root:
  502. root = self.seltree.AddRoot("<hidden root>")
  503. parent = root
  504. data = {'node': node, 'mapset': mapset}
  505. item = self.seltree.AppendItem(parent, text = value, data = wx.TreeItemData(data))
  506. return item
  507. def OnKeyDown(self, event):
  508. """!Enables to select items using keyboard"""
  509. item = self.seltree.GetSelection()
  510. if event.GetKeyCode() == wx.WXK_DOWN:
  511. self.seltree.SelectItem(self.seltree.GetNextVisible(item))
  512. # problem with GetPrevVisible
  513. elif event.GetKeyCode() == wx.WXK_UP:
  514. if self.seltree.ItemHasChildren(item) and self.seltree.IsExpanded(self.seltree.GetPrevSibling(item)):
  515. itemPrev = self.seltree.GetLastChild(self.seltree.GetPrevSibling(item))
  516. else:
  517. itemPrev = self.seltree.GetPrevSibling(item)
  518. if not wx.TreeItemId.IsOk(itemPrev):
  519. itemPrev = self.seltree.GetItemParent(item)
  520. if item == self.seltree.GetFirstChild(self.seltree.GetRootItem())[0]:
  521. itemPrev = item
  522. self.seltree.SelectItem(itemPrev)
  523. # selects first item starting with the written text in next mapset
  524. elif event.GetKeyCode() == wx.WXK_TAB:
  525. selected = self.seltree.GetSelection()
  526. if self.seltree.ItemHasChildren(selected):
  527. parent = selected
  528. else:
  529. parent = self.seltree.GetItemParent(selected)
  530. nextSibling = self.seltree.GetNextSibling(parent)
  531. if wx.TreeItemId.IsOk(nextSibling):
  532. match = self.FindItem(nextSibling, self.GetCombo().GetValue().strip(), True)
  533. else:
  534. match = self.FindItem(self.seltree.GetFirstChild(self.seltree.GetItemParent(parent))[0],
  535. self.GetCombo().GetValue().strip(), True)
  536. self.seltree.SelectItem(match)
  537. elif event.GetKeyCode() == wx.WXK_RIGHT:
  538. if self.seltree.ItemHasChildren(item):
  539. self.seltree.Expand(item)
  540. elif event.GetKeyCode() == wx.WXK_LEFT:
  541. if self.seltree.ItemHasChildren(item):
  542. self.seltree.Collapse(item)
  543. elif event.GetKeyCode() == wx.WXK_ESCAPE:
  544. self.Dismiss()
  545. elif event.GetKeyCode() == wx.WXK_RETURN:
  546. if self.seltree.GetPyData(item)['node']:
  547. self.value = []
  548. else:
  549. fullName = self.seltree.GetItemText(item)
  550. if self.fullyQualified and self.seltree.GetPyData(item)['mapset']:
  551. fullName += '@' + self.seltree.GetPyData(item)['mapset']
  552. if self.multiple:
  553. self.value.append(fullName)
  554. else:
  555. if self.nmaps > 1: # see key_desc
  556. if len(self.value) >= self.nmaps:
  557. self.value = [fullName]
  558. else:
  559. self.value.append(fullName)
  560. else:
  561. self.value = [fullName]
  562. self.Dismiss()
  563. def OnLeftDown(self, evt):
  564. """!Do the combobox selection
  565. """
  566. item, flags = self.seltree.HitTest(evt.GetPosition())
  567. if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
  568. self.curitem = item
  569. if self.seltree.GetPyData(item)['node']:
  570. evt.Skip()
  571. return
  572. fullName = self.seltree.GetItemText(item)
  573. if self.fullyQualified and self.seltree.GetPyData(item)['mapset']:
  574. fullName += '@' + self.seltree.GetPyData(item)['mapset']
  575. if self.multiple:
  576. self.value.append(fullName)
  577. else:
  578. if self.nmaps > 1: # see key_desc
  579. if len(self.value) >= self.nmaps:
  580. self.value = [fullName]
  581. else:
  582. self.value.append(fullName)
  583. else:
  584. self.value = [fullName]
  585. self.Dismiss()
  586. evt.Skip()
  587. def SetData(self, **kargs):
  588. """!Set object properties"""
  589. ListCtrlComboPopup.SetData(self, **kargs)
  590. if 'type' in kargs:
  591. self.type = kargs['type']
  592. if self.type in ('stds', 'strds', 'str3ds', 'stvds'):
  593. # Initiate the temporal framework. Catch database error
  594. # and set the error flag for the stds listing.
  595. import grass.temporal as tgis
  596. try:
  597. tgis.init(True)
  598. except messages.FatalError, e:
  599. sys.stderr.write("Temporal GIS error:\n%s" % e)
  600. self.tgis_error = True
  601. if 'mapsets' in kargs:
  602. self.mapsets = kargs['mapsets']
  603. if 'nmaps' in kargs:
  604. self.nmaps = kargs['nmaps']
  605. if 'updateOnPopup' in kargs:
  606. self.updateOnPopup = kargs['updateOnPopup']
  607. if 'fullyQualified' in kargs:
  608. self.fullyQualified = kargs['fullyQualified']
  609. if 'extraItems' in kargs:
  610. self.extraItems = kargs['extraItems']
  611. def GetType(self):
  612. """!Get element type
  613. """
  614. return self.type
  615. class VectorDBInfo:
  616. """!Class providing information about attribute tables
  617. linked to a vector map"""
  618. def __init__(self, map):
  619. self.map = map
  620. # dictionary of layer number and associated (driver, database, table)
  621. self.layers = {}
  622. # dictionary of table and associated columns (type, length, values, ids)
  623. self.tables = {}
  624. if not self._CheckDBConnection(): # -> self.layers
  625. return
  626. self._DescribeTables() # -> self.tables
  627. def _CheckDBConnection(self):
  628. """!Check DB connection"""
  629. nuldev = file(os.devnull, 'w+')
  630. self.layers = grass.vector_db(map = self.map, stderr = nuldev)
  631. nuldev.close()
  632. return bool(len(self.layers.keys()) > 0)
  633. def _DescribeTables(self):
  634. """!Describe linked tables"""
  635. for layer in self.layers.keys():
  636. # determine column names and types
  637. table = self.layers[layer]["table"]
  638. columns = {} # {name: {type, length, [values], [ids]}}
  639. i = 0
  640. Debug.msg(1, "gselect.VectorDBInfo._DescribeTables(): table=%s driver=%s database=%s" % \
  641. (self.layers[layer]["table"], self.layers[layer]["driver"],
  642. self.layers[layer]["database"]))
  643. for item in grass.db_describe(table = self.layers[layer]["table"],
  644. driver = self.layers[layer]["driver"],
  645. database = self.layers[layer]["database"])['cols']:
  646. name, type, length = item
  647. # FIXME: support more datatypes
  648. if type.lower() == "integer":
  649. ctype = int
  650. elif type.lower() == "double precision":
  651. ctype = float
  652. else:
  653. ctype = str
  654. columns[name.strip()] = { 'index' : i,
  655. 'type' : type.lower(),
  656. 'ctype' : ctype,
  657. 'length' : int(length),
  658. 'values' : [],
  659. 'ids' : []}
  660. i += 1
  661. # check for key column
  662. # v.db.connect -g/p returns always key column name lowercase
  663. if self.layers[layer]["key"] not in columns.keys():
  664. for col in columns.keys():
  665. if col.lower() == self.layers[layer]["key"]:
  666. self.layers[layer]["key"] = col.upper()
  667. break
  668. self.tables[table] = columns
  669. return True
  670. def Reset(self):
  671. """!Reset"""
  672. for layer in self.layers:
  673. table = self.layers[layer]["table"] # get table desc
  674. for name in self.tables[table].keys():
  675. self.tables[table][name]['values'] = []
  676. self.tables[table][name]['ids'] = []
  677. def GetName(self):
  678. """!Get vector name"""
  679. return self.map
  680. def GetKeyColumn(self, layer):
  681. """!Get key column of given layer
  682. @param layer vector layer number
  683. """
  684. return str(self.layers[layer]['key'])
  685. def GetTable(self, layer):
  686. """!Get table name of given layer
  687. @param layer vector layer number
  688. """
  689. return self.layers[layer]['table']
  690. def GetDbSettings(self, layer):
  691. """!Get database settins
  692. @param layer layer number
  693. @return (driver, database)
  694. """
  695. return self.layers[layer]['driver'], self.layers[layer]['database']
  696. def GetTableDesc(self, table):
  697. """!Get table columns
  698. @param table table name
  699. """
  700. return self.tables[table]
  701. class LayerSelect(wx.ComboBox):
  702. def __init__(self, parent, id = wx.ID_ANY,
  703. size = globalvar.DIALOG_COMBOBOX_SIZE,
  704. vector = None, dsn = None, choices = [], all = False, default = None):
  705. """!Creates combo box for selecting vector map layer names
  706. @param vector vector map name (native or connected via v.external)
  707. @param dsn OGR data source name
  708. """
  709. super(LayerSelect, self).__init__(parent, id, size = size, choices = choices)
  710. self.all = all
  711. self.SetName("LayerSelect")
  712. # default value
  713. self.default = default
  714. self.InsertLayers(vector = vector, dsn = dsn)
  715. def InsertLayers(self, vector = None, dsn = None):
  716. """!Insert layers for a vector into the layer combobox
  717. @param vector vector map name (native or connected via v.external)
  718. @param dsn OGR data source name
  719. """
  720. layers = list()
  721. if vector:
  722. layers = GetVectorNumberOfLayers(vector)
  723. elif dsn:
  724. ret = RunCommand('v.in.ogr',
  725. read = True,
  726. quiet = True,
  727. flags = 'l',
  728. dsn = dsn)
  729. if ret:
  730. layers = ret.splitlines()
  731. if self.default:
  732. if len(layers) == 0:
  733. layers.insert(0, str(self.default))
  734. elif self.default not in layers:
  735. layers.append(self.default)
  736. if self.all:
  737. layers.insert(0, '-1')
  738. if len(layers) > 0:
  739. self.SetItems(layers)
  740. else:
  741. self.Clear()
  742. self.SetValue("")
  743. if self.default and self.default in layers:
  744. self.SetValue(self.default)
  745. class DriverSelect(wx.ComboBox):
  746. """!Creates combo box for selecting database driver.
  747. """
  748. def __init__(self, parent, choices, value,
  749. id=wx.ID_ANY, pos=wx.DefaultPosition,
  750. size=globalvar.DIALOG_LAYER_SIZE, **kargs):
  751. super(DriverSelect, self).__init__(parent, id, value, pos, size,
  752. choices, style=wx.CB_READONLY)
  753. self.SetName("DriverSelect")
  754. self.SetStringSelection(value)
  755. class DatabaseSelect(wx.TextCtrl):
  756. """!Creates combo box for selecting database driver.
  757. """
  758. def __init__(self, parent, value = '', id = wx.ID_ANY,
  759. size = globalvar.DIALOG_TEXTCTRL_SIZE, **kargs):
  760. super(DatabaseSelect, self).__init__(parent, id, value, size = size, **kargs)
  761. self.SetName("DatabaseSelect")
  762. class TableSelect(wx.ComboBox):
  763. """!Creates combo box for selecting attribute tables from the database
  764. """
  765. def __init__(self, parent,
  766. id = wx.ID_ANY, value = '',
  767. size = globalvar.DIALOG_COMBOBOX_SIZE, choices = [], **kargs):
  768. super(TableSelect, self).__init__(parent, id, value, size = size, choices = choices,
  769. style = wx.CB_READONLY, **kargs)
  770. self.SetName("TableSelect")
  771. if not choices:
  772. self.InsertTables()
  773. def InsertTables(self, driver = None, database = None):
  774. """!Insert attribute tables into combobox"""
  775. items = []
  776. if not driver or not database:
  777. connect = grass.db_connection()
  778. driver = connect['driver']
  779. database = connect['database']
  780. ret = RunCommand('db.tables',
  781. flags = 'p',
  782. read = True,
  783. driver = driver,
  784. database = database)
  785. if ret:
  786. for table in ret.splitlines():
  787. items.append(table)
  788. self.SetItems(items)
  789. self.SetValue('')
  790. class ColumnSelect(wx.combo.ComboCtrl):
  791. """!Creates combo box for selecting columns in the attribute table
  792. for a vector map.
  793. @param parent window parent
  794. @param id window id
  795. @param value default value
  796. @param size window size
  797. @param vector vector map name
  798. @param layer layer number
  799. @param multiple - True if it is possible to add multiple columns
  800. @param param parameters list (see menuform.py)
  801. @param **kwags wx.ComboBox parameters
  802. """
  803. def __init__(self, parent, id = wx.ID_ANY, value = '',
  804. size = globalvar.DIALOG_COMBOBOX_SIZE,
  805. vector = None, layer = 1, multiple = False,
  806. param = None, **kwargs):
  807. self.defaultValue = value
  808. self.param = param
  809. self.columns = []
  810. wx.combo.ComboCtrl.__init__(self, parent, id, size = size, **kwargs)
  811. self.GetChildren()[0].SetName("ColumnSelect")
  812. self.GetChildren()[0].type = type
  813. self.tcp = ListCtrlComboPopup()
  814. self.SetPopupControl(self.tcp)
  815. self.tcp.SetData(multiple = multiple)
  816. if vector:
  817. self.InsertColumns(vector, layer)
  818. self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  819. def GetColumns(self):
  820. return self.columns
  821. def OnKeyUp(self, event):
  822. """!Shows popupwindow if down arrow key is released"""
  823. if event.GetKeyCode() == wx.WXK_DOWN and not self.IsPopupShown():
  824. self.ShowPopup()
  825. else:
  826. event.Skip()
  827. def Clear(self):
  828. self.tcp.DeleteAllItems()
  829. self.SetValue('')
  830. def InsertColumns(self, vector, layer, excludeKey = False, excludeCols = None, type = None, dbInfo = None):
  831. """!Insert columns for a vector attribute table into the columns combobox
  832. @param vector vector name
  833. @param layer vector layer number
  834. @param excludeKey exclude key column from the list?
  835. @param excludeCols list of columns to be removed from the list
  836. @param type only columns of given type (given as list)
  837. """
  838. if not dbInfo:
  839. dbInfo = VectorDBInfo(vector)
  840. try:
  841. try:
  842. layer = int(layer)
  843. except TypeError:
  844. # assuming layer 1
  845. layer = 1
  846. table = dbInfo.GetTable(layer)
  847. columnchoices = dbInfo.GetTableDesc(table)
  848. keyColumn = dbInfo.GetKeyColumn(layer)
  849. self.columns = len(columnchoices.keys()) * ['']
  850. for key, val in columnchoices.iteritems():
  851. self.columns[val['index']] = key
  852. if excludeKey: # exclude key column
  853. self.columns.remove(keyColumn)
  854. if excludeCols: # exclude key column
  855. for key in columnchoices.iterkeys():
  856. if key in excludeCols:
  857. self.columns.remove(key)
  858. if type: # only selected column types
  859. for key, value in columnchoices.iteritems():
  860. if value['type'] not in type:
  861. try:
  862. self.columns.remove(key)
  863. except ValueError:
  864. pass
  865. except (KeyError, ValueError):
  866. self.columns[:] = []
  867. # update list
  868. self.tcp.DeleteAllItems()
  869. for col in self.columns:
  870. self.tcp.AddItem(col)
  871. self.SetValue(self.defaultValue)
  872. if self.param:
  873. value = self.param.get('value', '')
  874. if value != '' and value in self.columns:
  875. self.SetValue(value)
  876. def InsertTableColumns(self, table, driver=None, database=None):
  877. """!Insert table columns
  878. @param table table name
  879. @param driver driver name
  880. @param database database name
  881. """
  882. self.columns[:] = []
  883. ret = RunCommand('db.columns',
  884. read = True,
  885. driver = driver,
  886. database = database,
  887. table = table)
  888. if ret:
  889. self.columns = ret.splitlines()
  890. # update list
  891. self.tcp.DeleteAllItems()
  892. self.SetValue(self.defaultValue)
  893. for col in self.columns:
  894. self.tcp.AddItem(col)
  895. if self.param:
  896. value = self.param.get('value', '')
  897. if value != '' and value in self.columns:
  898. self.SetValue(value)
  899. class DbaseSelect(wx.lib.filebrowsebutton.DirBrowseButton):
  900. """!Widget for selecting GRASS Database"""
  901. def __init__(self, parent, **kwargs):
  902. super(DbaseSelect, self).__init__(parent, id = wx.ID_ANY,
  903. size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
  904. dialogTitle = _('Choose GIS Data Directory'),
  905. buttonText = _('Browse'),
  906. startDirectory = grass.gisenv()['GISDBASE'],
  907. **kwargs)
  908. class LocationSelect(wx.ComboBox):
  909. """!Widget for selecting GRASS location"""
  910. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  911. gisdbase = None, **kwargs):
  912. super(LocationSelect, self).__init__(parent, id, size = size,
  913. style = wx.CB_READONLY, **kwargs)
  914. self.SetName("LocationSelect")
  915. if not gisdbase:
  916. self.gisdbase = grass.gisenv()['GISDBASE']
  917. else:
  918. self.gisdbase = gisdbase
  919. self.SetItems(GetListOfLocations(self.gisdbase))
  920. def UpdateItems(self, dbase):
  921. """!Update list of locations
  922. @param dbase path to GIS database
  923. """
  924. self.gisdbase = dbase
  925. if dbase:
  926. self.SetItems(GetListOfLocations(self.gisdbase))
  927. else:
  928. self.SetItems([])
  929. class MapsetSelect(wx.combo.ComboCtrl):
  930. """!Widget for selecting GRASS mapset"""
  931. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  932. gisdbase = None, location = None, setItems = True,
  933. searchPath = False, new = False, skipCurrent = False, multiple = False, **kwargs):
  934. style = 0
  935. ### disabled, read-only widget has no TextCtrl children (TODO: rewrite)
  936. ### if not new and not multiple:
  937. ### style = wx.CB_READONLY
  938. wx.combo.ComboCtrl.__init__(self, parent, id, size = size,
  939. style = style, **kwargs)
  940. self.searchPath = searchPath
  941. self.skipCurrent = skipCurrent
  942. self.SetName("MapsetSelect")
  943. if not gisdbase:
  944. self.gisdbase = grass.gisenv()['GISDBASE']
  945. else:
  946. self.gisdbase = gisdbase
  947. if not location:
  948. self.location = grass.gisenv()['LOCATION_NAME']
  949. else:
  950. self.location = location
  951. self.tcp = ListCtrlComboPopup()
  952. self.SetPopupControl(self.tcp)
  953. self.tcp.SetData(multiple = multiple)
  954. if setItems:
  955. self.tcp.SetItems(self._getMapsets())
  956. def UpdateItems(self, location, dbase = None):
  957. """!Update list of mapsets for given location
  958. @param dbase path to GIS database (None to use currently selected)
  959. @param location name of location
  960. """
  961. if dbase:
  962. self.gisdbase = dbase
  963. self.location = location
  964. self.tcp.DeleteAllItems()
  965. if location:
  966. self.tcp.SetItems(self._getMapsets())
  967. else:
  968. self.tcp.SetItems([])
  969. def _getMapsets(self):
  970. if self.searchPath:
  971. mlist = RunCommand('g.mapsets',
  972. read = True, flags = 'p',
  973. sep = 'newline').splitlines()
  974. else:
  975. mlist = GetListOfMapsets(self.gisdbase, self.location,
  976. selectable = False)
  977. gisenv = grass.gisenv()
  978. if self.skipCurrent and \
  979. gisenv['LOCATION_NAME'] == self.location and \
  980. gisenv['MAPSET'] in mlist:
  981. mlist.remove(gisenv['MAPSET'])
  982. return mlist
  983. def GetStringSelection(self):
  984. """!For backward compatibility. MapsetSelect changed to allow
  985. multiple selection, this required to change super-class from
  986. wx.ComboBox to wx.combo.ComboCtrl"""
  987. return self.GetValue()
  988. def SetStringSelection(self, text):
  989. """!For backward compatibility. MapsetSelect changed to allow
  990. multiple selection, this required to change super-class from
  991. wx.ComboBox to wx.combo.ComboCtrl"""
  992. return self.SetValue(text)
  993. def SetSelection(self, sel=0):
  994. """!For backward compatibility. MapsetSelect changed to allow
  995. multiple selection, this required to change super-class from
  996. wx.ComboBox to wx.combo.ComboCtrl"""
  997. self.SetValue('') # TODO: implement SetSelection()
  998. def SetItems(self, items):
  999. """!For backward compatibility. MapsetSelect changed to allow
  1000. multiple selection, this required to change super-class from
  1001. wx.ComboBox to wx.combo.ComboCtrl"""
  1002. self.tcp.DeleteAllItems()
  1003. self.tcp.SetItems(items)
  1004. class SubGroupSelect(wx.ComboBox):
  1005. """!Widget for selecting subgroups"""
  1006. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
  1007. **kwargs):
  1008. super(SubGroupSelect, self).__init__(parent, id, size = size,
  1009. **kwargs)
  1010. self.SetName("SubGroupSelect")
  1011. def Insert(self, group):
  1012. """!Insert subgroups for defined group"""
  1013. if not group:
  1014. return
  1015. gisenv = grass.gisenv()
  1016. try:
  1017. name, mapset = group.split('@', 1)
  1018. except ValueError:
  1019. name = group
  1020. mapset = gisenv['MAPSET']
  1021. mlist = RunCommand('i.group', group=group,
  1022. read=True, flags='sg').splitlines()
  1023. try:
  1024. self.SetItems(mlist)
  1025. except OSError:
  1026. self.SetItems([])
  1027. class FormatSelect(wx.Choice):
  1028. def __init__(self, parent, srcType, ogr=False,
  1029. size=globalvar.DIALOG_SPIN_SIZE,
  1030. **kwargs):
  1031. """!Widget for selecting external (GDAL/OGR) format
  1032. @param parent parent window
  1033. @param srcType source type ('file', 'database', 'protocol')
  1034. @param ogr True for OGR otherwise GDAL
  1035. """
  1036. super(FormatSelect, self).__init__(parent, id=wx.ID_ANY, size=size,
  1037. **kwargs)
  1038. self.SetName("FormatSelect")
  1039. if ogr:
  1040. ftype = 'ogr'
  1041. else:
  1042. ftype = 'gdal'
  1043. formats = list()
  1044. for f in GetFormats()[ftype][srcType].values():
  1045. formats += f
  1046. self.SetItems(formats)
  1047. def GetExtension(self, name):
  1048. """!Get file extension by format name"""
  1049. formatToExt = dict()
  1050. formatToExt.update(rasterFormatExtension)
  1051. formatToExt.update(vectorFormatExtension)
  1052. return formatToExt.get(name, '')
  1053. # unused code since r47938
  1054. # wxGdalSelect, EVT_GDALSELECT = NewEvent()
  1055. class GdalSelect(wx.Panel):
  1056. def __init__(self, parent, panel, ogr=False, link=False, dest=False,
  1057. exclude=None):
  1058. """!Widget for selecting GDAL/OGR datasource, format
  1059. @todo Split into GdalSelect and OgrSelect and optionally to
  1060. GdalSelectOutput, OgrSelectOutput
  1061. @param parent parent window
  1062. @param ogr use OGR selector instead of GDAL
  1063. @param dest True for output (destination)
  1064. @param default deafult type (ignored when dest == True)
  1065. @param exclude list of types to be excluded
  1066. """
  1067. self.parent = parent
  1068. self.ogr = ogr
  1069. self.link = link
  1070. self.dest = dest
  1071. self._sourceType = None
  1072. wx.Panel.__init__(self, parent=panel)
  1073. self.reloadDataRequired = Signal('GdalSelect.reloadDataRequired')
  1074. if self.ogr:
  1075. settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
  1076. else:
  1077. settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
  1078. self.settsManager = ManageSettingsWidget(parent=self,
  1079. settingsFile=settingsFile)
  1080. self.settsManager.settingsChanged.connect(self.OnSettingsChanged)
  1081. self.settsManager.settingsSaving.connect(self.OnSettingsSaving)
  1082. self.inputBox = wx.StaticBox(parent=self)
  1083. if dest:
  1084. self.inputBox.SetLabel(" %s " % _("Output settings"))
  1085. else:
  1086. self.inputBox.SetLabel(" %s " % _("Source settings"))
  1087. # source type
  1088. sources = list()
  1089. self.sourceMap = { 'file' : -1,
  1090. 'dir' : -1,
  1091. 'db' : -1,
  1092. 'pro' : -1,
  1093. 'native' : -1 }
  1094. idx = 0
  1095. if dest:
  1096. sources.append(_("Native"))
  1097. self.sourceMap['native'] = idx
  1098. idx += 1
  1099. if exclude is None:
  1100. exclude = []
  1101. if 'file' not in exclude:
  1102. sources.append(_("File"))
  1103. self.sourceMap['file'] = idx
  1104. idx += 1
  1105. if 'directory' not in exclude:
  1106. sources.append(_("Directory"))
  1107. self.sourceMap['dir'] = idx
  1108. idx += 1
  1109. if 'database' not in exclude:
  1110. sources.append(_("Database"))
  1111. self.sourceMap['db'] = idx
  1112. idx += 1
  1113. if 'protocol' not in exclude:
  1114. sources.append(_("Protocol"))
  1115. self.sourceMap['pro'] = idx
  1116. idx += 1
  1117. self.sourceMapByIdx = {}
  1118. for name, idx in self.sourceMap.items():
  1119. self.sourceMapByIdx[ idx ] = name
  1120. self.source = wx.RadioBox(parent=self, id=wx.ID_ANY,
  1121. style=wx.RA_SPECIFY_COLS,
  1122. choices=sources)
  1123. if dest:
  1124. self.source.SetLabel(" %s " % _('Output type'))
  1125. else:
  1126. self.source.SetLabel(" %s " % _('Source type'))
  1127. self.source.SetSelection(0)
  1128. self.source.Bind(wx.EVT_RADIOBOX,
  1129. lambda evt: self.SetSourceType(self.sourceMapByIdx[evt.GetInt()]))
  1130. self.nativeWidgets = {}
  1131. self.fileWidgets = {}
  1132. self.dirWidgets = {}
  1133. self.dbWidgets = {}
  1134. self.protocolWidgets = {}
  1135. self.pgWidgets = {}
  1136. if ogr:
  1137. fType = 'ogr'
  1138. else:
  1139. fType = 'gdal'
  1140. # file
  1141. fileMask = '%(all)s (*.*)|*.*|' % {'all': _('All files')}
  1142. for name, ext in sorted(rasterFormatExtension.items()):
  1143. fileMask += '%(name)s (*.%(low)s;*.%(up)s)|*.%(low)s;*.%(up)s|' % {'name': name,
  1144. 'low': ext.lower(),
  1145. 'up': ext.upper()}
  1146. fileMask += '%s (*.zip;*.ZIP)|*.zip;*.ZIP|' % _('ZIP files')
  1147. fileMask += '%s (*.gz;*.GZ)|*.gz;*.GZ|' % _('GZIP files')
  1148. fileMask += '%s (*.tar;*.TAR)|*.tar;*.TAR|' % _('TAR files')
  1149. fileMask += '%s (*.tar.gz;*.TAR.GZ;*.tgz;*.TGZ)|*.tar.gz;*.TAR.GZ;*.tgz;*.TGZ' % _('TARGZ files')
  1150. # only contains formats with extensions hardcoded
  1151. self.filePanel = wx.Panel(parent=self)
  1152. browse = filebrowse.FileBrowseButton(parent=self.filePanel, id=wx.ID_ANY,
  1153. size=globalvar.DIALOG_GSELECT_SIZE,
  1154. labelText = _('File:'),
  1155. dialogTitle=_('Choose file to import'),
  1156. buttonText=_('Browse'),
  1157. startDirectory=os.getcwd(),
  1158. changeCallback=self.OnUpdate,
  1159. fileMask=fileMask)
  1160. self.fileWidgets['browse'] = browse
  1161. self.fileWidgets['options'] = wx.TextCtrl(parent=self.filePanel)
  1162. # directory
  1163. self.dirPanel = wx.Panel(parent=self)
  1164. browse = filebrowse.DirBrowseButton(parent=self.dirPanel, id=wx.ID_ANY,
  1165. size=globalvar.DIALOG_GSELECT_SIZE,
  1166. labelText=_('Directory:'),
  1167. dialogTitle=_('Choose input directory'),
  1168. buttonText=_('Browse'),
  1169. startDirectory=os.getcwd(),
  1170. changeCallback=self.OnUpdate)
  1171. self.dirWidgets['browse'] = browse
  1172. formatSelect = wx.Choice(parent=self.dirPanel, size=(300, -1))
  1173. self.dirWidgets['format'] = formatSelect
  1174. fileFormats = GetFormats(writableOnly=dest)[fType]['file']
  1175. formatSelect.SetItems(sorted(list(fileFormats)))
  1176. formatSelect.Bind(wx.EVT_CHOICE, lambda evt: self.SetExtension(self.dirWidgets['format'].GetStringSelection()))
  1177. formatSelect.Bind(wx.EVT_CHOICE, self.OnUpdate)
  1178. self.dirWidgets['extensionLabel'] = wx.StaticText(parent=self.dirPanel,
  1179. label = _("Extension:"))
  1180. self.dirWidgets['extension'] = wx.TextCtrl(parent=self.dirPanel)
  1181. self.dirWidgets['extension'].Bind(wx.EVT_TEXT, self.ExtensionChanged)
  1182. self.dirWidgets['options'] = wx.TextCtrl(parent=self.dirPanel)
  1183. if self.ogr:
  1184. shapefile = 'ESRI Shapefile'
  1185. if shapefile in fileFormats:
  1186. formatSelect.SetStringSelection(shapefile)
  1187. self.SetExtension(shapefile)
  1188. else:
  1189. tiff = 'GeoTIFF'
  1190. if tiff in fileFormats:
  1191. formatSelect.SetStringSelection(tiff)
  1192. self.SetExtension(tiff)
  1193. # database
  1194. self.dbPanel = wx.Panel(parent=self)
  1195. self.dbFormats = GetFormats(writableOnly=dest)[fType]['database']
  1196. dbChoice = wx.Choice(parent=self.dbPanel, choices=self.dbFormats)
  1197. dbChoice.Bind(wx.EVT_CHOICE, lambda evt: self.SetDatabase(db=dbChoice.GetStringSelection()))
  1198. self.dbWidgets['format'] = dbChoice
  1199. browse = filebrowse.FileBrowseButton(parent=self.dbPanel, id=wx.ID_ANY,
  1200. size=globalvar.DIALOG_GSELECT_SIZE,
  1201. labelText=_("Name:"),
  1202. dialogTitle=_('Choose file'),
  1203. buttonText=_('Browse'),
  1204. startDirectory=os.getcwd(),
  1205. changeCallback=self.OnUpdate)
  1206. self.dbWidgets['browse'] = browse
  1207. self.dbWidgets['choice'] = wx.Choice(parent=self.dbPanel)
  1208. self.dbWidgets['choice'].Bind(wx.EVT_CHOICE, self.OnUpdate)
  1209. self.dbWidgets['text'] = wx.TextCtrl(parent=self.dbPanel)
  1210. self.dbWidgets['text'].Bind(wx.EVT_TEXT, self.OnUpdate)
  1211. self.dbWidgets['textLabel1'] = wx.StaticText(parent=self.dbPanel, label=_("Name:"))
  1212. self.dbWidgets['textLabel2'] = wx.StaticText(parent=self.dbPanel, label=_("Name:"))
  1213. self.dbWidgets['featType'] = wx.RadioBox(parent=self.dbPanel, id=wx.ID_ANY,
  1214. label = " %s " % _("Feature type:"),
  1215. choices = [_("simple features"), _("topological")],
  1216. majorDimension=2,
  1217. style = wx.RA_SPECIFY_COLS)
  1218. if dest:
  1219. self.dbWidgets['featType'].Disable()
  1220. else:
  1221. self.dbWidgets['featType'].Hide()
  1222. browse = filebrowse.DirBrowseButton(parent=self.dbPanel, id=wx.ID_ANY,
  1223. size=globalvar.DIALOG_GSELECT_SIZE,
  1224. labelText=_('Directory:'),
  1225. dialogTitle=_('Choose input directory'),
  1226. buttonText=_('Browse'),
  1227. startDirectory=os.getcwd(),
  1228. changeCallback=self.OnUpdate)
  1229. self.dbWidgets['dirbrowse'] = browse
  1230. self.dbWidgets['options'] = wx.TextCtrl(parent=self.dbPanel)
  1231. # protocol
  1232. self.protocolPanel = wx.Panel(parent=self)
  1233. protocolFormats = GetFormats(writableOnly=self.dest)[fType]['protocol']
  1234. protocolChoice = wx.Choice(parent=self.protocolPanel, choices=protocolFormats)
  1235. self.protocolWidgets['format'] = protocolChoice
  1236. self.protocolWidgets['text'] = wx.TextCtrl(parent=self.protocolPanel)
  1237. self.protocolWidgets['text'].Bind(wx.EVT_TEXT, self.OnUpdate)
  1238. self.protocolWidgets['options'] = wx.TextCtrl(parent=self.protocolPanel)
  1239. # native
  1240. self.nativePanel = wx.Panel(parent=self)
  1241. self._layout()
  1242. sourceType = 'file'
  1243. self.SetSourceType(sourceType) # needed always to fit dialog size
  1244. if self.dest:
  1245. current = RunCommand('v.external.out',
  1246. parent=self,
  1247. read=True, parse=grass.parse_key_val,
  1248. flags='g')
  1249. if current['format'] == 'native':
  1250. sourceType = 'native'
  1251. elif current['format'] in GetFormats()['ogr']['database']:
  1252. sourceType = 'db'
  1253. else:
  1254. sourceType = 'dir'
  1255. if self.dest:
  1256. wx.CallAfter(self._postInit, sourceType, current)
  1257. def _postInit(self, sourceType, data):
  1258. """!Fill in default values."""
  1259. format = data.get('format', '')
  1260. pg = 'conninfo' in data.keys()
  1261. if pg:
  1262. dsn=''
  1263. for item in data.get('conninfo').split(' '):
  1264. k, v = item.split('=')
  1265. if k == 'dbname':
  1266. dsn=v
  1267. break
  1268. optList = list()
  1269. for k, v in data.iteritems():
  1270. if k in ('format', 'conninfo', 'topology'):
  1271. continue
  1272. optList.append('%s=%s' % (k, v))
  1273. options = ','.join(optList)
  1274. else:
  1275. dsn = data.get('dsn')
  1276. options = data.get('options', '')
  1277. self.SetSourceType(sourceType)
  1278. self.source.SetSelection(self.sourceMap[sourceType])
  1279. dsn = os.path.expandvars(dsn) # v.external.out uses $HOME
  1280. # fill in default values
  1281. if sourceType == 'dir':
  1282. self.dirWidgets['format'].SetStringSelection(format)
  1283. self.dirWidgets['browse'].SetValue(dsn)
  1284. self.dirWidgets['options'].SetValue(options)
  1285. elif sourceType == 'db':
  1286. self.dbWidgets['format'].SetStringSelection(format)
  1287. self.dbWidgets['options'].SetValue(options)
  1288. name = self._getCurrentDbWidgetName()
  1289. if name == 'choice':
  1290. if dsn in self.dbWidgets[name].GetItems():
  1291. self.dbWidgets[name].SetStringSelection(dsn)
  1292. if 'topology' in data.keys():
  1293. self.dbWidgets['featType'].SetSelection(1)
  1294. else:
  1295. self.dbWidgets[name].SetValue(dsn)
  1296. def _layout(self):
  1297. """!Layout"""
  1298. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1299. self.changingSizer = wx.StaticBoxSizer(self.inputBox, wx.VERTICAL)
  1300. # file
  1301. paddingSizer = wx.BoxSizer(wx.VERTICAL)
  1302. sizer = wx.GridBagSizer(vgap=5, hgap=10)
  1303. paddingSizer.Add(item=self.fileWidgets['browse'],
  1304. flag=wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
  1305. border=30)
  1306. sizer.Add(item=paddingSizer, flag=wx.EXPAND, pos=(0, 0), span=(1, 2))
  1307. sizer.AddGrowableCol(0)
  1308. if self.dest:
  1309. sizer.Add(item=wx.StaticText(parent=self.filePanel,
  1310. label = _("Creation options:")),
  1311. flag = wx.ALIGN_CENTER_VERTICAL,
  1312. pos = (1, 0))
  1313. sizer.Add(item=self.fileWidgets['options'],
  1314. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1315. pos=(1, 1))
  1316. else:
  1317. self.fileWidgets['options'].Hide()
  1318. self.filePanel.SetSizer(sizer)
  1319. # directory
  1320. sizer = wx.GridBagSizer(vgap=3, hgap=10)
  1321. sizer.Add(item=wx.StaticText(parent=self.dirPanel,
  1322. label = _("Format:")),
  1323. flag=wx.ALIGN_CENTER_VERTICAL,
  1324. pos=(0, 0))
  1325. sizer.Add(item=self.dirWidgets['format'],
  1326. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1327. pos=(0, 1))
  1328. sizer.Add(item=self.dirWidgets['extensionLabel'],
  1329. flag=wx.ALIGN_CENTER_VERTICAL,
  1330. pos=(0, 2))
  1331. sizer.Add(item=self.dirWidgets['extension'],
  1332. flag=wx.ALIGN_CENTER_VERTICAL,
  1333. pos=(0, 3))
  1334. sizer.Add(item=self.dirWidgets['browse'],
  1335. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1336. pos=(1, 0), span=(1, 4))
  1337. if self.dest:
  1338. sizer.Add(item=wx.StaticText(parent=self.dirPanel,
  1339. label = _("Creation options:")),
  1340. flag = wx.ALIGN_CENTER_VERTICAL,
  1341. pos = (2, 0))
  1342. sizer.Add(item=self.dirWidgets['options'],
  1343. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1344. pos=(2, 1))
  1345. helpBtn = wx.Button(parent=self.dirPanel, id=wx.ID_HELP)
  1346. helpBtn.Bind(wx.EVT_BUTTON, self.OnHelp)
  1347. sizer.Add(item=helpBtn,
  1348. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1349. pos=(2, 2))
  1350. self.dirWidgets['extensionLabel'].Hide()
  1351. self.dirWidgets['extension'].Hide()
  1352. else:
  1353. self.dirWidgets['options'].Hide()
  1354. sizer.AddGrowableCol(1)
  1355. self.dirPanel.SetSizer(sizer)
  1356. # database
  1357. sizer = wx.GridBagSizer(vgap=1, hgap=5)
  1358. sizer.Add(item=wx.StaticText(parent=self.dbPanel,
  1359. label = _("Format:")),
  1360. flag=wx.ALIGN_CENTER_VERTICAL,
  1361. pos=(0, 0))
  1362. sizer.Add(item=self.dbWidgets['format'],
  1363. flag=wx.ALIGN_CENTER_VERTICAL,
  1364. pos=(0, 1))
  1365. sizer.Add(item=self.dbWidgets['textLabel1'],
  1366. flag=wx.ALIGN_CENTER_VERTICAL,
  1367. pos=(1, 0))
  1368. sizer.Add(item=self.dbWidgets['text'],
  1369. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1370. pos=(1, 1), span=(1, 2))
  1371. sizer.Add(item=self.dbWidgets['browse'],
  1372. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1373. pos=(2, 0), span=(1, 3))
  1374. sizer.Add(item=self.dbWidgets['dirbrowse'],
  1375. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1376. pos=(3, 0), span=(1, 2))
  1377. sizer.Add(item=self.dbWidgets['textLabel2'],
  1378. flag=wx.ALIGN_CENTER_VERTICAL,
  1379. pos=(4, 0))
  1380. sizer.Add(item=self.dbWidgets['choice'],
  1381. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1382. pos=(4, 1), span=(1, 2))
  1383. if self.dest:
  1384. sizer.Add(item=self.dbWidgets['featType'],
  1385. pos=(0, 2), flag=wx.EXPAND)
  1386. sizer.Add(item=wx.StaticText(parent=self.dbPanel,
  1387. label = _("Creation options:")),
  1388. flag = wx.ALIGN_CENTER_VERTICAL,
  1389. pos = (5, 0))
  1390. sizer.Add(item=self.dbWidgets['options'],
  1391. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1392. pos=(5, 1), span=(1, 2))
  1393. # help button
  1394. helpBtn = wx.Button(parent=self.dbPanel, id=wx.ID_HELP)
  1395. helpBtn.Bind(wx.EVT_BUTTON, self.OnHelp)
  1396. sizer.Add(item=helpBtn,
  1397. pos=(5, 3))
  1398. else:
  1399. self.dbWidgets['options'].Hide()
  1400. self.dbPanel.SetSizer(sizer)
  1401. sizer.SetEmptyCellSize((0, 0))
  1402. sizer.AddGrowableCol(1)
  1403. # protocol
  1404. sizer = wx.GridBagSizer(vgap=3, hgap=3)
  1405. sizer.Add(item=wx.StaticText(parent=self.protocolPanel,
  1406. label = _("Format:")),
  1407. flag=wx.ALIGN_CENTER_VERTICAL,
  1408. pos=(0, 0))
  1409. sizer.Add(item=self.protocolWidgets['format'],
  1410. flag=wx.ALIGN_CENTER_VERTICAL,
  1411. pos=(0, 1))
  1412. sizer.Add(item=wx.StaticText(parent=self.protocolPanel,
  1413. label = _("Protocol:")),
  1414. flag=wx.ALIGN_CENTER_VERTICAL,
  1415. pos=(1, 0))
  1416. sizer.Add(item=self.protocolWidgets['text'],
  1417. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1418. pos=(1, 1))
  1419. if self.dest:
  1420. sizer.Add(item=wx.StaticText(parent=self.protocolPanel,
  1421. label = _("Creation options:")),
  1422. flag = wx.ALIGN_CENTER_VERTICAL,
  1423. pos = (2, 0))
  1424. sizer.Add(item=self.protocolWidgets['options'],
  1425. flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  1426. pos=(2, 1))
  1427. else:
  1428. self.protocolWidgets['options'].Hide()
  1429. sizer.AddGrowableCol(1)
  1430. self.protocolPanel.SetSizer(sizer)
  1431. # native
  1432. sizer = wx.BoxSizer(wx.VERTICAL)
  1433. sizer.Add(item=wx.StaticText(parent=self.nativePanel,
  1434. label = _("No settings available")),
  1435. flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, border=5)
  1436. self.nativePanel.SetSizer(sizer)
  1437. for panel in (self.nativePanel, self.filePanel,
  1438. self.dirPanel, self.dbPanel,
  1439. self.protocolPanel):
  1440. self.changingSizer.Add(item=panel, proportion=1,
  1441. flag=wx.EXPAND)
  1442. mainSizer.Add(item=self.settsManager, proportion=0,
  1443. flag=wx.ALL | wx.EXPAND, border=5)
  1444. mainSizer.Add(item=self.source, proportion=0,
  1445. flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5)
  1446. mainSizer.Add(item=self.changingSizer, proportion=1,
  1447. flag=wx.ALL | wx.EXPAND, border=5)
  1448. self.mainSizer = mainSizer
  1449. self.SetSizer(mainSizer)
  1450. mainSizer.Fit(self)
  1451. def _getExtension(self, name):
  1452. """!Get file extension by format name"""
  1453. formatToExt = dict()
  1454. formatToExt.update(rasterFormatExtension)
  1455. formatToExt.update(vectorFormatExtension)
  1456. return formatToExt.get(name, '')
  1457. def SetSourceType(self, sourceType):
  1458. """!Set source type (db, file, dir, ...).
  1459. Does not switch radioboxes."""
  1460. self._sourceType = sourceType
  1461. self.changingSizer.Show(item=self.filePanel, show=(sourceType == 'file'))
  1462. self.changingSizer.Show(item=self.nativePanel, show=(sourceType == 'native'))
  1463. self.changingSizer.Show(item=self.dirPanel, show=(sourceType == 'dir'))
  1464. self.changingSizer.Show(item=self.protocolPanel, show=(sourceType == 'pro'))
  1465. self.changingSizer.Show(item=self.dbPanel, show=(sourceType is 'db'))
  1466. self.changingSizer.Layout()
  1467. if sourceType == 'db':
  1468. self.dbWidgets['format'].SetItems(self.dbFormats)
  1469. if self.dbFormats:
  1470. if 'PostgreSQL' in self.dbFormats:
  1471. self.dbWidgets['format'].SetStringSelection('PostgreSQL')
  1472. else:
  1473. self.dbWidgets['format'].SetSelection(0)
  1474. self.dbWidgets['format'].Enable()
  1475. if sourceType == 'db':
  1476. db = self.dbWidgets['format'].GetStringSelection()
  1477. self.SetDatabase(db)
  1478. if not self.dest:
  1479. self.reloadDataRequired.emit(data=None)
  1480. self._reloadLayers()
  1481. def OnSettingsChanged(self, data):
  1482. """!User changed setting"""
  1483. # data list: [type, dsn, format, options]
  1484. if len(data) == 3:
  1485. data.append('')
  1486. elif len < 3:
  1487. return
  1488. self.source.SetSelection(self.sourceMap[data[0]])
  1489. self.SetSourceType(data[0])
  1490. if data[0] == 'file':
  1491. self.fileWidgets['browse'].SetValue(data[1])
  1492. self.fileWidgets['options'].SetValue(data[3])
  1493. elif data[0] == 'dir':
  1494. self.dirWidgets['browse'].SetValue(data[1])
  1495. self.dirWidgets['format'].SetStringSelection(data[2])
  1496. self.dirWidgets['options'].SetValue(data[3])
  1497. self.SetExtension(data[2])
  1498. elif data[0] == 'pro':
  1499. self.protocolWidgets['text'].SetValue(data[2])
  1500. self.protocolWidgets['options'].SetValue(data[3])
  1501. elif data[0] == 'db':
  1502. name = self._getCurrentDbWidgetName()
  1503. if name =='choice':
  1504. if len(data[1].split(':', 1)) > 1:
  1505. for item in data[1].split(':', 1)[1].split(','):
  1506. key, value = item.split('=', 1)
  1507. if key == 'dbname':
  1508. self.dbWidgets[name].SetStringSelection(value)
  1509. break
  1510. else:
  1511. self.dbWidgets[name].SetStringSelection(data[1])
  1512. else:
  1513. self.dbWidgets[name].SetValue(data[1])
  1514. self.dbWidgets['options'].SetValue(data[3])
  1515. if not self.dest:
  1516. self.reloadDataRequired.emit(data=None)
  1517. self._reloadLayers()
  1518. def OnSettingsSaving(self, name):
  1519. """!Saving data"""
  1520. if not self.GetDsn():
  1521. GMessage(parent = self,
  1522. message = _("No data source defined, settings are not saved."))
  1523. return
  1524. self.settsManager.SetDataToSave((self._sourceType, self.GetDsn(),
  1525. self.GetFormat(), self.GetOptions()))
  1526. self.settsManager.SaveSettings(name)
  1527. def _getExtPatternGlob(self, ext):
  1528. """!Get pattern for case-insensitive globing"""
  1529. pattern = '*.'
  1530. for c in ext:
  1531. pattern += '[%s%s]' % (c.lower(), c.upper())
  1532. return pattern
  1533. def _getCurrentDbWidgetName(self):
  1534. """!Returns active dns database widget name."""
  1535. for widget in ('browse', 'dirbrowse', 'text', 'choice'):
  1536. if self.dbWidgets[widget].IsShown():
  1537. return widget
  1538. def GetDsn(self):
  1539. """!Get datasource name
  1540. """
  1541. if self._sourceType == 'db':
  1542. if self.dbWidgets['format'].GetStringSelection() in ('PostgreSQL',
  1543. 'PostGIS Raster driver'):
  1544. dsn = 'PG:dbname=%s' % self.dbWidgets['choice'].GetStringSelection()
  1545. else:
  1546. name = self._getCurrentDbWidgetName()
  1547. if name == 'choice':
  1548. dsn = self.dbWidgets[name].GetStringSelection()
  1549. else:
  1550. dsn = self.dbWidgets[name].GetValue()
  1551. else:
  1552. if self._sourceType == 'file':
  1553. dsn = self.fileWidgets['browse'].GetValue()
  1554. elif self._sourceType == 'dir':
  1555. dsn = self.dirWidgets['browse'].GetValue()
  1556. elif self._sourceType == 'pro':
  1557. dsn = self.protocolWidgets['text'].GetValue()
  1558. else:
  1559. dsn = ''
  1560. # check compressed files
  1561. try:
  1562. ext = os.path.splitext(dsn)[1].lower()
  1563. except KeyError:
  1564. ext = None
  1565. if ext == '.zip':
  1566. dsn = '/vsizip/' + dsn
  1567. elif ext == '.gzip':
  1568. dsn = '/vsigzip/' + dsn
  1569. elif ext in ('.tar', '.tar.gz', '.tgz'):
  1570. dsn = '/vsitar/' + dsn
  1571. return dsn
  1572. def SetDatabase(self, db):
  1573. """!Update database panel."""
  1574. sizer = self.dbPanel.GetSizer()
  1575. showBrowse = db in ('SQLite', 'Rasterlite')
  1576. showDirbrowse = db in ('FileGDB')
  1577. showChoice = db in ('PostgreSQL','PostGIS WKT Raster driver',
  1578. 'PostGIS Raster driver')
  1579. enableFeatType = self.dest and self.ogr and db in ('PostgreSQL')
  1580. showText = not(showBrowse or showChoice or showDirbrowse)
  1581. sizer.Show(self.dbWidgets['browse'], show=showBrowse)
  1582. sizer.Show(self.dbWidgets['dirbrowse'], show=showDirbrowse)
  1583. sizer.Show(self.dbWidgets['choice'], show=showChoice)
  1584. sizer.Show(self.dbWidgets['textLabel2'], show=showChoice)
  1585. sizer.Show(self.dbWidgets['text'], show=showText)
  1586. sizer.Show(self.dbWidgets['textLabel1'], show=showText)
  1587. self.dbWidgets['featType'].Enable(enableFeatType)
  1588. if showChoice:
  1589. # try to get list of PG databases
  1590. dbNames = RunCommand('db.databases', quiet=True, read=True,
  1591. driver='pg').splitlines()
  1592. if dbNames is not None:
  1593. self.dbWidgets['choice'].SetItems(sorted(dbNames))
  1594. self.dbWidgets['choice'].SetSelection(0)
  1595. elif grass.find_program('psql', '--help'):
  1596. if not self.dbWidgets['choice'].GetItems():
  1597. p = grass.Popen(['psql', '-ltA'], stdout = grass.PIPE)
  1598. ret = p.communicate()[0]
  1599. if ret:
  1600. dbNames = list()
  1601. for line in ret.splitlines():
  1602. sline = line.split('|')
  1603. if len(sline) < 2:
  1604. continue
  1605. dbname = sline[0]
  1606. if dbname:
  1607. dbNames.append(dbname)
  1608. self.dbWidgets['choice'].SetItems(db)
  1609. self.dbWidgets['choice'].SetSelection(0)
  1610. else:
  1611. sizer.Show(self.dbWidgets['text'])
  1612. sizer.Show(self.dbWidgets['choice'], False)
  1613. sizer.Layout()
  1614. def OnUpdate(self, event):
  1615. """!Update required - load layers."""
  1616. if not self.dest:
  1617. self._reloadLayers()
  1618. event.Skip()
  1619. def _reloadLayers(self):
  1620. """!Reload list of layers"""
  1621. dsn = self.GetDsn()
  1622. if not dsn:
  1623. return
  1624. data = list()
  1625. layerId = 1
  1626. if self.ogr:
  1627. ret = RunCommand('v.external',
  1628. quiet = True,
  1629. read = True,
  1630. flags = 't',
  1631. dsn = dsn)
  1632. if not ret:
  1633. self.reloadDataRequired.emit(data=None)
  1634. return
  1635. layerId = 1
  1636. for line in ret.splitlines():
  1637. layerName, featureType, projection = map(lambda x: x.strip(), line.split(','))
  1638. if projection == '0':
  1639. projectionMatch = _("No")
  1640. else:
  1641. projectionMatch = _("Yes")
  1642. grassName = GetValidLayerName(layerName)
  1643. data.append((layerId, layerName, featureType, projectionMatch, grassName))
  1644. layerId += 1
  1645. else:
  1646. if self._sourceType == 'file':
  1647. baseName = os.path.basename(dsn)
  1648. grassName = GetValidLayerName(baseName.split('.', -1)[0])
  1649. data.append((layerId, baseName, grassName))
  1650. elif self._sourceType == 'dir':
  1651. ext = self.dirWidgets['extension'].GetValue()
  1652. for filename in glob.glob(os.path.join(dsn, "%s") % self._getExtPatternGlob(ext)):
  1653. baseName = os.path.basename(filename)
  1654. grassName = GetValidLayerName(baseName.split('.', -1)[0])
  1655. data.append((layerId, baseName, grassName))
  1656. layerId += 1
  1657. # unused code since r47938
  1658. # if self.ogr:
  1659. # dsn += '@OGR'
  1660. #
  1661. # evt = wxGdalSelect(dsn = dsn)
  1662. # evt.SetId(self.input[self.dsnType][1].GetId())
  1663. # wx.PostEvent(self.parent, evt)
  1664. if self.parent.GetName() == 'MultiImportDialog':
  1665. self.reloadDataRequired.emit(data=data)
  1666. def ExtensionChanged(self, event):
  1667. if not self.dest:
  1668. # reload layers
  1669. self._reloadLayers()
  1670. def SetExtension(self, name):
  1671. """!Extension changed"""
  1672. ext = self._getExtension(name)
  1673. self.dirWidgets['extension'].SetValue(ext)
  1674. def GetType(self):
  1675. """!Get source type"""
  1676. return self._sourceType
  1677. def GetFormat(self):
  1678. """!Get format as string"""
  1679. if self._sourceType == 'dir':
  1680. format = self.dirWidgets['format'].GetStringSelection()
  1681. elif self._sourceType == 'pro':
  1682. format = self.protocolWidgets['format'].GetStringSelection()
  1683. elif self._sourceType == 'db':
  1684. format = self.dbWidgets['format'].GetStringSelection()
  1685. else:
  1686. format = ''
  1687. return format.replace(' ', '_')
  1688. def GetFormatExt(self):
  1689. """!Get format extension"""
  1690. return self._getExtension(self.GetFormat())
  1691. def GetOptions(self):
  1692. """!Get creation options"""
  1693. if self._sourceType == 'file':
  1694. options = self.fileWidgets['options'].GetValue()
  1695. elif self._sourceType == 'dir':
  1696. options = self.dirWidgets['options'].GetValue()
  1697. elif self._sourceType == 'pro':
  1698. options = self.protocolWidgets['options'].GetValue()
  1699. elif self._sourceType == 'db':
  1700. if self.dbWidgets['featType'].GetSelection() == 1:
  1701. options = 'topology=yes '
  1702. else:
  1703. options = ''
  1704. options += self.dbWidgets['options'].GetValue()
  1705. return options.strip()
  1706. def OnHelp(self, event):
  1707. """!Show related manual page"""
  1708. cmd = ''
  1709. if self.dest:
  1710. if self.ogr:
  1711. cmd = 'v.external.out'
  1712. else:
  1713. cmd = 'r.external.out'
  1714. else:
  1715. if self.link:
  1716. if self.ogr:
  1717. cmd = 'v.external'
  1718. else:
  1719. cmd = 'r.external'
  1720. else:
  1721. if self.ogr:
  1722. cmd = 'v.in.ogr'
  1723. else:
  1724. cmd = 'r.in.gdal'
  1725. RunCommand('g.manual', entry = cmd)
  1726. class ProjSelect(wx.ComboBox):
  1727. """!Widget for selecting input raster/vector map used by
  1728. r.proj/v.proj modules."""
  1729. def __init__(self, parent, isRaster, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  1730. **kwargs):
  1731. super(ProjSelect, self).__init__(parent, id, size = size,
  1732. style = wx.CB_READONLY, **kwargs)
  1733. self.SetName("ProjSelect")
  1734. self.isRaster = isRaster
  1735. def UpdateItems(self, dbase, location, mapset):
  1736. """!Update list of maps
  1737. """
  1738. if not dbase:
  1739. dbase = grass.gisenv()['GISDBASE']
  1740. if not mapset:
  1741. mapset = grass.gisenv()['MAPSET']
  1742. if self.isRaster:
  1743. ret = RunCommand('r.proj',
  1744. quiet = True,
  1745. read = True,
  1746. flags = 'l',
  1747. dbase = dbase,
  1748. location = location,
  1749. mapset = mapset)
  1750. else:
  1751. ret = RunCommand('v.proj',
  1752. quiet = True,
  1753. read = True,
  1754. flags = 'l',
  1755. dbase = dbase,
  1756. location = location,
  1757. mapset = mapset)
  1758. listMaps = list()
  1759. if ret:
  1760. for line in ret.splitlines():
  1761. listMaps.append(line.strip())
  1762. ListSortLower(listMaps)
  1763. self.SetItems(listMaps)
  1764. self.SetValue('')
  1765. class ElementSelect(wx.Choice):
  1766. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  1767. **kwargs):
  1768. """!Widget for selecting GIS element
  1769. @param parent parent window
  1770. """
  1771. super(ElementSelect, self).__init__(parent, id, size = size,
  1772. **kwargs)
  1773. self.SetName("ElementSelect")
  1774. task = gtask.parse_interface('g.list')
  1775. p = task.get_param(value = 'type')
  1776. self.values = p.get('values', [])
  1777. self.valuesDesc = p.get('values_desc', [])
  1778. self.SetItems(self.valuesDesc)
  1779. def GetValue(self, name):
  1780. """!Translate value
  1781. @param name element name
  1782. """
  1783. idx = self.valuesDesc.index(name)
  1784. if idx > -1:
  1785. return self.values[idx]
  1786. return ''
  1787. class OgrTypeSelect(wx.Panel):
  1788. def __init__(self, parent, panel, **kwargs):
  1789. """!Widget to choose OGR feature type
  1790. @param parent parent window
  1791. @param panel wx.Panel instance used as parent window
  1792. """
  1793. wx.Panel.__init__(self, parent = panel, id = wx.ID_ANY)
  1794. self.ftype = wx.Choice(parent = self, id = wx.ID_ANY,
  1795. size = (200, -1),
  1796. choices = (_("Point"), _("LineString"), _("Polygon")))
  1797. self._layout()
  1798. def _layout(self):
  1799. """!Do layout"""
  1800. sizer = wx.BoxSizer(wx.HORIZONTAL)
  1801. sizer.Add(item = wx.StaticText(parent = self,
  1802. id = wx.ID_ANY,
  1803. label = _("Feature type:")),
  1804. proportion = 1,
  1805. flag = wx.ALIGN_CENTER_VERTICAL,
  1806. border = 5)
  1807. sizer.Add(item = self.ftype,
  1808. proportion = 0,
  1809. flag = wx.EXPAND | wx.ALIGN_RIGHT)
  1810. self.SetSizer(sizer)
  1811. sizer.Fit(self)
  1812. def GetType(self):
  1813. """!Get selected type as string
  1814. @return feature type as string
  1815. """
  1816. sel = self.ftype.GetSelection()
  1817. if sel == 0:
  1818. return 'point'
  1819. elif sel == 1:
  1820. return 'line'
  1821. elif sel == 2:
  1822. return 'boundary'
  1823. class CoordinatesSelect(wx.Panel):
  1824. def __init__(self, parent, giface, multiple = False, **kwargs):
  1825. """!Widget to get coordinates from map window by mouse click
  1826. @param parent parent window
  1827. @param giface GRASS interface
  1828. @param multiple - True if it is possible to insert more coordinates
  1829. """
  1830. self._giface = giface
  1831. self.multiple = multiple
  1832. self.mapWin = None
  1833. self.drawMapWin = None
  1834. super(CoordinatesSelect, self).__init__(parent=parent, id=wx.ID_ANY)
  1835. self.coordsField = wx.TextCtrl(parent=self, id=wx.ID_ANY,
  1836. size=globalvar.DIALOG_TEXTCTRL_SIZE,
  1837. validator=CoordinatesValidator())
  1838. icon = wx.Bitmap(os.path.join(globalvar.ETCICONDIR, "grass", "pointer.png"))
  1839. self.buttonInsCoords = buttons.ThemedGenBitmapToggleButton(parent=self, id=wx.ID_ANY,
  1840. bitmap=icon,
  1841. size=globalvar.DIALOG_COLOR_SIZE)
  1842. self.registered = False
  1843. self.buttonInsCoords.Bind(wx.EVT_BUTTON, self._onClick)
  1844. switcher = self._giface.GetMapDisplay().GetToolSwitcher()
  1845. switcher.AddCustomToolToGroup(group='mouseUse',
  1846. btnId=self.buttonInsCoords.GetId(),
  1847. toggleHandler=self.buttonInsCoords.SetValue)
  1848. self._doLayout()
  1849. self.coordsField.Bind(wx.EVT_TEXT, lambda event : self._draw(delay=1))
  1850. def _doLayout(self):
  1851. self.dialogSizer = wx.BoxSizer(wx.HORIZONTAL)
  1852. self.dialogSizer.Add(item = self.coordsField,
  1853. proportion = 1,
  1854. flag = wx.EXPAND)
  1855. self.dialogSizer.Add(item = self.buttonInsCoords)
  1856. self.SetSizer(self.dialogSizer)
  1857. def _onClick(self, event):
  1858. """!Button for interacitve inserting of coordinates clicked"""
  1859. self.mapWin = self._giface.GetMapWindow()
  1860. if self.buttonInsCoords.GetToggle() and self.mapWin:
  1861. switcher = self._giface.GetMapDisplay().GetToolSwitcher()
  1862. switcher.ToolChanged(self.buttonInsCoords.GetId())
  1863. if self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  1864. self._onMapClickHandler,
  1865. 'cross') == False:
  1866. return
  1867. self.registered = True
  1868. self._giface.GetMapDisplay().Raise()
  1869. else:
  1870. if self.mapWin and \
  1871. self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  1872. self._onMapClickHandler):
  1873. self.registered = False
  1874. return
  1875. def drawCleanUp(self):
  1876. if self.drawMapWin:
  1877. self.drawMapWin.UnregisterGraphicsToDraw(self.pointsToDraw)
  1878. def _draw(self, delay):
  1879. """!Draws points representing inserted coordinates in mapwindow."""
  1880. if self.drawMapWin != self.mapWin:
  1881. self.drawCleanUp()
  1882. if self.mapWin:
  1883. self.drawMapWin = self.mapWin
  1884. self.pointsToDraw = self.drawMapWin.RegisterGraphicsToDraw(graphicsType="point")
  1885. if self.drawMapWin:
  1886. items = self.pointsToDraw.GetAllItems()
  1887. for i in items:
  1888. self.pointsToDraw.DeleteItem(i)
  1889. coords = self._getCoords()
  1890. if coords is not None:
  1891. for i in range(len(coords)/2):
  1892. i = i * 2
  1893. self.pointsToDraw.AddItem(coords=(coords[i], coords[i + 1]))
  1894. self._giface.updateMap.emit(render=False, renderVector=False, delay=delay)
  1895. def _getCoords(self):
  1896. """!Get list of coordinates.
  1897. @return None if values are not valid
  1898. """
  1899. if self.coordsField.GetValidator().Validate():
  1900. return self.coordsField.GetValue().split(',')
  1901. return None
  1902. def _onMapClickHandler(self, event):
  1903. """!Gets coordinates from mapwindow"""
  1904. if event == "unregistered":
  1905. return
  1906. e, n = self.mapWin.GetLastEN()
  1907. prevCoords = ""
  1908. if self.multiple:
  1909. prevCoords = self.coordsField.GetValue().strip()
  1910. if prevCoords != "":
  1911. prevCoords += ","
  1912. value = prevCoords + str(e) + "," + str(n)
  1913. self.coordsField.SetValue(value)
  1914. self._draw(delay=0)
  1915. def OnClose(self):
  1916. """!Unregistrates _onMapClickHandler from mapWin"""
  1917. self.drawCleanUp()
  1918. self._giface.updateMap.emit(render=False, renderVector=False)
  1919. switcher = self._giface.GetMapDisplay().GetToolSwitcher()
  1920. switcher.RemoveCustomToolFromGroup(self.buttonInsCoords.GetId())
  1921. if self.mapWin and self.registered:
  1922. self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  1923. self._onMapClickHandler)
  1924. def GetTextWin(self):
  1925. """!Get TextCtrl widget"""
  1926. return self.coordsField
  1927. class SignatureSelect(wx.ComboBox):
  1928. """!Widget for selecting signatures"""
  1929. def __init__(self, parent, element, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
  1930. **kwargs):
  1931. super(SignatureSelect, self).__init__(parent, id, size = size,
  1932. **kwargs)
  1933. self.element = element
  1934. self.SetName("SignatureSelect")
  1935. def Insert(self, group, subgroup = None):
  1936. """!Insert signatures for defined group/subgroup
  1937. @param group group name (can be fully-qualified)
  1938. @param subgroup non fully-qualified name of subgroup
  1939. """
  1940. if not group:
  1941. return
  1942. gisenv = grass.gisenv()
  1943. try:
  1944. name, mapset = group.split('@', 1)
  1945. except ValueError:
  1946. name = group
  1947. mapset = gisenv['MAPSET']
  1948. path = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'], mapset,
  1949. 'group', name)
  1950. if subgroup:
  1951. path = os.path.join(path, 'subgroup', subgroup)
  1952. try:
  1953. items = list()
  1954. for element in os.listdir(os.path.join(path, self.element)):
  1955. items.append(element)
  1956. self.SetItems(items)
  1957. except OSError:
  1958. self.SetItems([])
  1959. self.SetValue('')