1""" 2@package gui_core.gselect 3 4@brief Custom control that selects elements 5 6Classes: 7 - :class:`Select` 8 - :class:`VectorSelect` 9 - :class:`ListCtrlComboPopup` 10 - :class:`TreeCrtlComboPopup` 11 - :class:`VectorDBInfo` 12 - :class:`LayerSelect` 13 - :class:`DriverSelect` 14 - :class:`DatabaseSelect` 15 - :class:`TableSelect` 16 - :class:`ColumnSelect` 17 - :class:`DbaseSelect` 18 - :class:`LocationSelect` 19 - :class:`MapsetSelect` 20 - :class:`SubGroupSelect` 21 - :class:`FormatSelect` 22 - :class:`GdalSelect` 23 - :class:`ProjSelect` 24 - :class:`ElementSelect` 25 - :class:`OgrTypeSelect` 26 - :class:`CoordinatesSelect` 27 - :class:`VectorCategorySelect` 28 - :class:`SignatureSelect` 29 - :class:`SeparatorSelect` 30 - :class:`SqlWhereSelect` 31 32(C) 2007-2018 by the GRASS Development Team 33 34This program is free software under the GNU General Public License 35(>=v2). Read the file COPYING that comes with GRASS for details. 36 37@author Michael Barton 38@author Martin Landa <landa.martin gmail.com> 39@author Vaclav Petras <wenzeslaus gmail.com> (menu customization) 40@author Stepan Turek <stepan.turek seznam.cz> (CoordinatesSelect, ListCtrlComboPopup) 41@author Matej Krejci <matejkrejci gmail.com> (VectorCategorySelect) 42""" 43 44from __future__ import print_function 45 46import os 47import sys 48import glob 49import copy 50import six 51 52import wx 53 54from core import globalvar 55import wx.lib.buttons as buttons 56import wx.lib.filebrowsebutton as filebrowse 57 58 59 60import grass.script as grass 61from grass.script import task as gtask 62from grass.exceptions import CalledModuleError 63 64from gui_core.widgets import ManageSettingsWidget, CoordinatesValidator 65 66from core.gcmd import RunCommand, GError, GMessage, GWarning, GException 67from core.utils import GetListOfLocations, GetListOfMapsets, \ 68 GetFormats, rasterFormatExtension, vectorFormatExtension 69from core.utils import GetSettingsPath, GetValidLayerName, ListSortLower 70from core.utils import GetVectorNumberOfLayers 71from core.settings import UserSettings 72from core.debug import Debug 73from gui_core.vselect import VectorSelectBase 74from gui_core.wrap import TreeCtrl, Button, StaticText, StaticBox, \ 75 TextCtrl, Panel, ComboPopup, ComboCtrl 76 77from grass.pydispatch.signal import Signal 78 79 80class Select(ComboCtrl): 81 82 def __init__( 83 self, parent, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, 84 type=None, multiple=False, nmaps=1, mapsets=None, 85 updateOnPopup=True, onPopup=None, fullyQualified=True, 86 extraItems={}, 87 layerTree=None, validator=wx.DefaultValidator): 88 """Custom control to create a ComboBox with a tree control to 89 display and select GIS elements within acessible mapsets. 90 Elements can be selected with mouse. Can allow multiple 91 selections, when argument <em>multiple</em> is True. Multiple 92 selections are separated by commas. 93 94 :param type: type of GIS elements ('raster, 'vector', ...) 95 :param multiple: True for multiple input 96 :param nmaps: number of maps to be entered 97 :param mapsets: force list of mapsets (otherwise search path) 98 :param updateOnPopup: True for updating list of elements on popup 99 :param onPopup: function to be called on Popup 100 :param fullyQualified: True to provide fully qualified names (map@mapset) 101 :param extraItems: extra items to add (given as dictionary) - see gmodeler for usage 102 :param layerTree: show only elements from given layer tree if not None 103 :param validator: validator for TextCtrl 104 """ 105 ComboCtrl.__init__( 106 self, 107 parent=parent, 108 id=id, 109 size=size, 110 validator=validator) 111 if globalvar.CheckWxVersion([3]): 112 self.SetName("Select") 113 else: 114 self.GetChildren()[0].SetName("Select") 115 116 self.GetChildren()[0].type = type 117 118 self.tcp = TreeCtrlComboPopup() 119 self.SetPopupControl(self.tcp) 120 self.SetPopupExtents(0, 100) 121 if type: 122 self.tcp.SetData( 123 type=type, 124 mapsets=mapsets, 125 multiple=multiple, 126 nmaps=nmaps, 127 updateOnPopup=updateOnPopup, 128 onPopup=onPopup, 129 fullyQualified=fullyQualified, 130 extraItems=extraItems, 131 layerTree=layerTree) 132 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) 133 134 def OnKeyDown(self, event): 135 """Open popup and send key events to the tree.""" 136 if self.IsPopupShown(): 137 # on some configurations we get key down, with some only key up 138 # so we are trying to catch either key up or down 139 # this mess shouldn't be necessary with wxPython 3 140 self.tcp.OnKeyUp(event) 141 else: 142 if event.GetKeyCode() == wx.WXK_DOWN: 143 self.ShowPopup() 144 event.Skip() 145 146 def SetElementList(self, type, mapsets=None): 147 """Set element list 148 149 :param type: GIS element type 150 :param mapsets: list of acceptable mapsets (None for all in search path) 151 """ 152 self.tcp.SetData(type=type, mapsets=mapsets) 153 154 def GetElementList(self): 155 """Load elements""" 156 self.tcp.GetElementList() 157 158 def SetType(self, etype, multiple=False, nmaps=1, 159 mapsets=None, updateOnPopup=True, onPopup=None): 160 """Param set element type for widget 161 162 :param etype: element type, see gselect.ElementSelect 163 """ 164 self.tcp.SetData(type=etype, mapsets=mapsets, 165 multiple=multiple, nmaps=nmaps, 166 updateOnPopup=updateOnPopup, onPopup=onPopup) 167 168 169class VectorSelect(Select): 170 171 def __init__(self, parent, ftype, **kwargs): 172 """Custom to create a ComboBox with a tree control to display and 173 select vector maps. You can filter the vector maps. If you 174 don't need this feature use Select class instead 175 176 :param ftype: filter vector maps based on feature type 177 """ 178 Select.__init__(self, parent=parent, id=wx.ID_ANY, 179 type='vector', **kwargs) 180 181 self.ftype = ftype 182 183 # remove vector maps which do not contain given feature type 184 self.tcp.SetFilter(self._isElement) 185 186 def _isElement(self, vectorName): 187 """Check if element should be filtered out""" 188 try: 189 if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1: 190 return False 191 except KeyError: 192 return False 193 194 return True 195 196 197class ListCtrlComboPopup(ComboPopup): 198 """Create a list ComboBox using TreeCtrl with hidden root. 199 200 .. todo:: 201 202 use event.EventObject instead of hardcoding (see forms.py) 203 https://groups.google.com/forum/#!topic/wxpython-users/pRz6bi0k0XY 204 205 """ 206 # overridden ComboPopup methods 207 208 def Init(self): 209 self.value = [] # for multiple is False -> 210 # len(self.value) in [0,1] 211 self.curitem = None 212 self.multiple = False 213 self.updateOnPopup = True 214 self.filterItems = [] # limit items based on this list, 215 # see layerTree parameter 216 217 def Create(self, parent): 218 self.seltree = TreeCtrl(parent, style=wx.TR_HIDE_ROOT | 219 wx.TR_HAS_BUTTONS | 220 wx.TR_SINGLE | 221 wx.TR_LINES_AT_ROOT | 222 wx.SIMPLE_BORDER | 223 wx.TR_FULL_ROW_HIGHLIGHT) 224 self.seltree.Bind(wx.EVT_MOTION, self.OnMotion) 225 self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) 226 # the following dummy handler are needed to keep tree events 227 # from propagating up to the parent GIS Manager layer tree 228 self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, lambda x: None) 229 self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, lambda x: None) 230 self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, lambda x: None) 231 self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None) 232 self.seltree.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda x: None) 233 self.seltree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, lambda x: None) 234 # navigation in list/tree is handled automatically since wxPython 3 235 # for older versions, we have to workaround it and write our own 236 # navigation 237 if globalvar.CheckWxVersion(version=[3]): 238 self.seltree.Bind( 239 wx.EVT_TREE_ITEM_ACTIVATED, 240 self._onItemConfirmed) 241 self.seltree.Bind(wx.EVT_TREE_KEY_DOWN, self._onDismissPopup) 242 else: 243 self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, lambda x: None) 244 self.seltree.Bind(wx.EVT_KEY_UP, self.OnKeyUp) 245 246 return True 247 248 def GetControl(self): 249 return self.seltree 250 251 def GetComboCtrl(self): 252 if globalvar.wxPythonPhoenix: 253 return super(ListCtrlComboPopup, self).GetComboCtrl() 254 else: 255 return self.GetCombo() 256 257 def GetStringValue(self): 258 """Get value as a string separated by commas 259 """ 260 return ','.join(self.value) 261 262 def SetStringValue(self, value): 263 # this assumes that item strings are unique... 264 root = self.seltree.GetRootItem() 265 if not root: 266 return 267 winValue = self.GetComboCtrl().GetValue().strip(',') 268 self.value = [] 269 if winValue: 270 self.value = winValue.split(',') 271 272 def OnPopup(self, force=False): 273 """Limited only for first selected 274 """ 275 if not force and not self.updateOnPopup: 276 return 277 278 # selects map starting according to written text 279 inputText = self.GetComboCtrl().GetValue().strip() 280 if inputText: 281 root = self.seltree.GetRootItem() 282 match = self.FindItem(root, inputText, startLetters=True) 283 if match.IsOk(): 284 self.seltree.EnsureVisible(match) 285 self.seltree.SelectItem(match) 286 287 def GetAdjustedSize(self, minWidth, prefHeight, maxHeight): 288 """Reads UserSettings to get height (which was 200 in old implementation). 289 """ 290 height = UserSettings.Get( 291 group='appearance', 292 key='gSelectPopupHeight', 293 subkey='value') 294 return wx.Size(minWidth, min(height, maxHeight)) 295 296 def FindItem(self, parentItem, text, startLetters=False): 297 """Finds item with given name or starting with given text 298 """ 299 startletters = startLetters 300 item, cookie = self.seltree.GetFirstChild(parentItem) 301 while wx.TreeItemId.IsOk(item): 302 if self.seltree.GetItemText(item) == text: 303 return item 304 if self.seltree.ItemHasChildren(item): 305 item = self.FindItem(item, text, startLetters=startletters) 306 if wx.TreeItemId.IsOk(item): 307 return item 308 elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]): 309 return item 310 item, cookie = self.seltree.GetNextChild(parentItem, cookie) 311 return wx.TreeItemId() 312 313 def AddItem(self, value): 314 root = self.seltree.GetRootItem() 315 if not root: 316 root = self.seltree.AddRoot("<hidden root>") 317 self.seltree.AppendItem(root, text=value) 318 319 def SetItems(self, items): 320 root = self.seltree.GetRootItem() 321 if not root: 322 root = self.seltree.AddRoot("<hidden root>") 323 for item in items: 324 self.seltree.AppendItem(root, text=item) 325 326 def OnKeyUp(self, event): 327 """Enable to select items using keyboard. 328 329 Unused with wxPython 3, can be removed in the future. 330 """ 331 item = self.seltree.GetSelection() 332 if event.GetKeyCode() == wx.WXK_DOWN: 333 self.seltree.SelectItem(self.seltree.GetNextVisible(item)) 334 335 elif event.GetKeyCode() == wx.WXK_UP: 336 itemPrev = self.seltree.GetPrevSibling(item) 337 self.seltree.SelectItem(itemPrev) 338 339 elif event.GetKeyCode() == wx.WXK_ESCAPE: 340 self.Dismiss() 341 342 elif event.GetKeyCode() == wx.WXK_RETURN: 343 self.seltree.SelectItem(item) 344 self.curitem = item 345 self._selectTreeItem(item) 346 self.Dismiss() 347 348 def _onDismissPopup(self, event): 349 """Hide popup without selecting item on Esc""" 350 if event.GetKeyCode() == wx.WXK_ESCAPE: 351 self.Dismiss() 352 else: 353 event.Skip() 354 355 def _selectTreeItem(self, item): 356 item_str = self.seltree.GetItemText(item) 357 if self.multiple: 358 if item_str not in self.value: 359 self.value.append(item_str) 360 else: 361 self.value = [item_str] 362 363 def _onItemConfirmed(self, event): 364 item = event.GetItem() 365 self._selectTreeItem(item) 366 self.Dismiss() 367 368 def OnMotion(self, evt): 369 """Have the selection follow the mouse, like in a real combobox 370 """ 371 item, flags = self.seltree.HitTest(evt.GetPosition()) 372 if item and flags & wx.TREE_HITTEST_ONITEMLABEL: 373 self.seltree.SelectItem(item) 374 self.curitem = item 375 evt.Skip() 376 377 def OnLeftDown(self, evt): 378 """Do the combobox selection 379 """ 380 if self.curitem is None: 381 return 382 383 self._selectTreeItem(self.curitem) 384 self.Dismiss() 385 386 evt.Skip() 387 388 def SetData(self, **kargs): 389 """Set object properties""" 390 if 'multiple' in kargs: 391 self.multiple = kargs['multiple'] 392 if 'onPopup' in kargs: 393 self.onPopup = kargs['onPopup'] 394 if kargs.get('layerTree', None): 395 self.filterItems = [] # reset 396 ltype = kargs['type'] 397 for layer in kargs['layerTree'].GetVisibleLayers( 398 skipDigitized=True): 399 if layer.GetType() != ltype: 400 continue 401 self.filterItems.append(layer.GetName()) 402 403 def DeleteAllItems(self): 404 """Delete all items in popup""" 405 self.seltree.DeleteAllItems() 406 407 408class TreeCtrlComboPopup(ListCtrlComboPopup): 409 """Create a tree ComboBox for selecting maps and other GIS elements 410 in accessible mapsets within the current location 411 """ 412 # overridden ComboPopup methods 413 414 def Init(self): 415 416 ListCtrlComboPopup.Init(self) 417 self.nmaps = 1 418 self.type = None 419 self.mapsets = None 420 self.onPopup = None 421 self.fullyQualified = True 422 self.extraItems = dict() 423 424 self.SetFilter(None) 425 self.tgis_error = False 426 427 def SetFilter(self, filter): 428 """Set filter for GIS elements, see e.g. VectorSelect""" 429 self.filterElements = filter 430 431 def OnPopup(self, force=False): 432 """Limited only for first selected""" 433 if not force and not self.updateOnPopup: 434 return 435 if self.onPopup: 436 selected, exclude = self.onPopup(self.type) 437 else: 438 selected = None 439 exclude = False 440 441 self.GetElementList(selected, exclude) 442 443 ListCtrlComboPopup.OnPopup(self, force) 444 445 def GetElementList(self, elements=None, exclude=False): 446 """Get filtered list of GIS elements in accessible mapsets 447 and display as tree with all relevant elements displayed 448 beneath each mapset branch 449 """ 450 # update list 451 self.seltree.DeleteAllItems() 452 if self.type: 453 self._getElementList(self.type, self.mapsets, elements, exclude) 454 455 if len(self.value) > 0: 456 root = self.seltree.GetRootItem() 457 if not root: 458 return 459 item = self.FindItem(root, self.value[0]) 460 try: 461 self.seltree.EnsureVisible(item) 462 self.seltree.SelectItem(item) 463 except: 464 pass 465 466 def _getElementList(self, element, mapsets=None, 467 elements=None, exclude=False): 468 """Get list of GIS elements in accessible mapsets and display as tree 469 with all relevant elements displayed beneath each mapset branch 470 471 :param element: GIS element 472 :param mapsets: list of acceptable mapsets (None for all mapsets in search path) 473 :param elements: list of forced GIS elements 474 :param exclude: True to exclude, False for forcing the list (elements) 475 """ 476 # get current mapset 477 curr_mapset = grass.gisenv()['MAPSET'] 478 479 # map element types to g.list types 480 elementdict = {'cell': 'raster', 481 'raster': 'raster', 482 'grid3': 'raster_3d', 483 'raster_3d': 'raster_3d', 484 'vector': 'vector', 485 'paint/labels': 'label', 486 'label': 'label', 487 'windows': 'region', 488 'region': 'region', 489 'group': 'group', 490 'stds': 'stds', 491 'strds': 'strds', 492 'str3ds': 'str3ds', 493 'stvds': 'stvds'} 494 495 # to support multiple elements 496 element_list = element.split(',') 497 renamed_elements = [] 498 for elem in element_list: 499 if elem not in elementdict: 500 self.AddItem(_('Not selectable element'), node=False) 501 return 502 else: 503 renamed_elements.append(elementdict[elem]) 504 505 if element in ('stds', 'strds', 'str3ds', 'stvds'): 506 if not self.tgis_error: 507 import grass.temporal as tgis 508 filesdict = tgis.tlist_grouped( 509 elementdict[element], element == 'stds') 510 else: 511 filesdict = None 512 else: 513 filesdict = grass.list_grouped(renamed_elements, 514 check_search_path=False) 515 516 # add extra items first 517 if self.extraItems: 518 for group, items in six.iteritems(self.extraItems): 519 node = self.AddItem(group, node=True) 520 self.seltree.SetItemTextColour(node, wx.Colour(50, 50, 200)) 521 for item in items: 522 self.AddItem(item, node=False, parent=node) 523 self.seltree.ExpandAllChildren(node) 524 525 # list of mapsets in current location 526 if mapsets is None: 527 mapsets = grass.mapsets(search_path=True) 528 529 # current mapset first 530 if curr_mapset in mapsets and mapsets[0] != curr_mapset: 531 mapsets.remove(curr_mapset) 532 mapsets.insert(0, curr_mapset) 533 534 first_mapset = None 535 for mapset in mapsets: 536 mapset_node = self.AddItem( 537 _('Mapset') + ': ' + mapset, node=True, mapset=mapset) 538 node = mapset_node 539 if not first_mapset: 540 first_mapset = mapset_node 541 542 self.seltree.SetItemTextColour(mapset_node, wx.Colour(50, 50, 200)) 543 if mapset not in filesdict: 544 continue 545 try: 546 if isinstance(filesdict[mapset], dict): 547 for elementType in filesdict[mapset].keys(): 548 node = self.AddItem( 549 _('Type: ') + elementType, 550 mapset=mapset, 551 node=True, 552 parent=mapset_node) 553 self.seltree.SetItemTextColour( 554 node, wx.Colour(50, 50, 200)) 555 elem_list = filesdict[mapset][elementType] 556 self._addItems( 557 elist=elem_list, 558 elements=elements, 559 mapset=mapset, 560 exclude=exclude, 561 node=node) 562 else: 563 elem_list = filesdict[mapset] 564 self._addItems(elist=elem_list, elements=elements, 565 mapset=mapset, exclude=exclude, node=node) 566 except Exception as e: 567 sys.stderr.write(_("GSelect: invalid item: %s") % e) 568 continue 569 570 if self.seltree.ItemHasChildren(mapset_node): 571 sel = UserSettings.Get( 572 group='appearance', 573 key='elementListExpand', 574 subkey='selection') 575 collapse = True 576 577 if sel == 0: # collapse all except PERMANENT and current 578 if mapset in ('PERMANENT', curr_mapset): 579 collapse = False 580 elif sel == 1: # collapse all except PERMANENT 581 if mapset == 'PERMANENT': 582 collapse = False 583 elif sel == 2: # collapse all except current 584 if mapset == curr_mapset: 585 collapse = False 586 elif sel == 3: # collapse all 587 pass 588 elif sel == 4: # expand all 589 collapse = False 590 591 if collapse: 592 self.seltree.CollapseAllChildren(mapset_node) 593 else: 594 self.seltree.ExpandAllChildren(mapset_node) 595 596 if first_mapset: 597 # select first mapset (MSW hack) 598 self.seltree.SelectItem(first_mapset) 599 600 # helpers 601 def _addItems(self, elist, elements, mapset, exclude, node): 602 """Helper function for adding multiple items (maps, stds). 603 604 :param list elist: list of map/stds names 605 :param list elements: list of forced elements 606 :param str mapset: mapset name 607 :param exclude: True to exclude, False for forcing the list 608 :param node: parent node 609 """ 610 elist = grass.natural_sort(elist) 611 for elem in elist: 612 if elem != '': 613 fullqElem = elem + '@' + mapset 614 if self.filterItems and fullqElem not in self.filterItems: 615 continue # skip items missed in self.filterItems 616 617 if elements is not None: 618 if (exclude and fullqElem in elements) or \ 619 (not exclude and fullqElem not in elements): 620 continue 621 622 if self.filterElements: 623 if self.filterElements(fullqElem): 624 self.AddItem( 625 elem, mapset=mapset, node=False, parent=node) 626 else: 627 self.AddItem(elem, mapset=mapset, node=False, parent=node) 628 629 def AddItem(self, value, mapset=None, node=True, parent=None): 630 if not parent: 631 root = self.seltree.GetRootItem() 632 if not root: 633 root = self.seltree.AddRoot("<hidden root>") 634 parent = root 635 636 data = {'node': node, 'mapset': mapset} 637 638 item = self.seltree.AppendItem( 639 parent, text=value, data=data) 640 return item 641 642 def OnKeyUp(self, event): 643 """Enables to select items using keyboard 644 645 Unused with wxPython 3, can be removed in the future. 646 """ 647 648 item = self.seltree.GetSelection() 649 if event.GetKeyCode() == wx.WXK_DOWN: 650 self.seltree.SelectItem(self.seltree.GetNextVisible(item)) 651 652 # problem with GetPrevVisible 653 elif event.GetKeyCode() == wx.WXK_UP: 654 if self.seltree.ItemHasChildren(item) and self.seltree.IsExpanded( 655 self.seltree.GetPrevSibling(item)): 656 itemPrev = self.seltree.GetLastChild( 657 self.seltree.GetPrevSibling(item)) 658 else: 659 itemPrev = self.seltree.GetPrevSibling(item) 660 if not wx.TreeItemId.IsOk(itemPrev): 661 itemPrev = self.seltree.GetItemParent(item) 662 if item == self.seltree.GetFirstChild( 663 self.seltree.GetRootItem())[0]: 664 itemPrev = item 665 self.seltree.SelectItem(itemPrev) 666 667 # selects first item starting with the written text in next mapset 668 elif event.GetKeyCode() == wx.WXK_TAB: 669 selected = self.seltree.GetSelection() 670 if self.seltree.ItemHasChildren(selected): 671 parent = selected 672 else: 673 parent = self.seltree.GetItemParent(selected) 674 nextSibling = self.seltree.GetNextSibling(parent) 675 if wx.TreeItemId.IsOk(nextSibling): 676 match = self.FindItem( 677 nextSibling, self.GetCombo().GetValue().strip(), True) 678 else: 679 match = self.FindItem( 680 self.seltree.GetFirstChild( 681 self.seltree.GetItemParent(parent))[0], 682 self.GetCombo().GetValue().strip(), 683 True) 684 self.seltree.SelectItem(match) 685 686 elif event.GetKeyCode() == wx.WXK_RIGHT: 687 if self.seltree.ItemHasChildren(item): 688 self.seltree.Expand(item) 689 690 elif event.GetKeyCode() == wx.WXK_LEFT: 691 if self.seltree.ItemHasChildren(item): 692 self.seltree.Collapse(item) 693 694 elif event.GetKeyCode() == wx.WXK_ESCAPE: 695 self.Dismiss() 696 697 elif event.GetKeyCode() == wx.WXK_RETURN: 698 if self.seltree.GetItemData(item)['node']: 699 self.value = [] 700 else: 701 self._selectTreeItem(item) 702 703 self.Dismiss() 704 705 def OnLeftDown(self, evt): 706 """Do the combobox selection 707 """ 708 item, flags = self.seltree.HitTest(evt.GetPosition()) 709 if item and flags & wx.TREE_HITTEST_ONITEMLABEL: 710 self.curitem = item 711 712 if self.seltree.GetItemData(item)['node']: 713 evt.Skip() 714 return 715 716 self._selectTreeItem(item) 717 self.Dismiss() 718 719 evt.Skip() 720 721 def _selectTreeItem(self, item): 722 fullName = self.seltree.GetItemText(item) 723 if self.fullyQualified and self.seltree.GetItemData(item)['mapset']: 724 fullName += '@' + self.seltree.GetItemData(item)['mapset'] 725 726 if self.multiple: 727 self.value.append(fullName) 728 else: 729 if self.nmaps > 1: # see key_desc 730 if len(self.value) >= self.nmaps: 731 self.value = [fullName] 732 else: 733 self.value.append(fullName) 734 else: 735 self.value = [fullName] 736 737 def _onItemConfirmed(self, event): 738 item = event.GetItem() 739 if self.seltree.GetItemData(item)['node']: 740 self.value = [] 741 else: 742 self._selectTreeItem(item) 743 self.Dismiss() 744 745 def SetData(self, **kargs): 746 """Set object properties""" 747 ListCtrlComboPopup.SetData(self, **kargs) 748 if 'type' in kargs: 749 self.type = kargs['type'] 750 if self.type in ('stds', 'strds', 'str3ds', 'stvds'): 751 # Initiate the temporal framework. Catch database error 752 # and set the error flag for the stds listing. 753 try: 754 import grass.temporal as tgis 755 from grass.pygrass import messages 756 try: 757 tgis.init(True) 758 except messages.FatalError as e: 759 sys.stderr.write(_("Temporal GIS error:\n%s") % e) 760 self.tgis_error = True 761 except ImportError as e: 762 # PyGRASS (ctypes) is the likely cause 763 sys.stderr.write(_( 764 "Unable to import pyGRASS: %s\n" 765 "Some functionality will be not accessible") % e) 766 self.tgis_error = True 767 if 'mapsets' in kargs: 768 self.mapsets = kargs['mapsets'] 769 if 'nmaps' in kargs: 770 self.nmaps = kargs['nmaps'] 771 if 'updateOnPopup' in kargs: 772 self.updateOnPopup = kargs['updateOnPopup'] 773 if 'fullyQualified' in kargs: 774 self.fullyQualified = kargs['fullyQualified'] 775 if 'extraItems' in kargs: 776 self.extraItems = kargs['extraItems'] 777 778 def GetType(self): 779 """Get element type 780 """ 781 return self.type 782 783 784class VectorDBInfo: 785 """Class providing information about attribute tables 786 linked to a vector map""" 787 788 def __init__(self, map): 789 self.map = map 790 791 # dictionary of layer number and associated (driver, database, table) 792 self.layers = {} 793 # dictionary of table and associated columns (type, length, values, 794 # ids) 795 self.tables = {} 796 797 if not self._CheckDBConnection(): # -> self.layers 798 return 799 800 self._DescribeTables() # -> self.tables 801 802 def _CheckDBConnection(self): 803 """Check DB connection""" 804 nuldev = open(os.devnull, 'w+') 805 # if map is not defined (happens with vnet initialization) or it 806 # doesn't exist 807 try: 808 self.layers = grass.vector_db(map=self.map, stderr=nuldev) 809 except CalledModuleError: 810 return False 811 finally: # always close nuldev 812 nuldev.close() 813 814 return bool(len(self.layers.keys()) > 0) 815 816 def _DescribeTables(self): 817 """Describe linked tables""" 818 for layer in self.layers.keys(): 819 # determine column names and types 820 table = self.layers[layer]["table"] 821 columns = {} # {name: {type, length, [values], [ids]}} 822 i = 0 823 Debug.msg( 824 1, 825 "gselect.VectorDBInfo._DescribeTables(): table=%s driver=%s database=%s" % 826 (self.layers[layer]["table"], 827 self.layers[layer]["driver"], 828 self.layers[layer]["database"])) 829 for item in grass.db_describe( 830 table=self.layers[layer]["table"], 831 driver=self.layers[layer]["driver"], 832 database=self.layers[layer]["database"])['cols']: 833 name, type, length = item 834 # FIXME: support more datatypes 835 if type.lower() == "integer": 836 ctype = int 837 elif type.lower() == "double precision": 838 ctype = float 839 else: 840 ctype = str 841 842 columns[name.strip()] = {'index': i, 843 'type': type.lower(), 844 'ctype': ctype, 845 'length': int(length), 846 'values': [], 847 'ids': []} 848 i += 1 849 850 # check for key column 851 # v.db.connect -g/p returns always key column name lowercase 852 if self.layers[layer]["key"] not in columns.keys(): 853 for col in columns.keys(): 854 if col.lower() == self.layers[layer]["key"]: 855 self.layers[layer]["key"] = col.upper() 856 break 857 858 self.tables[table] = columns 859 860 return True 861 862 def Reset(self): 863 """Reset""" 864 for layer in self.layers: 865 table = self.layers[layer]["table"] # get table desc 866 for name in self.tables[table].keys(): 867 self.tables[table][name]['values'] = [] 868 self.tables[table][name]['ids'] = [] 869 870 def GetName(self): 871 """Get vector name""" 872 return self.map 873 874 def GetKeyColumn(self, layer): 875 """Get key column of given layer 876 877 :param layer: vector layer number 878 """ 879 return str(self.layers[layer]['key']) 880 881 def GetTable(self, layer): 882 """Get table name of given layer 883 884 :param layer: vector layer number 885 """ 886 if layer not in self.layers: 887 raise GException(_("No table linked to layer <{}>.".format(layer))) 888 return self.layers[layer]['table'] 889 890 def GetDbSettings(self, layer): 891 """Get database settins 892 893 :param layer: layer number 894 895 :return: (driver, database) 896 """ 897 return self.layers[layer]['driver'], self.layers[layer]['database'] 898 899 def GetTableDesc(self, table): 900 """Get table columns 901 902 :param table: table name 903 """ 904 return self.tables[table] 905 906 907class LayerSelect(wx.ComboBox): 908 909 def __init__(self, parent, id=wx.ID_ANY, 910 size=globalvar.DIALOG_COMBOBOX_SIZE, 911 vector=None, dsn=None, choices=[], all=False, default=None): 912 """Creates combo box for selecting vector map layer names 913 914 :param str vector: vector map name (native or connected via v.external) 915 :param str dsn: OGR data source name 916 """ 917 super( 918 LayerSelect, 919 self).__init__( 920 parent, 921 id, 922 size=size, 923 choices=choices) 924 925 self.all = all 926 927 self.SetName("LayerSelect") 928 929 # default value 930 self.default = default 931 932 self.InsertLayers(vector=vector, dsn=dsn) 933 934 def InsertLayers(self, vector=None, dsn=None): 935 """Insert layers for a vector into the layer combobox 936 937 :param str vector: vector map name (native or connected via v.external) 938 :param str dsn: OGR data source name 939 """ 940 layers = list() 941 942 if vector: 943 layers = GetVectorNumberOfLayers(vector) 944 elif dsn: 945 ret = RunCommand('v.in.ogr', 946 read=True, 947 quiet=True, 948 flags='l', 949 input=dsn) 950 if ret: 951 layers = ret.splitlines() 952 953 if self.default: 954 if len(layers) == 0: 955 layers.insert(0, str(self.default)) 956 elif self.default not in layers: 957 layers.append(self.default) 958 959 if self.all: 960 layers.insert(0, '-1') 961 962 if len(layers) > 0: 963 self.SetItems(layers) 964 else: 965 self.Clear() 966 967 self.SetValue("") 968 969 if self.default and self.default in layers: 970 self.SetValue(self.default) 971 972 973class DriverSelect(wx.ComboBox): 974 """Creates combo box for selecting database driver. 975 """ 976 977 def __init__(self, parent, choices, value, 978 id=wx.ID_ANY, pos=wx.DefaultPosition, 979 size=globalvar.DIALOG_LAYER_SIZE, **kargs): 980 981 super(DriverSelect, self).__init__(parent, id, value, pos, size, 982 choices, style=wx.CB_READONLY) 983 984 self.SetName("DriverSelect") 985 986 self.SetStringSelection(value) 987 988 989class DatabaseSelect(TextCtrl): 990 """Creates combo box for selecting database driver. 991 """ 992 993 def __init__(self, parent, value='', id=wx.ID_ANY, 994 size=globalvar.DIALOG_TEXTCTRL_SIZE, **kargs): 995 super( 996 DatabaseSelect, 997 self).__init__( 998 parent, 999 id, 1000 value, 1001 size=size, 1002 **kargs) 1003 self.SetName("DatabaseSelect") 1004 1005 1006class TableSelect(wx.ComboBox): 1007 """Creates combo box for selecting attribute tables from the database 1008 """ 1009 1010 def __init__(self, parent, 1011 id=wx.ID_ANY, value='', 1012 size=globalvar.DIALOG_COMBOBOX_SIZE, choices=[], **kargs): 1013 super( 1014 TableSelect, 1015 self).__init__( 1016 parent, 1017 id, 1018 value, 1019 size=size, 1020 choices=choices, 1021 style=wx.CB_READONLY, 1022 **kargs) 1023 self.SetName("TableSelect") 1024 1025 if not choices: 1026 self.InsertTables() 1027 1028 def InsertTables(self, driver=None, database=None): 1029 """Insert attribute tables into combobox""" 1030 items = [] 1031 1032 if not driver or not database: 1033 connect = grass.db_connection() 1034 1035 driver = connect['driver'] 1036 database = connect['database'] 1037 1038 ret = RunCommand('db.tables', 1039 flags='p', 1040 read=True, 1041 driver=driver, 1042 database=database) 1043 1044 if ret: 1045 for table in ret.splitlines(): 1046 items.append(table) 1047 1048 self.SetItems(items) 1049 self.SetValue('') 1050 1051 1052class ColumnSelect(ComboCtrl): 1053 """Creates combo box for selecting columns in the attribute table 1054 for a vector map. 1055 1056 :param parent: window parent 1057 :param id: window id 1058 :param value: default value 1059 :param size: window size 1060 :param str vector: vector map name 1061 :param layer: layer number 1062 :param multiple: True if it is possible to add multiple columns 1063 :param param: parameters list (see menuform.py) 1064 :param kwags: wx.ComboBox parameters 1065 """ 1066 1067 def __init__(self, parent, id=wx.ID_ANY, value='', 1068 size=globalvar.DIALOG_COMBOBOX_SIZE, 1069 vector=None, layer=1, multiple=False, 1070 param=None, **kwargs): 1071 self.defaultValue = value 1072 self.param = param 1073 self.columns = [] 1074 1075 ComboCtrl.__init__(self, parent, id, size=size, **kwargs) 1076 self.GetChildren()[0].SetName("ColumnSelect") 1077 self.GetChildren()[0].type = type 1078 1079 self.tcp = ListCtrlComboPopup() 1080 self.SetPopupControl(self.tcp) 1081 self.tcp.SetData(multiple=multiple) 1082 1083 if vector: 1084 self.InsertColumns(vector, layer) 1085 self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp) 1086 1087 def GetColumns(self): 1088 return self.columns 1089 1090 def OnKeyUp(self, event): 1091 """Shows popupwindow if down arrow key is released""" 1092 if event.GetKeyCode() == wx.WXK_DOWN and not self.IsPopupShown(): 1093 self.ShowPopup() 1094 else: 1095 event.Skip() 1096 1097 def Clear(self): 1098 self.tcp.DeleteAllItems() 1099 self.SetValue('') 1100 1101 def InsertColumns(self, vector, layer, excludeKey=False, 1102 excludeCols=None, type=None, dbInfo=None): 1103 """Insert columns for a vector attribute table into the columns combobox 1104 1105 :param str vector: vector name 1106 :param int layer: vector layer number 1107 :param excludeKey: exclude key column from the list? 1108 :param excludeCols: list of columns to be removed from the list 1109 :param type: only columns of given type (given as list) 1110 """ 1111 if not dbInfo: 1112 dbInfo = VectorDBInfo(vector) 1113 1114 try: 1115 try: 1116 layer = int(layer) 1117 except TypeError: 1118 # assuming layer 1 1119 layer = 1 1120 table = dbInfo.GetTable(layer) 1121 columnchoices = dbInfo.GetTableDesc(table) 1122 keyColumn = dbInfo.GetKeyColumn(layer) 1123 self.columns = len(columnchoices.keys()) * [''] 1124 for key, val in six.iteritems(columnchoices): 1125 self.columns[val['index']] = key 1126 if excludeKey: # exclude key column 1127 self.columns.remove(keyColumn) 1128 if excludeCols: # exclude key column 1129 for key in six.iterkeys(columnchoices): 1130 if key in excludeCols: 1131 self.columns.remove(key) 1132 if type: # only selected column types 1133 for key, value in six.iteritems(columnchoices): 1134 if value['type'] not in type: 1135 try: 1136 self.columns.remove(key) 1137 except ValueError: 1138 pass 1139 except (KeyError, ValueError, GException): 1140 self.columns[:] = [] 1141 1142 # update list 1143 self.tcp.DeleteAllItems() 1144 for col in self.columns: 1145 self.tcp.AddItem(col) 1146 1147 self.SetValue(self.defaultValue) 1148 1149 if self.param: 1150 value = self.param.get('value', '') 1151 if value != '' and value in self.columns: 1152 self.SetValue(value) 1153 1154 def InsertTableColumns(self, table, driver=None, database=None): 1155 """Insert table columns 1156 1157 :param str table: table name 1158 :param str driver: driver name 1159 :param str database: database name 1160 """ 1161 self.columns[:] = [] 1162 1163 ret = RunCommand('db.columns', 1164 read=True, 1165 driver=driver, 1166 database=database, 1167 table=table) 1168 1169 if ret: 1170 self.columns = ret.splitlines() 1171 1172 # update list 1173 self.tcp.DeleteAllItems() 1174 self.SetValue(self.defaultValue) 1175 1176 for col in self.columns: 1177 self.tcp.AddItem(col) 1178 if self.param: 1179 value = self.param.get('value', '') 1180 if value != '' and value in self.columns: 1181 self.SetValue(value) 1182 1183 1184class DbaseSelect(wx.lib.filebrowsebutton.DirBrowseButton): 1185 """Widget for selecting GRASS Database""" 1186 1187 def __init__(self, parent, **kwargs): 1188 super( 1189 DbaseSelect, 1190 self).__init__( 1191 parent, 1192 id=wx.ID_ANY, 1193 size=globalvar.DIALOG_GSELECT_SIZE, 1194 labelText='', 1195 dialogTitle=_('Choose GIS Data Directory'), 1196 buttonText=_('Browse'), 1197 startDirectory=grass.gisenv()['GISDBASE'], 1198 **kwargs) 1199 1200 1201class LocationSelect(wx.ComboBox): 1202 """Widget for selecting GRASS location""" 1203 1204 def __init__( 1205 self, parent, id=wx.ID_ANY, size=globalvar.DIALOG_COMBOBOX_SIZE, 1206 gisdbase=None, **kwargs): 1207 super(LocationSelect, self).__init__(parent, id, size=size, **kwargs) 1208 self.SetName("LocationSelect") 1209 1210 if not gisdbase: 1211 self.gisdbase = grass.gisenv()['GISDBASE'] 1212 else: 1213 self.gisdbase = gisdbase 1214 1215 self.SetItems(GetListOfLocations(self.gisdbase)) 1216 1217 def UpdateItems(self, dbase): 1218 """Update list of locations 1219 1220 :param str dbase: path to GIS database 1221 """ 1222 self.gisdbase = dbase 1223 if dbase: 1224 self.SetItems(GetListOfLocations(self.gisdbase)) 1225 else: 1226 self.SetItems([]) 1227 1228 1229class MapsetSelect(wx.ComboBox): 1230 """Widget for selecting GRASS mapset""" 1231 1232 def __init__(self, parent, id=wx.ID_ANY, 1233 size=globalvar.DIALOG_COMBOBOX_SIZE, gisdbase=None, 1234 location=None, setItems=True, searchPath=False, new=False, 1235 skipCurrent=False, multiple=False, **kwargs): 1236 style = 0 1237 # disabled, read-only widget has no TextCtrl children (TODO: rewrite) 1238 # if not new and not multiple: 1239 ### style = wx.CB_READONLY 1240 1241 wx.ComboBox.__init__(self, parent, id, size=size, 1242 style=style, **kwargs) 1243 self.searchPath = searchPath 1244 self.skipCurrent = skipCurrent 1245 self.SetName("MapsetSelect") 1246 self.value = '' 1247 self.multiple = multiple 1248 if not gisdbase: 1249 self.gisdbase = grass.gisenv()['GISDBASE'] 1250 else: 1251 self.gisdbase = gisdbase 1252 1253 if not location: 1254 self.location = grass.gisenv()['LOCATION_NAME'] 1255 else: 1256 self.location = location 1257 1258 if setItems: 1259 self.SetItems(self._getMapsets()) 1260 1261 if self.multiple: 1262 self.Bind(wx.EVT_COMBOBOX, self._onSelection) 1263 self.Bind(wx.EVT_TEXT, self._onSelection) 1264 1265 def _onSelection(self, event): 1266 value = self.GetValue() 1267 if value: 1268 if self.value: 1269 self.value += ',' 1270 self.value += value 1271 self.SetValue(self.value) 1272 else: 1273 self.value = value 1274 event.Skip() 1275 1276 def UpdateItems(self, location, dbase=None): 1277 """Update list of mapsets for given location 1278 1279 :param str dbase: path to GIS database (None to use currently 1280 selected) 1281 :param str location: name of location 1282 """ 1283 if dbase: 1284 self.gisdbase = dbase 1285 self.location = location 1286 1287 if location: 1288 self.SetItems(self._getMapsets()) 1289 else: 1290 self.SetItems([]) 1291 1292 def _getMapsets(self): 1293 if self.searchPath: 1294 mlist = RunCommand('g.mapsets', 1295 read=True, flags='p', 1296 sep='newline').splitlines() 1297 else: 1298 mlist = GetListOfMapsets(self.gisdbase, self.location, 1299 selectable=False) 1300 1301 gisenv = grass.gisenv() 1302 if self.skipCurrent and \ 1303 gisenv['LOCATION_NAME'] == self.location and \ 1304 gisenv['MAPSET'] in mlist: 1305 mlist.remove(gisenv['MAPSET']) 1306 1307 return mlist 1308 1309 1310class SubGroupSelect(wx.ComboBox): 1311 """Widget for selecting subgroups""" 1312 1313 def __init__(self, parent, id=wx.ID_ANY, 1314 size=globalvar.DIALOG_GSELECT_SIZE, **kwargs): 1315 super(SubGroupSelect, self).__init__(parent, id, size=size, 1316 **kwargs) 1317 self.SetName("SubGroupSelect") 1318 1319 def Insert(self, group): 1320 """Insert subgroups for defined group""" 1321 if not group: 1322 return 1323 gisenv = grass.gisenv() 1324 try: 1325 name, mapset = group.split('@', 1) 1326 except ValueError: 1327 name = group 1328 mapset = gisenv['MAPSET'] 1329 1330 mlist = RunCommand('i.group', group=group, 1331 read=True, flags='sg').splitlines() 1332 try: 1333 self.SetItems(mlist) 1334 except OSError: 1335 self.SetItems([]) 1336 1337 1338class FormatSelect(wx.Choice): 1339 1340 def __init__(self, parent, srcType, ogr=False, 1341 size=globalvar.DIALOG_SPIN_SIZE, 1342 **kwargs): 1343 """Widget for selecting external (GDAL/OGR) format 1344 1345 :param parent: parent window 1346 :param srcType: source type ('file', 'database', 'protocol') 1347 :param ogr: True for OGR otherwise GDAL 1348 """ 1349 super(FormatSelect, self).__init__(parent, id=wx.ID_ANY, size=size, 1350 **kwargs) 1351 self.SetName("FormatSelect") 1352 1353 if ogr: 1354 ftype = 'ogr' 1355 else: 1356 ftype = 'gdal' 1357 1358 formats = list() 1359 for f in GetFormats()[ftype][srcType].values(): 1360 formats += f 1361 self.SetItems(formats) 1362 1363 def GetExtension(self, name): 1364 """Get file extension by format name""" 1365 formatToExt = dict() 1366 formatToExt.update(rasterFormatExtension) 1367 formatToExt.update(vectorFormatExtension) 1368 1369 return formatToExt.get(name, '') 1370 1371 1372class GdalSelect(wx.Panel): 1373 1374 def __init__(self, parent, panel, ogr=False, link=False, dest=False, 1375 exclude=None, settings=True): 1376 """Widget for selecting GDAL/OGR datasource, format 1377 1378 .. todo:: 1379 Split into GdalSelect and OgrSelect and optionally to 1380 GdalSelectOutput, OgrSelectOutput 1381 1382 :param parent: parent window 1383 :param bool ogr: use OGR selector instead of GDAL 1384 :param bool dest: True for output (destination) 1385 :param default: default type (ignored when dest == True) 1386 :param exclude: list of types to be excluded 1387 """ 1388 self.parent = parent 1389 self.ogr = ogr 1390 self.link = link 1391 self.dest = dest 1392 self._sourceType = None 1393 1394 wx.Panel.__init__(self, parent=panel, name='GdalSelect') 1395 1396 self.reloadDataRequired = Signal('GdalSelect.reloadDataRequired') 1397 1398 self.inputBox = StaticBox(parent=self) 1399 if dest: 1400 self.inputBox.SetLabel(" %s " % _("Output settings")) 1401 else: 1402 self.inputBox.SetLabel(" %s " % _("Source input")) 1403 1404 # source type 1405 sources = list() 1406 self.sourceMap = {'file': -1, 1407 'dir': -1, 1408 'db': -1, 1409 'pro': -1, 1410 'native': -1} 1411 idx = 0 1412 if dest: 1413 sources.append(_("Native")) 1414 self.sourceMap['native'] = idx 1415 idx += 1 1416 if exclude is None: 1417 exclude = [] 1418 if 'file' not in exclude: 1419 sources.append(_("File")) 1420 self.sourceMap['file'] = idx 1421 idx += 1 1422 if 'directory' not in exclude: 1423 sources.append(_("Directory")) 1424 self.sourceMap['dir'] = idx 1425 idx += 1 1426 if 'database' not in exclude: 1427 sources.append(_("Database")) 1428 self.sourceMap['db'] = idx 1429 idx += 1 1430 if 'protocol' not in exclude: 1431 sources.append(_("Protocol")) 1432 self.sourceMap['pro'] = idx 1433 idx += 1 1434 self.sourceMapByIdx = {} 1435 for name, idx in self.sourceMap.items(): 1436 self.sourceMapByIdx[idx] = name 1437 1438 self.source = wx.RadioBox(parent=self, id=wx.ID_ANY, 1439 style=wx.RA_SPECIFY_COLS, 1440 choices=sources) 1441 if dest: 1442 self.source.SetLabel(" %s " % _('Output type')) 1443 else: 1444 self.source.SetLabel(" %s " % _('Source type')) 1445 1446 self.source.SetSelection(0) 1447 self.source.Bind( 1448 wx.EVT_RADIOBOX, 1449 lambda evt: self.SetSourceType( 1450 self.sourceMapByIdx[ 1451 evt.GetInt()])) 1452 1453 self.nativeWidgets = {} 1454 self.fileWidgets = {} 1455 self.dirWidgets = {} 1456 self.dbWidgets = {} 1457 self.protocolWidgets = {} 1458 self.pgWidgets = {} 1459 1460 if ogr: 1461 fType = 'ogr' 1462 else: 1463 fType = 'gdal' 1464 1465 # file 1466 fileMask = '%(all)s (*)|*|' % {'all': _('All files')} 1467 if not ogr: 1468 extList = rasterFormatExtension 1469 fileMask += ('%(name)s (*.%(low1)s;*.%(low2)s;*.%(up1)s;*.%(up2)s)|' 1470 '*.%(low1)s;*.%(low2)s;*.%(up1)s;*.%(up2)s|' % 1471 {'name': 'GeoTIFF', 'low1': 'tif', 'low2': 'tiff', 'up1': 'TIF', 'up2': 'TIFF'}) 1472 else: 1473 extList = vectorFormatExtension 1474 fileMask += '%(name)s (*.%(low)s;*.%(up)s)|*.%(low)s;*.%(up)s|' % { 1475 'name': 'ESRI Shapefile', 'low': 'shp', 'up': 'SHP'} 1476 1477 for name, ext in sorted(extList.items()): 1478 if name in ('ESRI Shapefile', 'GeoTIFF'): 1479 continue 1480 fileMask += '%(name)s (*.%(low)s;*.%(up)s)|*.%(low)s;*.%(up)s|' % { 1481 'name': name, 'low': ext.lower(), 'up': ext.upper()} 1482 fileMask += '%s (*.zip;*.ZIP)|*.zip;*.ZIP|' % _('ZIP files') 1483 fileMask += '%s (*.gz;*.GZ)|*.gz;*.GZ|' % _('GZIP files') 1484 fileMask += '%s (*.tar;*.TAR)|*.tar;*.TAR|' % _('TAR files') 1485 # don't include last '|' - windows and mac throw error 1486 fileMask += '%s (*.tar.gz;*.TAR.GZ;*.tgz;*.TGZ)|*.tar.gz;*.TAR.GZ;*.tgz;*.TGZ' % _('TARGZ files') 1487 # only contains formats with extensions hardcoded 1488 1489 self.filePanel = wx.Panel(parent=self) 1490 browse = filebrowse.FileBrowseButton( 1491 parent=self.filePanel, 1492 id=wx.ID_ANY, 1493 size=globalvar.DIALOG_GSELECT_SIZE, 1494 labelText=_('File:'), 1495 dialogTitle=_('Choose file to import'), 1496 buttonText=_('Browse'), 1497 startDirectory=os.getcwd(), 1498 changeCallback=self.OnUpdate, 1499 fileMask=fileMask) 1500 browse.GetChildren()[1].SetName('GdalSelectDataSource') 1501 self.fileWidgets['browse'] = browse 1502 self.fileWidgets['options'] = TextCtrl(parent=self.filePanel) 1503 1504 # directory 1505 self.dirPanel = wx.Panel(parent=self) 1506 browse = filebrowse.DirBrowseButton( 1507 parent=self.dirPanel, 1508 id=wx.ID_ANY, 1509 size=globalvar.DIALOG_GSELECT_SIZE, 1510 labelText=_('Directory:'), 1511 dialogTitle=_('Choose input directory'), 1512 buttonText=_('Browse'), 1513 startDirectory=os.getcwd(), 1514 changeCallback=self.OnUpdate) 1515 browse.GetChildren()[1].SetName('GdalSelectDataSource') 1516 1517 self.dirWidgets['browse'] = browse 1518 formatSelect = wx.Choice(parent=self.dirPanel) 1519 self.dirWidgets['format'] = formatSelect 1520 fileFormats = GetFormats(writableOnly=dest)[fType]['file'] 1521 formatSelect.SetItems(sorted(list(fileFormats))) 1522 formatSelect.Bind( 1523 wx.EVT_CHOICE, lambda evt: self.SetExtension( 1524 self.dirWidgets['format'].GetStringSelection())) 1525 formatSelect.Bind(wx.EVT_CHOICE, self.OnUpdate) 1526 1527 self.dirWidgets['extensionLabel'] = StaticText( 1528 parent=self.dirPanel, label=_("Extension:")) 1529 self.dirWidgets['extension'] = TextCtrl(parent=self.dirPanel) 1530 self.dirWidgets['extension'].Bind(wx.EVT_TEXT, self.ExtensionChanged) 1531 self.dirWidgets['options'] = TextCtrl(parent=self.dirPanel) 1532 if self.ogr: 1533 shapefile = 'ESRI Shapefile' 1534 if shapefile in fileFormats: 1535 formatSelect.SetStringSelection(shapefile) 1536 self.SetExtension(shapefile) 1537 else: 1538 tiff = 'GeoTIFF' 1539 if tiff in fileFormats: 1540 formatSelect.SetStringSelection(tiff) 1541 self.SetExtension(tiff) 1542 1543 # database 1544 self.dbPanel = wx.Panel(parent=self) 1545 self.dbFormats = GetFormats(writableOnly=dest)[fType]['database'] 1546 dbChoice = wx.Choice(parent=self.dbPanel, choices=self.dbFormats) 1547 dbChoice.Bind( 1548 wx.EVT_CHOICE, 1549 lambda evt: self.SetDatabase( 1550 db=dbChoice.GetStringSelection())) 1551 self.dbWidgets['format'] = dbChoice 1552 1553 browse = filebrowse.FileBrowseButton( 1554 parent=self.dbPanel, 1555 id=wx.ID_ANY, 1556 size=globalvar.DIALOG_GSELECT_SIZE, 1557 labelText=_("Name:"), 1558 dialogTitle=_('Choose file'), 1559 buttonText=_('Browse'), 1560 startDirectory=os.getcwd(), 1561 changeCallback=self.OnUpdate) 1562 browse.GetChildren()[1].SetName('GdalSelectDataSource') 1563 1564 self.dbWidgets['browse'] = browse 1565 self.dbWidgets['choice'] = wx.Choice( 1566 parent=self.dbPanel, name='GdalSelectDataSource') 1567 self.dbWidgets['choice'].Bind(wx.EVT_CHOICE, self.OnUpdate) 1568 self.dbWidgets['text'] = TextCtrl( 1569 parent=self.dbPanel, name='GdalSelectDataSource') 1570 self.dbWidgets['text'].Bind(wx.EVT_TEXT, self.OnUpdate) 1571 self.dbWidgets['textLabel1'] = StaticText( 1572 parent=self.dbPanel, label=_("Name:")) 1573 self.dbWidgets['textLabel2'] = StaticText( 1574 parent=self.dbPanel, label=_("Name:")) 1575 self.dbWidgets['featType'] = wx.RadioBox( 1576 parent=self.dbPanel, 1577 id=wx.ID_ANY, 1578 label=" %s " % 1579 _("Feature type:"), 1580 choices=[ 1581 _("simple features"), 1582 _("topological")], 1583 majorDimension=2, 1584 style=wx.RA_SPECIFY_COLS) 1585 if dest: 1586 self.dbWidgets['featType'].Disable() 1587 else: 1588 self.dbWidgets['featType'].Hide() 1589 browse = filebrowse.DirBrowseButton( 1590 parent=self.dbPanel, 1591 id=wx.ID_ANY, 1592 size=globalvar.DIALOG_GSELECT_SIZE, 1593 labelText=_('Directory:'), 1594 dialogTitle=_('Choose input directory'), 1595 buttonText=_('Browse'), 1596 startDirectory=os.getcwd(), 1597 changeCallback=self.OnUpdate) 1598 self.dbWidgets['dirbrowse'] = browse 1599 self.dbWidgets['options'] = TextCtrl(parent=self.dbPanel) 1600 1601 # protocol 1602 self.protocolPanel = wx.Panel(parent=self) 1603 protocolFormats = GetFormats(writableOnly=self.dest)[fType]['protocol'] 1604 protocolChoice = wx.Choice( 1605 parent=self.protocolPanel, 1606 choices=protocolFormats) 1607 self.protocolWidgets['format'] = protocolChoice 1608 1609 self.protocolWidgets['text'] = TextCtrl(parent=self.protocolPanel) 1610 self.protocolWidgets['text'].Bind(wx.EVT_TEXT, self.OnUpdate) 1611 self.protocolWidgets['options'] = TextCtrl( 1612 parent=self.protocolPanel) 1613 1614 # native 1615 self.nativePanel = wx.Panel(parent=self) 1616 1617 self._layout() 1618 sourceType = 'file' 1619 self.SetSourceType(sourceType) # needed always to fit dialog size 1620 if self.dest: 1621 current = RunCommand('v.external.out', 1622 parent=self, 1623 read=True, parse=grass.parse_key_val, 1624 flags='g') 1625 if current['format'] == 'native': 1626 sourceType = 'native' 1627 elif current['format'] in GetFormats()['ogr']['database']: 1628 sourceType = 'db' 1629 else: 1630 sourceType = 'dir' 1631 1632 if self.dest: 1633 wx.CallAfter(self._postInit, sourceType, current) 1634 1635 def _postInit(self, sourceType, data): 1636 """Fill in default values.""" 1637 format = data.get('format', '') 1638 pg = 'conninfo' in data.keys() 1639 if pg: 1640 dsn = '' 1641 for item in data.get('conninfo').split(' '): 1642 k, v = item.split('=') 1643 if k == 'dbname': 1644 dsn = v 1645 break 1646 optList = list() 1647 for k, v in six.iteritems(data): 1648 if k in ('format', 'conninfo', 'topology'): 1649 continue 1650 optList.append('%s=%s' % (k, v)) 1651 options = ','.join(optList) 1652 else: 1653 dsn = data.get('dsn') 1654 options = data.get('options', '') 1655 1656 self.SetSourceType(sourceType) 1657 self.source.SetSelection(self.sourceMap[sourceType]) 1658 1659 # v.external.out does not return dsn for the native format 1660 if dsn: 1661 dsn = os.path.expandvars(dsn) # v.external.out uses $HOME 1662 # fill in default values 1663 if sourceType == 'dir': 1664 self.dirWidgets['format'].SetStringSelection(format) 1665 self.dirWidgets['browse'].SetValue(dsn) 1666 self.dirWidgets['options'].SetValue(options) 1667 elif sourceType == 'db': 1668 self.dbWidgets['format'].SetStringSelection(format) 1669 self.dbWidgets['options'].SetValue(options) 1670 name = self._getCurrentDbWidgetName() 1671 if name == 'choice': 1672 if dsn in self.dbWidgets[name].GetItems(): 1673 self.dbWidgets[name].SetStringSelection(dsn) 1674 if 'topology' in data.keys(): 1675 self.dbWidgets['featType'].SetSelection(1) 1676 else: 1677 self.dbWidgets[name].SetValue(dsn) 1678 1679 def _layout(self): 1680 """Layout""" 1681 self.mainSizer = wx.BoxSizer(wx.VERTICAL) 1682 1683 self.changingSizer = wx.StaticBoxSizer(self.inputBox, wx.VERTICAL) 1684 1685 # file 1686 paddingSizer = wx.BoxSizer(wx.VERTICAL) 1687 sizer = wx.GridBagSizer(vgap=5, hgap=10) 1688 paddingSizer.Add(self.fileWidgets['browse'], 1689 flag=wx.BOTTOM | wx.EXPAND, 1690 border=35) 1691 sizer.Add(paddingSizer, flag=wx.EXPAND, pos=(0, 0), span=(1, 2)) 1692 sizer.AddGrowableCol(0) 1693 if self.dest: 1694 sizer.Add(StaticText(parent=self.filePanel, 1695 label=_("Creation options:")), 1696 flag=wx.ALIGN_CENTER_VERTICAL, 1697 pos=(1, 0)) 1698 sizer.Add(self.fileWidgets['options'], 1699 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1700 pos=(1, 1)) 1701 1702 else: 1703 self.fileWidgets['options'].Hide() 1704 self.filePanel.SetSizer(sizer) 1705 1706 # directory 1707 sizer = wx.GridBagSizer(vgap=3, hgap=10) 1708 sizer.Add(StaticText(parent=self.dirPanel, 1709 label=_("Format:")), 1710 flag=wx.ALIGN_CENTER_VERTICAL, 1711 pos=(0, 0)) 1712 sizer.Add(self.dirWidgets['format'], 1713 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1714 pos=(0, 1)) 1715 sizer.Add(self.dirWidgets['extensionLabel'], 1716 flag=wx.ALIGN_CENTER_VERTICAL, 1717 pos=(0, 2)) 1718 sizer.Add(self.dirWidgets['extension'], 1719 flag=wx.ALIGN_CENTER_VERTICAL, 1720 pos=(0, 3)) 1721 sizer.Add(self.dirWidgets['browse'], 1722 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1723 pos=(1, 0), span=(1, 4)) 1724 if self.dest: 1725 sizer.Add(StaticText(parent=self.dirPanel, 1726 label=_("Creation options:")), 1727 flag=wx.ALIGN_CENTER_VERTICAL, 1728 pos=(2, 0)) 1729 sizer.Add(self.dirWidgets['options'], 1730 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1731 pos=(2, 1)) 1732 helpBtn = Button(parent=self.dirPanel, id=wx.ID_HELP) 1733 helpBtn.Bind(wx.EVT_BUTTON, self.OnHelp) 1734 sizer.Add(helpBtn, 1735 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1736 pos=(2, 2)) 1737 1738 self.dirWidgets['extensionLabel'].Hide() 1739 self.dirWidgets['extension'].Hide() 1740 else: 1741 self.dirWidgets['options'].Hide() 1742 sizer.AddGrowableCol(1) 1743 self.dirPanel.SetSizer(sizer) 1744 1745 # database 1746 sizer = wx.GridBagSizer(vgap=1, hgap=5) 1747 sizer.Add(StaticText(parent=self.dbPanel, 1748 label=_("Format:")), 1749 flag=wx.ALIGN_CENTER_VERTICAL, 1750 pos=(0, 0)) 1751 sizer.Add(self.dbWidgets['format'], 1752 flag=wx.ALIGN_CENTER_VERTICAL, 1753 pos=(0, 1)) 1754 sizer.Add(self.dbWidgets['textLabel1'], 1755 flag=wx.ALIGN_CENTER_VERTICAL, 1756 pos=(1, 0)) 1757 sizer.Add(self.dbWidgets['text'], 1758 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1759 pos=(1, 1), span=(1, 2)) 1760 sizer.Add(self.dbWidgets['browse'], 1761 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1762 pos=(2, 0), span=(1, 3)) 1763 sizer.Add(self.dbWidgets['dirbrowse'], 1764 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1765 pos=(3, 0), span=(1, 2)) 1766 sizer.Add(self.dbWidgets['textLabel2'], 1767 flag=wx.ALIGN_CENTER_VERTICAL, 1768 pos=(4, 0)) 1769 sizer.Add(self.dbWidgets['choice'], 1770 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1771 pos=(4, 1), span=(1, 2)) 1772 if self.dest: 1773 sizer.Add(self.dbWidgets['featType'], 1774 pos=(0, 2), flag=wx.EXPAND) 1775 1776 sizer.Add(StaticText(parent=self.dbPanel, 1777 label=_("Creation options:")), 1778 flag=wx.ALIGN_CENTER_VERTICAL, 1779 pos=(5, 0)) 1780 sizer.Add(self.dbWidgets['options'], 1781 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1782 pos=(5, 1), span=(1, 2)) 1783 1784 # help button 1785 helpBtn = Button(parent=self.dbPanel, id=wx.ID_HELP) 1786 helpBtn.Bind(wx.EVT_BUTTON, self.OnHelp) 1787 sizer.Add(helpBtn, 1788 pos=(5, 3)) 1789 1790 else: 1791 self.dbWidgets['options'].Hide() 1792 1793 self.dbPanel.SetSizer(sizer) 1794 sizer.SetEmptyCellSize((0, 0)) 1795 sizer.AddGrowableCol(1) 1796 1797 # protocol 1798 sizer = wx.GridBagSizer(vgap=3, hgap=3) 1799 sizer.Add(StaticText(parent=self.protocolPanel, 1800 label=_("Format:")), 1801 flag=wx.ALIGN_CENTER_VERTICAL, 1802 pos=(0, 0)) 1803 sizer.Add(self.protocolWidgets['format'], 1804 flag=wx.ALIGN_CENTER_VERTICAL, 1805 pos=(0, 1)) 1806 sizer.Add(StaticText(parent=self.protocolPanel, 1807 label=_("Protocol:")), 1808 flag=wx.ALIGN_CENTER_VERTICAL, 1809 pos=(1, 0)) 1810 sizer.Add(self.protocolWidgets['text'], 1811 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1812 pos=(1, 1)) 1813 if self.dest: 1814 sizer.Add(StaticText(parent=self.protocolPanel, 1815 label=_("Creation options:")), 1816 flag=wx.ALIGN_CENTER_VERTICAL, 1817 pos=(2, 0)) 1818 sizer.Add(self.protocolWidgets['options'], 1819 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1820 pos=(2, 1)) 1821 1822 else: 1823 self.protocolWidgets['options'].Hide() 1824 sizer.AddGrowableCol(1) 1825 self.protocolPanel.SetSizer(sizer) 1826 1827 # native 1828 sizer = wx.BoxSizer(wx.VERTICAL) 1829 sizer.Add(StaticText(parent=self.nativePanel, 1830 label=_("No settings available")), 1831 flag=wx.ALL | wx.EXPAND, border=5) 1832 self.nativePanel.SetSizer(sizer) 1833 1834 for panel in (self.nativePanel, self.filePanel, 1835 self.dirPanel, self.dbPanel, 1836 self.protocolPanel): 1837 1838 self.changingSizer.Add(panel, proportion=1, 1839 flag=wx.EXPAND) 1840 1841 self.mainSizer.Add(self.source, proportion=0, 1842 flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5) 1843 self.mainSizer.Add(self.changingSizer, proportion=1, 1844 flag=wx.ALL | wx.EXPAND, border=5) 1845 self.SetSizer(self.mainSizer) 1846 self.mainSizer.Fit(self) 1847 1848 def _getExtension(self, name): 1849 """Get file extension by format name""" 1850 formatToExt = dict() 1851 formatToExt.update(rasterFormatExtension) 1852 formatToExt.update(vectorFormatExtension) 1853 1854 return formatToExt.get(name, '') 1855 1856 def SetSourceType(self, sourceType): 1857 """Set source type (db, file, dir, ...). 1858 Does not switch radioboxes.""" 1859 self._sourceType = sourceType 1860 self.changingSizer.Show( 1861 self.filePanel, show=( 1862 sourceType == 'file')) 1863 self.changingSizer.Show( 1864 self.nativePanel, show=( 1865 sourceType == 'native')) 1866 self.changingSizer.Show(self.dirPanel, show=(sourceType == 'dir')) 1867 self.changingSizer.Show( 1868 self.protocolPanel, show=( 1869 sourceType == 'pro')) 1870 self.changingSizer.Show(self.dbPanel, show=(sourceType == 'db')) 1871 1872 self.changingSizer.Layout() 1873 1874 if sourceType == 'db': 1875 self.dbWidgets['format'].SetItems(self.dbFormats) 1876 if self.dbFormats: 1877 if 'PostgreSQL' in self.dbFormats: 1878 self.dbWidgets['format'].SetStringSelection('PostgreSQL') 1879 else: 1880 self.dbWidgets['format'].SetSelection(0) 1881 self.dbWidgets['format'].Enable() 1882 1883 if sourceType == 'db': 1884 db = self.dbWidgets['format'].GetStringSelection() 1885 self.SetDatabase(db) 1886 1887 if not self.dest: 1888 self.reloadDataRequired.emit(listData=None, data=None) 1889 self._reloadLayers() 1890 1891 def OnSettingsChanged(self, data): 1892 """User changed setting""" 1893 # data list: [type, dsn, format, options] 1894 if len(data) == 3: 1895 data.append('') 1896 elif len(data) < 3: 1897 return 1898 1899 self.source.SetSelection(self.sourceMap[data[0]]) 1900 self.SetSourceType(data[0]) 1901 if data[0] == 'file': 1902 self.fileWidgets['browse'].SetValue(data[1]) 1903 self.fileWidgets['options'].SetValue(data[3]) 1904 elif data[0] == 'dir': 1905 self.dirWidgets['browse'].SetValue(data[1]) 1906 self.dirWidgets['format'].SetStringSelection(data[2]) 1907 self.dirWidgets['options'].SetValue(data[3]) 1908 self.SetExtension(data[2]) 1909 elif data[0] == 'pro': 1910 self.protocolWidgets['text'].SetValue(data[2]) 1911 self.protocolWidgets['options'].SetValue(data[3]) 1912 elif data[0] == 'db': 1913 name = self._getCurrentDbWidgetName() 1914 if name == 'choice': 1915 if len(data[1].split(':', 1)) > 1: 1916 for item in data[1].split(':', 1)[1].split(','): 1917 key, value = item.split('=', 1) 1918 if key == 'dbname': 1919 self.dbWidgets[name].SetStringSelection(value) 1920 break 1921 else: 1922 self.dbWidgets[name].SetStringSelection(data[1]) 1923 else: 1924 self.dbWidgets[name].SetValue(data[1]) 1925 self.dbWidgets['options'].SetValue(data[3]) 1926 1927 if not self.dest: 1928 self.reloadDataRequired.emit(listData=None, data=None) 1929 self._reloadLayers() 1930 1931 def AttachSettings(self): 1932 if self.ogr: 1933 settingsFile = os.path.join(GetSettingsPath(), 'wxOGR') 1934 else: 1935 settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL') 1936 1937 self.settsManager = ManageSettingsWidget(parent=self, 1938 settingsFile=settingsFile) 1939 self.settsManager.settingsChanged.connect(self.OnSettingsChanged) 1940 self.settsManager.settingsSaving.connect(self.OnSettingsSaving) 1941 1942 # do layout 1943 self.mainSizer.Insert(0, self.settsManager, 1944 flag=wx.ALL | wx.EXPAND, border=5) 1945 1946 def OnSettingsSaving(self, name): 1947 """Saving data""" 1948 if not self.GetDsn(): 1949 GMessage(parent=self, message=_( 1950 "No data source defined, settings are not saved.")) 1951 return 1952 1953 self.settsManager.SetDataToSave((self._sourceType, self.GetDsn(), 1954 self.GetFormat(), self.GetOptions())) 1955 self.settsManager.SaveSettings(name) 1956 1957 def _getExtPatternGlob(self, ext): 1958 """Get pattern for case-insensitive globing""" 1959 pattern = '*.' 1960 for c in ext: 1961 pattern += '[%s%s]' % (c.lower(), c.upper()) 1962 return pattern 1963 1964 def _getCurrentDbWidgetName(self): 1965 """Returns active dns database widget name.""" 1966 for widget in ('browse', 'dirbrowse', 'text', 'choice'): 1967 if self.dbWidgets[widget].IsShown(): 1968 return widget 1969 1970 def GetDsn(self): 1971 """Get datasource name 1972 """ 1973 if self._sourceType == 'db': 1974 if self.dbWidgets['format'].GetStringSelection() in( 1975 'PostgreSQL', 'PostGIS Raster driver'): 1976 1977 dsn = 'PG:dbname=%s' % self.dbWidgets[ 1978 'choice'].GetStringSelection() 1979 else: 1980 name = self._getCurrentDbWidgetName() 1981 if name == 'choice': 1982 dsn = self.dbWidgets[name].GetStringSelection() 1983 else: 1984 dsn = self.dbWidgets[name].GetValue() 1985 1986 else: 1987 if self._sourceType == 'file': 1988 dsn = self.fileWidgets['browse'].GetValue() 1989 elif self._sourceType == 'dir': 1990 dsn = self.dirWidgets['browse'].GetValue() 1991 elif self._sourceType == 'pro': 1992 dsn = self.protocolWidgets['text'].GetValue() 1993 else: 1994 dsn = '' 1995 # check compressed files 1996 try: 1997 ext = os.path.splitext(dsn)[1].lower() 1998 except KeyError: 1999 ext = None 2000 2001 if ext == '.zip': 2002 dsn = '/vsizip/' + dsn 2003 elif ext == '.gzip': 2004 dsn = '/vsigzip/' + dsn 2005 elif ext in ('.tar', '.tar.gz', '.tgz'): 2006 dsn = '/vsitar/' + dsn 2007 2008 return dsn 2009 2010 def SetDatabase(self, db): 2011 """Update database panel.""" 2012 sizer = self.dbPanel.GetSizer() 2013 showBrowse = db in ('SQLite', 'Rasterlite') 2014 showDirbrowse = db in ('FileGDB') 2015 showChoice = db in ('PostgreSQL', 'PostGIS WKT Raster driver', 2016 'PostGIS Raster driver') 2017 enableFeatType = self.dest and self.ogr and db in ('PostgreSQL') 2018 showText = not(showBrowse or showChoice or showDirbrowse) 2019 2020 sizer.Show(self.dbWidgets['browse'], show=showBrowse) 2021 sizer.Show(self.dbWidgets['dirbrowse'], show=showDirbrowse) 2022 sizer.Show(self.dbWidgets['choice'], show=showChoice) 2023 sizer.Show(self.dbWidgets['textLabel2'], show=showChoice) 2024 sizer.Show(self.dbWidgets['text'], show=showText) 2025 sizer.Show(self.dbWidgets['textLabel1'], show=showText) 2026 self.dbWidgets['featType'].Enable(enableFeatType) 2027 if showChoice: 2028 # try to get list of PG databases 2029 dbNames = RunCommand( 2030 'db.databases', 2031 parent=self, 2032 quiet=True, 2033 read=True, 2034 driver='pg').splitlines() 2035 if dbNames is not None: 2036 self.dbWidgets['choice'].SetItems(sorted(dbNames)) 2037 self.dbWidgets['choice'].SetSelection(0) 2038 elif grass.find_program('psql', '--help'): 2039 if not self.dbWidgets['choice'].GetItems(): 2040 p = grass.Popen(['psql', '-ltA'], stdout=grass.PIPE) 2041 ret = p.communicate()[0] 2042 if ret: 2043 dbNames = list() 2044 for line in ret.splitlines(): 2045 sline = line.split('|') 2046 if len(sline) < 2: 2047 continue 2048 dbname = sline[0] 2049 if dbname: 2050 dbNames.append(dbname) 2051 self.dbWidgets['choice'].SetItems(db) 2052 self.dbWidgets['choice'].SetSelection(0) 2053 else: 2054 sizer.Show(self.dbWidgets['text']) 2055 sizer.Show(self.dbWidgets['choice'], False) 2056 2057 sizer.Layout() 2058 2059 def OnUpdate(self, event): 2060 """Update required - load layers.""" 2061 if not self.dest: 2062 self._reloadLayers() 2063 2064 event.Skip() 2065 2066 def _reloadLayers(self): 2067 """Reload list of layers""" 2068 2069 def hasRastSameProjAsLocation(dsn): 2070 2071 ret = RunCommand('r.external', 2072 quiet=True, 2073 read=True, 2074 flags='t', 2075 input=dsn) 2076 2077 # v.external returns info for individual bands, however projection is shared by all bands -> 2078 # (it is possible to take first line) 2079 2080 lines = ret.splitlines() 2081 projectionMatch = '0' 2082 if lines: 2083 bandNumber, bandType, projectionMatch = map( 2084 lambda x: x.strip(), lines[0].split(',')) 2085 2086 return projectionMatch 2087 2088 def getProjMatchCaption(projectionMatch): 2089 2090 if projectionMatch == '0': 2091 projectionMatchCaption = _("No") 2092 else: 2093 projectionMatchCaption = _("Yes") 2094 2095 return projectionMatchCaption 2096 2097 dsn = self.GetDsn() 2098 if not dsn: 2099 return 2100 2101 data = list() 2102 listData = list() 2103 layerId = 1 2104 2105 if self.ogr: 2106 ret = RunCommand('v.external', 2107 quiet=True, 2108 read=True, 2109 flags='t', 2110 input=dsn) 2111 if not ret: 2112 self.reloadDataRequired.emit(listData=None, data=None) 2113 return 2114 2115 layerId = 1 2116 for line in ret.splitlines(): 2117 layerName, featureType, projectionMatch, geometryColumn = map( 2118 lambda x: x.strip(), line.split(',')) 2119 projectionMatchCaption = getProjMatchCaption(projectionMatch) 2120 grassName = GetValidLayerName(layerName) 2121 if geometryColumn: 2122 featureType = geometryColumn + '/' + featureType 2123 listData.append( 2124 (layerId, 2125 layerName, 2126 featureType, 2127 projectionMatchCaption, 2128 grassName)) 2129 data.append( 2130 (layerId, 2131 layerName, 2132 featureType, 2133 int(projectionMatch), 2134 grassName)) 2135 layerId += 1 2136 else: 2137 if self._sourceType == 'file': 2138 baseName = os.path.basename(dsn) 2139 grassName = GetValidLayerName(baseName.split('.', -1)[0]) 2140 projectionMatch = hasRastSameProjAsLocation(dsn) 2141 projectionMatchCaption = getProjMatchCaption(projectionMatch) 2142 listData.append( 2143 (layerId, baseName, projectionMatchCaption, grassName)) 2144 data.append( 2145 (layerId, baseName, int(projectionMatch), grassName)) 2146 elif self._sourceType == 'dir': 2147 ext = self.dirWidgets['extension'].GetValue() 2148 for filename in glob.glob(os.path.join( 2149 dsn, "%s") % self._getExtPatternGlob(ext)): 2150 baseName = os.path.basename(filename) 2151 grassName = GetValidLayerName(baseName.split('.', -1)[0]) 2152 projectionMatch = hasRastSameProjAsLocation(filename) 2153 projectionMatchCaption = getProjMatchCaption( 2154 projectionMatch) 2155 listData.append( 2156 (layerId, baseName, projectionMatchCaption, grassName)) 2157 data.append( 2158 (layerId, baseName, int(projectionMatch), grassName)) 2159 layerId += 1 2160 2161 # emit signal 2162 self.reloadDataRequired.emit(listData=listData, data=data) 2163 2164 def ExtensionChanged(self, event): 2165 if not self.dest: 2166 # reload layers 2167 self._reloadLayers() 2168 2169 def SetExtension(self, name): 2170 """Extension changed""" 2171 ext = self._getExtension(name) 2172 self.dirWidgets['extension'].SetValue(ext) 2173 2174 def GetType(self): 2175 """Get source type""" 2176 return self._sourceType 2177 2178 def GetFormat(self): 2179 """Get format as string""" 2180 if self._sourceType == 'dir': 2181 format = self.dirWidgets['format'].GetStringSelection() 2182 elif self._sourceType == 'pro': 2183 format = self.protocolWidgets['format'].GetStringSelection() 2184 elif self._sourceType == 'db': 2185 format = self.dbWidgets['format'].GetStringSelection() 2186 else: 2187 format = '' 2188 2189 return format.replace(' ', '_') 2190 2191 def GetFormatExt(self): 2192 """Get format extension""" 2193 return self._getExtension(self.GetFormat()) 2194 2195 def GetOptions(self): 2196 """Get creation options""" 2197 if self._sourceType == 'file': 2198 options = self.fileWidgets['options'].GetValue() 2199 elif self._sourceType == 'dir': 2200 options = self.dirWidgets['options'].GetValue() 2201 elif self._sourceType == 'pro': 2202 options = self.protocolWidgets['options'].GetValue() 2203 elif self._sourceType == 'db': 2204 if self.dbWidgets['featType'].GetSelection() == 1: 2205 options = 'topology=yes ' 2206 else: 2207 options = '' 2208 options += self.dbWidgets['options'].GetValue() 2209 2210 return options.strip() 2211 2212 def OnHelp(self, event): 2213 """Show related manual page""" 2214 cmd = '' 2215 if self.dest: 2216 if self.ogr: 2217 cmd = 'v.external.out' 2218 else: 2219 cmd = 'r.external.out' 2220 else: 2221 if self.link: 2222 if self.ogr: 2223 cmd = 'v.external' 2224 else: 2225 cmd = 'r.external' 2226 else: 2227 if self.ogr: 2228 cmd = 'v.in.ogr' 2229 else: 2230 cmd = 'r.in.gdal' 2231 2232 RunCommand('g.manual', entry=cmd) 2233 2234 2235class ProjSelect(wx.ComboBox): 2236 """Widget for selecting input raster/vector map used by 2237 r.proj/v.proj modules.""" 2238 2239 def __init__(self, parent, isRaster, id=wx.ID_ANY, 2240 size=globalvar.DIALOG_COMBOBOX_SIZE, **kwargs): 2241 super(ProjSelect, self).__init__(parent, id, size=size, **kwargs) 2242 self.SetName("ProjSelect") 2243 self.isRaster = isRaster 2244 2245 def UpdateItems(self, dbase, location, mapset): 2246 """Update list of maps 2247 2248 """ 2249 if not dbase: 2250 dbase = grass.gisenv()['GISDBASE'] 2251 if not mapset: 2252 mapset = grass.gisenv()['MAPSET'] 2253 if self.isRaster: 2254 ret = RunCommand('r.proj', 2255 quiet=True, 2256 read=True, 2257 flags='l', 2258 dbase=dbase, 2259 location=location, 2260 mapset=mapset) 2261 else: 2262 ret = RunCommand('v.proj', 2263 quiet=True, 2264 read=True, 2265 flags='l', 2266 dbase=dbase, 2267 location=location, 2268 mapset=mapset) 2269 listMaps = list() 2270 if ret: 2271 for line in ret.splitlines(): 2272 listMaps.append(line.strip()) 2273 ListSortLower(listMaps) 2274 2275 self.SetItems(listMaps) 2276 self.SetValue('') 2277 2278 2279class ElementSelect(wx.Choice): 2280 2281 def __init__(self, parent, id=wx.ID_ANY, elements=None, 2282 size=globalvar.DIALOG_COMBOBOX_SIZE, 2283 **kwargs): 2284 """Widget for selecting GIS element 2285 2286 :param parent: parent window 2287 :param elements: filter elements 2288 """ 2289 super(ElementSelect, self).__init__(parent, id, size=size, 2290 **kwargs) 2291 self.SetName("ElementSelect") 2292 2293 task = gtask.parse_interface('g.list') 2294 p = task.get_param(value='type') 2295 self.values = p.get('values', []) 2296 self.valuesDesc = p.get('values_desc', []) 2297 2298 if elements: 2299 values = [] 2300 valuesDesc = [] 2301 for idx in range(0, len(self.values)): 2302 value = self.values[idx] 2303 if value in elements: 2304 values.append(value) 2305 valuesDesc.append(self.valuesDesc[idx]) 2306 self.values = values 2307 self.valuesDesc = valuesDesc 2308 2309 self.SetItems(self.valuesDesc) 2310 2311 def GetValue(self, name): 2312 """Translate value 2313 2314 :param name: element name 2315 """ 2316 idx = self.valuesDesc.index(name) 2317 if idx > -1: 2318 return self.values[idx] 2319 return '' 2320 2321 2322class OgrTypeSelect(wx.Panel): 2323 2324 def __init__(self, parent, panel, **kwargs): 2325 """Widget to choose OGR feature type 2326 2327 :param parent: parent window 2328 :param panel: wx.Panel instance used as parent window 2329 """ 2330 wx.Panel.__init__(self, parent=panel, id=wx.ID_ANY) 2331 2332 self.ftype = wx.Choice(parent=self, id=wx.ID_ANY, size=( 2333 200, -1), choices=(_("Point"), _("LineString"), _("Polygon"))) 2334 self._layout() 2335 2336 def _layout(self): 2337 """Do layout""" 2338 sizer = wx.BoxSizer(wx.HORIZONTAL) 2339 sizer.Add(StaticText(parent=self, 2340 id=wx.ID_ANY, 2341 label=_("Feature type:")), 2342 proportion=1, 2343 flag=wx.ALIGN_CENTER_VERTICAL, 2344 border=5) 2345 sizer.Add(self.ftype, 2346 proportion=0, 2347 flag=wx.EXPAND | wx.ALIGN_RIGHT) 2348 2349 self.SetSizer(sizer) 2350 sizer.Fit(self) 2351 2352 def GetType(self): 2353 """Get selected type as string 2354 2355 :return: feature type as string 2356 """ 2357 sel = self.ftype.GetSelection() 2358 if sel == 0: 2359 return 'point' 2360 elif sel == 1: 2361 return 'line' 2362 elif sel == 2: 2363 return 'boundary' 2364 2365 2366class CoordinatesSelect(Panel): 2367 2368 def __init__(self, parent, giface, multiple=False, **kwargs): 2369 """Widget to get coordinates from map window by mouse click 2370 2371 :param parent: parent window 2372 :param giface: GRASS interface 2373 :param multiple: - True if it is possible to insert more coordinates 2374 """ 2375 self._giface = giface 2376 self.multiple = multiple 2377 self.mapWin = None 2378 self.drawMapWin = None 2379 2380 super(CoordinatesSelect, self).__init__(parent=parent, id=wx.ID_ANY) 2381 2382 self.coordsField = TextCtrl(parent=self, id=wx.ID_ANY, 2383 size=globalvar.DIALOG_TEXTCTRL_SIZE, 2384 validator=CoordinatesValidator()) 2385 2386 icon = wx.Bitmap( 2387 os.path.join( 2388 globalvar.ICONDIR, 2389 "grass", 2390 "pointer.png")) 2391 self.buttonInsCoords = buttons.ThemedGenBitmapToggleButton( 2392 parent=self, id=wx.ID_ANY, bitmap=icon, size=globalvar.DIALOG_COLOR_SIZE) 2393 self.registered = False 2394 self.buttonInsCoords.Bind(wx.EVT_BUTTON, self._onClick) 2395 2396 mapdisp = self._giface.GetMapDisplay() 2397 if mapdisp: 2398 switcher = mapdisp.GetToolSwitcher() 2399 switcher.AddCustomToolToGroup( 2400 group='mouseUse', 2401 btnId=self.buttonInsCoords.GetId(), 2402 toggleHandler=self.buttonInsCoords.SetValue) 2403 self._doLayout() 2404 self.coordsField.Bind(wx.EVT_TEXT, lambda event: self._draw(delay=1)) 2405 2406 def _doLayout(self): 2407 self.dialogSizer = wx.BoxSizer(wx.HORIZONTAL) 2408 self.dialogSizer.Add(self.coordsField, 2409 proportion=1, 2410 flag=wx.EXPAND) 2411 self.dialogSizer.Add(self.buttonInsCoords) 2412 self.SetSizer(self.dialogSizer) 2413 2414 def _onClick(self, event): 2415 """Button for interacitve inserting of coordinates clicked""" 2416 2417 self.mapWin = self._giface.GetMapWindow() 2418 if self.buttonInsCoords.GetToggle() and self.mapWin: 2419 switcher = self._giface.GetMapDisplay().GetToolSwitcher() 2420 switcher.ToolChanged(self.buttonInsCoords.GetId()) 2421 if self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN, 2422 self._onMapClickHandler, 2423 'cross') == False: 2424 return 2425 2426 self.registered = True 2427 self._giface.GetMapDisplay().Raise() 2428 else: 2429 if self.mapWin and self.mapWin.UnregisterMouseEventHandler( 2430 wx.EVT_LEFT_DOWN, self._onMapClickHandler): 2431 self.registered = False 2432 return 2433 2434 def drawCleanUp(self): 2435 if self.drawMapWin: 2436 self.drawMapWin.UnregisterGraphicsToDraw(self.pointsToDraw) 2437 2438 def _draw(self, delay): 2439 """Draws points representing inserted coordinates in mapwindow.""" 2440 if self.drawMapWin != self.mapWin: 2441 self.drawCleanUp() 2442 if self.mapWin: 2443 self.drawMapWin = self.mapWin 2444 self.pointsToDraw = self.drawMapWin.RegisterGraphicsToDraw( 2445 graphicsType="point") 2446 2447 if self.drawMapWin: 2448 items = self.pointsToDraw.GetAllItems() 2449 for i in items: 2450 self.pointsToDraw.DeleteItem(i) 2451 2452 coords = self._getCoords() 2453 if coords is not None: 2454 for i in range(len(coords) // 2): 2455 i = i * 2 2456 self.pointsToDraw.AddItem( 2457 coords=(coords[i], coords[i + 1])) 2458 2459 self._giface.updateMap.emit( 2460 render=False, renderVector=False, delay=delay) 2461 2462 def _getCoords(self): 2463 """Get list of coordinates. 2464 2465 :return: None if values are not valid 2466 """ 2467 if self.coordsField.GetValidator().Validate(self): 2468 return self.coordsField.GetValue().split(',') 2469 2470 return None 2471 2472 def _onMapClickHandler(self, event): 2473 """Gets coordinates from mapwindow""" 2474 if event == "unregistered": 2475 return 2476 2477 e, n = self.mapWin.GetLastEN() 2478 prevCoords = "" 2479 2480 if self.multiple: 2481 prevCoords = self.coordsField.GetValue().strip() 2482 if prevCoords != "": 2483 prevCoords += "," 2484 2485 value = prevCoords + str(e) + "," + str(n) 2486 self.coordsField.SetValue(value) 2487 2488 self._draw(delay=0) 2489 2490 def OnClose(self): 2491 """Unregistrates _onMapClickHandler from mapWin""" 2492 self.drawCleanUp() 2493 self._giface.updateMap.emit(render=False, renderVector=False) 2494 2495 mapdisp = self._giface.GetMapDisplay() 2496 if mapdisp: 2497 switcher = mapdisp.GetToolSwitcher() 2498 switcher.RemoveCustomToolFromGroup(self.buttonInsCoords.GetId()) 2499 2500 if self.mapWin and self.registered: 2501 self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, 2502 self._onMapClickHandler) 2503 2504 def GetTextWin(self): 2505 """Get TextCtrl widget""" 2506 return self.coordsField 2507 2508 2509class VectorCategorySelect(wx.Panel): 2510 """Widget that allows interactive selection of vector features""" 2511 2512 def __init__(self, parent, giface, task=None): 2513 super(VectorCategorySelect, self).__init__(parent=parent, id=wx.ID_ANY) 2514 self.task = task 2515 self.parent = parent 2516 self.giface = giface 2517 2518 self.selectedFeatures = None 2519 self.registered = False 2520 self._vectorSelect = None 2521 2522 self.mapdisp = self.giface.GetMapDisplay() 2523 2524 self.catsField = TextCtrl(parent=self, id=wx.ID_ANY, 2525 size=globalvar.DIALOG_TEXTCTRL_SIZE) 2526 2527 icon = wx.Bitmap( 2528 os.path.join( 2529 globalvar.ICONDIR, 2530 "grass", 2531 "select.png")) 2532 self.buttonVecSelect = buttons.ThemedGenBitmapToggleButton( 2533 parent=self, id=wx.ID_ANY, bitmap=icon, size=globalvar.DIALOG_COLOR_SIZE) 2534 self.buttonVecSelect.Bind(wx.EVT_BUTTON, self._onClick) 2535 2536 if self.mapdisp: 2537 switcher = self.mapdisp.GetToolSwitcher() 2538 switcher.AddCustomToolToGroup( 2539 group='mouseUse', 2540 btnId=self.buttonVecSelect.GetId(), 2541 toggleHandler=self.buttonVecSelect.SetValue) 2542 2543 self._layout() 2544 2545 def _isMapSelected(self): 2546 """Check if layer list contains at least one selected map 2547 """ 2548 layerList = self.giface.GetLayerList() 2549 layerSelected = layerList.GetSelectedLayer() 2550 if layerSelected is None: 2551 GWarning( 2552 _("No vector map selected in layer manager. Operation canceled.")) 2553 return False 2554 2555 return True 2556 2557 def _chckMap(self): 2558 """Check if selected map in 'input' widget is the same as selected map in lmgr """ 2559 if self._isMapSelected(): 2560 layerList = self.giface.GetLayerList() 2561 layerSelected = layerList.GetSelectedLayer() 2562 # d.vect module 2563 inputName = self.task.get_param(value='map', raiseError=False) 2564 if not inputName: 2565 inputName = self.task.get_param('input') 2566 if inputName['value'] != str(layerSelected): 2567 if inputName['value'] == '' or inputName['value'] is None: 2568 GWarning(_("Input vector map is not selected")) 2569 return False 2570 GWarning( 2571 _( 2572 "Input vector map <%s> and selected map <%s> in layer manager are different. " 2573 "Operation canceled.") % 2574 (inputName['value'], str(layerSelected))) 2575 return False 2576 return True 2577 return False 2578 2579 def _onClick(self, evt=None): 2580 if self.task is not None: 2581 if not self._chckMap(): 2582 self.buttonVecSelect.SetValue(False) 2583 return 2584 else: 2585 if not self._isMapSelected(): 2586 self.buttonVecSelect.SetValue(False) 2587 return 2588 if self._vectorSelect is None: 2589 2590 if self.mapdisp: 2591 if self.buttonVecSelect.IsEnabled(): 2592 switcher = self.mapdisp.GetToolSwitcher() 2593 switcher.ToolChanged(self.buttonVecSelect.GetId()) 2594 2595 self._vectorSelect = VectorSelectBase( 2596 self.mapdisp, self.giface) 2597 if self.mapdisp.GetWindow().RegisterMouseEventHandler( 2598 wx.EVT_LEFT_DOWN, self._onMapClickHandler, 'cross') == False: 2599 return 2600 self.registered = True 2601 self.mapdisp.Raise() 2602 else: 2603 self.OnClose() 2604 2605 def OnClose(self, event=None): 2606 if not self.mapdisp: 2607 return 2608 2609 switcher = self.mapdisp.GetToolSwitcher() 2610 switcher.RemoveCustomToolFromGroup(self.buttonVecSelect.GetId()) 2611 if self._vectorSelect is not None: 2612 tmp = self._vectorSelect.GetLineStringSelectedCats() 2613 self._vectorSelect.OnClose() 2614 self.catsField.SetValue(tmp) 2615 self._vectorSelect = None 2616 2617 def _onMapClickHandler(self, event): 2618 """Update category text input widget""" 2619 if event == "unregistered": 2620 return 2621 2622 if self.task is None: 2623 if not self._isMapSelected(): 2624 self.OnClose() 2625 else: 2626 self.catsField.SetValue( 2627 self._vectorSelect.GetLineStringSelectedCats()) 2628 else: 2629 if not self._chckMap(): 2630 self.OnClose() 2631 else: 2632 self.catsField.SetValue( 2633 self._vectorSelect.GetLineStringSelectedCats()) 2634 2635 def GetTextWin(self): 2636 return self.catsField 2637 2638 def GetValue(self): 2639 return self.catsField.GetValue() 2640 2641 def SetValue(self, value): 2642 self.catsField.SetValue(value) 2643 2644 def _layout(self): 2645 self.dialogSizer = wx.BoxSizer(wx.HORIZONTAL) 2646 self.dialogSizer.Add(self.catsField, 2647 proportion=1, 2648 flag=wx.EXPAND) 2649 2650 self.dialogSizer.Add(self.buttonVecSelect) 2651 self.SetSizer(self.dialogSizer) 2652 2653 2654class SignatureSelect(wx.ComboBox): 2655 """Widget for selecting signatures""" 2656 2657 def __init__(self, parent, element, id=wx.ID_ANY, 2658 size=globalvar.DIALOG_GSELECT_SIZE, **kwargs): 2659 super(SignatureSelect, self).__init__(parent, id, size=size, 2660 **kwargs) 2661 self.element = element 2662 self.SetName("SignatureSelect") 2663 2664 def Insert(self, group, subgroup=None): 2665 """Insert signatures for defined group/subgroup 2666 2667 :param group: group name (can be fully-qualified) 2668 :param subgroup: non fully-qualified name of subgroup 2669 """ 2670 if not group: 2671 return 2672 gisenv = grass.gisenv() 2673 try: 2674 name, mapset = group.split('@', 1) 2675 except ValueError: 2676 name = group 2677 mapset = gisenv['MAPSET'] 2678 2679 path = os.path.join( 2680 gisenv['GISDBASE'], 2681 gisenv['LOCATION_NAME'], 2682 mapset, 'group', name) 2683 2684 if subgroup: 2685 path = os.path.join(path, 'subgroup', subgroup) 2686 try: 2687 items = list() 2688 for element in os.listdir(os.path.join(path, self.element)): 2689 items.append(element) 2690 self.SetItems(items) 2691 except OSError: 2692 self.SetItems([]) 2693 self.SetValue('') 2694 2695 2696class SeparatorSelect(wx.ComboBox): 2697 """Widget for selecting seperator""" 2698 2699 def __init__(self, parent, id=wx.ID_ANY, 2700 size=globalvar.DIALOG_GSELECT_SIZE, **kwargs): 2701 super(SeparatorSelect, self).__init__(parent, id, size=size, 2702 **kwargs) 2703 self.SetName("SeparatorSelect") 2704 self.SetItems(['pipe', 'comma', 'space', 'tab', 'newline']) 2705 2706 2707class SqlWhereSelect(wx.Panel): 2708 2709 def __init__(self, parent, **kwargs): 2710 """Widget to define SQL WHERE condition. 2711 2712 :param parent: parent window 2713 """ 2714 super(SqlWhereSelect, self).__init__(parent=parent, id=wx.ID_ANY) 2715 self.parent = parent 2716 self.vector_map = None 2717 2718 self.sqlField = TextCtrl(parent=self, id=wx.ID_ANY, 2719 size=globalvar.DIALOG_TEXTCTRL_SIZE) 2720 self.GetChildren()[0].SetName("SqlWhereSelect") 2721 icon = wx.Bitmap( 2722 os.path.join( 2723 globalvar.ICONDIR, 2724 "grass", 2725 "table.png")) 2726 self.buttonInsSql = buttons.ThemedGenBitmapButton( 2727 parent=self, id=wx.ID_ANY, bitmap=icon, size=globalvar.DIALOG_COLOR_SIZE) 2728 self.buttonInsSql.Bind(wx.EVT_BUTTON, self._onClick) 2729 2730 self._doLayout() 2731 2732 2733 def _doLayout(self): 2734 self.dialogSizer = wx.BoxSizer(wx.HORIZONTAL) 2735 self.dialogSizer.Add(self.sqlField, 2736 proportion=1, 2737 flag=wx.EXPAND) 2738 self.dialogSizer.Add(self.buttonInsSql) 2739 self.SetSizer(self.dialogSizer) 2740 2741 def GetTextWin(self): 2742 return self.sqlField 2743 2744 def _onClick(self, event): 2745 from dbmgr.sqlbuilder import SQLBuilderWhere 2746 try: 2747 if not self.vector_map: 2748 raise GException(_('No vector map selected')) 2749 win = SQLBuilderWhere(parent=self, 2750 vectmap=self.vector_map, 2751 layer=self.vector_layer) 2752 win.Show() 2753 except GException as e: 2754 GMessage(parent=self.parent, message='{}'.format(e)) 2755 2756 def SetData(self, vector, layer): 2757 self.vector_map = vector 2758 self.vector_layer = int(layer) # TODO: support layer names 2759 2760 def SetValue(self, value): 2761 self.sqlField.SetValue(value) 2762