|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
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