gselect.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. """!
  2. @package gselect
  3. @brief Custom control that selects elements
  4. Classes:
  5. - Select
  6. - VectorSelect
  7. - TreeCrtlComboPopup
  8. - VectorDBInfo
  9. - LayerSelect
  10. - DriverSelect
  11. - DatabaseSelect
  12. - ColumnSelect
  13. - LocationSelect
  14. - MapsetSelect
  15. (C) 2007-2009 by the GRASS Development Team This program is free
  16. software under the GNU General Public License (>=v2). Read the file
  17. COPYING that comes with GRASS for details.
  18. @author Michael Barton
  19. @author Martin Landa <landa.martin gmail.com>
  20. """
  21. import os
  22. import sys
  23. import wx
  24. import wx.combo
  25. import grass.script as grass
  26. import globalvar
  27. import gcmd
  28. import utils
  29. from preferences import globalSettings as UserSettings
  30. class Select(wx.combo.ComboCtrl):
  31. def __init__(self, parent, id, size = globalvar.DIALOG_GSELECT_SIZE,
  32. type = None, multiple = False, mapsets = None, exclude = [],
  33. updateOnPopup = True):
  34. """!Custom control to create a ComboBox with a tree control
  35. to display and select GIS elements within acessible mapsets.
  36. Elements can be selected with mouse. Can allow multiple selections, when
  37. argument multiple=True. Multiple selections are separated by commas.
  38. """
  39. wx.combo.ComboCtrl.__init__(self, parent=parent, id=id, size=size)
  40. self.GetChildren()[0].SetName("Select")
  41. self.GetChildren()[0].type = type
  42. self.tcp = TreeCtrlComboPopup()
  43. self.SetPopupControl(self.tcp)
  44. self.SetPopupExtents(0,100)
  45. if type:
  46. self.tcp.SetData(type = type, mapsets = mapsets,
  47. exclude = exclude, multiple = multiple,
  48. updateOnPopup = updateOnPopup)
  49. def SetElementList(self, type, mapsets = None, exclude = []):
  50. """!Set element list
  51. @param type GIS element type
  52. @param mapsets list of acceptable mapsets (None for all in search path)
  53. @param exclude list of GIS elements to be excluded
  54. """
  55. self.tcp.SetData(type = type, mapsets = mapsets,
  56. exclude = exclude)
  57. def GetElementList(self):
  58. """!Load elements"""
  59. self.tcp.GetElementList()
  60. class VectorSelect(Select):
  61. def __init__(self, parent, ftype, **kwargs):
  62. """!Custom to create a ComboBox with a tree control to display and
  63. select vector maps. Control allows to filter vector maps. If you
  64. don't need this feature use Select class instead
  65. @ftype filter vector maps based on feature type
  66. """
  67. Select.__init__(self, parent = parent, id = wx.ID_ANY,
  68. type = 'vector', **kwargs)
  69. self.ftype = ftype
  70. # remove vector maps which do not contain given feature type
  71. self.tcp.SetFilter(self.__isElement)
  72. def __isElement(self, vectorName):
  73. """!Check if element should be filtered out"""
  74. try:
  75. if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1:
  76. return False
  77. except KeyError:
  78. return False
  79. return True
  80. class TreeCtrlComboPopup(wx.combo.ComboPopup):
  81. """!Create a tree ComboBox for selecting maps and other GIS elements
  82. in accessible mapsets within the current location
  83. """
  84. # overridden ComboPopup methods
  85. def Init(self):
  86. self.value = [] # for multiple is False -> len(self.value) in [0,1]
  87. self.curitem = None
  88. self.multiple = False
  89. self.type = None
  90. self.mapsets = []
  91. self.exclude = []
  92. self.SetFilter(None)
  93. def Create(self, parent):
  94. self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
  95. |wx.TR_HAS_BUTTONS
  96. |wx.TR_SINGLE
  97. |wx.TR_LINES_AT_ROOT
  98. |wx.SIMPLE_BORDER
  99. |wx.TR_FULL_ROW_HIGHLIGHT)
  100. self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
  101. self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
  102. self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.mapsetExpanded)
  103. self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.mapsetCollapsed)
  104. self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.mapsetActivated)
  105. self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, self.mapsetSelected)
  106. self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
  107. # the following dummy handler are needed to keep tree events from propagating up to
  108. # the parent GIS Manager layer tree
  109. def mapsetExpanded(self, event):
  110. pass
  111. def mapsetCollapsed(self, event):
  112. pass
  113. def mapsetActivated(self, event):
  114. pass
  115. def mapsetSelected(self, event):
  116. pass
  117. # end of dummy events
  118. def GetControl(self):
  119. return self.seltree
  120. def GetStringValue(self):
  121. str = ""
  122. for value in self.value:
  123. str += value + ","
  124. str = str.rstrip(',')
  125. return str
  126. def SetFilter(self, filter):
  127. """!Set filter for GIS elements, see e.g. VectorSelect"""
  128. self.filterElements = filter
  129. def OnPopup(self, force = False):
  130. """!Limited only for first selected"""
  131. if not force and not self.updateOnPopup:
  132. return
  133. self.GetElementList()
  134. def GetElementList(self):
  135. """!Get filtered list of GIS elements in accessible mapsets
  136. and display as tree with all relevant elements displayed
  137. beneath each mapset branch
  138. """
  139. # update list
  140. self.seltree.DeleteAllItems()
  141. self._getElementList(self.type, self.mapsets, self.exclude)
  142. if len(self.value) > 0:
  143. root = self.seltree.GetRootItem()
  144. if not root:
  145. return
  146. item = self.FindItem(root, self.value[0])
  147. try:
  148. self.seltree.EnsureVisible(item)
  149. self.seltree.SelectItem(item)
  150. except:
  151. pass
  152. def SetStringValue(self, value):
  153. # this assumes that item strings are unique...
  154. root = self.seltree.GetRootItem()
  155. if not root:
  156. return
  157. found = self.FindItem(root, value)
  158. if found:
  159. self.value.append(found)
  160. self.seltree.SelectItem(found)
  161. def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
  162. return wx.Size(minWidth, min(200, maxHeight))
  163. def _getElementList(self, element, mapsets=None, exclude=[]):
  164. """!Get list of GIS elements in accessible mapsets and display as tree
  165. with all relevant elements displayed beneath each mapset branch
  166. @param element GIS element
  167. @param mapsets list of acceptable mapsets (None for all mapsets in search path)
  168. @param exclude list of GIS elements to be excluded
  169. """
  170. # get current mapset
  171. curr_mapset = grass.gisenv()['MAPSET']
  172. # list of mapsets in current location
  173. if mapsets is None:
  174. mapsets = utils.ListOfMapsets()
  175. # map element types to g.mlist types
  176. elementdict = {'cell':'rast',
  177. 'raster':'rast',
  178. 'rast':'rast',
  179. 'raster files':'rast',
  180. 'grid3':'rast3d',
  181. 'rast3d':'rast3d',
  182. 'raster3D':'rast3d',
  183. 'raster3D files':'rast3d',
  184. 'vector':'vect',
  185. 'vect':'vect',
  186. 'binary vector files':'vect',
  187. 'dig':'oldvect',
  188. 'oldvect':'oldvect',
  189. 'old vector':'oldvect',
  190. 'dig_ascii':'asciivect',
  191. 'asciivect':'asciivect',
  192. 'asciivector':'asciivect',
  193. 'ascii vector files':'asciivect',
  194. 'icons':'icon',
  195. 'icon':'icon',
  196. 'paint icon files':'icon',
  197. 'paint/labels':'labels',
  198. 'labels':'labels',
  199. 'label':'labels',
  200. 'paint label files':'labels',
  201. 'site_lists':'sites',
  202. 'sites':'sites',
  203. 'site list':'sites',
  204. 'site list files':'sites',
  205. 'windows':'region',
  206. 'region':'region',
  207. 'region definition':'region',
  208. 'region definition files':'region',
  209. 'windows3d':'region3d',
  210. 'region3d':'region3d',
  211. 'region3D definition':'region3d',
  212. 'region3D definition files':'region3d',
  213. 'group':'group',
  214. 'imagery group':'group',
  215. 'imagery group files':'group',
  216. '3d.view':'3dview',
  217. '3dview':'3dview',
  218. '3D viewing parameters':'3dview',
  219. '3D view parameters':'3dview'}
  220. if element not in elementdict:
  221. self.AddItem(_('Not selectable element'))
  222. return
  223. # get directory tree nodes
  224. # reorder mapsets based on search path (TODO)
  225. for i in range(len(mapsets)):
  226. if i > 0 and mapsets[i] == curr_mapset:
  227. mapsets[i] = mapsets[0]
  228. mapsets[0] = curr_mapset
  229. if globalvar.have_mlist:
  230. filesdict = grass.mlist_grouped(elementdict[element])
  231. else:
  232. filesdict = grass.list_grouped(elementdict[element])
  233. first_dir = None
  234. for dir in mapsets:
  235. dir_node = self.AddItem('Mapset: ' + dir)
  236. if not first_dir:
  237. first_dir = dir_node
  238. self.seltree.SetItemTextColour(dir_node, wx.Colour(50, 50, 200))
  239. try:
  240. elem_list = filesdict[dir]
  241. elem_list.sort(key=str.lower)
  242. for elem in elem_list:
  243. if elem != '':
  244. fullqElem = elem + '@' + dir
  245. if len(exclude) > 0 and fullqElem in exclude:
  246. continue
  247. if self.filterElements:
  248. if self.filterElements(fullqElem):
  249. self.AddItem(fullqElem, parent=dir_node)
  250. else:
  251. self.AddItem(fullqElem, parent=dir_node)
  252. except:
  253. continue
  254. if self.seltree.ItemHasChildren(dir_node):
  255. sel = UserSettings.Get(group='general', key='elementListExpand',
  256. subkey='selection')
  257. collapse = True
  258. if sel == 0: # collapse all except PERMANENT and current
  259. if dir in ('PERMANENT', curr_mapset):
  260. collapse = False
  261. elif sel == 1: # collapse all except PERMANENT
  262. if dir == 'PERMANENT':
  263. collapse = False
  264. elif sel == 2: # collapse all except current
  265. if dir == curr_mapset:
  266. collapse = False
  267. elif sel == 3: # collapse all
  268. pass
  269. elif sel == 4: # expand all
  270. collapse = False
  271. if collapse:
  272. self.seltree.Collapse(dir_node)
  273. else:
  274. self.seltree.Expand(dir_node)
  275. if first_dir:
  276. # select first mapset (MSW hack)
  277. self.seltree.SelectItem(first_dir)
  278. # helpers
  279. def FindItem(self, parentItem, text):
  280. item, cookie = self.seltree.GetFirstChild(parentItem)
  281. while item:
  282. if self.seltree.GetItemText(item) == text:
  283. return item
  284. if self.seltree.ItemHasChildren(item):
  285. item = self.FindItem(item, text)
  286. item, cookie = self.seltree.GetNextChild(parentItem, cookie)
  287. return wx.TreeItemId()
  288. def AddItem(self, value, parent=None):
  289. if not parent:
  290. root = self.seltree.GetRootItem()
  291. if not root:
  292. root = self.seltree.AddRoot("<hidden root>")
  293. parent = root
  294. item = self.seltree.AppendItem(parent, text=value)
  295. return item
  296. def OnMotion(self, evt):
  297. # have the selection follow the mouse, like in a real combobox
  298. item, flags = self.seltree.HitTest(evt.GetPosition())
  299. if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
  300. self.seltree.SelectItem(item)
  301. self.curitem = item
  302. evt.Skip()
  303. def OnLeftDown(self, evt):
  304. # do the combobox selection
  305. item, flags = self.seltree.HitTest(evt.GetPosition())
  306. if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
  307. self.curitem = item
  308. if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
  309. self.value = [] # cannot select mapset item
  310. else:
  311. if self.multiple is True:
  312. # text item should be unique
  313. self.value.append(self.seltree.GetItemText(item))
  314. else:
  315. self.value = [self.seltree.GetItemText(item), ]
  316. self.Dismiss()
  317. evt.Skip()
  318. def SetData(self, **kargs):
  319. """!Set object properties"""
  320. if kargs.has_key('type'):
  321. self.type = kargs['type']
  322. if kargs.has_key('mapsets'):
  323. self.mapsets = kargs['mapsets']
  324. if kargs.has_key('exclude'):
  325. self.exclude = kargs['exclude']
  326. if kargs.has_key('multiple'):
  327. self.multiple = kargs['multiple']
  328. if kargs.has_key('updateOnPopup'):
  329. self.updateOnPopup = kargs['updateOnPopup']
  330. class VectorDBInfo:
  331. """!Class providing information about attribute tables
  332. linked to a vector map"""
  333. def __init__(self, map):
  334. self.map = map
  335. # dictionary of layer number and associated (driver, database, table)
  336. self.layers = {}
  337. # dictionary of table and associated columns (type, length, values, ids)
  338. self.tables = {}
  339. if not self.__CheckDBConnection(): # -> self.layers
  340. return
  341. self.__DescribeTables() # -> self.tables
  342. def __CheckDBConnection(self):
  343. """!Check DB connection"""
  344. nuldev = file(os.devnull, 'w+')
  345. self.layers = grass.vector_db(map=self.map, stderr=nuldev)
  346. nuldev.close()
  347. if (len(self.layers.keys()) == 0):
  348. return False
  349. return True
  350. def __DescribeTables(self):
  351. """!Describe linked tables"""
  352. for layer in self.layers.keys():
  353. # determine column names and types
  354. table = self.layers[layer]["table"]
  355. columns = {} # {name: {type, length, [values], [ids]}}
  356. i = 0
  357. for item in grass.db_describe(table = self.layers[layer]["table"],
  358. driver = self.layers[layer]["driver"],
  359. database = self.layers[layer]["database"])['cols']:
  360. name, type, length = item
  361. # FIXME: support more datatypes
  362. if type.lower() == "integer":
  363. ctype = int
  364. elif type.lower() == "double precision":
  365. ctype = float
  366. else:
  367. ctype = str
  368. columns[name.strip()] = { 'index' : i,
  369. 'type' : type.lower(),
  370. 'ctype' : ctype,
  371. 'length' : int(length),
  372. 'values' : [],
  373. 'ids' : []}
  374. i += 1
  375. # check for key column
  376. # v.db.connect -g/p returns always key column name lowercase
  377. if self.layers[layer]["key"] not in columns.keys():
  378. for col in columns.keys():
  379. if col.lower() == self.layers[layer]["key"]:
  380. self.layers[layer]["key"] = col.upper()
  381. break
  382. self.tables[table] = columns
  383. return True
  384. def Reset(self):
  385. """!Reset"""
  386. for layer in self.layers:
  387. table = self.layers[layer]["table"] # get table desc
  388. columns = self.tables[table]
  389. for name in self.tables[table].keys():
  390. self.tables[table][name]['values'] = []
  391. self.tables[table][name]['ids'] = []
  392. def GetName(self):
  393. """!Get vector name"""
  394. return self.map
  395. def GetKeyColumn(self, layer):
  396. """!Get key column of given layer
  397. @param layer vector layer number
  398. """
  399. return self.layers[layer]['key']
  400. def GetTable(self, layer):
  401. """!Get table name of given layer
  402. @param layer vector layer number
  403. """
  404. return self.layers[layer]['table']
  405. def GetDbSettings(self, layer):
  406. """!Get database settins
  407. @param layer layer number
  408. @return (driver, database)
  409. """
  410. return self.layers[layer]['driver'], self.layers[layer]['database']
  411. def GetTableDesc(self, table):
  412. """!Get table columns
  413. @param table table name
  414. """
  415. return self.tables[table]
  416. class LayerSelect(wx.Choice):
  417. """!Creates combo box for selecting data layers defined for vector.
  418. The 'layer' terminology is likely to change for GRASS 7
  419. """
  420. def __init__(self, parent,
  421. id=wx.ID_ANY, pos=wx.DefaultPosition,
  422. size=globalvar.DIALOG_LAYER_SIZE,
  423. vector=None, choices=[], all=False, default=None):
  424. super(LayerSelect, self).__init__(parent, id, pos=pos, size=size,
  425. choices=choices)
  426. self.all = all
  427. self.SetName("LayerSelect")
  428. # default value
  429. self.default = default
  430. if len(choices) > 1:
  431. return
  432. if vector:
  433. self.InsertLayers(vector)
  434. else:
  435. if all:
  436. self.SetItems(['-1', '1'])
  437. else:
  438. self.SetItems(['1'])
  439. self.SetStringSelection('1')
  440. def InsertLayers(self, vector):
  441. """!Insert layers for a vector into the layer combobox"""
  442. layerchoices = utils.GetVectorNumberOfLayers(vector)
  443. if self.all:
  444. layerchoices.insert(0, '-1')
  445. if len(layerchoices) > 1:
  446. self.SetItems(layerchoices)
  447. self.SetStringSelection('1')
  448. else:
  449. self.SetItems(['1'])
  450. self.SetStringSelection('1')
  451. if self.default:
  452. self.SetStringSelection(str(self.default))
  453. class DriverSelect(wx.ComboBox):
  454. """!Creates combo box for selecting database driver.
  455. """
  456. def __init__(self, parent, choices, value,
  457. id=wx.ID_ANY, pos=wx.DefaultPosition,
  458. size=globalvar.DIALOG_LAYER_SIZE, **kargs):
  459. super(DriverSelect, self).__init__(parent, id, value, pos, size,
  460. choices, style=wx.CB_READONLY)
  461. self.SetName("DriverSelect")
  462. self.SetStringSelection(value)
  463. class DatabaseSelect(wx.TextCtrl):
  464. """!Creates combo box for selecting database driver.
  465. """
  466. def __init__(self, parent, value='',
  467. id=wx.ID_ANY, pos=wx.DefaultPosition,
  468. size=globalvar.DIALOG_TEXTCTRL_SIZE, **kargs):
  469. super(DatabaseSelect, self).__init__(parent, id, value, pos, size)
  470. self.SetName("DatabaseSelect")
  471. class TableSelect(wx.ComboBox):
  472. """!Creates combo box for selecting attribute tables from the database
  473. """
  474. def __init__(self, parent,
  475. id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
  476. size=globalvar.DIALOG_COMBOBOX_SIZE,
  477. choices=[]):
  478. super(TableSelect, self).__init__(parent, id, value, pos, size, choices,
  479. style=wx.CB_READONLY)
  480. self.SetName("TableSelect")
  481. if not choices:
  482. self.InsertTables()
  483. def InsertTables(self, driver=None, database=None):
  484. """!Insert attribute tables into combobox"""
  485. items = []
  486. if not driver or not database:
  487. connect = grass.db_connection()
  488. driver = connect['driver']
  489. database = connect['database']
  490. ret = gcmd.RunCommand('db.tables',
  491. flags = 'p',
  492. read = True,
  493. driver = driver,
  494. database = database)
  495. if ret:
  496. for table in ret.splitlines():
  497. items.append(table)
  498. self.SetItems(items)
  499. self.SetValue('')
  500. class ColumnSelect(wx.ComboBox):
  501. """
  502. Creates combo box for selecting columns in the attribute table for a vector.
  503. The 'layer' terminology is likely to change for GRASS 7
  504. """
  505. def __init__(self, parent,
  506. id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
  507. size=globalvar.DIALOG_COMBOBOX_SIZE, vector=None,
  508. layer=1, choices=[]):
  509. super(ColumnSelect, self).__init__(parent, id, value, pos, size, choices,
  510. style=wx.CB_READONLY)
  511. self.SetName("ColumnSelect")
  512. if vector:
  513. self.InsertColumns(vector, layer)
  514. def InsertColumns(self, vector, layer, excludeKey = False, type = None):
  515. """!Insert columns for a vector attribute table into the columns combobox
  516. @param vector vector name
  517. @param layer vector layer number
  518. @param excludeKey exclude key column from the list?
  519. @param type only columns of given type (given as list)
  520. """
  521. dbInfo = VectorDBInfo(vector)
  522. try:
  523. table = dbInfo.GetTable(int(layer))
  524. columnchoices = dbInfo.GetTableDesc(table)
  525. keyColumn = dbInfo.GetKeyColumn(int(layer))
  526. columns = len(columnchoices.keys()) * ['']
  527. for key, val in columnchoices.iteritems():
  528. columns[val['index']] = key
  529. if excludeKey: # exclude key column
  530. columns.remove(keyColumn)
  531. if type: # only selected column types
  532. for key, value in columnchoices.iteritems():
  533. if value['type'] not in type:
  534. columns.remove(key)
  535. except (KeyError, ValueError):
  536. columns = []
  537. self.SetItems(columns)
  538. self.SetValue('')
  539. def InsertTableColumns(self, table, driver=None, database=None):
  540. """!Insert table columns"""
  541. columns = []
  542. ret = gcmd.RunCommand('db.columns',
  543. read = True,
  544. driver = driver,
  545. database = database,
  546. table = table)
  547. if ret:
  548. columns = ret.splitlines()
  549. self.SetItems(columns)
  550. class LocationSelect(wx.ComboBox):
  551. """!Widget for selecting GRASS location"""
  552. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  553. gisdbase = None, **kwargs):
  554. super(LocationSelect, self).__init__(parent, id, size = size,
  555. style = wx.CB_READONLY, **kwargs)
  556. self.SetName("LocationSelect")
  557. if not gisdbase:
  558. self.gisdbase = grass.gisenv()['GISDBASE']
  559. else:
  560. self.gisdbase = gisdbase
  561. self.SetItems(utils.GetListOfLocations(self.gisdbase))
  562. class MapsetSelect(wx.ComboBox):
  563. """!Widget for selecting GRASS mapset"""
  564. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  565. gisdbase = None, location = None, setItems = True, **kwargs):
  566. super(MapsetSelect, self).__init__(parent, id, size = size,
  567. style = wx.CB_READONLY, **kwargs)
  568. self.SetName("MapsetSelect")
  569. if not gisdbase:
  570. self.gisdbase = grass.gisenv()['GISDBASE']
  571. else:
  572. self.gisdbase = gisdbase
  573. if not location:
  574. self.location = grass.gisenv()['LOCATION_NAME']
  575. else:
  576. self.location = location
  577. if setItems:
  578. self.SetItems(utils.GetListOfMapsets(self.gisdbase, self.location, selectable = True)) # selectable