GRASS Programmer's Manual  6.4.4(2014)-r
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
gselect.py
Go to the documentation of this file.
1 """!
2 @package gui_core.gselect
3 
4 @brief Custom control that selects elements
5 
6 Classes:
7  - gselect::Select
8  - gselect::VectorSelect
9  - gselect::TreeCrtlComboPopup
10  - gselect::VectorDBInfo
11  - gselect::LayerSelect
12  - gselect::DriverSelect
13  - gselect::DatabaseSelect
14  - gselect::TableSelect
15  - gselect::ColumnSelect
16  - gselect::DbaseSelect
17  - gselect::LocationSelect
18  - gselect::MapsetSelect
19  - gselect::SubGroupSelect
20  - gselect::FormatSelect
21  - gselect::GdalSelect
22  - gselect::ProjSelect
23  - gselect::ElementSelect
24 
25 (C) 2007-2011 by the GRASS Development Team
26 
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 
30 @author Michael Barton
31 @author Martin Landa <landa.martin gmail.com>
32 @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
33 """
34 
35 import os
36 import sys
37 import glob
38 
39 import wx
40 import wx.combo
41 import wx.lib.filebrowsebutton as filebrowse
42 from wx.lib.newevent import NewEvent
43 
44 from core import globalvar
45 
46 import grass.script as grass
47 from grass.script import task as gtask
48 
49 from core.gcmd import RunCommand, GError, GMessage
50 from core.utils import GetListOfLocations, GetListOfMapsets, GetFormats
51 from core.utils import GetSettingsPath, GetValidLayerName, ListSortLower, GetAllVectorLayers
52 from core.settings import UserSettings
53 from core.debug import Debug
54 
55 wxGdalSelect, EVT_GDALSELECT = NewEvent()
56 
57 class Select(wx.combo.ComboCtrl):
58  def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
59  type = None, multiple = False, mapsets = None,
60  updateOnPopup = True, onPopup = None,
61  fullyQualified = True):
62  """!Custom control to create a ComboBox with a tree control to
63  display and select GIS elements within acessible mapsets.
64  Elements can be selected with mouse. Can allow multiple
65  selections, when argument multiple=True. Multiple selections
66  are separated by commas.
67 
68  @param type type of GIS elements ('raster, 'vector', ...)
69  @param multiple multiple input allowed?
70  @param mapsets force list of mapsets (otherwise search path)
71  @param updateOnPopup True for updating list of elements on popup
72  @param onPopup function to be called on Popup
73  @param fullyQualified True to provide fully qualified names (map@mapset)
74  """
75  wx.combo.ComboCtrl.__init__(self, parent=parent, id=id, size=size)
76  self.GetChildren()[0].SetName("Select")
77  self.GetChildren()[0].type = type
78 
80  self.SetPopupControl(self.tcp)
81  self.SetPopupExtents(0, 100)
82  if type:
83  self.tcp.SetData(type = type, mapsets = mapsets,
84  multiple = multiple,
85  updateOnPopup = updateOnPopup, onPopup = onPopup,
86  fullyQualified = fullyQualified)
87  self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp)
88 
89  def OnKeyUp(self, event):
90  """!Shows popupwindow if down arrow key is released"""
91  if event.GetKeyCode() == wx.WXK_DOWN and not self.IsPopupShown():
92  self.ShowPopup()
93  else:
94  event.Skip()
95 
96  def SetElementList(self, type, mapsets = None):
97  """!Set element list
98 
99  @param type GIS element type
100  @param mapsets list of acceptable mapsets (None for all in search path)
101  """
102  self.tcp.SetData(type = type, mapsets = mapsets)
103 
104  def GetElementList(self):
105  """!Load elements"""
106  self.tcp.GetElementList()
107 
108  def SetType(self, etype, multiple = False, mapsets = None,
109  updateOnPopup = True, onPopup = None):
110  """!Param set element type for widget
111 
112  @param etype element type, see gselect.ElementSelect
113  """
114  self.tcp.SetData(type = etype, mapsets = mapsets,
115  multiple = multiple,
116  updateOnPopup = updateOnPopup, onPopup = onPopup)
117 
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 
124  @ftype filter vector maps based on feature type
125  """
126  Select.__init__(self, parent = parent, id = wx.ID_ANY,
127  type = 'vector', **kwargs)
128 
129  self.ftype = ftype
130 
131  # remove vector maps which do not contain given feature type
132  self.tcp.SetFilter(self._isElement)
133 
134  def _isElement(self, vectorName):
135  """!Check if element should be filtered out"""
136  try:
137  if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1:
138  return False
139  except KeyError:
140  return False
141 
142  return True
143 
144 class TreeCtrlComboPopup(wx.combo.ComboPopup):
145  """!Create a tree ComboBox for selecting maps and other GIS elements
146  in accessible mapsets within the current location
147  """
148  # overridden ComboPopup methods
149  def Init(self):
150  self.value = [] # for multiple is False -> len(self.value) in [0,1]
151  self.curitem = None
152  self.multiple = False
153  self.type = None
154  self.mapsets = None
155  self.updateOnPopup = True
156  self.onPopup = None
157  self.fullyQualified = True
158 
159  self.SetFilter(None)
160 
161  def Create(self, parent):
162  self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
163  |wx.TR_HAS_BUTTONS
164  |wx.TR_SINGLE
165  |wx.TR_LINES_AT_ROOT
166  |wx.SIMPLE_BORDER
167  |wx.TR_FULL_ROW_HIGHLIGHT)
168  self.seltree.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
169  self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
170  self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
171  # the following dummy handler are needed to keep tree events
172  # from propagating up to the parent GIS Manager layer tree
173  self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, lambda x: None)
174  self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, lambda x: None)
175  self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, lambda x: None)
176  self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, lambda x: None)
177  self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
178  self.seltree.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda x: None)
179  self.seltree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, lambda x: None)
180 
181  def GetControl(self):
182  return self.seltree
183 
184  def GetStringValue(self):
185  """!Get value as a string separated by commas"""
186  return ','.join(self.value)
187 
188  def SetFilter(self, filter):
189  """!Set filter for GIS elements, see e.g. VectorSelect"""
190  self.filterElements = filter
191 
192  def OnPopup(self, force = False):
193  """!Limited only for first selected"""
194  if not force and not self.updateOnPopup:
195  return
196  if self.onPopup:
197  selected, exclude = self.onPopup(self.type)
198  else:
199  selected = None
200  exclude = False
201 
202  self.GetElementList(selected, exclude)
203 
204  # selects map starting according to written text
205  inputText = self.GetCombo().GetValue().strip()
206  if inputText:
207  root = self.seltree.GetRootItem()
208  match = self.FindItem(root, inputText, startLetters = True)
209  self.seltree.EnsureVisible(match)
210  self.seltree.SelectItem(match)
211 
212 
213  def GetElementList(self, elements = None, exclude = False):
214  """!Get filtered list of GIS elements in accessible mapsets
215  and display as tree with all relevant elements displayed
216  beneath each mapset branch
217  """
218  # update list
219  self.seltree.DeleteAllItems()
220  self._getElementList(self.type, self.mapsets, elements, exclude)
221 
222  if len(self.value) > 0:
223  root = self.seltree.GetRootItem()
224  if not root:
225  return
226  item = self.FindItem(root, self.value[0])
227  try:
228  self.seltree.EnsureVisible(item)
229  self.seltree.SelectItem(item)
230  except:
231  pass
232 
233  def SetStringValue(self, value):
234  # this assumes that item strings are unique...
235  root = self.seltree.GetRootItem()
236  if not root:
237  return
238  found = self.FindItem(root, value)
239  winValue = self.GetCombo().GetValue().strip(',')
240  self.value = []
241  if winValue:
242  self.value = winValue.split(',')
243 
244  if found:
245  self.value.append(found)
246  self.seltree.SelectItem(found)
247 
248  def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
249  """!Reads UserSettings to get height (which was 200 in old implementation).
250  """
251  height = UserSettings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
252  return wx.Size(minWidth, min(height, maxHeight))
253 
254  def _getElementList(self, element, mapsets = None, elements = None, exclude = False):
255  """!Get list of GIS elements in accessible mapsets and display as tree
256  with all relevant elements displayed beneath each mapset branch
257 
258  @param element GIS element
259  @param mapsets list of acceptable mapsets (None for all mapsets in search path)
260  @param elements list of forced GIS elements
261  @param exclude True to exclude, False for forcing the list (elements)
262  """
263  # get current mapset
264  curr_mapset = grass.gisenv()['MAPSET']
265 
266  # map element types to g.mlist types
267  elementdict = {'cell':'rast',
268  'raster':'rast',
269  'rast':'rast',
270  'raster files':'rast',
271  'grid3':'rast3d',
272  'rast3d':'rast3d',
273  '3d-raster':'rast3d',
274  'raster3d':'rast3d',
275  'raster3D files':'rast3d',
276  'vector':'vect',
277  'vect':'vect',
278  'binary vector files':'vect',
279  'dig':'oldvect',
280  'oldvect':'oldvect',
281  'old vector':'oldvect',
282  'dig_ascii':'asciivect',
283  'asciivect':'asciivect',
284  'asciivector':'asciivect',
285  'ascii vector files':'asciivect',
286  'icons':'icon',
287  'icon':'icon',
288  'paint icon files':'icon',
289  'paint/labels':'labels',
290  'labels':'labels',
291  'label':'labels',
292  'paint label files':'labels',
293  'site_lists':'sites',
294  'sites':'sites',
295  'site list':'sites',
296  'site list files':'sites',
297  'windows':'region',
298  'region':'region',
299  'region definition':'region',
300  'region definition files':'region',
301  'windows3d':'region3d',
302  'region3d':'region3d',
303  'region3D definition':'region3d',
304  'region3D definition files':'region3d',
305  'group':'group',
306  'imagery group':'group',
307  'imagery group files':'group',
308  '3d.view':'3dview',
309  '3dview':'3dview',
310  '3D viewing parameters':'3dview',
311  '3D view parameters':'3dview'}
312 
313  if element not in elementdict:
314  self.AddItem(_('Not selectable element'))
315  return
316 
317  if globalvar.have_mlist:
318  filesdict = grass.mlist_grouped(elementdict[element],
319  check_search_path = False)
320  else:
321  filesdict = grass.list_grouped(elementdict[element],
322  check_search_path = False)
323 
324  # list of mapsets in current location
325  if mapsets is None:
326  mapsets = grass.mapsets(search_path = True)
327 
328  # current mapset first
329  if curr_mapset in mapsets and mapsets[0] != curr_mapset:
330  mapsets.remove(curr_mapset)
331  mapsets.insert(0, curr_mapset)
332 
333  first_mapset = None
334  for mapset in mapsets:
335  mapset_node = self.AddItem(_('Mapset') + ': ' + mapset)
336  if not first_mapset:
337  first_mapset = mapset_node
338 
339  self.seltree.SetItemTextColour(mapset_node, wx.Colour(50, 50, 200))
340  if mapset not in filesdict:
341  continue
342  try:
343  elem_list = filesdict[mapset]
344  elem_list.sort()
345  for elem in elem_list:
346  if elem != '':
347  fullqElem = elem + '@' + mapset
348  if elements is not None:
349  if (exclude and fullqElem in elements) or \
350  (not exclude and fullqElem not in elements):
351  continue
352 
353  if self.filterElements:
354  if self.filterElements(fullqElem):
355  self.AddItem(elem, parent = mapset_node)
356  else:
357  self.AddItem(elem, parent = mapset_node)
358  except StandardError, e:
359  sys.stderr.write(_("GSelect: invalid item: %s") % e)
360  continue
361 
362  if self.seltree.ItemHasChildren(mapset_node):
363  sel = UserSettings.Get(group='appearance', key='elementListExpand',
364  subkey='selection')
365  collapse = True
366 
367  if sel == 0: # collapse all except PERMANENT and current
368  if mapset in ('PERMANENT', curr_mapset):
369  collapse = False
370  elif sel == 1: # collapse all except PERMANENT
371  if mapset == 'PERMANENT':
372  collapse = False
373  elif sel == 2: # collapse all except current
374  if mapset == curr_mapset:
375  collapse = False
376  elif sel == 3: # collapse all
377  pass
378  elif sel == 4: # expand all
379  collapse = False
380 
381  if collapse:
382  self.seltree.Collapse(mapset_node)
383  else:
384  self.seltree.Expand(mapset_node)
385 
386  if first_mapset:
387  # select first mapset (MSW hack)
388  self.seltree.SelectItem(first_mapset)
389 
390  # helpers
391  def FindItem(self, parentItem, text, startLetters = False):
392  """!Finds item with given name or starting with given text"""
393  startletters = startLetters
394  item, cookie = self.seltree.GetFirstChild(parentItem)
395  while wx.TreeItemId.IsOk(item):
396  if self.seltree.GetItemText(item) == text:
397  return item
398  if self.seltree.ItemHasChildren(item):
399  item = self.FindItem(item, text, startLetters = startletters)
400  if wx.TreeItemId.IsOk(item):
401  return item
402  elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]):
403  return item
404  item, cookie = self.seltree.GetNextChild(parentItem, cookie)
405  return wx.TreeItemId()
406 
407  def AddItem(self, value, parent=None):
408  if not parent:
409  root = self.seltree.GetRootItem()
410  if not root:
411  root = self.seltree.AddRoot("<hidden root>")
412  parent = root
413 
414  item = self.seltree.AppendItem(parent, text=value)
415  return item
416 
417  # able to recieve only wx.EVT_KEY_UP
418  def OnKeyUp(self, event):
419  """!Enables to select items using keyboard"""
420 
421  item = self.seltree.GetSelection()
422  if event.GetKeyCode() == wx.WXK_DOWN:
423  self.seltree.SelectItem(self.seltree.GetNextVisible(item))
424 
425  # problem with GetPrevVisible
426  elif event.GetKeyCode() == wx.WXK_UP:
427  if self.seltree.ItemHasChildren(item) and self.seltree.IsExpanded(self.seltree.GetPrevSibling(item)):
428  itemPrev = self.seltree.GetLastChild(self.seltree.GetPrevSibling(item))
429  else:
430  itemPrev = self.seltree.GetPrevSibling(item)
431  if not wx.TreeItemId.IsOk(itemPrev):
432  itemPrev = self.seltree.GetItemParent(item)
433  if item == self.seltree.GetFirstChild(self.seltree.GetRootItem())[0]:
434  itemPrev = item
435  self.seltree.SelectItem(itemPrev)
436 
437  # selects first item starting with the written text in next mapset
438  elif event.GetKeyCode() == wx.WXK_TAB:
439  selected = self.seltree.GetSelection()
440  if self.seltree.ItemHasChildren(selected):
441  parent = selected
442  else:
443  parent = self.seltree.GetItemParent(selected)
444  nextSibling = self.seltree.GetNextSibling(parent)
445  if wx.TreeItemId.IsOk(nextSibling):
446  match = self.FindItem(nextSibling, self.GetCombo().GetValue().strip(), True)
447  else:
448  match = self.FindItem(self.seltree.GetFirstChild(self.seltree.GetItemParent(parent))[0],
449  self.GetCombo().GetValue().strip(), True)
450  self.seltree.SelectItem(match)
451 
452  elif event.GetKeyCode() == wx.WXK_RIGHT:
453  if self.seltree.ItemHasChildren(item):
454  self.seltree.Expand(item)
455 
456  elif event.GetKeyCode() == wx.WXK_LEFT:
457  if self.seltree.ItemHasChildren(item):
458  self.seltree.Collapse(item)
459 
460  elif event.GetKeyCode() == wx.WXK_ESCAPE:
461  self.Dismiss()
462 
463  elif event.GetKeyCode() == wx.WXK_RETURN:
464  if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
465  self.value = []
466  else:
467  mapsetItem = self.seltree.GetItemParent(item)
468  fullName = self.seltree.GetItemText(item)
469  if self.fullyQualified:
470  fullName += '@' + self.seltree.GetItemText(mapsetItem).split(':', -1)[1].strip()
471 
472  if self.multiple is True:
473  # text item should be unique
474  self.value.append(fullName)
475  else:
476  self.value = [fullName]
477 
478  self.Dismiss()
479 
480  def OnMotion(self, evt):
481  """!Have the selection follow the mouse, like in a real combobox
482  """
483  item, flags = self.seltree.HitTest(evt.GetPosition())
484  if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
485  self.seltree.SelectItem(item)
486  self.curitem = item
487  evt.Skip()
488 
489  def OnLeftDown(self, evt):
490  """!Do the combobox selection
491  """
492  item, flags = self.seltree.HitTest(evt.GetPosition())
493  if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
494  self.curitem = item
495 
496  if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
497  self.value = [] # cannot select mapset item
498  else:
499  mapsetItem = self.seltree.GetItemParent(item)
500  fullName = self.seltree.GetItemText(item)
501  if self.fullyQualified:
502  fullName += '@' + self.seltree.GetItemText(mapsetItem).split(':', -1)[1].strip()
503 
504  if self.multiple is True:
505  # text item should be unique
506  self.value.append(fullName)
507  else:
508  self.value = [fullName]
509 
510  self.Dismiss()
511 
512  evt.Skip()
513 
514  def SetData(self, **kargs):
515  """!Set object properties"""
516  if 'type' in kargs:
517  self.type = kargs['type']
518  if 'mapsets' in kargs:
519  self.mapsets = kargs['mapsets']
520  if 'multiple' in kargs:
521  self.multiple = kargs['multiple']
522  if 'updateOnPopup' in kargs:
523  self.updateOnPopup = kargs['updateOnPopup']
524  if 'onPopup' in kargs:
525  self.onPopup = kargs['onPopup']
526  if 'fullyQualified' in kargs:
527  self.fullyQualified = kargs['fullyQualified']
528 
529  def GetType(self):
530  """!Get element type
531  """
532  return self.type
533 
535  """!Class providing information about attribute tables
536  linked to a vector map"""
537  def __init__(self, map):
538  self.map = map
539 
540  # dictionary of layer number and associated (driver, database, table)
541  self.layers = {}
542  # dictionary of table and associated columns (type, length, values, ids)
543  self.tables = {}
544 
545  if not self._CheckDBConnection(): # -> self.layers
546  return
547 
548  self._DescribeTables() # -> self.tables
549 
550  def _CheckDBConnection(self):
551  """!Check DB connection"""
552  nuldev = file(os.devnull, 'w+')
553  self.layers = grass.vector_db(map=self.map, stderr=nuldev)
554  nuldev.close()
555 
556  if (len(self.layers.keys()) == 0):
557  return False
558 
559  return True
560 
561  def _DescribeTables(self):
562  """!Describe linked tables"""
563  for layer in self.layers.keys():
564  # determine column names and types
565  table = self.layers[layer]["table"]
566  columns = {} # {name: {type, length, [values], [ids]}}
567  i = 0
568  Debug.msg(1, "gselect.VectorDBInfo._DescribeTables(): table=%s driver=%s database=%s" % \
569  (self.layers[layer]["table"], self.layers[layer]["driver"],
570  self.layers[layer]["database"]))
571  for item in grass.db_describe(table = self.layers[layer]["table"],
572  driver = self.layers[layer]["driver"],
573  database = self.layers[layer]["database"])['cols']:
574  name, type, length = item
575  # FIXME: support more datatypes
576  if type.lower() == "integer":
577  ctype = int
578  elif type.lower() == "double precision":
579  ctype = float
580  else:
581  ctype = str
582 
583  columns[name.strip()] = { 'index' : i,
584  'type' : type.lower(),
585  'ctype' : ctype,
586  'length' : int(length),
587  'values' : [],
588  'ids' : []}
589  i += 1
590 
591  # check for key column
592  # v.db.connect -g/p returns always key column name lowercase
593  if self.layers[layer]["key"] not in columns.keys():
594  for col in columns.keys():
595  if col.lower() == self.layers[layer]["key"]:
596  self.layers[layer]["key"] = col.upper()
597  break
598 
599  self.tables[table] = columns
600 
601  return True
602 
603  def Reset(self):
604  """!Reset"""
605  for layer in self.layers:
606  table = self.layers[layer]["table"] # get table desc
607  columns = self.tables[table]
608  for name in self.tables[table].keys():
609  self.tables[table][name]['values'] = []
610  self.tables[table][name]['ids'] = []
611 
612  def GetName(self):
613  """!Get vector name"""
614  return self.map
615 
616  def GetKeyColumn(self, layer):
617  """!Get key column of given layer
618 
619  @param layer vector layer number
620  """
621  return str(self.layers[layer]['key'])
622 
623  def GetTable(self, layer):
624  """!Get table name of given layer
625 
626  @param layer vector layer number
627  """
628  return self.layers[layer]['table']
629 
630  def GetDbSettings(self, layer):
631  """!Get database settins
632 
633  @param layer layer number
634 
635  @return (driver, database)
636  """
637  return self.layers[layer]['driver'], self.layers[layer]['database']
638 
639  def GetTableDesc(self, table):
640  """!Get table columns
641 
642  @param table table name
643  """
644  return self.tables[table]
645 
646 class LayerSelect(wx.ComboBox):
647  """!Creates combo box for selecting data layers defined for vector.
648  """
649  def __init__(self, parent,
650  id = wx.ID_ANY, pos = wx.DefaultPosition,
651  size = globalvar.DIALOG_LAYER_SIZE,
652  vector = None, choices = [], initial = [], default = None):
653 
654  super(LayerSelect, self).__init__(parent, id, pos=pos, size=size,
655  choices=choices)
656 
657  self.parent = parent
658  self.initial = initial
659 
660  self.SetName("LayerSelect")
661 
662  # default value
663  self.default = default
664  self.current = None
665  self.Bind(wx.EVT_COMBOBOX, self._selectionChanged)
666 
667  self.InsertLayers(vector = vector)
668 
669  def InsertLayers(self, vector):
670  """!Insert layers for a vector into the layer combobox
671 
672  @param vector name of vector map
673  """
674  if vector:
675  layers = GetAllVectorLayers(vector)
676  else:
677  layers = list()
678 
679  for layer in self.initial:
680  if layer in layers:
681  continue
682  layers.append(layer)
683 
684  if self.default:
685  if len(layers) == 0:
686  layers.insert(0, str(self.default))
687  elif self.default not in layers:
688  layers.append(self.default)
689 
690  if len(layers) >= 1:
691  self.SetItems(layers)
692 
693  self.Select()
694 
695  def _selectionChanged(self, event):
696  """!Selection changed, store value."""
697  self.current = self.GetValue()
698  event.Skip()
699 
700  def Select(self):
701  """!Select value (currently selected or default)"""
702  items = self.GetItems()
703  if items:
704  if self.current is not None and self.current in items:
705  self.SetStringSelection(self.current)
706  elif self.default:
707  self.SetStringSelection(str(self.default))
708  else:
709  self.SetSelection(0)
710  else:
711  self.SetValue('')
712 
713 class DriverSelect(wx.ComboBox):
714  """!Creates combo box for selecting database driver.
715  """
716  def __init__(self, parent, choices, value,
717  id=wx.ID_ANY, pos=wx.DefaultPosition,
718  size=globalvar.DIALOG_LAYER_SIZE, **kargs):
719 
720  super(DriverSelect, self).__init__(parent, id, value, pos, size,
721  choices, style=wx.CB_READONLY)
722 
723  self.SetName("DriverSelect")
724 
725  self.SetStringSelection(value)
726 
727 class DatabaseSelect(wx.TextCtrl):
728  """!Creates combo box for selecting database driver.
729  """
730  def __init__(self, parent, value='',
731  id=wx.ID_ANY, pos=wx.DefaultPosition,
732  size=globalvar.DIALOG_TEXTCTRL_SIZE, **kargs):
733 
734  super(DatabaseSelect, self).__init__(parent, id, value, pos, size)
735 
736  self.SetName("DatabaseSelect")
737 
738 class TableSelect(wx.ComboBox):
739  """!Creates combo box for selecting attribute tables from the database
740  """
741  def __init__(self, parent,
742  id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
743  size=globalvar.DIALOG_COMBOBOX_SIZE,
744  choices=[]):
745 
746  super(TableSelect, self).__init__(parent, id, value, pos, size, choices,
747  style=wx.CB_READONLY)
748 
749  self.SetName("TableSelect")
750 
751  if not choices:
752  self.InsertTables()
753 
754  def InsertTables(self, driver=None, database=None):
755  """!Insert attribute tables into combobox"""
756  items = []
757 
758  if not driver or not database:
759  connect = grass.db_connection()
760 
761  driver = connect['driver']
762  database = connect['database']
763 
764  ret = RunCommand('db.tables',
765  flags = 'p',
766  read = True,
767  driver = driver,
768  database = database)
769 
770  if ret:
771  for table in ret.splitlines():
772  items.append(table)
773 
774  self.SetItems(items)
775  self.SetValue('')
776 
777 class ColumnSelect(wx.ComboBox):
778  """!Creates combo box for selecting columns in the attribute table
779  for a vector map.
780 
781  @param parent window parent
782  @param id window id
783  @param value default value
784  @param size window size
785  @param vector vector map name
786  @param layer layer number
787  @param param parameters list (see menuform.py)
788  @param **kwags wx.ComboBox parameters
789  """
790  def __init__(self, parent, id = wx.ID_ANY, value = '',
791  size=globalvar.DIALOG_COMBOBOX_SIZE,
792  vector = None, layer = 1, param = None, **kwargs):
793  self.defaultValue = value
794  self.param = param
795 
796  super(ColumnSelect, self).__init__(parent, id, value, size = size, **kwargs)
797  self.SetName("ColumnSelect")
798 
799  if vector:
800  self.InsertColumns(vector, layer)
801 
802  def InsertColumns(self, vector, layer, excludeKey = False, excludeCols = None, type = None, dbInfo = None):
803  """!Insert columns for a vector attribute table into the columns combobox
804 
805  @param vector vector name
806  @param layer vector layer number
807  @param excludeKey exclude key column from the list?
808  @param excludeCols list of columns to be removed from the list
809  @param type only columns of given type (given as list)
810  """
811  if not dbInfo:
812  dbInfo = VectorDBInfo(vector)
813 
814  try:
815  table = dbInfo.GetTable(int(layer))
816  columnchoices = dbInfo.GetTableDesc(table)
817  keyColumn = dbInfo.GetKeyColumn(int(layer))
818  columns = len(columnchoices.keys()) * ['']
819  for key, val in columnchoices.iteritems():
820  columns[val['index']] = key
821  if excludeKey: # exclude key column
822  columns.remove(keyColumn)
823  if excludeCols: # exclude key column
824  for key in columnchoices.iterkeys():
825  if key in excludeCols:
826  columns.remove(key)
827  if type: # only selected column types
828  for key, value in columnchoices.iteritems():
829  if value['type'] not in type:
830  try:
831  columns.remove(key)
832  except ValueError:
833  pass
834  except (KeyError, ValueError):
835  columns = list()
836 
837  self.SetItems(columns)
838  self.SetValue(self.defaultValue)
839 
840  if self.param:
841  value = self.param.get('value', '')
842  if value != '' and value in columns:
843  self.SetValue(value)
844 
845  def InsertTableColumns(self, table, driver=None, database=None):
846  """!Insert table columns
847 
848  @param table table name
849  @param driver driver name
850  @param database database name
851  """
852  columns = list()
853 
854  ret = RunCommand('db.columns',
855  read = True,
856  driver = driver,
857  database = database,
858  table = table)
859 
860  if ret:
861  columns = ret.splitlines()
862 
863  self.SetItems(columns)
864  self.SetValue(self.defaultValue)
865 
866  if self.param:
867  value = self.param.get('value', '')
868  if value != '' and value in columns:
869  self.SetValue(value)
870 
871 class DbaseSelect(wx.lib.filebrowsebutton.DirBrowseButton):
872  """!Widget for selecting GRASS Database"""
873  def __init__(self, parent, **kwargs):
874  super(DbaseSelect, self).__init__(parent, id = wx.ID_ANY,
875  size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
876  dialogTitle = _('Choose GIS Data Directory'),
877  buttonText = _('Browse'),
878  startDirectory = grass.gisenv()['GISDBASE'],
879  **kwargs)
880 
881 class LocationSelect(wx.ComboBox):
882  """!Widget for selecting GRASS location"""
883  def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
884  gisdbase = None, **kwargs):
885  super(LocationSelect, self).__init__(parent, id, size = size,
886  style = wx.CB_READONLY, **kwargs)
887  self.SetName("LocationSelect")
888 
889  if not gisdbase:
890  self.gisdbase = grass.gisenv()['GISDBASE']
891  else:
892  self.gisdbase = gisdbase
893 
894  self.SetItems(GetListOfLocations(self.gisdbase))
895 
896  def UpdateItems(self, dbase):
897  """!Update list of locations
898 
899  @param dbase path to GIS database
900  """
901  self.gisdbase = dbase
902  if dbase:
903  self.SetItems(GetListOfLocations(self.gisdbase))
904  else:
905  self.SetItems([])
906 
907 class MapsetSelect(wx.ComboBox):
908  """!Widget for selecting GRASS mapset"""
909  def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
910  gisdbase = None, location = None, setItems = True,
911  searchPath = False, skipCurrent = False, **kwargs):
912  super(MapsetSelect, self).__init__(parent, id, size = size,
913  style = wx.CB_READONLY, **kwargs)
914  self.searchPath = searchPath
915  self.skipCurrent = skipCurrent
916 
917  self.SetName("MapsetSelect")
918  if not gisdbase:
919  self.gisdbase = grass.gisenv()['GISDBASE']
920  else:
921  self.gisdbase = gisdbase
922 
923  if not location:
924  self.location = grass.gisenv()['LOCATION_NAME']
925  else:
926  self.location = location
927 
928  if setItems:
929  self.SetItems(self._getMapsets())
930 
931  def UpdateItems(self, location, dbase = None):
932  """!Update list of mapsets for given location
933 
934  @param dbase path to GIS database (None to use currently selected)
935  @param location name of location
936  """
937  if dbase:
938  self.gisdbase = dbase
939  self.location = location
940  if location:
941  self.SetItems(self._getMapsets())
942  else:
943  self.SetItems([])
944 
945  def _getMapsets(self):
946  if self.searchPath:
947  mlist = RunCommand('g.mapsets',
948  read = True, flags = 'p',
949  fs = 'newline').splitlines()
950  else:
951  mlist = GetListOfMapsets(self.gisdbase, self.location,
952  selectable = False)
953 
954  gisenv = grass.gisenv()
955  if self.skipCurrent and \
956  gisenv['LOCATION_NAME'] == self.location and \
957  gisenv['MAPSET'] in mlist:
958  mlist.remove(gisenv['MAPSET'])
959 
960  return mlist
961 
962 class SubGroupSelect(wx.ComboBox):
963  """!Widget for selecting subgroups"""
964  def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
965  **kwargs):
966  super(SubGroupSelect, self).__init__(parent, id, size = size,
967  **kwargs)
968  self.SetName("SubGroupSelect")
969 
970  def Insert(self, group):
971  """!Insert subgroups for defined group"""
972  if not group:
973  return
974  gisenv = grass.gisenv()
975  try:
976  name, mapset = group.split('@', 1)
977  except ValueError:
978  name = group
979  mapset = gisenv['MAPSET']
980 
981  path = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'], mapset,
982  'group', name, 'subgroup')
983  try:
984  self.SetItems(os.listdir(path))
985  except OSError:
986  self.SetItems([])
987  self.SetValue('')
988 
989 class FormatSelect(wx.Choice):
990  def __init__(self, parent, ogr = False,
991  sourceType = None, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
992  **kwargs):
993  """!Widget for selecting external (GDAL/OGR) format
994 
995  @param parent parent window
996  @param sourceType source type ('file', 'directory', 'database', 'protocol') or None
997  @param ogr True for OGR otherwise GDAL
998  """
999  super(FormatSelect, self).__init__(parent, id, size = size,
1000  **kwargs)
1001  self.SetName("FormatSelect")
1002 
1003  if ogr:
1004  ftype = 'ogr'
1005  else:
1006  ftype = 'gdal'
1007 
1008  formats = list()
1009  for f in GetFormats()[ftype].values():
1010  formats += f
1011  self.SetItems(formats)
1012 
1013  def GetExtension(self, name):
1014  """!Get file extension by format name"""
1015  formatToExt = {
1016  # raster
1017  'GeoTIFF' : 'tif',
1018  'Erdas Imagine Images (.img)' : 'img',
1019  'Ground-based SAR Applications Testbed File Format (.gff)' : 'gff',
1020  'Arc/Info Binary Grid' : 'adf',
1021  'Portable Network Graphics' : 'png',
1022  'JPEG JFIF' : 'jpg',
1023  'Japanese DEM (.mem)' : 'mem',
1024  'Graphics Interchange Format (.gif)' : 'gif',
1025  'X11 PixMap Format' : 'xpm',
1026  'MS Windows Device Independent Bitmap' : 'bmp',
1027  'SPOT DIMAP' : 'dim',
1028  'RadarSat 2 XML Product' : 'xml',
1029  'EarthWatch .TIL' : 'til',
1030  'ERMapper .ers Labelled' : 'ers',
1031  'ERMapper Compressed Wavelets' : 'ecw',
1032  'GRIdded Binary (.grb)' : 'grb',
1033  'EUMETSAT Archive native (.nat)' : 'nat',
1034  'Idrisi Raster A.1' : 'rst',
1035  'Golden Software ASCII Grid (.grd)' : 'grd',
1036  'Golden Software Binary Grid (.grd)' : 'grd',
1037  'Golden Software 7 Binary Grid (.grd)' : 'grd',
1038  'R Object Data Store' : 'r',
1039  'USGS DOQ (Old Style)' : 'doq',
1040  'USGS DOQ (New Style)' : 'doq',
1041  'ENVI .hdr Labelled' : 'hdr',
1042  'ESRI .hdr Labelled' : 'hdr',
1043  'Generic Binary (.hdr Labelled)' : 'hdr',
1044  'PCI .aux Labelled' : 'aux',
1045  'EOSAT FAST Format' : 'fst',
1046  'VTP .bt (Binary Terrain) 1.3 Format' : 'bt',
1047  'FARSITE v.4 Landscape File (.lcp)' : 'lcp',
1048  'Swedish Grid RIK (.rik)' : 'rik',
1049  'USGS Optional ASCII DEM (and CDED)' : 'dem',
1050  'Northwood Numeric Grid Format .grd/.tab' : '',
1051  'Northwood Classified Grid Format .grc/.tab' : '',
1052  'ARC Digitized Raster Graphics' : 'arc',
1053  'Magellan topo (.blx)' : 'blx',
1054  'SAGA GIS Binary Grid (.sdat)' : 'sdat',
1055  # vector
1056  'ESRI Shapefile' : 'shp',
1057  'UK .NTF' : 'ntf',
1058  'SDTS' : 'ddf',
1059  'DGN' : 'dgn',
1060  'VRT' : 'vrt',
1061  'REC' : 'rec',
1062  'BNA' : 'bna',
1063  'CSV' : 'csv',
1064  'GML' : 'gml',
1065  'GPX' : 'gpx',
1066  'KML' : 'kml',
1067  'GMT' : 'gmt',
1068  'PGeo' : 'mdb',
1069  'XPlane' : 'dat',
1070  'AVCBin' : 'adf',
1071  'AVCE00' : 'e00',
1072  'DXF' : 'dxf',
1073  'Geoconcept' : 'gxt',
1074  'GeoRSS' : 'xml',
1075  'GPSTrackMaker' : 'gtm',
1076  'VFK' : 'vfk'
1077  }
1078 
1079  try:
1080  return formatToExt[name]
1081  except KeyError:
1082  return ''
1083 
1084 class GdalSelect(wx.Panel):
1085  def __init__(self, parent, panel, ogr = False, link = False, dest = False,
1086  default = 'file', exclude = [], envHandler = None):
1087  """!Widget for selecting GDAL/OGR datasource, format
1088 
1089  @param parent parent window
1090  @param ogr use OGR selector instead of GDAL
1091  @param dest True for output (destination)
1092  @param default deafult type (ignored when dest == True)
1093  @param exclude list of types to be excluded
1094  """
1095  self.parent = parent
1096  self.ogr = ogr
1097  self.dest = dest
1098  wx.Panel.__init__(self, parent = panel, id = wx.ID_ANY)
1099 
1100  self.settingsBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1101  label = " %s " % _("Settings"))
1102 
1103  self.inputBox = wx.StaticBox(parent = self, id = wx.ID_ANY)
1104  if dest:
1105  self.inputBox.SetLabel(" %s " % _("Output settings"))
1106  else:
1107  self.inputBox.SetLabel(" %s " % _("Source settings"))
1108 
1109  # source type
1110  sources = list()
1111  self.sourceMap = { 'file' : -1,
1112  'dir' : -1,
1113  'db' : -1,
1114  'pro' : -1,
1115  'native' : -1 }
1116  idx = 0
1117  if dest:
1118  sources.append(_("Native"))
1119  self.sourceMap['native'] = idx
1120  idx += 1
1121  if 'file' not in exclude:
1122  sources.append(_("File"))
1123  self.sourceMap['file'] = idx
1124  idx += 1
1125  if 'directory' not in exclude:
1126  sources.append(_("Directory"))
1127  self.sourceMap['dir'] = idx
1128  idx += 1
1129  if 'database' not in exclude:
1130  sources.append(_("Database"))
1131  self.sourceMap['db'] = idx
1132  idx += 1
1133  if 'protocol' not in exclude:
1134  sources.append(_("Protocol"))
1135  self.sourceMap['pro'] = idx
1136  idx += 1
1137 
1138  if self.ogr:
1139  self.settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
1140  else:
1141  self.settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
1142 
1143  self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
1144  self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsLoad)
1145  self._settings = self._loadSettings() # -> self.settingsChoice.SetItems()
1146  self.btnSettingsSave = wx.Button(parent = self, id = wx.ID_SAVE)
1147  self.btnSettingsSave.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
1148  self.btnSettingsSave.SetToolTipString(_("Save current settings"))
1149  self.btnSettingsDel = wx.Button(parent = self, id = wx.ID_REMOVE)
1150  self.btnSettingsDel.Bind(wx.EVT_BUTTON, self.OnSettingsDelete)
1151  self.btnSettingsSave.SetToolTipString(_("Delete currently selected settings"))
1152 
1153  self.source = wx.RadioBox(parent = self, id = wx.ID_ANY,
1154  style = wx.RA_SPECIFY_COLS,
1155  choices = sources)
1156  if dest:
1157  self.source.SetLabel(" %s " % _('Output type'))
1158  else:
1159  self.source.SetLabel(" %s " % _('Source type'))
1160 
1161  self.source.SetSelection(0)
1162  self.source.Bind(wx.EVT_RADIOBOX, self.OnSetType)
1163 
1164  # dsn widgets
1165  if not ogr:
1166  filemask = 'GeoTIFF (%s)|%s|%s (*.*)|*.*' % \
1167  (self._getExtPattern('tif'), self._getExtPattern('tif'), _('All files'))
1168  else:
1169  filemask = 'ESRI Shapefile (%s)|%s|%s (*.*)|*.*' % \
1170  (self._getExtPattern('shp'), self._getExtPattern('shp'), _('All files'))
1171 
1172  dsnFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
1173  size=globalvar.DIALOG_GSELECT_SIZE, labelText = '',
1174  dialogTitle=_('Choose file to import'),
1175  buttonText=_('Browse'),
1176  startDirectory=os.getcwd(),
1177  changeCallback=self.OnSetDsn,
1178  fileMask=filemask)
1179  dsnFile.Hide()
1180 
1181  dsnDir = filebrowse.DirBrowseButton(parent=self, id=wx.ID_ANY,
1182  size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
1183  dialogTitle=_('Choose input directory'),
1184  buttonText=_('Browse'),
1185  startDirectory=os.getcwd(),
1186  changeCallback=self.OnSetDsn)
1187  dsnDir.SetName('GdalSelect')
1188  dsnDir.Hide()
1189 
1190  dsnDbFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
1191  size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
1192  dialogTitle=_('Choose file'),
1193  buttonText=_('Browse'),
1194  startDirectory=os.getcwd(),
1195  changeCallback=self.OnSetDsn)
1196  dsnDbFile.Hide()
1197  dsnDbFile.SetName('GdalSelect')
1198 
1199  dsnDbText = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1200  dsnDbText.Hide()
1201  dsnDbText.Bind(wx.EVT_TEXT, self.OnSetDsn)
1202  dsnDbText.SetName('GdalSelect')
1203 
1204  dsnDbChoice = wx.Choice(parent = self, id = wx.ID_ANY)
1205  dsnDbChoice.Hide()
1206  dsnDbChoice.Bind(wx.EVT_CHOICE, self.OnSetDsn)
1207  dsnDbChoice.SetName('GdalSelect')
1208 
1209  dsnPro = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1210  dsnPro.Hide()
1211  dsnPro.Bind(wx.EVT_TEXT, self.OnSetDsn)
1212  dsnPro.SetName('GdalSelect')
1213 
1214  # format
1215  self.format = FormatSelect(parent = self,
1216  ogr = ogr)
1217  self.format.Bind(wx.EVT_CHOICE, self.OnSetFormat)
1218  self.extension = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1219  self.extension.Bind(wx.EVT_TEXT, self.OnSetExtension)
1220  self.extension.Hide()
1221 
1222  if ogr:
1223  fType = 'ogr'
1224  else:
1225  fType = 'gdal'
1226  self.input = { 'file' : [_("File:"),
1227  dsnFile,
1228  GetFormats()[fType]['file']],
1229  'dir' : [_("Name:"),
1230  dsnDir,
1231  GetFormats()[fType]['file']],
1232  'db' : [_("Name:"),
1233  dsnDbText,
1234  GetFormats()[fType]['database']],
1235  'pro' : [_("Protocol:"),
1236  dsnPro,
1237  GetFormats()[fType]['protocol']],
1238  'db-win' : { 'file' : dsnDbFile,
1239  'text' : dsnDbText,
1240  'choice' : dsnDbChoice },
1241  'native' : [_("Name:"), dsnDir, ''],
1242  }
1243 
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  self.dsnType = 'native'
1251  elif current['format'] in GetFormats()['ogr']['database']:
1252  self.dsnType = 'db'
1253  else:
1254  self.dsnType = 'dir'
1255  else:
1256  self.dsnType = default
1257 
1258  self.dsnText = wx.StaticText(parent = self, id = wx.ID_ANY,
1259  label = self.input[self.dsnType][0],
1260  size = (75, -1))
1261  self.extensionText = wx.StaticText(parent = self, id = wx.ID_ANY,
1262  label = _("Extension:"))
1263  self.extensionText.Hide()
1264 
1265  self.creationOpt = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1266  if not self.dest:
1267  self.creationOpt.Hide()
1268 
1269  self._layout()
1270 
1271  self.OnSetType(event = None, sel = self.sourceMap[self.dsnType])
1272  if self.dest:
1273  if current['format'] != 'native':
1274  self.OnSetFormat(event = None, format = current['format'])
1275  self.OnSetDsn(event = None, path = current['dsn'])
1276  self.creationOpt.SetValue(current['options'])
1277  else:
1278  if not ogr:
1279  self.OnSetFormat(event = None, format = 'GeoTIFF')
1280  else:
1281  self.OnSetFormat(event = None, format = 'ESRI Shapefile')
1282 
1283  def _layout(self):
1284  """!Layout"""
1285  mainSizer = wx.BoxSizer(wx.VERTICAL)
1286 
1287  settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
1288  settingsSizer.Add(item = wx.StaticText(parent = self,
1289  id = wx.ID_ANY,
1290  label = _("Load settings:")),
1291  flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
1292  border = 5)
1293  settingsSizer.Add(item = self.settingsChoice,
1294  proportion = 1,
1295  flag = wx.EXPAND)
1296  settingsSizer.Add(item = self.btnSettingsSave,
1297  flag = wx.LEFT | wx.RIGHT,
1298  border = 5)
1299  settingsSizer.Add(item = self.btnSettingsDel,
1300  flag = wx.RIGHT,
1301  border = 5)
1302 
1303  inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
1304 
1305  self.dsnSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
1306 
1307  row = 0
1308  self.dsnSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1309  label = _("Format:")),
1310  flag = wx.ALIGN_CENTER_VERTICAL,
1311  pos = (row, 0))
1312  self.dsnSizer.Add(item=self.format,
1313  flag = wx.ALIGN_CENTER_VERTICAL,
1314  pos = (row, 1))
1315  self.dsnSizer.Add(item = self.extensionText,
1316  flag=wx.ALIGN_CENTER_VERTICAL,
1317  pos = (row, 2))
1318  self.dsnSizer.Add(item=self.extension,
1319  flag = wx.ALIGN_CENTER_VERTICAL,
1320  pos = (row, 3))
1321  row += 1
1322  self.dsnSizer.Add(item = self.dsnText,
1323  flag = wx.ALIGN_CENTER_VERTICAL,
1324  pos = (row, 0))
1325  self.dsnSizer.Add(item = self.input[self.dsnType][1],
1326  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
1327  pos = (row, 1), span = (1, 3))
1328  row += 1
1329  if self.creationOpt.IsShown():
1330  self.dsnSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1331  label = _("Creation options:")),
1332  flag = wx.ALIGN_CENTER_VERTICAL,
1333  pos = (row, 0))
1334  self.dsnSizer.Add(item = self.creationOpt,
1335  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
1336  pos = (row, 1), span = (1, 3))
1337  row += 1
1338  self.dsnSizer.AddGrowableCol(3)
1339 
1340  inputSizer.Add(item=self.dsnSizer, proportion = 1,
1341  flag=wx.EXPAND | wx.BOTTOM, border = 10)
1342 
1343  mainSizer.Add(item=settingsSizer, proportion=0,
1344  flag=wx.ALL | wx.EXPAND, border=5)
1345  mainSizer.Add(item=self.source, proportion=0,
1346  flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5)
1347  mainSizer.Add(item=inputSizer, proportion=0,
1348  flag=wx.ALL | wx.EXPAND, border=5)
1349 
1350  self.SetSizer(mainSizer)
1351  mainSizer.Fit(self)
1352 
1353  def _getExtPatternGlob(self, ext):
1354  """!Get pattern for case-insensitive globing"""
1355  pattern = '*.'
1356  for c in ext:
1357  pattern += '[%s%s]' % (c.lower(), c.upper())
1358  return pattern
1359 
1360  def _getExtPattern(self, ext):
1361  """!Get pattern for case-insensitive file mask"""
1362  return '*.%s;*.%s' % (ext.lower(), ext.upper())
1363 
1364  def OnSettingsLoad(self, event):
1365  """!Load named settings"""
1366  name = event.GetString()
1367  if name not in self._settings:
1368  GError(parent = self,
1369  message = _("Settings <%s> not found") % name)
1370  return
1371  data = self._settings[name]
1372  self.OnSetType(event = None, sel = self.sourceMap[data[0]])
1373  self.OnSetFormat(event = None, format = data[2])
1374  self.OnSetDsn(event = None, path = data[1])
1375  self.creationOpt.SetValue(data[3])
1376 
1377  def OnSettingsSave(self, event):
1378  """!Save settings"""
1379  dlg = wx.TextEntryDialog(parent = self,
1380  message = _("Name:"),
1381  caption = _("Save settings"))
1382  if dlg.ShowModal() != wx.ID_OK:
1383  return
1384 
1385  # check required params
1386  if not dlg.GetValue():
1387  GMessage(parent = self,
1388  message = _("Name not given, settings is not saved."))
1389  return
1390 
1391  if not self.GetDsn():
1392  GMessage(parent = self,
1393  message = _("No data source defined, settings is not saved."))
1394  return
1395 
1396  name = dlg.GetValue()
1397 
1398  # check if settings item already exists
1399  if name in self._settings:
1400  dlgOwt = wx.MessageDialog(self, message = _("Settings <%s> already exists. "
1401  "Do you want to overwrite the settings?") % name,
1402  caption = _("Save settings"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
1403  if dlgOwt.ShowModal() != wx.ID_YES:
1404  dlgOwt.Destroy()
1405  return
1406 
1407  self._settings[name] = (self.dsnType, self.GetDsn(),
1408  self.format.GetStringSelection(),
1409  self.creationOpt.GetValue())
1410 
1411  if self._saveSettings() == 0:
1412  self._settings = self._loadSettings()
1413  self.settingsChoice.SetStringSelection(name)
1414 
1415  dlg.Destroy()
1416 
1417  def OnSettingsDelete(self, event):
1418  """!Save settings"""
1419  name = self.settingsChoice.GetStringSelection()
1420  if not name:
1421  GMessage(parent = self,
1422  message = _("No settings is defined. Operation canceled."))
1423  return
1424 
1425  self._settings.pop(name)
1426  if self._saveSettings() == 0:
1427  self._settings = self._loadSettings()
1428 
1429  def _saveSettings(self):
1430  """!Save settings into the file
1431 
1432  @return 0 on success
1433  @return -1 on failure
1434  """
1435  try:
1436  fd = open(self.settingsFile, 'w')
1437  for key, value in self._settings.iteritems():
1438  fd.write('%s;%s;%s;%s\n' % (key, value[0], value[1], value[2]))
1439  except IOError:
1440  GError(parent = self,
1441  message = _("Unable to save settings"))
1442  return -1
1443  else:
1444  fd.close()
1445 
1446  return 0
1447 
1448  def _loadSettings(self):
1449  """!Load settings from the file
1450 
1451  The file is defined by self.SettingsFile.
1452 
1453  @return parsed dict
1454  @return empty dict on error
1455  """
1456  data = dict()
1457  if not os.path.exists(self.settingsFile):
1458  return data
1459 
1460  try:
1461  fd = open(self.settingsFile, 'r')
1462  for line in fd.readlines():
1463  try:
1464  lineData = line.rstrip('\n').split(';')
1465  if len(lineData) > 4:
1466  # type, dsn, format, options
1467  data[lineData[0]] = (lineData[1], lineData[2], lineData[3], lineData[4])
1468  else:
1469  data[lineData[0]] = (lineData[1], lineData[2], lineData[3], '')
1470  except ValueError:
1471  pass
1472  except IOError:
1473  return data
1474  else:
1475  fd.close()
1476 
1477  self.settingsChoice.SetItems(sorted(data.keys()))
1478 
1479  return data
1480 
1481  def OnSetType(self, event, sel = None):
1482  """!Datasource type changed.
1483 
1484  @todo improve showing/hiding widgets
1485  """
1486  if event:
1487  sel = event.GetSelection()
1488  else:
1489  self.source.SetSelection(sel)
1490  win = self.input[self.dsnType][1]
1491  if win:
1492  self.dsnSizer.Detach(win)
1493  win.Hide()
1494 
1495  if sel == self.sourceMap['file']: # file
1496  self.dsnType = 'file'
1497  format = self.input[self.dsnType][2][0]
1498  try:
1499  ext = self.format.GetExtension(format)
1500  if not ext:
1501  raise KeyError
1502  format += ' (%s)|%s|%s (*.*)|*.*' % \
1503  (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
1504  except KeyError:
1505  format += '%s (*.*)|*.*' % _('All files')
1506 
1507  win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
1508  size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
1509  dialogTitle=_('Choose file to import'),
1510  buttonText=_('Browse'),
1511  startDirectory=os.getcwd(),
1512  changeCallback=self.OnSetDsn,
1513  fileMask = format)
1514  self.input[self.dsnType][1] = win
1515 
1516  elif sel == self.sourceMap['dir']: # directory
1517  self.dsnType = 'dir'
1518  elif sel == self.sourceMap['db']: # database
1519  self.dsnType = 'db'
1520  elif sel == self.sourceMap['pro']: # protocol
1521  self.dsnType = 'pro'
1522  elif sel == self.sourceMap['native']:
1523  self.dsnType = 'native'
1524 
1525  if self.dsnType == 'db':
1526  self.input[self.dsnType][1] = self.input['db-win']['text']
1527  win = self.input[self.dsnType][1]
1528 
1529  self.dsnSizer.Add(item = self.input[self.dsnType][1],
1530  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
1531  pos = (1, 1), span = (1, 3))
1532  win.SetValue('')
1533  win.Show()
1534 
1535  if sel == self.sourceMap['native']: # native
1536  win.Enable(False)
1537  self.format.Enable(False)
1538  self.creationOpt.Enable(False)
1539  self.parent.btnOk.Enable(True)
1540  else:
1541  if not win.IsEnabled():
1542  win.Enable(True)
1543  if not self.format.IsEnabled():
1544  self.format.Enable(True)
1545  self.creationOpt.Enable(True)
1546  self.dsnText.SetLabel(self.input[self.dsnType][0])
1547  self.format.SetItems(self.input[self.dsnType][2])
1548  if self.parent.GetName() == 'MultiImportDialog':
1549  self.parent.list.DeleteAllItems()
1550 
1551  if sel in (self.sourceMap['file'], self.sourceMap['dir']):
1552  if not self.ogr:
1553  self.OnSetFormat(event = None, format = 'GeoTIFF')
1554  else:
1555  self.OnSetFormat(event = None, format = 'ESRI Shapefile')
1556 
1557  if sel == self.sourceMap['dir'] and not self.dest:
1558  if not self.extension.IsShown():
1559  self.extensionText.Show()
1560  self.extension.Show()
1561  else:
1562  if self.extension.IsShown():
1563  self.extensionText.Hide()
1564  self.extension.Hide()
1565 
1566  self.dsnSizer.Layout()
1567 
1568  def GetDsn(self):
1569  """!Get datasource name
1570  """
1571  if self.format.GetStringSelection() == 'PostgreSQL':
1572  dsn = 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
1573  else:
1574  dsn = self.input[self.dsnType][1].GetValue()
1575 
1576  return dsn
1577 
1578  def OnSetDsn(self, event, path = None):
1579  """!Input DXF file/OGR dsn defined, update list of layer
1580  widget"""
1581  if event:
1582  path = event.GetString()
1583  else:
1584  if self.format.GetStringSelection() == 'PostgreSQL':
1585  for item in path.split(':', 1)[1].split(','):
1586  key, value = item.split('=', 1)
1587  if key == 'dbname':
1588  if not self.input[self.dsnType][1].SetStringSelection(value):
1589  GMessage(_("Database <%s> not accessible.") % value,
1590  parent = self)
1591  break
1592  else:
1593  self.input[self.dsnType][1].SetValue(path)
1594 
1595  if not path:
1596  if self.dest:
1597  self.parent.btnOk.Enable(False)
1598  return
1599 
1600  if self.dest:
1601  self.parent.btnOk.Enable(True)
1602  else:
1603  self._reloadLayers()
1604 
1605  if event:
1606  event.Skip()
1607 
1608  def _reloadLayers(self):
1609  """!Reload list of layers"""
1610  dsn = self.GetDsn()
1611  if not dsn:
1612  return
1613 
1614  data = list()
1615  layerId = 1
1616 
1617  if self.ogr:
1618  ret = RunCommand('v.in.ogr',
1619  quiet = True,
1620  read = True,
1621  flags = 'l',
1622  dsn = dsn)
1623  if not ret:
1624  self.parent.list.LoadData()
1625  if hasattr(self, "btn_run"):
1626  self.btn_run.Enable(False)
1627  return
1628 
1629  layerId = 1
1630  for line in ret.split(','):
1631  layerName = line.strip()
1632  grassName = GetValidLayerName(layerName)
1633  data.append((layerId, layerName, grassName))
1634  layerId += 1
1635  else:
1636  if self.dsnType == 'file':
1637  baseName = os.path.basename(dsn)
1638  grassName = GetValidLayerName(baseName.split('.', -1)[0])
1639  data.append((layerId, baseName, grassName))
1640  elif self.dsnType == 'dir':
1641  ext = self.extension.GetValue()
1642  for filename in glob.glob(os.path.join(dsn, "%s") % self._getExtPatternGlob(ext)):
1643  baseName = os.path.basename(filename)
1644  grassName = GetValidLayerName(baseName.split('.', -1)[0])
1645  data.append((layerId, baseName, grassName))
1646  layerId += 1
1647  if self.ogr:
1648  dsn += '@OGR'
1649 
1650  evt = wxGdalSelect(dsn = dsn)
1651  evt.SetId(self.input[self.dsnType][1].GetId())
1652  wx.PostEvent(self.parent, evt)
1653 
1654  if self.parent.GetName() == 'MultiImportDialog':
1655  self.parent.list.LoadData(data)
1656  if len(data) > 0:
1657  self.parent.btn_run.Enable(True)
1658  else:
1659  self.parent.btn_run.Enable(False)
1660 
1661  def OnSetExtension(self, event):
1662  """!Extension changed"""
1663  if not self.dest:
1664  # reload layers
1665  self._reloadLayers()
1666 
1667  def OnSetFormat(self, event, format = None):
1668  """!Format changed"""
1669  if self.dsnType not in ['file', 'dir', 'db']:
1670  return
1671 
1672  win = self.input[self.dsnType][1]
1673  self.dsnSizer.Detach(win)
1674 
1675  if self.dsnType == 'file':
1676  win.Destroy()
1677  else: # database
1678  win.Hide()
1679 
1680  if event:
1681  format = event.GetString()
1682  else:
1683  self.format.SetStringSelection(format)
1684 
1685  if self.dsnType == 'file':
1686  try:
1687  ext = self.format.GetExtension(format)
1688  if not ext:
1689  raise KeyError
1690  format += ' (%s)|%s|%s (*.*)|*.*' % \
1691  (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
1692  except KeyError:
1693  format += '%s (*.*)|*.*' % _('All files')
1694 
1695  win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
1696  size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
1697  dialogTitle=_('Choose file'),
1698  buttonText=_('Browse'),
1699  startDirectory=os.getcwd(),
1700  changeCallback=self.OnSetDsn,
1701  fileMask = format)
1702 
1703  elif self.dsnType == 'dir':
1704  pass
1705 
1706  else: # database
1707  if format == 'SQLite' or format == 'Rasterlite':
1708  win = self.input['db-win']['file']
1709  elif format == 'PostgreSQL' or format == 'PostGIS WKT Raster driver':
1710  if grass.find_program('psql', ['--help']):
1711  win = self.input['db-win']['choice']
1712  if not win.GetItems():
1713  p = grass.Popen(['psql', '-ltA'], stdout = grass.PIPE)
1714  ret = p.communicate()[0]
1715  if ret:
1716  db = list()
1717  for line in ret.splitlines():
1718  sline = line.split('|')
1719  if len(sline) < 2:
1720  continue
1721  dbname = sline[0]
1722  if dbname:
1723  db.append(dbname)
1724  win.SetItems(db)
1725  if self.dest and win.GetStringSelection():
1726  self.parent.btnOk.Enable(True)
1727  else:
1728  win = self.input['db-win']['text']
1729  else:
1730  win = self.input['db-win']['text']
1731 
1732  self.input[self.dsnType][1] = win
1733  if not win.IsShown():
1734  win.Show()
1735  self.dsnSizer.Add(item = self.input[self.dsnType][1],
1736  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
1737  pos = (1, 1), span = (1, 3))
1738  self.dsnSizer.Layout()
1739 
1740  # update extension
1741  self.extension.SetValue(self.GetFormatExt())
1742 
1743  if not self.dest:
1744  # reload layers
1745  self._reloadLayers()
1746 
1747  def GetType(self):
1748  """!Get source type"""
1749  return self.dsnType
1750 
1751  def GetDsnWin(self):
1752  """!Get list of DSN windows"""
1753  win = list()
1754  for stype in ('file', 'dir', 'pro'):
1755  win.append(self.input[stype][1])
1756  for stype in ('file', 'text', 'choice'):
1757  win.append(self.input['db-win'][stype])
1758 
1759  return win
1760 
1761  def GetFormat(self):
1762  """!Get format as string"""
1763  return self.format.GetStringSelection().replace(' ', '_')
1764 
1765  def GetFormatExt(self):
1766  """!Get format extension"""
1767  return self.format.GetExtension(self.format.GetStringSelection())
1768 
1769  def GetOptions(self):
1770  """!Get creation options"""
1771  if not self.creationOpt.IsShown():
1772  return ''
1773 
1774  return self.creationOpt.GetValue()
1775 
1776 class ProjSelect(wx.ComboBox):
1777  """!Widget for selecting input raster/vector map used by
1778  r.proj/v.proj modules."""
1779  def __init__(self, parent, isRaster, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
1780  **kwargs):
1781  super(ProjSelect, self).__init__(parent, id, size = size,
1782  style = wx.CB_READONLY, **kwargs)
1783  self.SetName("ProjSelect")
1784  self.isRaster = isRaster
1785 
1786  def UpdateItems(self, dbase, location, mapset):
1787  """!Update list of maps
1788 
1789  """
1790  if not dbase:
1791  dbase = grass.gisenv()['GISDBASE']
1792  if not mapset:
1793  mapset = grass.gisenv()['MAPSET']
1794  if self.isRaster:
1795  ret = RunCommand('r.proj',
1796  quiet = True,
1797  read = True,
1798  flags = 'l',
1799  dbase = dbase,
1800  location = location,
1801  mapset = mapset)
1802  else:
1803  ret = RunCommand('v.proj',
1804  quiet = True,
1805  read = True,
1806  flags = 'l',
1807  dbase = dbase,
1808  location = location,
1809  mapset = mapset)
1810  listMaps = list()
1811  if ret:
1812  for line in ret.splitlines():
1813  listMaps.append(line.strip())
1814  ListSortLower(listMaps)
1815 
1816  self.SetItems(listMaps)
1817  self.SetValue('')
1818 
1819 class ElementSelect(wx.Choice):
1820  def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
1821  **kwargs):
1822  """!Widget for selecting GIS element
1823 
1824  @param parent parent window
1825  """
1826  super(ElementSelect, self).__init__(parent, id, size = size,
1827  **kwargs)
1828  self.SetName("ElementSelect")
1829 
1830  task = gtask.parse_interface('g.list')
1831  p = task.get_param(value = 'type')
1832  self.values = p.get('values', [])
1833 
1834  self.SetItems(self.values)
1835 
1836  def GetValue(self, name):
1837  """!Translate value
1838 
1839  @param name element name
1840  """
1841  return name
def InsertTableColumns
Insert table columns.
Definition: gselect.py:845
def GetListOfLocations
Get list of GRASS locations in given dbase.
Definition: core/utils.py:639
def SetElementList
Set element list.
Definition: gselect.py:96
def GetValue
Definition: widgets.py:118
wxGUI command interface
def __init__
Custom to create a ComboBox with a tree control to display and select vector maps.
Definition: gselect.py:119
def GetFormat
Get format as string.
Definition: gselect.py:1761
def GetDbSettings
Get database settins.
Definition: gselect.py:630
def __init__
Widget for selecting external (GDAL/OGR) format.
Definition: gselect.py:992
def GetSettingsPath
Get full path to the settings directory.
Definition: core/utils.py:801
def OnLeftDown
Do the combobox selection.
Definition: gselect.py:489
def _selectionChanged
Selection changed, store value.
Definition: gselect.py:695
def __init__
Custom control to create a ComboBox with a tree control to display and select GIS elements within ace...
Definition: gselect.py:61
#define min(x, y)
Definition: draw2.c:68
def UpdateItems
Update list of locations.
Definition: gselect.py:896
wxGUI debugging
def GetDsnWin
Get list of DSN windows.
Definition: gselect.py:1751
Creates combo box for selecting data layers defined for vector.
Definition: gselect.py:646
def _saveSettings
Save settings into the file.
Definition: gselect.py:1429
def _reloadLayers
Reload list of layers.
Definition: gselect.py:1608
def GetDsn
Get datasource name.
Definition: gselect.py:1568
def GetElementList
Load elements.
Definition: gselect.py:104
def GetListOfMapsets
Get list of mapsets in given GRASS location.
Definition: core/utils.py:662
def OnMotion
Have the selection follow the mouse, like in a real combobox.
Definition: gselect.py:480
def _CheckDBConnection
Check DB connection.
Definition: gselect.py:550
def GetElementList
Get filtered list of GIS elements in accessible mapsets and display as tree with all relevant element...
Definition: gselect.py:213
def GetTableDesc
Get table columns.
Definition: gselect.py:639
def OnSetDsn
Input DXF file/OGR dsn defined, update list of layer widget.
Definition: gselect.py:1578
def Select
Select value (currently selected or default)
Definition: gselect.py:700
def __init__
Widget for selecting GDAL/OGR datasource, format.
Definition: gselect.py:1086
def OnSettingsDelete
Save settings.
Definition: gselect.py:1417
def SetType
Param set element type for widget.
Definition: gselect.py:109
def SetValue
Definition: widgets.py:115
def GetType
Get element type.
Definition: gselect.py:529
def OnKeyUp
Enables to select items using keyboard.
Definition: gselect.py:418
def _DescribeTables
Describe linked tables.
Definition: gselect.py:561
def _loadSettings
Load settings from the file.
Definition: gselect.py:1448
def Insert
Insert subgroups for defined group.
Definition: gselect.py:970
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def GetFormats
Get GDAL/OGR formats.
Definition: core/utils.py:790
def OnSetFormat
Format changed.
Definition: gselect.py:1667
def SetData
Set object properties.
Definition: gselect.py:514
Widget for selecting input raster/vector map used by r.proj/v.proj modules.
Definition: gselect.py:1776
def _layout
Layout.
Definition: gselect.py:1283
def UpdateItems
Update list of mapsets for given location.
Definition: gselect.py:931
Creates combo box for selecting attribute tables from the database.
Definition: gselect.py:738
Widget for selecting GRASS Database.
Definition: gselect.py:871
Creates combo box for selecting database driver.
Definition: gselect.py:713
def OnSetExtension
Extension changed.
Definition: gselect.py:1661
def InsertTables
Insert attribute tables into combobox.
Definition: gselect.py:754
def GetKeyColumn
Get key column of given layer.
Definition: gselect.py:616
def _isElement
Check if element should be filtered out.
Definition: gselect.py:134
def GetTable
Get table name of given layer.
Definition: gselect.py:623
def GetAdjustedSize
Reads UserSettings to get height (which was 200 in old implementation).
Definition: gselect.py:248
def GetValue
Translate value.
Definition: gselect.py:1836
def _getExtPatternGlob
Get pattern for case-insensitive globing.
Definition: gselect.py:1353
Widget for selecting GRASS location.
Definition: gselect.py:881
def ListSortLower
Sort list items (not case-sensitive)
Definition: core/utils.py:287
def GetValidLayerName
Make layer name SQL compliant, based on G_str_to_sql()
Definition: core/utils.py:175
def OnSettingsLoad
Load named settings.
Definition: gselect.py:1364
def _getElementList
Get list of GIS elements in accessible mapsets and display as tree with all relevant elements display...
Definition: gselect.py:254
def OnSetType
Datasource type changed.
Definition: gselect.py:1481
def InsertLayers
Insert layers for a vector into the layer combobox.
Definition: gselect.py:669
def OnSettingsSave
Save settings.
Definition: gselect.py:1377
def OnPopup
Limited only for first selected.
Definition: gselect.py:192
Widget for selecting GRASS mapset.
Definition: gselect.py:907
Widget for selecting subgroups.
Definition: gselect.py:962
def _getExtPattern
Get pattern for case-insensitive file mask.
Definition: gselect.py:1360
def FindItem
Finds item with given name or starting with given text.
Definition: gselect.py:391
Creates combo box for selecting columns in the attribute table for a vector map.
Definition: gselect.py:777
Misc utilities for wxGUI.
Creates combo box for selecting database driver.
Definition: gselect.py:727
def GetAllVectorLayers
Returns list of all vector layers as strings.
Definition: core/utils.py:332
def Reset
Reset.
Definition: gselect.py:603
def GetOptions
Get creation options.
Definition: gselect.py:1769
def InsertColumns
Insert columns for a vector attribute table into the columns combobox.
Definition: gselect.py:802
def GetExtension
Get file extension by format name.
Definition: gselect.py:1013
def SetFilter
Set filter for GIS elements, see e.g.
Definition: gselect.py:188
def GetStringValue
Get value as a string separated by commas.
Definition: gselect.py:184
def __init__
Widget for selecting GIS element.
Definition: gselect.py:1821
Default GUI settings.
Class providing information about attribute tables linked to a vector map.
Definition: gselect.py:534
def GetName
Get vector name.
Definition: gselect.py:612
def UpdateItems
Update list of maps.
Definition: gselect.py:1786
def GetFormatExt
Get format extension.
Definition: gselect.py:1765
def OnKeyUp
Shows popupwindow if down arrow key is released.
Definition: gselect.py:89
def GetType
Get source type.
Definition: gselect.py:1747
def RunCommand
Run GRASS command.
Definition: gcmd.py:633
Create a tree ComboBox for selecting maps and other GIS elements in accessible mapsets within the cur...
Definition: gselect.py:144