source: grass/trunk/gui/wxpython/gui_core/gselect.py

Last change on this file was 74307, checked in by neteler, 5 years ago

i18N: cleanup gettext usage for Python code (fixes #3790) (contributed by pmav99)

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