GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
gselect.py
Go to the documentation of this file.
00001 """!
00002 @package gui_core.gselect
00003 
00004 @brief Custom control that selects elements
00005 
00006 Classes:
00007  - gselect::Select
00008  - gselect::VectorSelect
00009  - gselect::TreeCrtlComboPopup
00010  - gselect::VectorDBInfo
00011  - gselect::LayerSelect
00012  - gselect::DriverSelect
00013  - gselect::DatabaseSelect
00014  - gselect::TableSelect
00015  - gselect::ColumnSelect
00016  - gselect::DbaseSelect
00017  - gselect::LocationSelect
00018  - gselect::MapsetSelect
00019  - gselect::SubGroupSelect
00020  - gselect::FormatSelect
00021  - gselect::GdalSelect
00022  - gselect::ProjSelect
00023  - gselect::ElementSelect
00024 
00025 (C) 2007-2011 by the GRASS Development Team 
00026 
00027 This program is free software under the GNU General Public License
00028 (>=v2). Read the file COPYING that comes with GRASS for details.
00029 
00030 @author Michael Barton
00031 @author Martin Landa <landa.martin gmail.com>
00032 @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
00033 """
00034 
00035 import os
00036 import sys
00037 import glob
00038 
00039 import wx
00040 import wx.combo
00041 import wx.lib.filebrowsebutton as filebrowse
00042 from wx.lib.newevent import NewEvent
00043 
00044 from core import globalvar
00045 
00046 import grass.script as grass
00047 from   grass.script import task as gtask
00048 
00049 from core.gcmd     import RunCommand, GError, GMessage
00050 from core.utils    import GetListOfLocations, GetListOfMapsets, GetFormats
00051 from core.utils    import GetSettingsPath, GetValidLayerName, ListSortLower, GetVectorNumberOfLayers
00052 from core.settings import UserSettings
00053 from core.debug    import Debug
00054 
00055 wxGdalSelect, EVT_GDALSELECT = NewEvent()
00056 
00057 class Select(wx.combo.ComboCtrl):
00058     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
00059                  type = None, multiple = False, mapsets = None,
00060                  updateOnPopup = True, onPopup = None,
00061                  fullyQualified = True):
00062         """!Custom control to create a ComboBox with a tree control to
00063         display and select GIS elements within acessible mapsets.
00064         Elements can be selected with mouse. Can allow multiple
00065         selections, when argument multiple=True. Multiple selections
00066         are separated by commas.
00067 
00068         @param type type of GIS elements ('raster, 'vector', ...)
00069         @param multiple multiple input allowed?
00070         @param mapsets force list of mapsets (otherwise search path)
00071         @param updateOnPopup True for updating list of elements on popup
00072         @param onPopup function to be called on Popup
00073         @param fullyQualified True to provide fully qualified names (map@mapset)
00074         """
00075         wx.combo.ComboCtrl.__init__(self, parent=parent, id=id, size=size)
00076         self.GetChildren()[0].SetName("Select")
00077         self.GetChildren()[0].type = type
00078         
00079         self.tcp = TreeCtrlComboPopup()
00080         self.SetPopupControl(self.tcp)
00081         self.SetPopupExtents(0, 100)
00082         if type:
00083             self.tcp.SetData(type = type, mapsets = mapsets,
00084                              multiple = multiple,
00085                              updateOnPopup = updateOnPopup, onPopup = onPopup,
00086                              fullyQualified = fullyQualified)
00087         self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp)
00088      
00089     def OnKeyUp(self, event):
00090         """!Shows popupwindow if down arrow key is released"""
00091         if event.GetKeyCode() == wx.WXK_DOWN:
00092             self.ShowPopup() 
00093         else:
00094             event.Skip()
00095         
00096     def SetElementList(self, type, mapsets = None):
00097         """!Set element list
00098 
00099         @param type GIS element type
00100         @param mapsets list of acceptable mapsets (None for all in search path)
00101         """
00102         self.tcp.SetData(type = type, mapsets = mapsets)
00103         
00104     def GetElementList(self):
00105         """!Load elements"""
00106         self.tcp.GetElementList()
00107     
00108     def SetType(self, etype, multiple = False, mapsets = None,
00109                 updateOnPopup = True, onPopup = None):
00110         """!Param set element type for widget
00111 
00112         @param etype element type, see gselect.ElementSelect
00113         """
00114         self.tcp.SetData(type = etype, mapsets = mapsets,
00115                          multiple = multiple,
00116                          updateOnPopup = updateOnPopup, onPopup = onPopup)
00117         
00118 class VectorSelect(Select):
00119     def __init__(self, parent, ftype, **kwargs):
00120         """!Custom to create a ComboBox with a tree control to display and
00121         select vector maps. Control allows to filter vector maps. If you
00122         don't need this feature use Select class instead
00123         
00124         @ftype filter vector maps based on feature type
00125         """
00126         Select.__init__(self, parent = parent, id = wx.ID_ANY,
00127                         type = 'vector', **kwargs)
00128         
00129         self.ftype = ftype
00130         
00131         # remove vector maps which do not contain given feature type
00132         self.tcp.SetFilter(self._isElement)
00133         
00134     def _isElement(self, vectorName):
00135         """!Check if element should be filtered out"""
00136         try:
00137             if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1:
00138                 return False
00139         except KeyError:
00140             return False
00141         
00142         return True
00143 
00144 class TreeCtrlComboPopup(wx.combo.ComboPopup):
00145     """!Create a tree ComboBox for selecting maps and other GIS elements
00146     in accessible mapsets within the current location
00147     """
00148     # overridden ComboPopup methods
00149     def Init(self):
00150         self.value = [] # for multiple is False -> len(self.value) in [0,1]
00151         self.curitem = None
00152         self.multiple = False
00153         self.type = None
00154         self.mapsets = None
00155         self.updateOnPopup = True
00156         self.onPopup = None
00157         self.fullyQualified = True
00158         
00159         self.SetFilter(None)
00160         
00161     def Create(self, parent):
00162         self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
00163                                    |wx.TR_HAS_BUTTONS
00164                                    |wx.TR_SINGLE
00165                                    |wx.TR_LINES_AT_ROOT
00166                                    |wx.SIMPLE_BORDER
00167                                    |wx.TR_FULL_ROW_HIGHLIGHT)
00168         self.seltree.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
00169         self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
00170         self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
00171         self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.mapsetExpanded)
00172         self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.mapsetCollapsed)
00173         self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.mapsetActivated)
00174         self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, self.mapsetSelected)
00175         self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
00176         
00177     # the following dummy handler are needed to keep tree events from propagating up to
00178     # the parent GIS Manager layer tree
00179     def mapsetExpanded(self, event):
00180         pass
00181 
00182     def mapsetCollapsed(self, event):
00183         pass
00184 
00185     def mapsetActivated(self, event):
00186         pass
00187 
00188     def mapsetSelected(self, event):
00189         pass
00190     # end of dummy events
00191 
00192     def GetControl(self):
00193         return self.seltree
00194 
00195     def GetStringValue(self):
00196         """!Get value as a string separated by commas"""
00197         return ','.join(self.value)
00198     
00199     def SetFilter(self, filter):
00200         """!Set filter for GIS elements, see e.g. VectorSelect"""
00201         self.filterElements = filter
00202     
00203     def OnPopup(self, force = False):
00204         """!Limited only for first selected"""
00205         if not force and not self.updateOnPopup:
00206             return
00207         if self.onPopup:
00208             selected, exclude = self.onPopup(self.type)
00209         else:
00210             selected = None
00211             exclude  = False
00212  
00213         self.GetElementList(selected, exclude)
00214         
00215         # selects map starting according to written text
00216         inputText = self.GetCombo().GetValue().strip()
00217         if inputText:
00218             root = self.seltree.GetRootItem()
00219             match = self.FindItem(root, inputText, startLetters = True)
00220             self.seltree.EnsureVisible(match)
00221             self.seltree.SelectItem(match)
00222             
00223       
00224     def GetElementList(self, elements = None, exclude = False):
00225         """!Get filtered list of GIS elements in accessible mapsets
00226         and display as tree with all relevant elements displayed
00227         beneath each mapset branch
00228         """
00229         # update list
00230         self.seltree.DeleteAllItems()
00231         self._getElementList(self.type, self.mapsets, elements, exclude)
00232         
00233         if len(self.value) > 0:
00234             root = self.seltree.GetRootItem()
00235             if not root:
00236                 return
00237             item = self.FindItem(root, self.value[0])
00238             try:
00239                 self.seltree.EnsureVisible(item)
00240                 self.seltree.SelectItem(item)
00241             except:
00242                 pass
00243             
00244     def SetStringValue(self, value):
00245         # this assumes that item strings are unique...
00246         root = self.seltree.GetRootItem()
00247         if not root:
00248             return
00249         found = self.FindItem(root, value)
00250         winValue = self.GetCombo().GetValue().strip(',')
00251         self.value = []
00252         if winValue:
00253             self.value = winValue.split(',')
00254         
00255         if found:
00256             self.value.append(found)
00257             self.seltree.SelectItem(found)
00258         
00259     def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
00260         """!Reads UserSettings to get height (which was 200 in old implementation).
00261         """
00262         height = UserSettings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
00263         return wx.Size(minWidth, min(height, maxHeight))
00264 
00265     def _getElementList(self, element, mapsets = None, elements = None, exclude = False):
00266         """!Get list of GIS elements in accessible mapsets and display as tree
00267         with all relevant elements displayed beneath each mapset branch
00268 
00269         @param element GIS element
00270         @param mapsets list of acceptable mapsets (None for all mapsets in search path)
00271         @param elements list of forced GIS elements
00272         @param exclude True to exclude, False for forcing the list (elements)
00273         """
00274         # get current mapset
00275         curr_mapset = grass.gisenv()['MAPSET']
00276         
00277         # map element types to g.mlist types
00278         elementdict = {'cell':'rast',
00279                        'raster':'rast',
00280                        'rast':'rast',
00281                        'raster files':'rast',
00282                        'grid3':'rast3d',
00283                        'rast3d':'rast3d',
00284                        '3d-raster':'rast3d',
00285                        'raster3d':'rast3d',
00286                        'raster3D files':'rast3d',
00287                        'vector':'vect',
00288                        'vect':'vect',
00289                        'binary vector files':'vect',
00290                        'dig':'oldvect',
00291                        'oldvect':'oldvect',
00292                        'old vector':'oldvect',
00293                        'dig_ascii':'asciivect',
00294                        'asciivect':'asciivect',
00295                        'asciivector':'asciivect',
00296                        'ascii vector files':'asciivect',
00297                        'icons':'icon',
00298                        'icon':'icon',
00299                        'paint icon files':'icon',
00300                        'paint/labels':'labels',
00301                        'labels':'labels',
00302                        'label':'labels',
00303                        'paint label files':'labels',
00304                        'site_lists':'sites',
00305                        'sites':'sites',
00306                        'site list':'sites',
00307                        'site list files':'sites',
00308                        'windows':'region',
00309                        'region':'region',
00310                        'region definition':'region',
00311                        'region definition files':'region',
00312                        'windows3d':'region3d',
00313                        'region3d':'region3d',
00314                        'region3D definition':'region3d',
00315                        'region3D definition files':'region3d',
00316                        'group':'group',
00317                        'imagery group':'group',
00318                        'imagery group files':'group',
00319                        '3d.view':'3dview',
00320                        '3dview':'3dview',
00321                        '3D viewing parameters':'3dview',
00322                        '3D view parameters':'3dview'}
00323         
00324         if element not in elementdict:
00325             self.AddItem(_('Not selectable element'))
00326             return
00327         
00328         if globalvar.have_mlist:
00329             filesdict = grass.mlist_grouped(elementdict[element],
00330                                             check_search_path = False)
00331         else:
00332             filesdict = grass.list_grouped(elementdict[element],
00333                                            check_search_path = False)
00334         
00335         # list of mapsets in current location
00336         if mapsets is None:
00337             mapsets = grass.mapsets(search_path = True)
00338         
00339         # current mapset first
00340         if curr_mapset in mapsets and mapsets[0] != curr_mapset:
00341             mapsets.remove(curr_mapset)
00342             mapsets.insert(0, curr_mapset)
00343         
00344         first_mapset = None
00345         for mapset in mapsets:
00346             mapset_node = self.AddItem(_('Mapset') + ': ' + mapset)
00347             if not first_mapset:
00348                 first_mapset = mapset_node
00349             
00350             self.seltree.SetItemTextColour(mapset_node, wx.Colour(50, 50, 200))
00351             if mapset not in filesdict:
00352                 continue
00353             try:
00354                 elem_list = filesdict[mapset]
00355                 elem_list.sort()
00356                 for elem in elem_list:
00357                     if elem != '':
00358                         fullqElem = elem + '@' + mapset
00359                         if elements is not None:
00360                             if (exclude and fullqElem in elements) or \
00361                                     (not exclude and fullqElem not in elements):
00362                                 continue
00363                         
00364                         if self.filterElements:
00365                             if self.filterElements(fullqElem):
00366                                 self.AddItem(elem, parent = mapset_node)
00367                         else:
00368                             self.AddItem(elem, parent = mapset_node)
00369             except StandardError, e:
00370                 sys.stderr.write(_("GSelect: invalid item: %s") % e)
00371                 continue
00372             
00373             if self.seltree.ItemHasChildren(mapset_node):
00374                 sel = UserSettings.Get(group='appearance', key='elementListExpand',
00375                                        subkey='selection')
00376                 collapse = True
00377 
00378                 if sel == 0: # collapse all except PERMANENT and current
00379                     if mapset in ('PERMANENT', curr_mapset):
00380                         collapse = False
00381                 elif sel == 1: # collapse all except PERMANENT
00382                     if mapset == 'PERMANENT':
00383                         collapse = False
00384                 elif sel == 2: # collapse all except current
00385                     if mapset == curr_mapset:
00386                         collapse = False
00387                 elif sel == 3: # collapse all
00388                     pass
00389                 elif sel == 4: # expand all
00390                     collapse = False
00391                 
00392                 if collapse:
00393                     self.seltree.Collapse(mapset_node)
00394                 else:
00395                     self.seltree.Expand(mapset_node)
00396         
00397         if first_mapset:
00398             # select first mapset (MSW hack)
00399             self.seltree.SelectItem(first_mapset)
00400     
00401     # helpers
00402     def FindItem(self, parentItem, text, startLetters = False):
00403         """!Finds item with given name or starting with given text"""
00404         startletters = startLetters
00405         item, cookie = self.seltree.GetFirstChild(parentItem)
00406         while wx.TreeItemId.IsOk(item):
00407             if self.seltree.GetItemText(item) == text:
00408                 return item
00409             if self.seltree.ItemHasChildren(item):
00410                 item = self.FindItem(item, text, startLetters = startletters)
00411                 if wx.TreeItemId.IsOk(item):
00412                     return item
00413             elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]):
00414                 return item
00415             item, cookie = self.seltree.GetNextChild(parentItem, cookie)
00416         return wx.TreeItemId()
00417     
00418     def AddItem(self, value, parent=None):
00419         if not parent:
00420             root = self.seltree.GetRootItem()
00421             if not root:
00422                 root = self.seltree.AddRoot("<hidden root>")
00423             parent = root
00424 
00425         item = self.seltree.AppendItem(parent, text=value)
00426         return item
00427     
00428     # able to recieve only wx.EVT_KEY_UP
00429     def OnKeyUp(self, event):
00430         """!Enables to select items using keyboard"""
00431         
00432         item = self.seltree.GetSelection()
00433         if event.GetKeyCode() == wx.WXK_DOWN:
00434             self.seltree.SelectItem(self.seltree.GetNextVisible(item))
00435             
00436         # problem with GetPrevVisible   
00437         elif event.GetKeyCode() == wx.WXK_UP: 
00438             if self.seltree.ItemHasChildren(item) and self.seltree.IsExpanded(self.seltree.GetPrevSibling(item)):
00439                 itemPrev = self.seltree.GetLastChild(self.seltree.GetPrevSibling(item))
00440             else:
00441                 itemPrev = self.seltree.GetPrevSibling(item)
00442                 if not wx.TreeItemId.IsOk(itemPrev):
00443                     itemPrev = self.seltree.GetItemParent(item)
00444                     if item == self.seltree.GetFirstChild(self.seltree.GetRootItem())[0]:
00445                         itemPrev = item
00446             self.seltree.SelectItem(itemPrev)
00447         
00448         # selects first item starting with the written text in next mapset
00449         elif event.GetKeyCode() == wx.WXK_TAB:
00450             selected = self.seltree.GetSelection()
00451             if self.seltree.ItemHasChildren(selected):
00452                 parent = selected
00453             else:
00454                 parent = self.seltree.GetItemParent(selected)
00455             nextSibling = self.seltree.GetNextSibling(parent)
00456             if wx.TreeItemId.IsOk(nextSibling):
00457                 match = self.FindItem(nextSibling, self.GetCombo().GetValue().strip(), True) 
00458             else: 
00459                 match = self.FindItem(self.seltree.GetFirstChild(self.seltree.GetItemParent(parent))[0],
00460                                         self.GetCombo().GetValue().strip(), True) 
00461             self.seltree.SelectItem(match)
00462             
00463         elif event.GetKeyCode() == wx.WXK_RIGHT:
00464             if self.seltree.ItemHasChildren(item):
00465                 self.seltree.Expand(item)
00466         
00467         elif event.GetKeyCode() == wx.WXK_LEFT:
00468             if self.seltree.ItemHasChildren(item):
00469                 self.seltree.Collapse(item)
00470                 
00471         elif event.GetKeyCode() == wx.WXK_ESCAPE:
00472             self.Dismiss()
00473             
00474         elif event.GetKeyCode() == wx.WXK_RETURN:
00475             if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
00476                 self.value = [] 
00477             else:
00478                 mapsetItem = self.seltree.GetItemParent(item)
00479                 fullName = self.seltree.GetItemText(item)
00480                 if self.fullyQualified:
00481                     fullName += '@' + self.seltree.GetItemText(mapsetItem).split(':', -1)[1].strip()
00482                 
00483                 if self.multiple is True:
00484                     # text item should be unique
00485                     self.value.append(fullName)
00486                 else:
00487                     self.value = [fullName]
00488             
00489             self.Dismiss()
00490         
00491     def OnMotion(self, evt):
00492         """!Have the selection follow the mouse, like in a real combobox
00493         """
00494         item, flags = self.seltree.HitTest(evt.GetPosition())
00495         if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
00496             self.seltree.SelectItem(item)
00497             self.curitem = item
00498         evt.Skip()
00499 
00500     def OnLeftDown(self, evt):
00501         """!Do the combobox selection
00502         """
00503         item, flags = self.seltree.HitTest(evt.GetPosition())
00504         if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
00505             self.curitem = item
00506             
00507             if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
00508                 self.value = [] # cannot select mapset item
00509             else:
00510                 mapsetItem = self.seltree.GetItemParent(item)
00511                 fullName = self.seltree.GetItemText(item)
00512                 if self.fullyQualified:
00513                     fullName += '@' + self.seltree.GetItemText(mapsetItem).split(':', -1)[1].strip()
00514                 
00515                 if self.multiple is True:
00516                     # text item should be unique
00517                     self.value.append(fullName)
00518                 else:
00519                     self.value = [fullName]
00520             
00521             self.Dismiss()
00522         
00523         evt.Skip()
00524 
00525     def SetData(self, **kargs):
00526         """!Set object properties"""
00527         if 'type' in kargs:
00528             self.type = kargs['type']
00529         if 'mapsets' in kargs:
00530             self.mapsets = kargs['mapsets']
00531         if 'multiple' in kargs:
00532             self.multiple = kargs['multiple']
00533         if 'updateOnPopup' in kargs:
00534             self.updateOnPopup = kargs['updateOnPopup']
00535         if 'onPopup' in kargs:
00536             self.onPopup = kargs['onPopup']
00537         if 'fullyQualified' in kargs:
00538             self.fullyQualified = kargs['fullyQualified']
00539         
00540     def GetType(self):
00541         """!Get element type
00542         """
00543         return self.type
00544     
00545 class VectorDBInfo:
00546     """!Class providing information about attribute tables
00547     linked to a vector map"""
00548     def __init__(self, map):
00549         self.map = map
00550 
00551         # dictionary of layer number and associated (driver, database, table)
00552         self.layers = {}
00553          # dictionary of table and associated columns (type, length, values, ids)
00554         self.tables = {}
00555         
00556         if not self._CheckDBConnection(): # -> self.layers
00557             return
00558 
00559         self._DescribeTables() # -> self.tables
00560 
00561     def _CheckDBConnection(self):
00562         """!Check DB connection"""
00563         nuldev = file(os.devnull, 'w+')
00564         self.layers = grass.vector_db(map=self.map, stderr=nuldev)
00565         nuldev.close()
00566         
00567         if (len(self.layers.keys()) == 0):
00568             return False
00569 
00570         return True
00571 
00572     def _DescribeTables(self):
00573         """!Describe linked tables"""
00574         for layer in self.layers.keys():
00575             # determine column names and types
00576             table = self.layers[layer]["table"]
00577             columns = {} # {name: {type, length, [values], [ids]}}
00578             i = 0
00579             Debug.msg(1, "gselect.VectorDBInfo._DescribeTables(): table=%s driver=%s database=%s" % \
00580                           (self.layers[layer]["table"], self.layers[layer]["driver"],
00581                            self.layers[layer]["database"]))
00582             for item in grass.db_describe(table = self.layers[layer]["table"],
00583                                           driver = self.layers[layer]["driver"],
00584                                           database = self.layers[layer]["database"])['cols']:
00585                 name, type, length = item
00586                 # FIXME: support more datatypes
00587                 if type.lower() == "integer":
00588                     ctype = int
00589                 elif type.lower() == "double precision":
00590                     ctype = float
00591                 else:
00592                     ctype = str
00593 
00594                 columns[name.strip()] = { 'index'  : i,
00595                                           'type'   : type.lower(),
00596                                           'ctype'  : ctype,
00597                                           'length' : int(length),
00598                                           'values' : [],
00599                                           'ids'    : []}
00600                 i += 1
00601             
00602             # check for key column
00603             # v.db.connect -g/p returns always key column name lowercase
00604             if self.layers[layer]["key"] not in columns.keys():
00605                 for col in columns.keys():
00606                     if col.lower() == self.layers[layer]["key"]:
00607                         self.layers[layer]["key"] = col.upper()
00608                         break
00609             
00610             self.tables[table] = columns
00611             
00612         return True
00613     
00614     def Reset(self):
00615         """!Reset"""
00616         for layer in self.layers:
00617             table = self.layers[layer]["table"] # get table desc
00618             columns = self.tables[table]
00619             for name in self.tables[table].keys():
00620                 self.tables[table][name]['values'] = []
00621                 self.tables[table][name]['ids']    = []
00622     
00623     def GetName(self):
00624         """!Get vector name"""
00625         return self.map
00626     
00627     def GetKeyColumn(self, layer):
00628         """!Get key column of given layer
00629         
00630         @param layer vector layer number
00631         """
00632         return str(self.layers[layer]['key'])
00633     
00634     def GetTable(self, layer):
00635         """!Get table name of given layer
00636         
00637         @param layer vector layer number
00638         """
00639         return self.layers[layer]['table']
00640     
00641     def GetDbSettings(self, layer):
00642         """!Get database settins
00643 
00644         @param layer layer number
00645         
00646         @return (driver, database)
00647         """
00648         return self.layers[layer]['driver'], self.layers[layer]['database']
00649     
00650     def GetTableDesc(self, table):
00651         """!Get table columns
00652 
00653         @param table table name
00654         """
00655         return self.tables[table]
00656 
00657 class LayerSelect(wx.ComboBox):
00658     """!Creates combo box for selecting data layers defined for vector.
00659     """
00660     def __init__(self, parent,
00661                  id = wx.ID_ANY, pos = wx.DefaultPosition,
00662                  size = globalvar.DIALOG_LAYER_SIZE,
00663                  vector = None, choices = [], initial = [], default = None):
00664         
00665         super(LayerSelect, self).__init__(parent, id, pos=pos, size=size,
00666                                           choices=choices)
00667         
00668         self.parent  = parent
00669         self.initial = initial
00670         
00671         self.SetName("LayerSelect")
00672 
00673         # default value
00674         self.default = default
00675 
00676         self.InsertLayers(vector = vector)
00677         
00678     def InsertLayers(self, vector):
00679         """!Insert layers for a vector into the layer combobox
00680 
00681         @param vector name of vector map
00682         """
00683         if vector:
00684             layers = GetVectorNumberOfLayers(vector)
00685         else:
00686             layers = list()
00687         
00688         for layer in self.initial:
00689             if layer in layers:
00690                 continue
00691             layers.append(layer)
00692         
00693         if self.default:
00694             if len(layers) == 0:
00695                 layers.insert(0, str(self.default))
00696             elif self.default not in layers:
00697                 layers.append(self.default)
00698         
00699         if len(layers) >= 1:
00700             self.SetItems(layers)
00701 
00702     def Reset(self):
00703         """!Reset value"""
00704         items = self.GetItems()
00705         if items:
00706             if '-1' in items:
00707                 self.SetStringSelection('-1')
00708             else:
00709                 self.SetSelection(0)
00710         else:
00711             self.SetValue('')
00712         
00713 class DriverSelect(wx.ComboBox):
00714     """!Creates combo box for selecting database driver.
00715     """
00716     def __init__(self, parent, choices, value,
00717                  id=wx.ID_ANY, pos=wx.DefaultPosition,
00718                  size=globalvar.DIALOG_LAYER_SIZE, **kargs):
00719 
00720         super(DriverSelect, self).__init__(parent, id, value, pos, size,
00721                                            choices, style=wx.CB_READONLY)
00722         
00723         self.SetName("DriverSelect")
00724         
00725         self.SetStringSelection(value)
00726 
00727 class DatabaseSelect(wx.TextCtrl):
00728     """!Creates combo box for selecting database driver.
00729     """
00730     def __init__(self, parent, value='',
00731                  id=wx.ID_ANY, pos=wx.DefaultPosition,
00732                  size=globalvar.DIALOG_TEXTCTRL_SIZE, **kargs):
00733         
00734         super(DatabaseSelect, self).__init__(parent, id, value, pos, size)
00735                                
00736         self.SetName("DatabaseSelect")
00737 
00738 class TableSelect(wx.ComboBox):
00739     """!Creates combo box for selecting attribute tables from the database
00740     """
00741     def __init__(self, parent,
00742                  id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
00743                  size=globalvar.DIALOG_COMBOBOX_SIZE,
00744                  choices=[]):
00745 
00746         super(TableSelect, self).__init__(parent, id, value, pos, size, choices,
00747                                           style=wx.CB_READONLY)
00748 
00749         self.SetName("TableSelect")
00750 
00751         if not choices:
00752             self.InsertTables()
00753                 
00754     def InsertTables(self, driver=None, database=None):
00755         """!Insert attribute tables into combobox"""
00756         items = []
00757 
00758         if not driver or not database:
00759             connect = grass.db_connection()
00760             
00761             driver = connect['driver']
00762             database = connect['database']
00763         
00764         ret = RunCommand('db.tables',
00765                          flags = 'p',
00766                          read = True,
00767                          driver = driver,
00768                          database = database)
00769         
00770         if ret:
00771             for table in ret.splitlines():
00772                 items.append(table)
00773             
00774         self.SetItems(items)
00775         self.SetValue('')
00776         
00777 class ColumnSelect(wx.ComboBox):
00778     """!Creates combo box for selecting columns in the attribute table
00779     for a vector map.
00780 
00781     @param parent window parent
00782     @param id window id
00783     @param value default value
00784     @param size window size
00785     @param vector vector map name
00786     @param layer layer number
00787     @param param parameters list (see menuform.py)
00788     @param **kwags wx.ComboBox parameters
00789     """
00790     def __init__(self, parent, id = wx.ID_ANY, value = '', 
00791                  size=globalvar.DIALOG_COMBOBOX_SIZE,
00792                  vector = None, layer = 1, param = None, **kwargs):
00793         self.defaultValue = value
00794         self.param = param
00795         
00796         super(ColumnSelect, self).__init__(parent, id, value, size = size, **kwargs)
00797         self.SetName("ColumnSelect")
00798         
00799         if vector:
00800             self.InsertColumns(vector, layer)
00801     
00802     def InsertColumns(self, vector, layer, excludeKey = False, excludeCols = None, type = None, dbInfo = None):
00803         """!Insert columns for a vector attribute table into the columns combobox
00804 
00805         @param vector vector name
00806         @param layer vector layer number
00807         @param excludeKey exclude key column from the list?
00808         @param excludeCols list of columns to be removed from the list
00809         @param type only columns of given type (given as list)
00810         """
00811         if not dbInfo:
00812             dbInfo = VectorDBInfo(vector)
00813         
00814         try:
00815             table = dbInfo.GetTable(int(layer))
00816             columnchoices = dbInfo.GetTableDesc(table)
00817             keyColumn = dbInfo.GetKeyColumn(int(layer))
00818             columns = len(columnchoices.keys()) * ['']
00819             for key, val in columnchoices.iteritems():
00820                 columns[val['index']] = key
00821             if excludeKey: # exclude key column
00822                 columns.remove(keyColumn)
00823             if excludeCols: # exclude key column
00824                 for key in columnchoices.iterkeys():
00825                     if key in excludeCols:
00826                         columns.remove(key)
00827             if type: # only selected column types
00828                 for key, value in columnchoices.iteritems():
00829                     if value['type'] not in type:
00830                         try:
00831                             columns.remove(key)
00832                         except ValueError:
00833                             pass
00834         except (KeyError, ValueError):
00835             columns = list()
00836             
00837         self.SetItems(columns)
00838         self.SetValue(self.defaultValue)
00839         
00840         if self.param:
00841             value = self.param.get('value', '')
00842             if value != '' and value in columns:
00843                 self.SetValue(value)
00844         
00845     def InsertTableColumns(self, table, driver=None, database=None):
00846         """!Insert table columns
00847 
00848         @param table table name
00849         @param driver driver name
00850         @param database database name
00851         """
00852         columns = list()
00853         
00854         ret = RunCommand('db.columns',
00855                          read = True,
00856                          driver = driver,
00857                          database = database,
00858                          table = table)
00859         
00860         if ret:
00861             columns = ret.splitlines()
00862         
00863         self.SetItems(columns)
00864         self.SetValue(self.defaultValue)
00865         
00866         if self.param:
00867             value = self.param.get('value', '')
00868             if value != '' and value in columns:
00869                 self.SetValue(value)
00870 
00871 class DbaseSelect(wx.lib.filebrowsebutton.DirBrowseButton):
00872     """!Widget for selecting GRASS Database"""
00873     def __init__(self, parent, **kwargs):
00874         super(DbaseSelect, self).__init__(parent, id = wx.ID_ANY,
00875                                           size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
00876                                           dialogTitle = _('Choose GIS Data Directory'),
00877                                           buttonText = _('Browse'),
00878                                           startDirectory = grass.gisenv()['GISDBASE'],
00879                                           **kwargs)
00880         
00881 class LocationSelect(wx.ComboBox):
00882     """!Widget for selecting GRASS location"""
00883     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
00884                  gisdbase = None, **kwargs):
00885         super(LocationSelect, self).__init__(parent, id, size = size, 
00886                                              style = wx.CB_READONLY, **kwargs)
00887         self.SetName("LocationSelect")
00888         
00889         if not gisdbase:
00890             self.gisdbase = grass.gisenv()['GISDBASE']
00891         else:
00892             self.gisdbase = gisdbase
00893         
00894         self.SetItems(GetListOfLocations(self.gisdbase))
00895 
00896     def UpdateItems(self, dbase):
00897         """!Update list of locations
00898 
00899         @param dbase path to GIS database
00900         """
00901         self.gisdbase = dbase
00902         if dbase:
00903             self.SetItems(GetListOfLocations(self.gisdbase))
00904         else:
00905             self.SetItems([])
00906         
00907 class MapsetSelect(wx.ComboBox):
00908     """!Widget for selecting GRASS mapset"""
00909     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
00910                  gisdbase = None, location = None, setItems = True,
00911                  searchPath = False, skipCurrent = False, **kwargs):
00912         super(MapsetSelect, self).__init__(parent, id, size = size, 
00913                                            style = wx.CB_READONLY, **kwargs)
00914         self.searchPath  = searchPath
00915         self.skipCurrent = skipCurrent
00916         
00917         self.SetName("MapsetSelect")
00918         if not gisdbase:
00919             self.gisdbase = grass.gisenv()['GISDBASE']
00920         else:
00921             self.gisdbase = gisdbase
00922         
00923         if not location:
00924             self.location = grass.gisenv()['LOCATION_NAME']
00925         else:
00926             self.location = location
00927         
00928         if setItems:
00929             self.SetItems(self._getMapsets())
00930         
00931     def UpdateItems(self, location, dbase = None):
00932         """!Update list of mapsets for given location
00933 
00934         @param dbase path to GIS database (None to use currently selected)
00935         @param location name of location
00936         """
00937         if dbase:
00938             self.gisdbase = dbase
00939         self.location = location
00940         if location:
00941             self.SetItems(self._getMapsets())
00942         else:
00943             self.SetItems([])
00944      
00945     def _getMapsets(self):
00946         if self.searchPath:
00947             mlist = RunCommand('g.mapsets',
00948                                read = True, flags = 'p',
00949                                fs = 'newline').splitlines()
00950         else:
00951             mlist = GetListOfMapsets(self.gisdbase, self.location,
00952                                      selectable = False)
00953         
00954         gisenv = grass.gisenv()
00955         if self.skipCurrent and \
00956                 gisenv['LOCATION_NAME'] == self.location and \
00957                 gisenv['MAPSET'] in mlist:
00958             mlist.remove(gisenv['MAPSET'])
00959         
00960         return mlist
00961     
00962 class SubGroupSelect(wx.ComboBox):
00963     """!Widget for selecting subgroups"""
00964     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE, 
00965                  **kwargs):
00966         super(SubGroupSelect, self).__init__(parent, id, size = size, 
00967                                              **kwargs)
00968         self.SetName("SubGroupSelect")
00969 
00970     def Insert(self, group):
00971         """!Insert subgroups for defined group"""
00972         if not group:
00973             return
00974         gisenv = grass.gisenv()
00975         try:
00976             name, mapset = group.split('@', 1)
00977         except ValueError:
00978             name = group
00979             mapset = gisenv['MAPSET']
00980         
00981         path = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'], mapset,
00982                             'group', name, 'subgroup')
00983         try:
00984             self.SetItems(os.listdir(path))
00985         except OSError:
00986             self.SetItems([])
00987         self.SetValue('')
00988 
00989 class FormatSelect(wx.Choice):
00990     def __init__(self, parent, ogr = False,
00991                  sourceType = None, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
00992                  **kwargs):
00993         """!Widget for selecting external (GDAL/OGR) format
00994 
00995         @param parent parent window
00996         @param sourceType source type ('file', 'directory', 'database', 'protocol') or None
00997         @param ogr True for OGR otherwise GDAL
00998         """
00999         super(FormatSelect, self).__init__(parent, id, size = size, 
01000                                            **kwargs)
01001         self.SetName("FormatSelect")
01002         
01003         if ogr:
01004             ftype = 'ogr'
01005         else:
01006             ftype = 'gdal'
01007         
01008         formats = list()
01009         for f in GetFormats()[ftype].values():
01010             formats += f
01011         self.SetItems(formats)
01012         
01013     def GetExtension(self, name):
01014         """!Get file extension by format name"""
01015         formatToExt = {
01016             # raster
01017             'GeoTIFF' : 'tif',
01018             'Erdas Imagine Images (.img)' : 'img',
01019             'Ground-based SAR Applications Testbed File Format (.gff)' : 'gff',
01020             'Arc/Info Binary Grid' : 'adf',
01021             'Portable Network Graphics' : 'png',
01022             'JPEG JFIF' : 'jpg',
01023             'Japanese DEM (.mem)' : 'mem',
01024             'Graphics Interchange Format (.gif)' : 'gif',
01025             'X11 PixMap Format' : 'xpm',
01026             'MS Windows Device Independent Bitmap' : 'bmp',
01027             'SPOT DIMAP' : 'dim',
01028             'RadarSat 2 XML Product' : 'xml',
01029             'EarthWatch .TIL' : 'til',
01030             'ERMapper .ers Labelled' : 'ers',
01031             'ERMapper Compressed Wavelets' : 'ecw',
01032             'GRIdded Binary (.grb)' : 'grb',
01033             'EUMETSAT Archive native (.nat)' : 'nat',
01034             'Idrisi Raster A.1' : 'rst',
01035             'Golden Software ASCII Grid (.grd)' : 'grd',
01036             'Golden Software Binary Grid (.grd)' : 'grd',
01037             'Golden Software 7 Binary Grid (.grd)' : 'grd',
01038             'R Object Data Store' : 'r',
01039             'USGS DOQ (Old Style)' : 'doq',
01040             'USGS DOQ (New Style)' : 'doq',
01041             'ENVI .hdr Labelled' : 'hdr',
01042             'ESRI .hdr Labelled' : 'hdr',
01043             'Generic Binary (.hdr Labelled)' : 'hdr',
01044             'PCI .aux Labelled' : 'aux',
01045             'EOSAT FAST Format' : 'fst',
01046             'VTP .bt (Binary Terrain) 1.3 Format' : 'bt',
01047             'FARSITE v.4 Landscape File (.lcp)' : 'lcp',
01048             'Swedish Grid RIK (.rik)' : 'rik',
01049             'USGS Optional ASCII DEM (and CDED)' : 'dem',
01050             'Northwood Numeric Grid Format .grd/.tab' : '',
01051             'Northwood Classified Grid Format .grc/.tab' : '',
01052             'ARC Digitized Raster Graphics' : 'arc',
01053             'Magellan topo (.blx)' : 'blx',
01054             'SAGA GIS Binary Grid (.sdat)' : 'sdat',
01055             # vector
01056             'ESRI Shapefile' : 'shp',
01057             'UK .NTF'        : 'ntf',
01058             'SDTS'           : 'ddf',
01059             'DGN'            : 'dgn',
01060             'VRT'            : 'vrt',
01061             'REC'            : 'rec',
01062             'BNA'            : 'bna',
01063             'CSV'            : 'csv',
01064             'GML'            : 'gml',
01065             'GPX'            : 'gpx',
01066             'KML'            : 'kml',
01067             'GMT'            : 'gmt',
01068             'PGeo'           : 'mdb',
01069             'XPlane'         : 'dat',
01070             'AVCBin'         : 'adf',
01071             'AVCE00'         : 'e00',
01072             'DXF'            : 'dxf',
01073             'Geoconcept'     : 'gxt',
01074             'GeoRSS'         : 'xml',
01075             'GPSTrackMaker'  : 'gtm',
01076             'VFK'            : 'vfk'
01077             }
01078         
01079         try:
01080             return formatToExt[name]
01081         except KeyError:
01082             return ''
01083         
01084 class GdalSelect(wx.Panel):
01085     def __init__(self, parent, panel, ogr = False, link = False, dest = False, 
01086                  default = 'file', exclude = [], envHandler = None):
01087         """!Widget for selecting GDAL/OGR datasource, format
01088         
01089         @param parent parent window
01090         @param ogr    use OGR selector instead of GDAL
01091         @param dest   True for output (destination)
01092         @param default deafult type (ignored when dest == True)
01093         @param exclude list of types to be excluded
01094         """
01095         self.parent = parent
01096         self.ogr    = ogr
01097         self.dest   = dest 
01098         wx.Panel.__init__(self, parent = panel, id = wx.ID_ANY)
01099 
01100         self.settingsBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
01101                                         label = " %s " % _("Settings"))
01102         
01103         self.inputBox = wx.StaticBox(parent = self, id = wx.ID_ANY)
01104         if dest:
01105             self.inputBox.SetLabel(" %s " % _("Output settings"))
01106         else:
01107             self.inputBox.SetLabel(" %s " % _("Source settings"))
01108         
01109         # source type
01110         sources = list()
01111         self.sourceMap = { 'file'   : -1,
01112                            'dir'    : -1,
01113                            'db'     : -1,
01114                            'pro'    : -1,
01115                            'native' : -1 }
01116         idx = 0
01117         if dest:
01118             sources.append(_("Native"))
01119             self.sourceMap['native'] = idx
01120             idx += 1
01121         if 'file' not in exclude:
01122             sources.append(_("File"))
01123             self.sourceMap['file'] = idx
01124             idx += 1
01125         if 'directory' not in exclude:
01126             sources.append(_("Directory"))
01127             self.sourceMap['dir'] = idx
01128             idx += 1
01129         if 'database' not in exclude:
01130             sources.append(_("Database"))
01131             self.sourceMap['db'] = idx
01132             idx += 1
01133         if 'protocol' not in exclude:
01134             sources.append(_("Protocol"))
01135             self.sourceMap['pro'] = idx
01136             idx += 1
01137         
01138         if self.ogr:
01139             self.settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
01140         else:
01141             self.settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
01142 
01143         self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
01144         self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsLoad)
01145         self._settings = self._loadSettings() # -> self.settingsChoice.SetItems()
01146         self.btnSettingsSave = wx.Button(parent = self, id = wx.ID_SAVE)
01147         self.btnSettingsSave.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
01148         self.btnSettingsSave.SetToolTipString(_("Save current settings"))
01149         self.btnSettingsDel = wx.Button(parent = self, id = wx.ID_REMOVE)
01150         self.btnSettingsDel.Bind(wx.EVT_BUTTON, self.OnSettingsDelete)
01151         self.btnSettingsSave.SetToolTipString(_("Delete currently selected settings"))
01152         
01153         self.source = wx.RadioBox(parent = self, id = wx.ID_ANY,
01154                                   style = wx.RA_SPECIFY_COLS,
01155                                   choices = sources)
01156         if dest:
01157             self.source.SetLabel(" %s " % _('Output type'))
01158         else:
01159             self.source.SetLabel(" %s " % _('Source type'))
01160         
01161         self.source.SetSelection(0)
01162         self.source.Bind(wx.EVT_RADIOBOX, self.OnSetType)
01163         
01164         # dsn widgets
01165         if not ogr:
01166             filemask = 'GeoTIFF (%s)|%s|%s (*.*)|*.*' % \
01167                 (self._getExtPattern('tif'), self._getExtPattern('tif'), _('All files'))
01168         else:
01169             filemask = 'ESRI Shapefile (%s)|%s|%s (*.*)|*.*' % \
01170                 (self._getExtPattern('shp'), self._getExtPattern('shp'), _('All files'))
01171         
01172         dsnFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
01173                                               size=globalvar.DIALOG_GSELECT_SIZE, labelText = '',
01174                                               dialogTitle=_('Choose file to import'),
01175                                               buttonText=_('Browse'),
01176                                               startDirectory=os.getcwd(),
01177                                               changeCallback=self.OnSetDsn,
01178                                               fileMask=filemask)
01179         dsnFile.Hide()
01180         
01181         dsnDir = filebrowse.DirBrowseButton(parent=self, id=wx.ID_ANY, 
01182                                             size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
01183                                             dialogTitle=_('Choose input directory'),
01184                                             buttonText=_('Browse'),
01185                                             startDirectory=os.getcwd(),
01186                                             changeCallback=self.OnSetDsn)
01187         dsnDir.SetName('GdalSelect')
01188         dsnDir.Hide()
01189         
01190         dsnDbFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
01191                                                 size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
01192                                                 dialogTitle=_('Choose file'),
01193                                                 buttonText=_('Browse'),
01194                                                 startDirectory=os.getcwd(),
01195                                                 changeCallback=self.OnSetDsn)
01196         dsnDbFile.Hide()
01197         dsnDbFile.SetName('GdalSelect')
01198 
01199         dsnDbText = wx.TextCtrl(parent = self, id = wx.ID_ANY)
01200         dsnDbText.Hide()
01201         dsnDbText.Bind(wx.EVT_TEXT, self.OnSetDsn)
01202         dsnDbText.SetName('GdalSelect')
01203 
01204         dsnDbChoice = wx.Choice(parent = self, id = wx.ID_ANY)
01205         dsnDbChoice.Hide()
01206         dsnDbChoice.Bind(wx.EVT_CHOICE, self.OnSetDsn)
01207         dsnDbChoice.SetName('GdalSelect')
01208 
01209         dsnPro = wx.TextCtrl(parent = self, id = wx.ID_ANY)
01210         dsnPro.Hide()
01211         dsnPro.Bind(wx.EVT_TEXT, self.OnSetDsn)
01212         dsnPro.SetName('GdalSelect')
01213 
01214         # format
01215         self.format = FormatSelect(parent = self,
01216                                    ogr = ogr)
01217         self.format.Bind(wx.EVT_CHOICE, self.OnSetFormat)
01218         self.extension = wx.TextCtrl(parent = self, id = wx.ID_ANY)
01219         self.extension.Bind(wx.EVT_TEXT, self.OnSetExtension)
01220         self.extension.Hide()
01221         
01222         if ogr:
01223             fType = 'ogr'
01224         else:
01225             fType = 'gdal'
01226         self.input = { 'file' : [_("File:"),
01227                                  dsnFile,
01228                                  GetFormats()[fType]['file']],
01229                        'dir'  : [_("Name:"),
01230                                  dsnDir,
01231                                  GetFormats()[fType]['file']],
01232                        'db'   : [_("Name:"),
01233                                  dsnDbText,
01234                                  GetFormats()[fType]['database']],
01235                        'pro'  : [_("Protocol:"),
01236                                  dsnPro,
01237                                  GetFormats()[fType]['protocol']],
01238                        'db-win' : { 'file'   : dsnDbFile,
01239                                     'text'   : dsnDbText,
01240                                     'choice' : dsnDbChoice },
01241                        'native' : [_("Name:"), dsnDir, ''],
01242                        }
01243         
01244         if self.dest:
01245             current = RunCommand('v.external.out',
01246                                  parent = self,
01247                                  read = True, parse = grass.parse_key_val,
01248                                  flags = 'g')
01249             if current['format'] == 'native':
01250                 self.dsnType = 'native'
01251             elif current['format'] in GetFormats()['ogr']['database']:
01252                 self.dsnType = 'db'
01253             else:
01254                 self.dsnType = 'dir'
01255         else:
01256             self.dsnType = default
01257         
01258         self.dsnText = wx.StaticText(parent = self, id = wx.ID_ANY,
01259                                      label = self.input[self.dsnType][0],
01260                                      size = (75, -1))
01261         self.extensionText = wx.StaticText(parent = self, id = wx.ID_ANY,
01262                                            label = _("Extension:"))
01263         self.extensionText.Hide()
01264         
01265         self.creationOpt = wx.TextCtrl(parent = self, id = wx.ID_ANY)
01266         if not self.dest:
01267             self.creationOpt.Hide()
01268         
01269         self._layout()
01270         
01271         self.OnSetType(event = None, sel = self.sourceMap[self.dsnType])
01272         if self.dest:
01273             if current['format'] != 'native':
01274                 self.OnSetFormat(event = None, format = current['format'])
01275                 self.OnSetDsn(event = None, path = current['dsn'])
01276                 self.creationOpt.SetValue(current['options'])
01277         else:
01278             if not ogr:
01279                 self.OnSetFormat(event = None, format = 'GeoTIFF')
01280             else:
01281                 self.OnSetFormat(event = None, format = 'ESRI Shapefile')
01282         
01283     def _layout(self):
01284         """!Layout"""
01285         mainSizer = wx.BoxSizer(wx.VERTICAL)
01286         
01287         settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
01288         settingsSizer.Add(item = wx.StaticText(parent = self,
01289                                                id = wx.ID_ANY,
01290                                                label = _("Load settings:")),
01291                           flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
01292                           border  = 5)
01293         settingsSizer.Add(item = self.settingsChoice,
01294                           proportion = 1,
01295                           flag = wx.EXPAND)
01296         settingsSizer.Add(item = self.btnSettingsSave,
01297                           flag = wx.LEFT | wx.RIGHT,
01298                           border = 5)
01299         settingsSizer.Add(item = self.btnSettingsDel,
01300                           flag = wx.RIGHT,
01301                           border = 5)
01302         
01303         inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
01304         
01305         self.dsnSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
01306         #self.dsnSizer.AddGrowableRow(0)
01307         self.dsnSizer.AddGrowableCol(3)
01308         
01309         row = 0
01310         self.dsnSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
01311                                                label = _("Format:")),
01312                           flag = wx.ALIGN_CENTER_VERTICAL,
01313                           pos = (row, 0))
01314         self.dsnSizer.Add(item=self.format,
01315                           flag = wx.ALIGN_CENTER_VERTICAL,
01316                           pos = (row, 1))
01317         self.dsnSizer.Add(item = self.extensionText,
01318                           flag=wx.ALIGN_CENTER_VERTICAL,
01319                           pos = (row, 2))
01320         self.dsnSizer.Add(item=self.extension,
01321                           flag = wx.ALIGN_CENTER_VERTICAL,
01322                           pos = (row, 3))
01323         row += 1
01324         self.dsnSizer.Add(item = self.dsnText,
01325                           flag = wx.ALIGN_CENTER_VERTICAL,
01326                           pos = (row, 0))
01327         self.dsnSizer.Add(item = self.input[self.dsnType][1],
01328                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
01329                           pos = (row, 1), span = (1, 3))
01330         row += 1
01331         if self.creationOpt.IsShown():
01332             self.dsnSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
01333                                                    label = _("Creation options:")),
01334                               flag = wx.ALIGN_CENTER_VERTICAL,
01335                               pos = (row, 0))
01336             self.dsnSizer.Add(item = self.creationOpt,
01337                               flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
01338                               pos = (row, 1), span = (1, 3))
01339             row += 1
01340         
01341         inputSizer.Add(item=self.dsnSizer, proportion = 1,
01342                        flag=wx.EXPAND | wx.BOTTOM, border = 10)
01343         
01344         mainSizer.Add(item=settingsSizer, proportion=0,
01345                       flag=wx.ALL | wx.EXPAND, border=5)
01346         mainSizer.Add(item=self.source, proportion=0,
01347                       flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5)
01348         mainSizer.Add(item=inputSizer, proportion=0,
01349                       flag=wx.ALL | wx.EXPAND, border=5)
01350         
01351         self.SetSizer(mainSizer)
01352         mainSizer.Fit(self)
01353 
01354     def _getExtPatternGlob(self, ext):
01355         """!Get pattern for case-insensitive globing"""
01356         pattern = '*.'
01357         for c in ext:
01358             pattern += '[%s%s]' % (c.lower(), c.upper())
01359         return pattern
01360     
01361     def _getExtPattern(self, ext):
01362         """!Get pattern for case-insensitive file mask"""
01363         return '*.%s;*.%s' % (ext.lower(), ext.upper())
01364 
01365     def OnSettingsLoad(self, event):
01366         """!Load named settings"""
01367         name = event.GetString()
01368         if name not in self._settings:
01369             GError(parent = self,
01370                    message = _("Settings <%s> not found") % name)
01371             return
01372         data = self._settings[name]
01373         self.OnSetType(event = None, sel = self.sourceMap[data[0]])
01374         self.OnSetFormat(event = None, format = data[2])
01375         self.OnSetDsn(event = None, path = data[1])
01376         self.creationOpt.SetValue(data[3])
01377         
01378     def OnSettingsSave(self, event):
01379         """!Save settings"""
01380         dlg = wx.TextEntryDialog(parent = self,
01381                                  message = _("Name:"),
01382                                  caption = _("Save settings"))
01383         if dlg.ShowModal() != wx.ID_OK:
01384             return
01385         
01386         # check required params
01387         if not dlg.GetValue():
01388             GMessage(parent = self,
01389                      message = _("Name not given, settings is not saved."))
01390             return
01391         
01392         if not self.GetDsn():
01393             GMessage(parent = self,
01394                      message = _("No data source defined, settings is not saved."))
01395             return
01396         
01397         name = dlg.GetValue()
01398         
01399         # check if settings item already exists
01400         if name in self._settings:
01401             dlgOwt = wx.MessageDialog(self, message = _("Settings <%s> already exists. "
01402                                                         "Do you want to overwrite the settings?") % name,
01403                                       caption = _("Save settings"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
01404             if dlgOwt.ShowModal() != wx.ID_YES:
01405                 dlgOwt.Destroy()
01406                 return
01407         
01408         self._settings[name] = (self.dsnType, self.GetDsn(),
01409                                 self.format.GetStringSelection(),
01410                                 self.creationOpt.GetValue())
01411         
01412         if self._saveSettings() == 0:
01413             self._settings = self._loadSettings()
01414             self.settingsChoice.SetStringSelection(name)
01415         
01416         dlg.Destroy()
01417  
01418     def OnSettingsDelete(self, event):
01419         """!Save settings"""
01420         name = self.settingsChoice.GetStringSelection()
01421         if not name:
01422             GMessage(parent = self,
01423                      message = _("No settings is defined. Operation canceled."))
01424             return
01425         
01426         self._settings.pop(name)
01427         if self._saveSettings() == 0:
01428             self._settings = self._loadSettings()
01429         
01430     def _saveSettings(self):
01431         """!Save settings into the file
01432 
01433         @return 0 on success
01434         @return -1 on failure
01435         """
01436         try:
01437             fd = open(self.settingsFile, 'w')
01438             for key, value in self._settings.iteritems():
01439                 fd.write('%s;%s;%s;%s\n' % (key, value[0], value[1], value[2]))
01440         except IOError:
01441             GError(parent = self,
01442                    message = _("Unable to save settings"))
01443             return -1
01444         else:
01445             fd.close()
01446         
01447         return 0
01448 
01449     def _loadSettings(self):
01450         """!Load settings from the file
01451 
01452         The file is defined by self.SettingsFile.
01453         
01454         @return parsed dict
01455         @return empty dict on error
01456         """
01457         data = dict()
01458         if not os.path.exists(self.settingsFile):
01459             return data
01460         
01461         try:
01462             fd = open(self.settingsFile, 'r')
01463             for line in fd.readlines():
01464                 try:
01465                     lineData = line.rstrip('\n').split(';')
01466                     if len(lineData) > 4:
01467                         # type, dsn, format, options
01468                         data[lineData[0]] = (lineData[1], lineData[2], lineData[3], lineData[4])
01469                     else:
01470                         data[lineData[0]] = (lineData[1], lineData[2], lineData[3], '')
01471                 except ValueError:
01472                     pass
01473         except IOError:
01474             return data
01475         else:
01476             fd.close()
01477         
01478         self.settingsChoice.SetItems(sorted(data.keys()))
01479         
01480         return data
01481 
01482     def OnSetType(self, event, sel = None):
01483         """!Datasource type changed"""
01484         if event:
01485             sel = event.GetSelection()
01486         else:
01487             self.source.SetSelection(sel)
01488         win = self.input[self.dsnType][1]
01489         if win:
01490             self.dsnSizer.Remove(win)
01491             win.Hide()
01492         
01493         if sel == self.sourceMap['file']:   # file
01494             self.dsnType = 'file'
01495             format = self.input[self.dsnType][2][0]
01496             try:
01497                 ext = self.format.GetExtension(format)
01498                 if not ext:
01499                     raise KeyError
01500                 format += ' (%s)|%s|%s (*.*)|*.*' % \
01501                     (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
01502             except KeyError:
01503                 format += '%s (*.*)|*.*' % _('All files')
01504             
01505             win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
01506                                               size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
01507                                               dialogTitle=_('Choose file to import'),
01508                                               buttonText=_('Browse'),
01509                                               startDirectory=os.getcwd(),
01510                                               changeCallback=self.OnSetDsn,
01511                                               fileMask = format)
01512             self.input[self.dsnType][1] = win
01513         
01514         elif sel == self.sourceMap['dir']: # directory
01515             self.dsnType = 'dir'
01516         elif sel == self.sourceMap['db']:  # database
01517             self.dsnType = 'db'
01518         elif sel == self.sourceMap['pro']: # protocol
01519             self.dsnType = 'pro'
01520         elif sel == self.sourceMap['native']:
01521             self.dsnType = 'native'
01522         
01523         if self.dsnType == 'db':
01524             self.input[self.dsnType][1] = self.input['db-win']['text']
01525         win = self.input[self.dsnType][1]
01526                 
01527         self.dsnSizer.Add(item = self.input[self.dsnType][1],
01528                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
01529                           pos = (1, 1), span = (1, 3))
01530         win.SetValue('')
01531         win.Show()
01532         
01533         if sel == self.sourceMap['native']: # native
01534             win.Enable(False)
01535             self.format.Enable(False)
01536             self.creationOpt.Enable(False)
01537             self.parent.btnOk.Enable(True)
01538         else:
01539             if not win.IsEnabled():
01540                 win.Enable(True)
01541             if not self.format.IsEnabled():
01542                 self.format.Enable(True)
01543                 self.creationOpt.Enable(True)
01544             self.dsnText.SetLabel(self.input[self.dsnType][0])
01545             self.format.SetItems(self.input[self.dsnType][2])
01546             if self.parent.GetName() == 'MultiImportDialog':
01547                 self.parent.list.DeleteAllItems()
01548         
01549         if sel in (self.sourceMap['file'], self.sourceMap['dir']):
01550             if not self.ogr:
01551                 self.OnSetFormat(event = None, format = 'GeoTIFF')
01552             else:
01553                 self.OnSetFormat(event = None, format = 'ESRI Shapefile')
01554         
01555         if sel == self.sourceMap['dir'] and not self.dest:
01556             if not self.extension.IsShown():
01557                 self.extensionText.Show()
01558                 self.extension.Show()
01559         else:
01560             if self.extension.IsShown():
01561                 self.extensionText.Hide()
01562                 self.extension.Hide()
01563         
01564         self.dsnSizer.Layout()
01565         
01566     def GetDsn(self):
01567         """!Get datasource name
01568         """
01569         if self.format.GetStringSelection() == 'PostgreSQL':
01570             dsn = 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
01571         else:
01572             dsn = self.input[self.dsnType][1].GetValue()
01573         
01574         return dsn
01575     
01576     def OnSetDsn(self, event, path = None):
01577         """!Input DXF file/OGR dsn defined, update list of layer
01578         widget"""
01579         if event:
01580             path = event.GetString()
01581         else:
01582             if self.format.GetStringSelection() == 'PostgreSQL':
01583                 for item in path.split(':', 1)[1].split(','):
01584                     key, value = item.split('=', 1)
01585                     if key == 'dbname':
01586                         if not self.input[self.dsnType][1].SetStringSelection(value):
01587                             GMessage(_("Database <%s> not accessible.") % value,
01588                                      parent = self)
01589                         break
01590             else:
01591                 self.input[self.dsnType][1].SetValue(path)
01592 
01593         if not path:
01594             if self.dest:
01595                 self.parent.btnOk.Enable(False)
01596             return
01597         
01598         if self.dest:
01599             self.parent.btnOk.Enable(True)
01600         else:
01601             self._reloadLayers()
01602         
01603         if event:
01604             event.Skip()
01605         
01606     def _reloadLayers(self):
01607         """!Reload list of layers"""
01608         dsn = self.GetDsn()
01609         if not dsn:
01610             return
01611         
01612         data = list()        
01613         layerId = 1
01614         
01615         if self.ogr:
01616             ret = RunCommand('v.in.ogr',
01617                              quiet = True,
01618                              read = True,
01619                              flags = 'l',
01620                              dsn = dsn)
01621             if not ret:
01622                 self.parent.list.LoadData()
01623                 if hasattr(self, "btn_run"):
01624                     self.btn_run.Enable(False)
01625                 return
01626             
01627             layerId = 1
01628             for line in ret.split(','):
01629                 layerName = line.strip()
01630                 grassName = GetValidLayerName(layerName)
01631                 data.append((layerId, layerName, grassName))
01632                 layerId += 1
01633         else:
01634             if self.dsnType == 'file':
01635                 baseName = os.path.basename(dsn)
01636                 grassName = GetValidLayerName(baseName.split('.', -1)[0])
01637                 data.append((layerId, baseName, grassName))
01638             elif self.dsnType == 'dir':
01639                 ext = self.extension.GetValue()
01640                 for filename in glob.glob(os.path.join(dsn, "%s") % self._getExtPatternGlob(ext)):
01641                     baseName = os.path.basename(filename)
01642                     grassName = GetValidLayerName(baseName.split('.', -1)[0])
01643                     data.append((layerId, baseName, grassName))
01644                     layerId += 1
01645         if self.ogr:
01646             dsn += '@OGR'
01647         
01648         evt = wxGdalSelect(dsn = dsn)
01649         evt.SetId(self.input[self.dsnType][1].GetId())
01650         wx.PostEvent(self.parent, evt)
01651         
01652         if self.parent.GetName() == 'MultiImportDialog':
01653             self.parent.list.LoadData(data)
01654             if len(data) > 0:
01655                 self.parent.btn_run.Enable(True)
01656             else:
01657                 self.parent.btn_run.Enable(False)
01658         
01659     def OnSetExtension(self, event):
01660         """!Extension changed"""
01661         if not self.dest:
01662             # reload layers
01663             self._reloadLayers()
01664         
01665     def OnSetFormat(self, event, format = None):
01666         """!Format changed"""
01667         if self.dsnType not in ['file', 'dir', 'db']:
01668             return
01669         
01670         win = self.input[self.dsnType][1]
01671         self.dsnSizer.Remove(win)
01672         
01673         if self.dsnType == 'file':
01674             win.Destroy()
01675         else: # database
01676             win.Hide()
01677         
01678         if event:
01679             format = event.GetString()
01680         else:
01681             self.format.SetStringSelection(format)
01682         
01683         if self.dsnType == 'file':
01684             try:
01685                 ext = self.format.GetExtension(format)
01686                 if not ext:
01687                     raise KeyError
01688                 format += ' (%s)|%s|%s (*.*)|*.*' % \
01689                     (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
01690             except KeyError:
01691                 format += '%s (*.*)|*.*' % _('All files')
01692             
01693             win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
01694                                               size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
01695                                               dialogTitle=_('Choose file'),
01696                                               buttonText=_('Browse'),
01697                                               startDirectory=os.getcwd(),
01698                                               changeCallback=self.OnSetDsn,
01699                                               fileMask = format)
01700             
01701         elif self.dsnType == 'dir':
01702             pass
01703         
01704         else: # database
01705             if format == 'SQLite' or format == 'Rasterlite':
01706                 win = self.input['db-win']['file']
01707             elif format == 'PostgreSQL' or format == 'PostGIS WKT Raster driver':
01708                 if grass.find_program('psql', ['--help']):
01709                     win = self.input['db-win']['choice']
01710                     if not win.GetItems():
01711                         p = grass.Popen(['psql', '-ltA'], stdout = grass.PIPE)
01712                         ret = p.communicate()[0]
01713                         if ret:
01714                             db = list()
01715                             for line in ret.splitlines():
01716                                 sline = line.split('|')
01717                                 if len(sline) < 2:
01718                                     continue
01719                                 dbname = sline[0]
01720                                 if dbname:
01721                                     db.append(dbname)
01722                             win.SetItems(db)
01723                     if self.dest and win.GetStringSelection():
01724                         self.parent.btnOk.Enable(True)
01725                 else:
01726                     win = self.input['db-win']['text']
01727             else:
01728                 win = self.input['db-win']['text']
01729         
01730         self.input[self.dsnType][1] = win
01731         if not win.IsShown():
01732             win.Show()
01733         self.dsnSizer.Add(item = self.input[self.dsnType][1],
01734                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
01735                           pos = (1, 1), span = (1, 3))
01736         self.dsnSizer.Layout()
01737         
01738         # update extension
01739         self.extension.SetValue(self.GetFormatExt())
01740 
01741         if not self.dest:
01742             # reload layers
01743             self._reloadLayers()
01744         
01745     def GetType(self):
01746         """!Get source type"""
01747         return self.dsnType
01748 
01749     def GetDsnWin(self):
01750         """!Get list of DSN windows"""
01751         win = list()
01752         for stype in ('file', 'dir', 'pro'):
01753             win.append(self.input[stype][1])
01754         for stype in ('file', 'text', 'choice'):
01755             win.append(self.input['db-win'][stype])
01756         
01757         return win
01758     
01759     def GetFormat(self):
01760         """!Get format as string"""
01761         return self.format.GetStringSelection().replace(' ', '_')
01762     
01763     def GetFormatExt(self):
01764         """!Get format extension"""
01765         return self.format.GetExtension(self.format.GetStringSelection())
01766     
01767     def GetOptions(self):
01768         """!Get creation options"""
01769         if not self.creationOpt.IsShown():
01770             return ''
01771         
01772         return self.creationOpt.GetValue()
01773 
01774 class ProjSelect(wx.ComboBox):
01775     """!Widget for selecting input raster/vector map used by
01776     r.proj/v.proj modules."""
01777     def __init__(self, parent, isRaster, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
01778                  **kwargs):
01779         super(ProjSelect, self).__init__(parent, id, size = size, 
01780                                          style = wx.CB_READONLY, **kwargs)
01781         self.SetName("ProjSelect")
01782         self.isRaster = isRaster
01783         
01784     def UpdateItems(self, dbase, location, mapset):
01785         """!Update list of maps
01786         
01787         """
01788         if not dbase:
01789             dbase = grass.gisenv()['GISDBASE']
01790         if not mapset:
01791             mapset = grass.gisenv()['MAPSET']
01792         if self.isRaster:
01793             ret = RunCommand('r.proj',
01794                              quiet = True,
01795                              read = True,
01796                              flags = 'l',
01797                              dbase = dbase,
01798                              location = location,
01799                              mapset = mapset)
01800         else:
01801             ret = RunCommand('v.proj',
01802                              quiet = True,
01803                              read = True,
01804                              flags = 'l',
01805                              dbase = dbase,
01806                              location = location,
01807                              mapset = mapset)
01808         listMaps = list()
01809         if ret:
01810             for line in ret.splitlines():
01811                 listMaps.append(line.strip())
01812         ListSortLower(listMaps)
01813         
01814         self.SetItems(listMaps)
01815         self.SetValue('')
01816 
01817 class ElementSelect(wx.Choice):
01818     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
01819                  **kwargs):
01820         """!Widget for selecting GIS element
01821         
01822         @param parent parent window
01823         """
01824         super(ElementSelect, self).__init__(parent, id, size = size, 
01825                                             **kwargs)
01826         self.SetName("ElementSelect")
01827         
01828         task = gtask.parse_interface('g.list')
01829         p = task.get_param(value = 'type')
01830         self.values = p.get('values', [])
01831                 
01832         self.SetItems(self.values)
01833 
01834     def GetValue(self, name):
01835         """!Translate value
01836 
01837         @param name element name
01838         """
01839         return name