| 1 | """
|
|---|
| 2 | @package gui_core.gselect
|
|---|
| 3 |
|
|---|
| 4 | @brief Custom control that selects elements
|
|---|
| 5 |
|
|---|
| 6 | Classes:
|
|---|
| 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 |
|
|---|
| 34 | This 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 |
|
|---|
| 44 | from __future__ import print_function
|
|---|
| 45 |
|
|---|
| 46 | import os
|
|---|
| 47 | import sys
|
|---|
| 48 | import glob
|
|---|
| 49 | import copy
|
|---|
| 50 | import six
|
|---|
| 51 |
|
|---|
| 52 | import wx
|
|---|
| 53 |
|
|---|
| 54 | from core import globalvar
|
|---|
| 55 | if globalvar.wxPythonPhoenix:
|
|---|
| 56 | ComboPopup = wx.ComboPopup
|
|---|
| 57 | ComboCtrl = wx.ComboCtrl
|
|---|
| 58 | else:
|
|---|
| 59 | import wx.combo
|
|---|
| 60 | ComboPopup = wx.combo.ComboPopup
|
|---|
| 61 | ComboCtrl = wx.combo.ComboCtrl
|
|---|
| 62 | import wx.lib.buttons as buttons
|
|---|
| 63 | import wx.lib.filebrowsebutton as filebrowse
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 | import grass.script as grass
|
|---|
| 68 | from grass.script import task as gtask
|
|---|
| 69 | from grass.exceptions import CalledModuleError
|
|---|
| 70 |
|
|---|
| 71 | from gui_core.widgets import ManageSettingsWidget, CoordinatesValidator
|
|---|
| 72 |
|
|---|
| 73 | from core.gcmd import RunCommand, GError, GMessage, GWarning, GException
|
|---|
| 74 | from core.utils import GetListOfLocations, GetListOfMapsets, \
|
|---|
| 75 | GetFormats, rasterFormatExtension, vectorFormatExtension
|
|---|
| 76 | from core.utils import GetSettingsPath, GetValidLayerName, ListSortLower
|
|---|
| 77 | from core.utils import GetVectorNumberOfLayers
|
|---|
| 78 | from core.settings import UserSettings
|
|---|
| 79 | from core.debug import Debug
|
|---|
| 80 | from gui_core.vselect import VectorSelectBase
|
|---|
| 81 | from gui_core.wrap import TreeCtrl, Button, StaticText, StaticBox, \
|
|---|
| 82 | TextCtrl, Panel
|
|---|
| 83 |
|
|---|
| 84 | from grass.pydispatch.signal import Signal
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 | class 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 |
|
|---|
| 176 | class 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 |
|
|---|
| 204 | class 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 |
|
|---|
| 415 | class 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 |
|
|---|
| 791 | class 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 |
|
|---|
| 914 | class 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 |
|
|---|
| 980 | class 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 |
|
|---|
| 996 | class 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 |
|
|---|
| 1013 | class 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 |
|
|---|
| 1059 | class 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 |
|
|---|
| 1191 | class 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 |
|
|---|
| 1208 | class 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 |
|
|---|
| 1236 | class 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 |
|
|---|
| 1317 | class 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 |
|
|---|
| 1345 | class 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 |
|
|---|
| 1379 | class 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 |
|
|---|
| 2240 | class 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 |
|
|---|
| 2284 | class 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 |
|
|---|
| 2327 | class 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 |
|
|---|
| 2371 | class 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 |
|
|---|
| 2514 | class 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 |
|
|---|
| 2656 | class 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 |
|
|---|
| 2698 | class 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 |
|
|---|
| 2709 | class 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)
|
|---|