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