4
@brief GRASS Attribute Table Manager
6
This program is based on FileHunter, published in 'The wxPython Linux
7
Tutorial' on wxPython WIKI pages.
9
It also uses some functions at
10
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426407
13
python dbm.py vector@mapset
18
- manager::VirtualAttributeList
19
- manager::AttributeManager
20
- manager::TableListCtrl
21
- manager::LayerListCtrl
24
(C) 2007-2009, 2011 by the GRASS Development Team
26
This program is free software under the GNU General Public License
27
(>=v2). Read the file COPYING that comes with GRASS for details.
29
@author Jachym Cepicky <jachym.cepicky gmail.com>
30
@author Martin Landa <landa.martin gmail.com>
40
if __name__ == "__main__":
41
sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
42
from core import globalvar
44
import wx.lib.mixins.listctrl as listmix
45
import wx.lib.flatnotebook as FN
47
import grass.script as grass
49
from dbmgr.sqlbuilder import SQLFrame
50
from core.gcmd import RunCommand, GException, GError, GMessage, GWarning
51
from core.utils import ListOfCatsToRange
52
from gui_core.dialogs import CreateNewVector
53
from dbmgr.vinfo import VectorDBInfo, unicodeValue, createDbInfoDesc
54
from core.debug import Debug
55
from dbmgr.dialogs import ModifyTableRecord
56
from core.settings import UserSettings
57
from gui_core.widgets import GNotebook
61
The log output is redirected to the status bar of the containing frame.
63
def __init__(self, parent):
66
def write(self, text_string):
67
"""!Update status bar"""
68
self.parent.SetStatusText(text_string.strip())
71
class VirtualAttributeList(wx.ListCtrl,
72
listmix.ListCtrlAutoWidthMixin,
73
listmix.ColumnSorterMixin):
75
Support virtual list class
77
def __init__(self, parent, log, mapDBInfo, layer):
79
# initialize variables
83
self.mapDBInfo = mapDBInfo
86
self.columns = {} # <- LoadData()
88
wx.ListCtrl.__init__(self, parent = parent, id = wx.ID_ANY,
89
style = wx.LC_REPORT | wx.LC_HRULES |
90
wx.LC_VRULES | wx.LC_VIRTUAL | wx.LC_SORT_ASCENDING)
93
keyColumn = self.LoadData(layer)
100
# add some attributes (colourful background for each item rows)
102
self.attr1 = wx.ListItemAttr()
103
self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
104
self.attr2 = wx.ListItemAttr()
105
self.attr2.SetBackgroundColour("white")
106
self.il = wx.ImageList(16, 16)
107
self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP, wx.ART_TOOLBAR,
109
self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
111
self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
114
listmix.ListCtrlAutoWidthMixin.__init__(self)
115
listmix.ColumnSorterMixin.__init__(self, len(self.columns))
117
# sort item by category (id)
119
self.SortListItems(col = keyColumn, ascending = True)
121
self.SortListItems(col = 0, ascending = True)
124
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
125
self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
126
self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColumnSort)
127
self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)
129
def Update(self, mapDBInfo):
130
"""!Update list according new mapDBInfo description"""
131
self.mapDBInfo = mapDBInfo
132
self.LoadData(self.layer)
134
def LoadData(self, layer, columns = None, where = None, sql = None):
135
"""!Load data into list
137
@param layer layer number
138
@param columns list of columns for output (-> v.db.select)
139
@param where where statement (-> v.db.select)
140
@param sql full sql statement (-> db.select)
142
@return id of key column
143
@return -1 if key column is not displayed
145
self.log.write(_("Loading data..."))
147
tableName = self.mapDBInfo.layers[layer]['table']
148
keyColumn = self.mapDBInfo.layers[layer]['key']
150
self.columns = self.mapDBInfo.tables[tableName]
152
raise GException(_("Attribute table <%s> not found. "
153
"For creating the table switch to "
154
"'Manage layers' tab.") % tableName)
157
columns = self.mapDBInfo.GetColumns(tableName)
159
all = self.mapDBInfo.GetColumns(tableName)
162
GError(parent = self,
163
message = _("Column <%(column)s> not found in "
164
"in the table <%(table)s>.") % \
165
{ 'column' : col, 'table' : tableName })
169
# for maps connected via v.external
170
keyId = columns.index(keyColumn)
177
# FIXME: Max. number of rows, while the GUI is still usable
179
# stdout can be very large, do not use PIPE, redirect to temp file
180
# TODO: more effective way should be implemented...
181
outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
183
# split on field sep breaks if varchar() column contains the
184
# values, so while sticking with ASCII we make it something
185
# highly unlikely to exist naturally.
188
cmdParams = dict(quiet = True,
194
cmdParams.update(dict(sql = sql,
195
output = outFile.name))
196
ret = RunCommand('db.select',
199
cmdParams.update(dict(map = self.mapDBInfo.map,
204
cmdParams.update(dict(columns = ','.join(columns)))
206
ret = RunCommand('v.db.select',
209
# These two should probably be passed to init more cleanly
210
# setting the numbers of items = number of elements in the dictionary
211
self.itemDataMap = {}
212
self.itemIndexMap = []
213
self.itemCatsMap = {}
215
self.DeleteAllItems()
218
for i in range(self.GetColumnCount()):
223
info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
226
for column in columns:
228
self.InsertColumnInfo(i, info)
232
self.log.write(_("Can display only 256 columns."))
238
# os.linesep doesn't work here (MSYS)
239
record = outFile.readline().replace('\n', '')
244
record = record.split(fs)
245
if len(columns) != len(record):
246
GError(parent = self,
247
message = _("Inconsistent number of columns "
248
"in the table <%(table)s>.") % \
249
{'table' : tableName })
250
self.columns = {} # because of IsEmpty method
253
self.AddDataRow(i, record, columns, keyId)
257
self.log.write(_("Viewing limit: 100000 records."))
264
width = self.columns[col]['length'] * 6 # FIXME
269
self.SetColumnWidth(col = i, width = width)
274
self.log.write(_("Number of loaded records: %d") % \
279
def AddDataRow(self, i, record, columns, keyId):
280
"""!Add row to the data list"""
281
self.itemDataMap[i] = []
282
keyColumn = self.mapDBInfo.layers[self.layer]['key']
286
if keyColumn == 'OGC_FID':
287
self.itemDataMap[i].append(i+1)
292
if self.columns[columns[j]]['ctype'] != types.StringType:
294
### casting disabled (2009/03)
295
### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value))
296
self.itemDataMap[i].append(value)
298
self.itemDataMap[i].append(_('Unknown value'))
300
# encode string values
302
self.itemDataMap[i].append(unicodeValue(value))
303
except UnicodeDecodeError:
304
self.itemDataMap[i].append(_("Unable to decode value. "
305
"Set encoding in GUI preferences ('Attributes')."))
307
if not cat and keyId > -1 and keyId == j:
309
cat = self.columns[columns[j]]['ctype'] (value)
310
except ValueError, e:
312
GError(parent = self,
313
message = _("Error loading attribute data. "
314
"Record number: %(rec)d. Unable to convert value '%(val)s' in "
315
"key column (%(key)s) to integer.\n\n"
316
"Details: %(detail)s") % \
317
{ 'rec' : i + 1, 'val' : value,
318
'key' : keyColumn, 'detail' : e})
321
self.itemIndexMap.append(i)
322
if keyId > -1: # load cats only when LoadData() is called first time
323
self.itemCatsMap[i] = cat
325
def OnItemSelected(self, event):
326
"""!Item selected. Add item to selected cats..."""
327
# cat = int(self.GetItemText(event.m_itemIndex))
328
# if cat not in self.selectedCats:
329
# self.selectedCats.append(cat)
330
# self.selectedCats.sort()
334
def OnItemDeselected(self, event):
335
"""!Item deselected. Remove item from selected cats..."""
336
# cat = int(self.GetItemText(event.m_itemIndex))
337
# if cat in self.selectedCats:
338
# self.selectedCats.remove(cat)
339
# self.selectedCats.sort()
343
def GetSelectedItems(self):
344
"""!Return list of selected items (category numbers)"""
346
item = self.GetFirstSelected()
348
cats.append(self.GetItemText(item))
349
item = self.GetNextSelected(item)
353
def GetColumnText(self, index, col):
354
"""!Return column text"""
355
item = self.GetItem(index, col)
356
return item.GetText()
358
def GetListCtrl(self):
362
def OnGetItemText(self, item, col):
364
index = self.itemIndexMap[item]
365
s = self.itemDataMap[index][col]
368
def OnGetItemAttr(self, item):
369
"""!Get item attributes"""
375
def OnColumnMenu(self, event):
376
"""!Column heading right mouse button -> pop-up menu"""
377
self._col = event.GetColumn()
379
popupMenu = wx.Menu()
381
if not hasattr (self, "popupID1"):
382
self.popupID1 = wx.NewId()
383
self.popupID2 = wx.NewId()
384
self.popupID3 = wx.NewId()
385
self.popupID4 = wx.NewId()
386
self.popupID5 = wx.NewId()
387
self.popupID6 = wx.NewId()
388
self.popupID7 = wx.NewId()
389
self.popupID8 = wx.NewId()
390
self.popupID9 = wx.NewId()
391
self.popupID10 = wx.NewId()
392
self.popupID11 = wx.NewId()
393
self.popupID12 = wx.NewId()
395
popupMenu.Append(self.popupID1, text = _("Sort ascending"))
396
popupMenu.Append(self.popupID2, text = _("Sort descending"))
397
popupMenu.AppendSeparator()
399
popupMenu.AppendMenu(self.popupID3, _("Calculate (only numeric columns)"),
401
if not self.log.parent.editable or \
402
self.columns[self.GetColumn(self._col).GetText()]['ctype'] not in (types.IntType, types.FloatType):
403
popupMenu.Enable(self.popupID3, False)
405
subMenu.Append(self.popupID4, text = _("Area size"))
406
subMenu.Append(self.popupID5, text = _("Line length"))
407
subMenu.Append(self.popupID6, text = _("Compactness of an area"))
408
subMenu.Append(self.popupID7, text = _("Fractal dimension of boundary defining a polygon"))
409
subMenu.Append(self.popupID8, text = _("Perimeter length of an area"))
410
subMenu.Append(self.popupID9, text = _("Number of features for each category"))
411
subMenu.Append(self.popupID10, text = _("Slope steepness of 3D line"))
412
subMenu.Append(self.popupID11, text = _("Line sinuousity"))
413
subMenu.Append(self.popupID12, text = _("Line azimuth"))
415
self.Bind (wx.EVT_MENU, self.OnColumnSortAsc, id = self.popupID1)
416
self.Bind (wx.EVT_MENU, self.OnColumnSortDesc, id = self.popupID2)
417
for id in (self.popupID4, self.popupID5, self.popupID6,
418
self.popupID7, self.popupID8, self.popupID9,
419
self.popupID10, self.popupID11, self.popupID12):
420
self.Bind(wx.EVT_MENU, self.OnColumnCompute, id = id)
422
self.PopupMenu(popupMenu)
425
def OnColumnSort(self, event):
426
"""!Column heading left mouse button -> sorting"""
427
self._col = event.GetColumn()
433
def OnColumnSortAsc(self, event):
434
"""!Sort values of selected column (ascending)"""
435
self.SortListItems(col = self._col, ascending = True)
438
def OnColumnSortDesc(self, event):
439
"""!Sort values of selected column (descending)"""
440
self.SortListItems(col = self._col, ascending = False)
443
def OnColumnCompute(self, event):
444
"""!Compute values of selected column"""
448
if id == self.popupID4:
450
elif id == self.popupID5:
452
elif id == self.popupID6:
454
elif id == self.popupID7:
456
elif id == self.popupID8:
458
elif id == self.popupID9:
460
elif id == self.popupID10:
462
elif id == self.popupID11:
464
elif id == self.popupID12:
470
RunCommand('v.to.db',
471
parent = self.parent,
472
map = self.mapDBInfo.map,
475
columns = self.GetColumn(self._col).GetText())
477
self.LoadData(self.layer)
479
def ColumnSort(self):
480
"""!Sort values of selected column (self._col)"""
481
# remove duplicated arrow symbol from column header
482
# FIXME: should be done automatically
484
info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
486
for column in range(self.GetColumnCount()):
487
info.m_text = self.GetColumn(column).GetText()
488
self.SetColumn(column, info)
490
def SortItems(self, sorter = cmp):
492
items = list(self.itemDataMap.keys())
493
items.sort(self.Sorter)
494
self.itemIndexMap = items
499
def Sorter(self, key1, key2):
500
colName = self.GetColumn(self._col).GetText()
501
ascending = self._colSortFlag[self._col]
503
item1 = self.columns[colName]["ctype"](self.itemDataMap[key1][self._col])
504
item2 = self.columns[colName]["ctype"](self.itemDataMap[key2][self._col])
506
item1 = self.itemDataMap[key1][self._col]
507
item2 = self.itemDataMap[key2][self._col]
509
if type(item1) == types.StringType or type(item2) == types.StringTypes:
510
cmpVal = locale.strcoll(str(item1), str(item2))
512
cmpVal = cmp(item1, item2)
515
# If the items are equal then pick something else to make the sort value unique
517
cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2))
524
def GetSortImages(self):
525
"""!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
526
return (self.sm_dn, self.sm_up)
529
"""!Check if list if empty"""
535
class AttributeManager(wx.Frame):
536
def __init__(self, parent, id = wx.ID_ANY,
537
title = None, vectorName = None, item = None, log = None,
538
selection = None, **kwargs):
539
"""!GRASS Attribute Table Manager window
541
@param parent parent window
543
@param title window title or None for default title
544
@param vetorName name of vector map
545
@param item item from Layer Tree
546
@param log log window
547
@param selection name of page to be selected
548
@param kwagrs other wx.Frame's arguments
550
self.vectorName = vectorName
551
self.parent = parent # GMFrame
552
self.treeItem = item # item in layer tree
553
if self.parent and self.parent.GetName() == "LayerManager" and \
554
self.treeItem and not self.vectorName:
555
maptree = self.parent.curr_page.maptree
556
name = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetName()
557
self.vectorName = name
559
# vector attributes can be changed only if vector map is in
561
if grass.find_file(name = self.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
564
self.editable = False
566
self.cmdLog = log # self.parent.goutput
568
wx.Frame.__init__(self, parent, id, *kwargs)
572
self.SetTitle("%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
578
self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
580
self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
583
self.map = self.parent.curr_page.maptree.Map
584
self.mapdisplay = self.parent.curr_page.maptree.mapdisplay
586
self.map = self.mapdisplay = None
588
# status bar log class
589
self.log = Log(self) # -> statusbar
591
# query map layer (if parent (GMFrame) is given)
594
# -> layers / tables description
595
self.mapDBInfo = VectorDBInfo(self.vectorName)
600
if len(self.mapDBInfo.layers.keys()) == 0:
601
GMessage(parent = self.parent,
602
message = _("Database connection for vector map <%s> "
603
"is not defined in DB file. "
604
"You can define new connection in "
605
"'Manage layers' tab.") % self.vectorName)
608
# list of command/SQL statements to be performed
610
self.listOfCommands = []
611
self.listOfSQLStatements = []
613
self.CreateStatusBar(number = 1)
615
# set up virtual lists (each layer)
616
### {layer: list, widgets...}
619
self.notebook = GNotebook(self.panel, style = globalvar.FNPageDStyle)
622
dbmStyle = { 'agwStyle' : globalvar.FNPageStyle }
624
dbmStyle = { 'style' : globalvar.FNPageStyle }
626
self.browsePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
628
self.notebook.AddPage(page = self.browsePage, text = _("Browse data"),
630
self.browsePage.SetTabAreaColour(globalvar.FNPageColor)
632
self.manageTablePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
634
self.notebook.AddPage(page = self.manageTablePage, text = _("Manage tables"),
636
if not self.editable:
637
self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
638
self.manageTablePage.SetTabAreaColour(globalvar.FNPageColor)
640
self.manageLayerPage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
642
self.notebook.AddPage(page = self.manageLayerPage, text = _("Manage layers"),
644
self.manageLayerPage.SetTabAreaColour(globalvar.FNPageColor)
645
if not self.editable:
646
self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
648
self._createBrowsePage()
649
self._createManageTablePage()
650
self._createManageLayerPage()
653
wx.CallAfter(self.notebook.SetSelectionByName, selection)
655
wx.CallAfter(self.notebook.SetSelection, 0) # select browse tab
658
self.btnQuit = wx.Button(parent = self.panel, id = wx.ID_EXIT)
659
self.btnQuit.SetToolTipString(_("Close Attribute Table Manager"))
660
self.btnReload = wx.Button(parent = self.panel, id = wx.ID_REFRESH)
661
self.btnReload.SetToolTipString(_("Reload attribute data (selected layer only)"))
664
self.btnQuit.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
665
self.btnReload.Bind(wx.EVT_BUTTON, self.OnDataReload)
666
self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
667
self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.browsePage)
668
self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.manageTablePage)
669
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
674
# self.SetMinSize(self.GetBestSize())
675
self.SetSize((700, 550)) # FIXME hard-coded size
676
self.SetMinSize(self.GetSize())
678
def _createBrowsePage(self, onlyLayer = -1):
679
"""!Create browse tab page"""
680
for layer in self.mapDBInfo.layers.keys():
681
if onlyLayer > 0 and layer != onlyLayer:
684
panel = wx.Panel(parent = self.browsePage, id = wx.ID_ANY)
686
#IMPORTANT NOTE: wx.StaticBox MUST be defined BEFORE any of the
687
# controls that are placed IN the wx.StaticBox, or it will freeze
690
listBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
691
label = " %s " % _("Attribute data - right-click to edit/manage records"))
692
listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
694
win = VirtualAttributeList(panel, self.log,
695
self.mapDBInfo, layer)
700
win.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDataItemActivated)
702
self.layerPage[layer] = {'browsePage': panel.GetId()}
705
if not self.editable:
706
label += _(" (readonly)")
707
self.browsePage.AddPage(page = panel, text = " %d / %s %s" % \
708
(layer, label, self.mapDBInfo.layers[layer]['table']))
710
pageSizer = wx.BoxSizer(wx.VERTICAL)
713
sqlBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
714
label = " %s " % _("SQL Query"))
716
sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL)
718
win.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnDataRightUp) #wxMSW
719
win.Bind(wx.EVT_RIGHT_UP, self.OnDataRightUp) #wxGTK
720
if UserSettings.Get(group = 'atm', key = 'leftDbClick', subkey = 'selection') == 0:
721
win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataItemEdit)
722
win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataItemEdit)
724
win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataDrawSelected)
725
win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataDrawSelected)
727
listSizer.Add(item = win, proportion = 1,
728
flag = wx.EXPAND | wx.ALL,
732
btnApply = wx.Button(parent = panel, id = wx.ID_APPLY)
733
btnApply.SetToolTipString(_("Apply SELECT statement and reload data records"))
734
btnApply.Bind(wx.EVT_BUTTON, self.OnApplySqlStatement)
735
btnSqlBuilder = wx.Button(parent = panel, id = wx.ID_ANY, label = _("SQL Builder"))
736
btnSqlBuilder.Bind(wx.EVT_BUTTON, self.OnBuilder)
738
sqlSimple = wx.RadioButton(parent = panel, id = wx.ID_ANY,
740
sqlSimple.SetValue(True)
741
sqlAdvanced = wx.RadioButton(parent = panel, id = wx.ID_ANY,
742
label = _("Advanced"))
743
sqlSimple.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
744
sqlAdvanced.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
746
sqlWhereColumn = wx.ComboBox(parent = panel, id = wx.ID_ANY,
748
style = wx.CB_SIMPLE | wx.CB_READONLY,
749
choices = self.mapDBInfo.GetColumns(self.mapDBInfo.layers[layer]['table']))
750
sqlWhereColumn.SetSelection(0)
751
sqlWhereCond = wx.Choice(parent = panel, id = wx.ID_ANY,
753
choices = ['=', '!=', '<', '<=', '>', '>='])
754
sqlWhereValue = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = "",
755
style = wx.TE_PROCESS_ENTER)
756
sqlWhereValue.SetToolTipString(_("Example: %s") % "MULTILANE = 'no' AND OBJECTID < 10")
758
sqlStatement = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
759
value = "SELECT * FROM %s" % \
760
self.mapDBInfo.layers[layer]['table'],
761
style = wx.TE_PROCESS_ENTER)
762
sqlStatement.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' AND OBJECTID < 10")
763
sqlWhereValue.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
764
sqlStatement.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
766
sqlLabel = wx.StaticText(parent = panel, id = wx.ID_ANY,
767
label = "SELECT * FROM %s WHERE " % \
768
self.mapDBInfo.layers[layer]['table'])
769
label_query = wx.StaticText(parent = panel, id = wx.ID_ANY,
772
sqlFlexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
774
sqlFlexSizer.Add(item = sqlSimple,
775
flag = wx.ALIGN_CENTER_VERTICAL)
776
sqlSimpleSizer = wx.BoxSizer(wx.HORIZONTAL)
777
sqlSimpleSizer.Add(item = sqlLabel,
778
flag = wx.ALIGN_CENTER_VERTICAL)
779
sqlSimpleSizer.Add(item = sqlWhereColumn,
780
flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
781
sqlSimpleSizer.Add(item = sqlWhereCond,
782
flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
784
sqlSimpleSizer.Add(item = sqlWhereValue, proportion = 1,
785
flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
786
sqlFlexSizer.Add(item = sqlSimpleSizer,
787
flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
788
sqlFlexSizer.Add(item = btnApply,
789
flag = wx.ALIGN_RIGHT)
790
sqlFlexSizer.Add(item = sqlAdvanced,
791
flag = wx.ALIGN_CENTER_VERTICAL)
792
sqlFlexSizer.Add(item = sqlStatement,
794
sqlFlexSizer.Add(item = btnSqlBuilder,
795
flag = wx.ALIGN_RIGHT)
796
sqlFlexSizer.AddGrowableCol(1)
798
sqlSizer.Add(item = sqlFlexSizer,
799
flag = wx.ALL | wx.EXPAND,
802
pageSizer.Add(item = listSizer,
804
flag = wx.ALL | wx.EXPAND,
807
pageSizer.Add(item = sqlSizer,
809
flag = wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND,
812
panel.SetSizer(pageSizer)
814
self.layerPage[layer]['data'] = win.GetId()
815
self.layerPage[layer]['simple'] = sqlSimple.GetId()
816
self.layerPage[layer]['advanced'] = sqlAdvanced.GetId()
817
self.layerPage[layer]['whereColumn'] = sqlWhereColumn.GetId()
818
self.layerPage[layer]['whereOperator'] = sqlWhereCond.GetId()
819
self.layerPage[layer]['where'] = sqlWhereValue.GetId()
820
self.layerPage[layer]['builder'] = btnSqlBuilder.GetId()
821
self.layerPage[layer]['statement'] = sqlStatement.GetId()
824
self.browsePage.SetSelection(0) # select first layer
826
self.layer = self.mapDBInfo.layers.keys()[0]
827
self.OnChangeSql(None)
828
self.log.write(_("Number of loaded records: %d") % \
829
self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
830
except (IndexError, KeyError):
833
def _createManageTablePage(self, onlyLayer = -1):
834
"""!Create manage page (create/link and alter tables)"""
835
for layer in self.mapDBInfo.layers.keys():
836
if onlyLayer > 0 and layer != onlyLayer:
839
if not layer in self.layerPage:
842
panel = wx.Panel(parent = self.manageTablePage, id = wx.ID_ANY)
843
self.layerPage[layer]['tablePage'] = panel.GetId()
845
if not self.editable:
846
label += _(" (readonly)")
847
self.manageTablePage.AddPage(page = panel,
848
text = " %d / %s %s" % (layer, label,
849
self.mapDBInfo.layers[layer]['table']))
851
pageSizer = wx.BoxSizer(wx.VERTICAL)
856
dbBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
857
label = " %s " % _("Database connection"))
858
dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL)
859
dbSizer.Add(item = createDbInfoDesc(panel, self.mapDBInfo, layer),
861
flag = wx.EXPAND | wx.ALL,
867
table = self.mapDBInfo.layers[layer]['table']
868
tableBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
869
label = " %s " % _("Table <%s> - right-click to delete column(s)") % table)
871
tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
873
tlist = self._createTableDesc(panel, table)
874
tlist.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnTableRightUp) #wxMSW
875
tlist.Bind(wx.EVT_RIGHT_UP, self.OnTableRightUp) #wxGTK
876
self.layerPage[layer]['tableData'] = tlist.GetId()
878
# manage columns (add)
879
addBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
880
label = " %s " % _("Add column"))
881
addSizer = wx.StaticBoxSizer(addBox, wx.HORIZONTAL)
883
column = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
884
size = (150, -1), style = wx.TE_PROCESS_ENTER)
885
column.Bind(wx.EVT_TEXT, self.OnTableAddColumnName)
886
column.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemAdd)
887
self.layerPage[layer]['addColName'] = column.GetId()
888
addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
889
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
891
addSizer.Add(item = column, proportion = 1,
892
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
895
ctype = wx.Choice (parent = panel, id = wx.ID_ANY,
896
choices = ["integer",
900
ctype.SetSelection(0)
901
ctype.Bind(wx.EVT_CHOICE, self.OnTableChangeType)
902
self.layerPage[layer]['addColType'] = ctype.GetId()
903
addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Type")),
904
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
906
addSizer.Add(item = ctype,
907
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
910
length = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
914
self.layerPage[layer]['addColLength'] = length.GetId()
915
addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Length")),
916
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
918
addSizer.Add(item = length,
919
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
922
btnAddCol = wx.Button(parent = panel, id = wx.ID_ADD)
923
btnAddCol.Bind(wx.EVT_BUTTON, self.OnTableItemAdd)
924
btnAddCol.Enable(False)
925
self.layerPage[layer]['addColButton'] = btnAddCol.GetId()
926
addSizer.Add(item = btnAddCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
929
# manage columns (rename)
930
renameBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
931
label = " %s " % _("Rename column"))
932
renameSizer = wx.StaticBoxSizer(renameBox, wx.HORIZONTAL)
934
column = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (150, -1),
935
style = wx.CB_SIMPLE | wx.CB_READONLY,
936
choices = self.mapDBInfo.GetColumns(table))
937
column.SetSelection(0)
938
self.layerPage[layer]['renameCol'] = column.GetId()
939
renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
940
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
942
renameSizer.Add(item = column, proportion = 1,
943
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
946
columnTo = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
947
size = (150, -1), style = wx.TE_PROCESS_ENTER)
948
columnTo.Bind(wx.EVT_TEXT, self.OnTableRenameColumnName)
949
columnTo.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemChange)
950
self.layerPage[layer]['renameColTo'] = columnTo.GetId()
951
renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("To")),
952
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
954
renameSizer.Add(item = columnTo, proportion = 1,
955
flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
958
btnRenameCol = wx.Button(parent = panel, id = wx.ID_ANY, label = _("&Rename"))
959
btnRenameCol.Bind(wx.EVT_BUTTON, self.OnTableItemChange)
960
btnRenameCol.Enable(False)
961
self.layerPage[layer]['renameColButton'] = btnRenameCol.GetId()
962
renameSizer.Add(item = btnRenameCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
965
tableSizer.Add(item = tlist,
966
flag = wx.ALL | wx.EXPAND,
970
pageSizer.Add(item=dbSizer,
971
flag = wx.ALL | wx.EXPAND,
975
pageSizer.Add(item = tableSizer,
976
flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
980
pageSizer.Add(item = addSizer,
981
flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
984
pageSizer.Add(item = renameSizer,
985
flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
989
panel.SetSizer(pageSizer)
991
self.manageTablePage.SetSelection(0) # select first layer
993
self.layer = self.mapDBInfo.layers.keys()[0]
997
def _createTableDesc(self, parent, table):
998
"""!Create list with table description"""
999
tlist = TableListCtrl(parent = parent, id = wx.ID_ANY,
1000
table = self.mapDBInfo.tables[table],
1001
columns = self.mapDBInfo.GetColumns(table))
1004
# itemDataMap = list.Populate()
1005
# listmix.ColumnSorterMixin.__init__(self, 2)
1009
def _createManageLayerPage(self):
1010
"""!Create manage page"""
1011
splitterWin = wx.SplitterWindow(parent = self.manageLayerPage, id = wx.ID_ANY)
1012
splitterWin.SetMinimumPaneSize(100)
1014
label = _("Layers of vector map")
1015
if not self.editable:
1016
label += _(" (readonly)")
1017
self.manageLayerPage.AddPage(page = splitterWin,
1018
text = label) # dummy page
1023
panelList = wx.Panel(parent = splitterWin, id = wx.ID_ANY)
1025
panelListSizer = wx.BoxSizer(wx.VERTICAL)
1026
layerBox = wx.StaticBox(parent = panelList, id = wx.ID_ANY,
1027
label = " %s " % _("List of layers"))
1028
layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
1030
self.layerList = self._createLayerDesc(panelList)
1031
self.layerList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnLayerRightUp) #wxMSW
1032
self.layerList.Bind(wx.EVT_RIGHT_UP, self.OnLayerRightUp) #wxGTK
1034
layerSizer.Add(item = self.layerList,
1035
flag = wx.ALL | wx.EXPAND,
1039
panelListSizer.Add(item = layerSizer,
1040
flag = wx.ALL | wx.EXPAND,
1044
panelList.SetSizer(panelListSizer)
1049
panelManage = wx.Panel(parent = splitterWin, id = wx.ID_ANY)
1051
manageSizer = wx.BoxSizer(wx.VERTICAL)
1053
self.manageLayerBook = LayerBook(parent = panelManage, id = wx.ID_ANY,
1054
parentDialog = self)
1056
manageSizer.Add(item = self.manageLayerBook,
1058
flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
1061
panelManage.SetSizer(manageSizer)
1062
splitterWin.SplitHorizontally(panelList, panelManage, 100)
1065
def _createLayerDesc(self, parent):
1066
"""!Create list of linked layers"""
1067
tlist = LayerListCtrl(parent = parent, id = wx.ID_ANY,
1068
layers = self.mapDBInfo.layers)
1072
# itemDataMap = list.Populate()
1073
# listmix.ColumnSorterMixin.__init__(self, 2)
1080
mainSizer = wx.BoxSizer(wx.VERTICAL)
1083
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1084
btnSizer.Add(item = self.btnReload, proportion = 1,
1085
flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
1086
btnSizer.Add(item = self.btnQuit, proportion = 1,
1087
flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
1089
mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND)
1090
mainSizer.Add(item = btnSizer, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
1092
self.panel.SetAutoLayout(True)
1093
self.panel.SetSizer(mainSizer)
1094
mainSizer.Fit(self.panel)
1097
def OnDataRightUp(self, event):
1098
"""!Table description area, context menu"""
1099
if not hasattr(self, "popupDataID1"):
1100
self.popupDataID1 = wx.NewId()
1101
self.popupDataID2 = wx.NewId()
1102
self.popupDataID3 = wx.NewId()
1103
self.popupDataID4 = wx.NewId()
1104
self.popupDataID5 = wx.NewId()
1105
self.popupDataID6 = wx.NewId()
1106
self.popupDataID7 = wx.NewId()
1107
self.popupDataID8 = wx.NewId()
1108
self.popupDataID9 = wx.NewId()
1109
self.popupDataID10 = wx.NewId()
1110
self.popupDataID11 = wx.NewId()
1112
self.Bind(wx.EVT_MENU, self.OnDataItemEdit, id = self.popupDataID1)
1113
self.Bind(wx.EVT_MENU, self.OnDataItemAdd, id = self.popupDataID2)
1114
self.Bind(wx.EVT_MENU, self.OnDataItemDelete, id = self.popupDataID3)
1115
self.Bind(wx.EVT_MENU, self.OnDataItemDeleteAll, id = self.popupDataID4)
1116
self.Bind(wx.EVT_MENU, self.OnDataSelectAll, id = self.popupDataID5)
1117
self.Bind(wx.EVT_MENU, self.OnDataSelectNone, id = self.popupDataID6)
1118
self.Bind(wx.EVT_MENU, self.OnDataDrawSelected, id = self.popupDataID7)
1119
self.Bind(wx.EVT_MENU, self.OnDataDrawSelectedZoom, id = self.popupDataID8)
1120
self.Bind(wx.EVT_MENU, self.OnExtractSelected, id = self.popupDataID9)
1121
self.Bind(wx.EVT_MENU, self.OnDeleteSelected, id = self.popupDataID11)
1122
self.Bind(wx.EVT_MENU, self.OnDataReload, id = self.popupDataID10)
1124
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1125
# generate popup-menu
1127
menu.Append(self.popupDataID1, _("Edit selected record"))
1128
selected = tlist.GetFirstSelected()
1129
if not self.editable or selected == -1 or tlist.GetNextSelected(selected) != -1:
1130
menu.Enable(self.popupDataID1, False)
1131
menu.Append(self.popupDataID2, _("Insert new record"))
1132
menu.Append(self.popupDataID3, _("Delete selected record(s)"))
1133
menu.Append(self.popupDataID4, _("Delete all records"))
1134
if not self.editable:
1135
menu.Enable(self.popupDataID2, False)
1136
menu.Enable(self.popupDataID3, False)
1137
menu.Enable(self.popupDataID4, False)
1138
menu.AppendSeparator()
1139
menu.Append(self.popupDataID5, _("Select all"))
1140
menu.Append(self.popupDataID6, _("Deselect all"))
1141
menu.AppendSeparator()
1142
menu.Append(self.popupDataID7, _("Highlight selected features"))
1143
menu.Append(self.popupDataID8, _("Highlight selected features and zoom"))
1144
if not self.map or len(tlist.GetSelectedItems()) == 0:
1145
menu.Enable(self.popupDataID7, False)
1146
menu.Enable(self.popupDataID8, False)
1147
menu.Append(self.popupDataID9, _("Extract selected features"))
1148
menu.Append(self.popupDataID11, _("Delete selected features"))
1149
if not self.editable:
1150
menu.Enable(self.popupDataID11, False)
1151
if tlist.GetFirstSelected() == -1:
1152
menu.Enable(self.popupDataID3, False)
1153
menu.Enable(self.popupDataID9, False)
1154
menu.Enable(self.popupDataID11, False)
1155
menu.AppendSeparator()
1156
menu.Append(self.popupDataID10, _("Reload"))
1158
self.PopupMenu(menu)
1162
self.log.write(_("Number of loaded records: %d") % \
1163
tlist.GetItemCount())
1165
def OnDataItemDelete(self, event):
1166
"""!Delete selected item(s) from the tlist (layer/category pair)"""
1167
dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1168
item = dlist.GetFirstSelected()
1170
table = self.mapDBInfo.layers[self.layer]["table"]
1171
key = self.mapDBInfo.layers[self.layer]["key"]
1174
# collect SQL statements
1176
index = dlist.itemIndexMap[item]
1177
indeces.append(index)
1179
cat = dlist.itemCatsMap[index]
1181
self.listOfSQLStatements.append('DELETE FROM %s WHERE %s=%d' % \
1184
item = dlist.GetNextSelected(item)
1186
if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1187
deleteDialog = wx.MessageBox(parent = self,
1188
message = _("Selected data records (%d) will be permanently deleted "
1189
"from table. Do you want to delete them?") % \
1190
(len(self.listOfSQLStatements)),
1191
caption = _("Delete records"),
1192
style = wx.YES_NO | wx.CENTRE)
1193
if deleteDialog != wx.YES:
1194
self.listOfSQLStatements = []
1199
indexTemp = copy.copy(dlist.itemIndexMap)
1200
dlist.itemIndexMap = []
1201
dataTemp = copy.deepcopy(dlist.itemDataMap)
1202
dlist.itemDataMap = {}
1203
catsTemp = copy.deepcopy(dlist.itemCatsMap)
1204
dlist.itemCatsMap = {}
1207
for index in indexTemp:
1208
if index in indeces:
1210
dlist.itemIndexMap.append(i)
1211
dlist.itemDataMap[i] = dataTemp[index]
1212
dlist.itemCatsMap[i] = catsTemp[index]
1216
dlist.SetItemCount(len(dlist.itemIndexMap))
1219
item = dlist.GetFirstSelected()
1221
dlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
1222
item = dlist.GetNextSelected(item)
1224
# submit SQL statements
1225
self.ApplyCommands()
1229
def OnDataItemDeleteAll(self, event):
1230
"""!Delete all items from the list"""
1231
dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1232
if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1233
deleteDialog = wx.MessageBox(parent = self,
1234
message = _("All data records (%d) will be permanently deleted "
1235
"from table. Do you want to delete them?") % \
1236
(len(dlist.itemIndexMap)),
1237
caption = _("Delete records"),
1238
style = wx.YES_NO | wx.CENTRE)
1239
if deleteDialog != wx.YES:
1242
dlist.DeleteAllItems()
1243
dlist.itemDataMap = {}
1244
dlist.itemIndexMap = []
1245
dlist.SetItemCount(0)
1247
table = self.mapDBInfo.layers[self.layer]["table"]
1248
self.listOfSQLStatements.append('DELETE FROM %s' % table)
1250
self.ApplyCommands()
1254
def _drawSelected(self, zoom):
1255
"""!Highlight selected features"""
1256
if not self.map or not self.mapdisplay:
1259
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1260
cats = map(int, tlist.GetSelectedItems())
1262
digitToolbar = self.mapdisplay.GetToolbar('vdigit')
1263
if digitToolbar and digitToolbar.GetLayer() and \
1264
digitToolbar.GetLayer().GetName() == self.vectorName:
1265
display = self.mapdisplay.GetMapWindow().GetDisplay()
1266
display.SetSelected(cats, layer = self.layer)
1268
n, s, w, e = display.GetRegionSelected()
1269
self.mapdisplay.Map.GetRegion(n = n, s = s, w = w, e = e,
1272
# add map layer with higlighted vector features
1273
self.AddQueryMapLayer() # -> self.qlayer
1275
# set opacity based on queried layer
1276
if self.parent and self.parent.GetName() == "LayerManager" and \
1278
maptree = self.parent.curr_page.maptree
1279
opacity = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetOpacity(float = True)
1280
self.qlayer.SetOpacity(opacity)
1282
keyColumn = self.mapDBInfo.layers[self.layer]['key']
1284
for range in ListOfCatsToRange(cats).split(','):
1286
min, max = range.split('-')
1287
where += '%s >= %d and %s <= %d or ' % \
1288
(keyColumn, int(min),
1289
keyColumn, int(max))
1291
where += '%s = %d or ' % (keyColumn, int(range))
1292
where = where.rstrip('or ')
1294
select = RunCommand('v.db.select',
1299
map = self.mapDBInfo.map,
1300
layer = int(self.layer),
1304
for line in select.splitlines():
1305
key, value = line.split('=')
1306
region[key.strip()] = float(value.strip())
1309
renderer = self.mapdisplay.GetMap()
1310
nsdist = 10 * ((renderer.GetCurrentRegion()['n'] - renderer.GetCurrentRegion()['s']) /
1312
ewdist = 10 * ((renderer.GetCurrentRegion()['e'] - renderer.GetCurrentRegion()['w']) /
1314
north = region['n'] + nsdist
1315
south = region['s'] - nsdist
1316
west = region['w'] - ewdist
1317
east = region['e'] + ewdist
1318
renderer.GetRegion(n = north, s = south, w = west, e = east, update = True)
1319
self.mapdisplay.GetMapWindow().ZoomHistory(n = north, s = south, w = west, e = east)
1322
self.mapdisplay.Map.AdjustRegion() # adjust resolution
1323
self.mapdisplay.Map.AlignExtentFromDisplay() # adjust extent
1324
self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
1326
self.mapdisplay.MapWindow.UpdateMap(render = False, renderVector = True)
1328
def OnDataDrawSelected(self, event):
1329
"""!Reload table description"""
1330
self._drawSelected(zoom = False)
1333
def OnDataDrawSelectedZoom(self, event):
1334
self._drawSelected(zoom = True)
1337
def OnDataItemAdd(self, event):
1338
"""!Add new record to the attribute table"""
1339
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1340
table = self.mapDBInfo.layers[self.layer]['table']
1341
keyColumn = self.mapDBInfo.layers[self.layer]['key']
1343
# (column name, value)
1346
# collect names of all visible columns
1348
for i in range(tlist.GetColumnCount()):
1349
columnName.append(tlist.GetColumn(i).GetText())
1351
# maximal category number
1352
if len(tlist.itemCatsMap.values()) > 0:
1353
maxCat = max(tlist.itemCatsMap.values())
1355
maxCat = 0 # starting category '1'
1357
# key column must be always presented
1358
if keyColumn not in columnName:
1359
columnName.insert(0, keyColumn) # insert key column on first position
1360
data.append((keyColumn, str(maxCat + 1)))
1365
# add other visible columns
1368
for col in columnName:
1369
ctype = self.mapDBInfo.tables[table][col]['ctype']
1370
ctypeStr = self.mapDBInfo.tables[table][col]['type']
1371
if col == keyColumn: # key
1372
if missingKey is False:
1373
data.append((col, ctype, ctypeStr, str(maxCat + 1)))
1376
data.append((col, ctype, ctypeStr, ''))
1380
dlg = ModifyTableRecord(parent = self,
1381
title = _("Insert new record"),
1382
data = data, keyEditable = (keyId, True))
1384
if dlg.ShowModal() == wx.ID_OK:
1385
try: # get category number
1386
cat = int(dlg.GetValues(columns = [keyColumn])[0])
1391
if cat in tlist.itemCatsMap.values():
1392
raise ValueError(_("Record with category number %d "
1393
"already exists in the table.") % cat)
1395
values = dlg.GetValues() # values (need to be casted)
1399
for i in range(len(values)):
1400
if len(values[i]) == 0: # NULL
1401
if columnName[i] == keyColumn:
1402
raise ValueError(_("Category number (column %s)"
1403
" is missing.") % keyColumn)
1408
if tlist.columns[columnName[i]]['ctype'] == int:
1409
# values[i] is stored as text.
1410
value = float(values[i])
1413
values[i] = tlist.columns[columnName[i]]['ctype'] (value)
1416
raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") %
1417
{'value' : str(values[i]),
1418
'type' : tlist.columns[columnName[i]]['type']})
1419
columnsString += '%s,' % columnName[i]
1420
if tlist.columns[columnName[i]]['ctype'] == str:
1421
valuesString += "'%s'," % values[i]
1423
valuesString += "%s," % values[i]
1425
except ValueError, err:
1426
GError(parent = self,
1427
message = _("Unable to insert new record.\n%s") % err,
1428
showTraceback = False)
1429
self.OnDataItemAdd(event)
1432
# remove category if need
1433
if missingKey is True:
1436
# add new item to the tlist
1437
if len(tlist.itemIndexMap) > 0:
1438
index = max(tlist.itemIndexMap) + 1
1442
tlist.itemIndexMap.append(index)
1443
tlist.itemDataMap[index] = values
1444
tlist.itemCatsMap[index] = cat
1445
tlist.SetItemCount(tlist.GetItemCount() + 1)
1447
self.listOfSQLStatements.append('INSERT INTO %s (%s) VALUES(%s)' % \
1449
columnsString.strip(','),
1450
valuesString.strip(',')))
1451
self.ApplyCommands()
1453
def OnDataItemEdit(self, event):
1454
"""!Edit selected record of the attribute table"""
1455
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1456
item = tlist.GetFirstSelected()
1460
table = self.mapDBInfo.layers[self.layer]['table']
1461
keyColumn = self.mapDBInfo.layers[self.layer]['key']
1462
cat = tlist.itemCatsMap[tlist.itemIndexMap[item]]
1464
# (column name, value)
1467
# collect names of all visible columns
1469
for i in range(tlist.GetColumnCount()):
1470
columnName.append(tlist.GetColumn(i).GetText())
1473
# key column must be always presented
1474
if keyColumn not in columnName:
1475
columnName.insert(0, keyColumn) # insert key column on first position
1476
data.append((keyColumn, str(cat)))
1482
# add other visible columns
1483
for i in range(len(columnName)):
1484
ctype = self.mapDBInfo.tables[table][columnName[i]]['ctype']
1485
ctypeStr = self.mapDBInfo.tables[table][columnName[i]]['type']
1486
if columnName[i] == keyColumn: # key
1487
if missingKey is False:
1488
data.append((columnName[i], ctype, ctypeStr, str(cat)))
1491
if missingKey is True:
1492
value = tlist.GetItem(item, i-1).GetText()
1494
value = tlist.GetItem(item, i).GetText()
1495
data.append((columnName[i], ctype, ctypeStr, value))
1497
dlg = ModifyTableRecord(parent = self,
1498
title = _("Update existing record"),
1499
data = data, keyEditable = (keyId, False))
1501
if dlg.ShowModal() == wx.ID_OK:
1502
values = dlg.GetValues() # string
1505
for i in range(len(values)):
1506
if i == keyId: # skip key column
1508
if tlist.GetItem(item, i).GetText() != values[i]:
1509
if len(values[i]) > 0:
1511
if missingKey is True:
1515
if tlist.columns[columnName[i]]['ctype'] != types.StringType:
1516
if tlist.columns[columnName[i]]['ctype'] == int:
1517
value = float(values[i])
1520
tlist.itemDataMap[item][idx] = \
1521
tlist.columns[columnName[i]]['ctype'] (value)
1523
tlist.itemDataMap[item][idx] = values[i]
1525
raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % \
1526
{'value' : str(values[i]),
1527
'type' : tlist.columns[columnName[i]]['type']})
1529
if tlist.columns[columnName[i]]['ctype'] == str:
1530
updateString += "%s='%s'," % (columnName[i], values[i])
1532
updateString += "%s=%s," % (columnName[i], values[i])
1534
updateString += "%s=NULL," % (columnName[i])
1536
except ValueError, err:
1537
GError(parent = self,
1538
message = _("Unable to update existing record.\n%s") % err,
1539
showTraceback = False)
1540
self.OnDataItemEdit(event)
1543
if len(updateString) > 0:
1544
self.listOfSQLStatements.append('UPDATE %s SET %s WHERE %s=%d' % \
1545
(table, updateString.strip(','),
1547
self.ApplyCommands()
1549
tlist.Update(self.mapDBInfo)
1551
def OnDataReload(self, event):
1552
"""!Reload tlist of records"""
1553
self.OnApplySqlStatement(None)
1554
self.listOfSQLStatements = []
1556
def OnDataSelectAll(self, event):
1557
"""!Select all items"""
1558
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1562
item = tlist.GetNextItem(item)
1565
tlist.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
1569
def OnDataSelectNone(self, event):
1570
"""!Deselect items"""
1571
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1575
item = tlist.GetNextItem(item, wx.LIST_STATE_SELECTED)
1578
tlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
1583
def OnTableChangeType(self, event):
1584
"""!Data type for new column changed. Enable or disable
1585
data length widget"""
1586
win = self.FindWindowById(self.layerPage[self.layer]['addColLength'])
1587
if event.GetString() == "varchar":
1592
def OnTableRenameColumnName(self, event):
1593
"""!Editing column name to be added to the table"""
1594
btn = self.FindWindowById(self.layerPage[self.layer]['renameColButton'])
1595
col = self.FindWindowById(self.layerPage[self.layer]['renameCol'])
1596
colTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo'])
1597
if len(col.GetValue()) > 0 and len(colTo.GetValue()) > 0:
1604
def OnTableAddColumnName(self, event):
1605
"""!Editing column name to be added to the table"""
1606
btn = self.FindWindowById(self.layerPage[self.layer]['addColButton'])
1607
if len(event.GetString()) > 0:
1614
def OnTableItemChange(self, event):
1615
"""!Rename column in the table"""
1616
tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1617
name = self.FindWindowById(self.layerPage[self.layer]['renameCol']).GetValue()
1618
nameTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']).GetValue()
1620
table = self.mapDBInfo.layers[self.layer]["table"]
1622
if not name or not nameTo:
1623
GError(parent = self,
1624
message = _("Unable to rename column. "
1625
"No column name defined."))
1628
item = tlist.FindItem(start = -1, str = name)
1630
if tlist.FindItem(start = -1, str = nameTo) > -1:
1631
GError(parent = self,
1632
message = _("Unable to rename column <%(column)s> to "
1633
"<%(columnTo)s>. Column already exists "
1634
"in the table <%(table)s>.") % \
1635
{'column' : name, 'columnTo' : nameTo,
1639
tlist.SetItemText(item, nameTo)
1641
self.listOfCommands.append(('v.db.renamecol',
1642
{ 'map' : self.vectorName,
1643
'layer' : self.layer,
1644
'column' : '%s,%s' % (name, nameTo) }
1647
GError(parent = self,
1648
message = _("Unable to rename column. "
1649
"Column <%(column)s> doesn't exist in the table <%(table)s>.") %
1650
{'column' : name, 'table' : table})
1654
self.ApplyCommands()
1657
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1658
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1659
self.FindWindowById(self.layerPage[self.layer]['renameColTo']).SetValue('')
1663
def OnTableRightUp(self, event):
1664
"""!Table description area, context menu"""
1665
if not hasattr(self, "popupTableID"):
1666
self.popupTableID1 = wx.NewId()
1667
self.popupTableID2 = wx.NewId()
1668
self.popupTableID3 = wx.NewId()
1669
self.Bind(wx.EVT_MENU, self.OnTableItemDelete, id = self.popupTableID1)
1670
self.Bind(wx.EVT_MENU, self.OnTableItemDeleteAll, id = self.popupTableID2)
1671
self.Bind(wx.EVT_MENU, self.OnTableReload, id = self.popupTableID3)
1673
# generate popup-menu
1675
menu.Append(self.popupTableID1, _("Drop selected column"))
1676
if self.FindWindowById(self.layerPage[self.layer]['tableData']).GetFirstSelected() == -1:
1677
menu.Enable(self.popupTableID1, False)
1678
menu.Append(self.popupTableID2, _("Drop all columns"))
1679
menu.AppendSeparator()
1680
menu.Append(self.popupTableID3, _("Reload"))
1682
self.PopupMenu(menu)
1685
def OnTableItemDelete(self, event):
1686
"""!Delete selected item(s) from the list"""
1687
tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1689
item = tlist.GetFirstSelected()
1691
if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1692
deleteDialog = wx.MessageBox(parent = self,
1693
message = _("Selected column '%s' will PERMANENTLY removed "
1694
"from table. Do you want to drop the column?") % \
1695
(tlist.GetItemText(item)),
1696
caption = _("Drop column(s)"),
1697
style = wx.YES_NO | wx.CENTRE)
1698
if deleteDialog != wx.YES:
1702
self.listOfCommands.append(('v.db.dropcol',
1703
{ 'map' : self.vectorName,
1704
'layer' : self.layer,
1705
'column' : tlist.GetItemText(item) }
1707
tlist.DeleteItem(item)
1708
item = tlist.GetFirstSelected()
1711
self.ApplyCommands()
1714
table = self.mapDBInfo.layers[self.layer]['table']
1715
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1716
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1720
def OnTableItemDeleteAll(self, event):
1721
"""!Delete all items from the list"""
1722
table = self.mapDBInfo.layers[self.layer]['table']
1723
cols = self.mapDBInfo.GetColumns(table)
1724
keyColumn = self.mapDBInfo.layers[self.layer]['key']
1725
if keyColumn in cols:
1726
cols.remove(keyColumn)
1728
if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1729
deleteDialog = wx.MessageBox(parent = self,
1730
message = _("Selected columns\n%s\nwill PERMANENTLY removed "
1731
"from table. Do you want to drop the columns?") % \
1733
caption = _("Drop column(s)"),
1734
style = wx.YES_NO | wx.CENTRE)
1735
if deleteDialog != wx.YES:
1739
self.listOfCommands.append(('v.db.dropcol',
1740
{ 'map' : self.vectorName,
1741
'layer' : self.layer,
1744
self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems()
1747
self.ApplyCommands()
1750
table = self.mapDBInfo.layers[self.layer]['table']
1751
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1752
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1756
def OnTableReload(self, event = None):
1757
"""!Reload table description"""
1758
self.FindWindowById(self.layerPage[self.layer]['tableData']).Populate(update = True)
1759
self.listOfCommands = []
1761
def OnTableItemAdd(self, event):
1762
"""!Add new column to the table"""
1763
table = self.mapDBInfo.layers[self.layer]['table']
1764
name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
1767
GError(parent = self,
1768
message = _("Unable to add column to the table. "
1769
"No column name defined."))
1772
ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \
1773
GetStringSelection()
1775
# cast type if needed
1776
if ctype == 'double':
1777
ctype = 'double precision'
1778
if ctype == 'varchar':
1779
length = int(self.FindWindowById(self.layerPage[self.layer]['addColLength']). \
1784
# add item to the list of table columns
1785
tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1786
# check for duplicate items
1787
if tlist.FindItem(start = -1, str = name) > -1:
1788
GError(parent = self,
1789
message = _("Column <%(column)s> already exists in table <%(table)s>.") % \
1790
{'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]})
1792
index = tlist.InsertStringItem(sys.maxint, str(name))
1793
tlist.SetStringItem(index, 0, str(name))
1794
tlist.SetStringItem(index, 1, str(ctype))
1795
tlist.SetStringItem(index, 2, str(length))
1797
# add v.db.addcol command to the list
1798
if ctype == 'varchar':
1799
ctype += ' (%d)' % length
1800
self.listOfCommands.append(('v.db.addcol',
1801
{ 'map' : self.vectorName,
1802
'layer' : self.layer,
1803
'columns' : '%s %s' % (name, ctype) }
1806
self.ApplyCommands()
1809
self.FindWindowById(self.layerPage[self.layer]['addColName']).SetValue('')
1810
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1811
self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1815
def OnLayerPageChanged(self, event):
1816
"""!Layer tab changed"""
1817
pageNum = event.GetSelection()
1818
self.layer = self.mapDBInfo.layers.keys()[pageNum]
1821
idCol = self.layerPage[self.layer]['whereColumn']
1826
self.OnChangeSql(None)
1828
self.log.write(_("Number of loaded records: %d") % \
1829
self.FindWindowById(self.layerPage[self.layer]['data']).\
1835
winCol = self.FindWindowById(idCol)
1836
table = self.mapDBInfo.layers[self.layer]["table"]
1837
self.mapDBInfo.GetColumns(table)
1841
def OnPageChanged(self, event):
1843
id = self.layerPage[self.layer]['data']
1847
if event.GetSelection() == 0 and id:
1848
win = self.FindWindowById(id)
1850
self.log.write(_("Number of loaded records: %d") % win.GetItemCount())
1853
self.btnReload.Enable()
1856
self.btnReload.Enable(False)
1860
def OnLayerRightUp(self, event):
1861
"""!Layer description area, context menu"""
1864
def OnChangeSql(self, event):
1865
"""!Switch simple/advanced sql statement"""
1866
if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
1867
self.FindWindowById(self.layerPage[self.layer]['where']).Enable(True)
1868
self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(False)
1869
self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(False)
1871
self.FindWindowById(self.layerPage[self.layer]['where']).Enable(False)
1872
self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(True)
1873
self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True)
1875
def ApplyCommands(self):
1876
"""!Apply changes"""
1877
# perform GRASS commands (e.g. v.db.addcol)
1878
wx.BeginBusyCursor()
1880
if len(self.listOfCommands) > 0:
1881
for cmd in self.listOfCommands:
1882
RunCommand(prog = cmd[0],
1887
self.mapDBInfo = VectorDBInfo(self.vectorName)
1888
table = self.mapDBInfo.layers[self.layer]['table']
1890
# update table description
1891
tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1892
tlist.Update(table = self.mapDBInfo.tables[table],
1893
columns = self.mapDBInfo.GetColumns(table))
1894
self.OnTableReload(None)
1897
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1898
tlist.Update(self.mapDBInfo)
1900
# reset list of commands
1901
self.listOfCommands = []
1903
# perform SQL non-select statements (e.g. 'delete from table where cat=1')
1904
if len(self.listOfSQLStatements) > 0:
1905
fd, sqlFilePath = tempfile.mkstemp(text=True)
1906
sqlFile = open(sqlFilePath, 'w')
1907
for sql in self.listOfSQLStatements:
1908
enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
1909
if not enc and 'GRASS_DB_ENCODING' in os.environ:
1910
enc = os.environ['GRASS_DB_ENCODING']
1912
sqlFile.write(sql.encode(enc) + ';')
1914
sqlFile.write(sql + ';')
1915
sqlFile.write(os.linesep)
1918
driver = self.mapDBInfo.layers[self.layer]["driver"]
1919
database = self.mapDBInfo.layers[self.layer]["database"]
1921
Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
1922
';'.join(["%s" % s for s in self.listOfSQLStatements]))
1924
RunCommand('db.execute',
1926
input = sqlFilePath,
1928
database = database)
1931
os.remove(sqlFilePath)
1932
# reset list of statements
1933
self.listOfSQLStatements = []
1937
def OnApplySqlStatement(self, event):
1938
"""!Apply simple/advanced sql statement"""
1939
keyColumn = -1 # index of key column
1940
listWin = self.FindWindowById(self.layerPage[self.layer]['data'])
1942
win = self.FindWindowById(self.layerPage[self.layer]['simple'])
1946
wx.BeginBusyCursor()
1948
# simple sql statement
1949
whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection()
1950
whereOpe = self.FindWindowById(self.layerPage[self.layer]['whereOperator']).GetStringSelection()
1951
whereVal = self.FindWindowById(self.layerPage[self.layer]['where']).GetValue().strip()
1953
if len(whereVal) > 0:
1954
keyColumn = listWin.LoadData(self.layer, where = whereCol + whereOpe + whereVal)
1956
keyColumn = listWin.LoadData(self.layer)
1957
except GException, e:
1958
GError(parent = self,
1959
message = _("Loading attribute data failed.\n\n%s") % e.value)
1960
self.FindWindowById(self.layerPage[self.layer]['where']).SetValue('')
1962
# advanced sql statement
1963
win = self.FindWindowById(self.layerPage[self.layer]['statement'])
1965
cols, where = self.ValidateSelectStatement(win.GetValue())
1966
if cols is None and where is None:
1967
sql = win.GetValue()
1969
GError(parent = self,
1970
message = _("Loading attribute data failed.\n"
1971
"Invalid SQL select statement.\n\n%s") % win.GetValue())
1972
win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
1976
if cols or where or sql:
1978
keyColumn = listWin.LoadData(self.layer, columns = cols,
1979
where = where, sql = sql)
1980
except GException, e:
1981
GError(parent = self,
1982
message = _("Loading attribute data failed.\n\n%s") % e.value)
1983
win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
1985
# sort by key column
1986
if sql and 'order by' in sql.lower():
1987
pass # don't order by key column
1990
listWin.SortListItems(col = keyColumn, ascending = True)
1992
listWin.SortListItems(col = 0, ascending = True)
1997
self.log.write(_("Number of loaded records: %d") % \
1998
self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
2000
def ValidateSelectStatement(self, statement):
2001
"""!Validate SQL select statement
2003
@return (columns, where)
2004
@return None on error
2006
if statement[0:7].lower() != 'select ':
2011
for c in statement[index:]:
2019
cols = cols.split(',')
2021
tablelen = len(self.mapDBInfo.layers[self.layer]['table'])
2023
if statement[index+1:index+6].lower() != 'from ' or \
2024
statement[index+6:index+6+tablelen] != '%s' % \
2025
(self.mapDBInfo.layers[self.layer]['table']):
2028
if len(statement[index+7+tablelen:]) > 0:
2029
index = statement.lower().find('where ')
2031
where = statement[index+6:]
2037
return (cols, where)
2039
def OnCloseWindow(self, event):
2040
"""!Cancel button pressed"""
2041
if self.parent and self.parent.GetName() == 'LayerManager':
2043
self.parent.dialogs['atm'].remove(self)
2045
if not isinstance(event, wx.CloseEvent):
2050
def OnBuilder(self,event):
2051
"""!SQL Builder button pressed -> show the SQLBuilder dialog"""
2052
if not self.builder:
2053
self.builder = SQLFrame(parent = self, id = wx.ID_ANY,
2054
title = _("SQL Builder"),
2055
vectmap = self.vectorName,
2056
evtHandler = self.OnBuilderEvt)
2059
self.builder.Raise()
2061
def OnBuilderEvt(self, event):
2062
if event == 'apply':
2063
sqlstr = self.builder.GetSQLStatement()
2064
self.FindWindowById(self.layerPage[self.layer]['statement']).SetValue(sqlstr)
2066
self.listOfSQLStatements.append(sqlstr)
2067
self.OnApplySqlStatement(None)
2068
# close builder on apply
2069
if self.builder.CloseOnApply():
2071
elif event == 'close':
2074
def OnTextEnter(self, event):
2077
def OnDataItemActivated(self, event):
2078
"""!Item activated, highlight selected item"""
2079
self.OnDataDrawSelected(event)
2083
def OnExtractSelected(self, event):
2084
"""!Extract vector objects selected in attribute browse window
2087
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
2088
# cats = tlist.selectedCats[:]
2089
cats = tlist.GetSelectedItems()
2091
GMessage(parent = self,
2092
message = _('Nothing to extract.'))
2095
# dialog to get file name
2096
dlg = CreateNewVector(parent = self, title = _('Extract selected features'),
2098
cmd = (('v.extract',
2099
{ 'input' : self.vectorName,
2100
'list' : ListOfCatsToRange(cats) },
2102
disableTable = True)
2106
name = dlg.GetName(full = True)
2107
if name and dlg.IsChecked('add'):
2108
# add layer to map layer tree
2109
self.parent.curr_page.maptree.AddLayer(ltype = 'vector',
2111
lcmd = ['d.vect', 'map=%s' % name])
2114
def OnDeleteSelected(self, event):
2115
"""!Delete vector objects selected in attribute browse window
2116
(attribures and geometry)
2118
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
2119
cats = tlist.GetSelectedItems()
2121
GMessage(parent = self,
2122
message = _('Nothing to delete.'))
2125
if 'vdigit' in self.mapdisplay.toolbars:
2126
digitToolbar = self.mapdisplay.toolbars['vdigit']
2127
if digitToolbar and digitToolbar.GetLayer() and \
2128
digitToolbar.GetLayer().GetName() == self.vectorName:
2129
display = self.mapdisplay.GetMapWindow().GetDisplay()
2130
display.SetSelected(map(int, cats), layer = self.layer)
2131
self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
2133
if self.OnDataItemDelete(None):
2135
self.mapdisplay.GetMapWindow().digit.DeleteSelectedLines()
2137
RunCommand('v.edit',
2140
map = self.vectorName,
2142
cats = ListOfCatsToRange(cats))
2144
self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
2146
def AddQueryMapLayer(self):
2149
Return True if map has been redrawn, False if no map is given
2151
tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
2153
self.layer : tlist.GetSelectedItems()
2156
if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0:
2160
self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer = False))
2162
self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats)
2166
def UpdateDialog(self, layer):
2167
"""!Updates dialog layout for given layer"""
2169
if layer in self.mapDBInfo.layers.keys():
2171
# draging pages disallowed
2172
# if self.browsePage.GetPageText(page).replace('Layer ', '').strip() == str(layer):
2173
# self.browsePage.DeletePage(page)
2175
self.browsePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
2176
self.manageTablePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
2177
# set current page selection
2178
self.notebook.SetSelectionByName('layers')
2180
# fetch fresh db info
2181
self.mapDBInfo = VectorDBInfo(self.vectorName)
2186
if layer in self.mapDBInfo.layers.keys():
2187
# 'browse data' page
2188
self._createBrowsePage(layer)
2189
# 'manage tables' page
2190
self._createManageTablePage(layer)
2191
# set current page selection
2192
self.notebook.SetSelectionByName('layers')
2195
# 'manage layers' page
2197
# update list of layers
2198
self.layerList.Update(self.mapDBInfo.layers)
2199
self.layerList.Populate(update = True)
2200
# update selected widgets
2201
listOfLayers = map(str, self.mapDBInfo.layers.keys())
2202
### delete layer page
2203
self.manageLayerBook.deleteLayer.SetItems(listOfLayers)
2204
if len(listOfLayers) > 0:
2205
self.manageLayerBook.deleteLayer.SetStringSelection(listOfLayers[0])
2206
tableName = self.mapDBInfo.layers[int(listOfLayers[0])]['table']
2207
maxLayer = max(self.mapDBInfo.layers.keys())
2211
self.manageLayerBook.deleteTable.SetLabel( \
2212
_('Drop also linked attribute table (%s)') % \
2215
self.manageLayerBook.addLayerWidgets['layer'][1].SetValue(\
2218
self.manageLayerBook.modifyLayerWidgets['layer'][1].SetItems(listOfLayers)
2219
self.manageLayerBook.OnChangeLayer(event = None)
2221
def GetVectorName(self):
2222
"""!Get vector name"""
2223
return self.vectorName
2225
def LoadData(self, layer, columns = None, where = None, sql = None):
2226
"""!Load data into list
2228
@param layer layer number
2229
@param columns list of columns for output
2230
@param where where statement
2231
@param sql full sql statement
2233
@return id of key column
2234
@return -1 if key column is not displayed
2236
listWin = self.FindWindowById(self.layerPage[layer]['data'])
2237
return listWin.LoadData(layer, columns, where, sql)
2239
class TableListCtrl(wx.ListCtrl,
2240
listmix.ListCtrlAutoWidthMixin):
2241
# listmix.TextEditMixin):
2242
"""!Table description list"""
2244
def __init__(self, parent, id, table, columns, pos = wx.DefaultPosition,
2245
size = wx.DefaultSize):
2247
self.parent = parent
2249
self.columns = columns
2250
wx.ListCtrl.__init__(self, parent, id, pos, size,
2251
style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
2254
listmix.ListCtrlAutoWidthMixin.__init__(self)
2255
# listmix.TextEditMixin.__init__(self)
2257
def Update(self, table, columns):
2258
"""!Update column description"""
2260
self.columns = columns
2262
def Populate(self, update = False):
2263
"""!Populate the list"""
2264
itemData = {} # requested by sorter
2267
headings = [_("Column name"), _("Data type"), _("Data length")]
2270
self.InsertColumn(col = i, heading = h)
2272
self.SetColumnWidth(col = 0, width = 350)
2273
self.SetColumnWidth(col = 1, width = 175)
2275
self.DeleteAllItems()
2278
for column in self.columns:
2279
index = self.InsertStringItem(sys.maxint, str(column))
2280
self.SetStringItem(index, 0, str(column))
2281
self.SetStringItem(index, 1, str(self.table[column]['type']))
2282
self.SetStringItem(index, 2, str(self.table[column]['length']))
2283
self.SetItemData(index, i)
2284
itemData[i] = (str(column),
2285
str(self.table[column]['type']),
2286
int(self.table[column]['length']))
2289
self.SendSizeEvent()
2293
class LayerListCtrl(wx.ListCtrl,
2294
listmix.ListCtrlAutoWidthMixin):
2295
# listmix.ColumnSorterMixin):
2296
# listmix.TextEditMixin):
2297
"""!Layer description list"""
2299
def __init__(self, parent, id, layers,
2300
pos = wx.DefaultPosition,
2301
size = wx.DefaultSize):
2303
self.parent = parent
2304
self.layers = layers
2305
wx.ListCtrl.__init__(self, parent, id, pos, size,
2306
style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
2309
listmix.ListCtrlAutoWidthMixin.__init__(self)
2310
# listmix.TextEditMixin.__init__(self)
2312
def Update(self, layers):
2313
"""!Update description"""
2314
self.layers = layers
2316
def Populate(self, update = False):
2317
"""!Populate the list"""
2318
itemData = {} # requested by sorter
2321
headings = [_("Layer"), _("Driver"), _("Database"), _("Table"), _("Key")]
2324
self.InsertColumn(col = i, heading = h)
2327
self.DeleteAllItems()
2330
for layer in self.layers.keys():
2331
index = self.InsertStringItem(sys.maxint, str(layer))
2332
self.SetStringItem(index, 0, str(layer))
2333
database = str(self.layers[layer]['database'])
2334
driver = str(self.layers[layer]['driver'])
2335
table = str(self.layers[layer]['table'])
2336
key = str(self.layers[layer]['key'])
2337
self.SetStringItem(index, 1, driver)
2338
self.SetStringItem(index, 2, database)
2339
self.SetStringItem(index, 3, table)
2340
self.SetStringItem(index, 4, key)
2341
self.SetItemData(index, i)
2342
itemData[i] = (str(layer),
2349
for i in range(self.GetColumnCount()):
2350
self.SetColumnWidth(col = i, width = wx.LIST_AUTOSIZE)
2351
if self.GetColumnWidth(col = i) < 60:
2352
self.SetColumnWidth(col = i, width = 60)
2354
self.SendSizeEvent()
2358
class LayerBook(wx.Notebook):
2359
"""!Manage layers (add, delete, modify)"""
2360
def __init__(self, parent, id,
2362
style = wx.BK_DEFAULT):
2363
wx.Notebook.__init__(self, parent, id, style = style)
2365
self.parent = parent
2366
self.parentDialog = parentDialog
2367
self.mapDBInfo = self.parentDialog.mapDBInfo
2372
drivers = RunCommand('db.drivers',
2377
self.listOfDrivers = []
2378
for drv in drivers.splitlines():
2379
self.listOfDrivers.append(drv.strip())
2382
# get default values
2384
self.defaultConnect = {}
2385
connect = RunCommand('db.connect',
2390
for line in connect.splitlines():
2391
item, value = line.split(':', 1)
2392
self.defaultConnect[item.strip()] = value.strip()
2394
if len(self.defaultConnect['driver']) == 0 or \
2395
len(self.defaultConnect['database']) == 0:
2396
GWarning(parent = self.parent,
2397
message = _("Unknown default DB connection. "
2398
"Please define DB connection using db.connect module."))
2400
self.defaultTables = self._getTables(self.defaultConnect['driver'],
2401
self.defaultConnect['database'])
2403
self.defaultColumns = self._getColumns(self.defaultConnect['driver'],
2404
self.defaultConnect['database'],
2405
self.defaultTables[0])
2407
self.defaultColumns = []
2409
self._createAddPage()
2410
self._createDeletePage()
2411
self._createModifyPage()
2413
def _createAddPage(self):
2414
"""!Add new layer"""
2415
self.addPanel = wx.Panel(parent = self, id = wx.ID_ANY)
2416
self.AddPage(page = self.addPanel, text = _("Add layer"))
2419
maxLayer = max(self.mapDBInfo.layers.keys())
2425
layerBox = wx.StaticBox (parent = self.addPanel, id = wx.ID_ANY,
2426
label = " %s " % (_("Layer description")))
2427
layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
2430
# list of layer widgets (label, value)
2432
self.addLayerWidgets = {'layer':
2433
(wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2434
label = '%s:' % _("Layer")),
2435
wx.SpinCtrl(parent = self.addPanel, id = wx.ID_ANY, size = (65, -1),
2436
initial = maxLayer+1,
2437
min = 1, max = 1e6)),
2439
(wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2440
label = '%s:' % _("Driver")),
2441
wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
2442
choices = self.listOfDrivers)),
2444
(wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2445
label = '%s:' % _("Database")),
2446
wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
2448
style = wx.TE_PROCESS_ENTER)),
2450
(wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2451
label = '%s:' % _("Table")),
2452
wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
2453
choices = self.defaultTables)),
2455
(wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2456
label = '%s:' % _("Key column")),
2457
wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
2458
choices = self.defaultColumns)),
2460
(wx.CheckBox(parent = self.addPanel, id = wx.ID_ANY,
2461
label = _("Insert record for each category into table")),
2465
# set default values for widgets
2466
self.addLayerWidgets['driver'][1].SetStringSelection(self.defaultConnect['driver'])
2467
self.addLayerWidgets['database'][1].SetValue(self.defaultConnect['database'])
2468
self.addLayerWidgets['table'][1].SetSelection(0)
2469
self.addLayerWidgets['key'][1].SetSelection(0)
2471
self.addLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
2472
self.addLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
2473
self.addLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
2476
self.addLayerWidgets['addCat'][0].SetToolTipString(_("You need to add categories "
2477
"by v.category module."))
2480
tableBox = wx.StaticBox (parent = self.addPanel, id = wx.ID_ANY,
2481
label = " %s " % (_("Table description")))
2482
tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
2485
# list of table widgets
2487
keyCol = UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value')
2488
self.tableWidgets = {'table': (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2489
label = '%s:' % _("Table name")),
2490
wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
2492
style = wx.TE_PROCESS_ENTER)),
2493
'key': (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2494
label = '%s:' % _("Key column")),
2495
wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
2497
style = wx.TE_PROCESS_ENTER))}
2499
self.tableWidgets['table'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
2500
self.tableWidgets['key'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
2502
btnTable = wx.Button(self.addPanel, wx.ID_ANY, _("&Create table"),
2504
btnTable.Bind(wx.EVT_BUTTON, self.OnCreateTable)
2506
btnLayer = wx.Button(self.addPanel, wx.ID_ANY, _("&Add layer"),
2508
btnLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
2510
btnDefault = wx.Button(self.addPanel, wx.ID_ANY, _("&Set default"),
2512
btnDefault.Bind(wx.EVT_BUTTON, self.OnSetDefault)
2516
pageSizer = wx.BoxSizer(wx.HORIZONTAL)
2519
dataSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
2521
for key in ('layer', 'driver', 'database', 'table', 'key', 'addCat'):
2522
label, value = self.addLayerWidgets[key]
2527
dataSizer.Add(item = label,
2528
flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0),
2536
style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT
2538
style = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND
2540
dataSizer.Add(item = value,
2541
flag = style, pos = (row, 1))
2544
dataSizer.AddGrowableCol(1)
2546
layerSizer.Add(item = dataSizer,
2548
flag = wx.ALL | wx.EXPAND,
2551
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2552
btnSizer.Add(item = btnDefault,
2554
flag = wx.ALL | wx.ALIGN_LEFT,
2557
btnSizer.Add(item = (5, 5),
2559
flag = wx.ALL | wx.EXPAND,
2562
btnSizer.Add(item = btnLayer,
2564
flag = wx.ALL | wx.ALIGN_RIGHT,
2567
layerSizer.Add(item = btnSizer,
2569
flag = wx.ALL | wx.EXPAND,
2573
dataSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
2574
for key in ['table', 'key']:
2575
label, value = self.tableWidgets[key]
2576
dataSizer.Add(item = label,
2577
flag = wx.ALIGN_CENTER_VERTICAL)
2578
dataSizer.Add(item = value,
2579
flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
2580
dataSizer.AddGrowableCol(1)
2582
tableSizer.Add(item = dataSizer,
2584
flag = wx.ALL | wx.EXPAND,
2587
tableSizer.Add(item = btnTable,
2589
flag = wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT,
2592
pageSizer.Add(item = layerSizer,
2594
flag = wx.ALL | wx.EXPAND,
2597
pageSizer.Add(item = tableSizer,
2599
flag = wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND,
2602
layerSizer.SetVirtualSizeHints(self.addPanel)
2603
self.addPanel.SetAutoLayout(True)
2604
self.addPanel.SetSizer(pageSizer)
2605
pageSizer.Fit(self.addPanel)
2607
def _createDeletePage(self):
2609
self.deletePanel = wx.Panel(parent = self, id = wx.ID_ANY)
2610
self.AddPage(page = self.deletePanel, text = _("Remove layer"))
2612
label = wx.StaticText(parent = self.deletePanel, id = wx.ID_ANY,
2613
label = '%s:' % _("Layer to remove"))
2615
self.deleteLayer = wx.ComboBox(parent = self.deletePanel, id = wx.ID_ANY, size = (100, -1),
2616
style = wx.CB_SIMPLE | wx.CB_READONLY,
2617
choices = map(str, self.mapDBInfo.layers.keys()))
2618
self.deleteLayer.SetSelection(0)
2619
self.deleteLayer.Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
2622
tableName = self.mapDBInfo.layers[int(self.deleteLayer.GetStringSelection())]['table']
2626
self.deleteTable = wx.CheckBox(parent = self.deletePanel, id = wx.ID_ANY,
2627
label = _('Drop also linked attribute table (%s)') % \
2631
self.deleteLayer.Enable(False)
2632
self.deleteTable.Enable(False)
2634
btnDelete = wx.Button(self.deletePanel, wx.ID_DELETE, _("&Remove layer"),
2636
btnDelete.Bind(wx.EVT_BUTTON, self.OnDeleteLayer)
2641
pageSizer = wx.BoxSizer(wx.VERTICAL)
2643
dataSizer = wx.BoxSizer(wx.VERTICAL)
2645
flexSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
2647
flexSizer.Add(item = label,
2648
flag = wx.ALIGN_CENTER_VERTICAL)
2649
flexSizer.Add(item = self.deleteLayer,
2650
flag = wx.ALIGN_CENTER_VERTICAL)
2651
flexSizer.AddGrowableCol(1)
2653
dataSizer.Add(item = flexSizer,
2655
flag = wx.ALL | wx.EXPAND,
2658
dataSizer.Add(item = self.deleteTable,
2660
flag = wx.ALL | wx.EXPAND,
2663
pageSizer.Add(item = dataSizer,
2665
flag = wx.ALL | wx.EXPAND,
2668
pageSizer.Add(item = btnDelete,
2670
flag = wx.ALL | wx.ALIGN_RIGHT,
2673
self.deletePanel.SetSizer(pageSizer)
2675
def _createModifyPage(self):
2677
self.modifyPanel = wx.Panel(parent = self, id = wx.ID_ANY)
2678
self.AddPage(page = self.modifyPanel, text = _("Modify layer"))
2681
# list of layer widgets (label, value)
2683
self.modifyLayerWidgets = {'layer':
2684
(wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2685
label = '%s:' % _("Layer")),
2686
wx.ComboBox(parent = self.modifyPanel, id = wx.ID_ANY,
2688
style = wx.CB_SIMPLE | wx.CB_READONLY,
2690
self.mapDBInfo.layers.keys()))),
2692
(wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2693
label = '%s:' % _("Driver")),
2694
wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
2696
choices = self.listOfDrivers)),
2698
(wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2699
label = '%s:' % _("Database")),
2700
wx.TextCtrl(parent = self.modifyPanel, id = wx.ID_ANY,
2701
value = '', size = (350, -1),
2702
style = wx.TE_PROCESS_ENTER)),
2704
(wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2705
label = '%s:' % _("Table")),
2706
wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
2708
choices = self.defaultTables)),
2710
(wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2711
label = '%s:' % _("Key column")),
2712
wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
2714
choices = self.defaultColumns))}
2716
# set default values for widgets
2717
self.modifyLayerWidgets['layer'][1].SetSelection(0)
2719
layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
2722
for label in self.modifyLayerWidgets.keys():
2723
self.modifyLayerWidgets[label][1].Enable(False)
2726
driver = self.mapDBInfo.layers[layer]['driver']
2727
database = self.mapDBInfo.layers[layer]['database']
2728
table = self.mapDBInfo.layers[layer]['table']
2730
listOfColumns = self._getColumns(driver, database, table)
2731
self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
2732
self.modifyLayerWidgets['database'][1].SetValue(database)
2733
if table in self.modifyLayerWidgets['table'][1].GetItems():
2734
self.modifyLayerWidgets['table'][1].SetStringSelection(table)
2736
if self.defaultConnect['schema'] != '':
2737
table = self.defaultConnect['schema'] + table # try with default schema
2739
table = 'public.' + table # try with 'public' schema
2740
self.modifyLayerWidgets['table'][1].SetStringSelection(table)
2741
self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
2742
self.modifyLayerWidgets['key'][1].SetSelection(0)
2745
self.modifyLayerWidgets['layer'][1].Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
2746
# self.modifyLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
2747
# self.modifyLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
2748
# self.modifyLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
2750
btnModify = wx.Button(self.modifyPanel, wx.ID_DELETE, _("&Modify layer"),
2752
btnModify.Bind(wx.EVT_BUTTON, self.OnModifyLayer)
2757
pageSizer = wx.BoxSizer(wx.VERTICAL)
2760
dataSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
2761
for key in ('layer', 'driver', 'database', 'table', 'key'):
2762
label, value = self.modifyLayerWidgets[key]
2763
dataSizer.Add(item = label,
2764
flag = wx.ALIGN_CENTER_VERTICAL)
2766
dataSizer.Add(item = value,
2767
flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
2769
dataSizer.Add(item = value,
2770
flag = wx.ALIGN_CENTER_VERTICAL)
2771
dataSizer.AddGrowableCol(1)
2773
pageSizer.Add(item = dataSizer,
2775
flag = wx.ALL | wx.EXPAND,
2778
pageSizer.Add(item = btnModify,
2780
flag = wx.ALL | wx.ALIGN_RIGHT,
2783
self.modifyPanel.SetSizer(pageSizer)
2785
def _getTables(self, driver, database):
2786
"""!Get list of tables for given driver and database"""
2789
ret = RunCommand('db.tables',
2794
database = database)
2797
GError(parent = self,
2798
message = _("Unable to get list of tables.\n"
2799
"Please use db.connect to set database parameters."))
2803
for table in ret.splitlines():
2804
tables.append(table)
2808
def _getColumns(self, driver, database, table):
2809
"""!Get list of column of given table"""
2812
ret = RunCommand('db.columns',
2817
database = database,
2823
for column in ret.splitlines():
2824
columns.append(column)
2828
def OnDriverChanged(self, event):
2829
"""!Driver selection changed, update list of tables"""
2830
driver = event.GetString()
2831
database = self.addLayerWidgets['database'][1].GetValue()
2833
winTable = self.addLayerWidgets['table'][1]
2834
winKey = self.addLayerWidgets['key'][1]
2835
tables = self._getTables(driver, database)
2837
winTable.SetItems(tables)
2838
winTable.SetSelection(0)
2840
if len(tables) == 0:
2845
def OnDatabaseChanged(self, event):
2846
"""!Database selection changed, update list of tables"""
2849
def OnTableChanged(self, event):
2850
"""!Table name changed, update list of columns"""
2851
driver = self.addLayerWidgets['driver'][1].GetStringSelection()
2852
database = self.addLayerWidgets['database'][1].GetValue()
2853
table = event.GetString()
2855
win = self.addLayerWidgets['key'][1]
2856
cols = self._getColumns(driver, database, table)
2862
def OnSetDefault(self, event):
2863
"""!Set default values"""
2864
driver = self.addLayerWidgets['driver'][1]
2865
database = self.addLayerWidgets['database'][1]
2866
table = self.addLayerWidgets['table'][1]
2867
key = self.addLayerWidgets['key'][1]
2869
driver.SetStringSelection(self.defaultConnect['driver'])
2870
database.SetValue(self.defaultConnect['database'])
2871
tables = self._getTables(self.defaultConnect['driver'],
2872
self.defaultConnect['database'])
2873
table.SetItems(tables)
2874
table.SetSelection(0)
2875
if len(tables) == 0:
2878
cols = self._getColumns(self.defaultConnect['driver'],
2879
self.defaultConnect['database'],
2886
def OnCreateTable(self, event):
2887
"""!Create new table (name and key column given)"""
2888
driver = self.addLayerWidgets['driver'][1].GetStringSelection()
2889
database = self.addLayerWidgets['database'][1].GetValue()
2890
table = self.tableWidgets['table'][1].GetValue()
2891
key = self.tableWidgets['key'][1].GetValue()
2893
if not table or not key:
2894
GError(parent = self,
2895
message = _("Unable to create new table. "
2896
"Table name or key column name is missing."))
2899
if table in self.addLayerWidgets['table'][1].GetItems():
2900
GError(parent = self,
2901
message = _("Unable to create new table. "
2902
"Table <%s> already exists in the database.") % table)
2906
sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
2908
RunCommand('db.execute',
2913
database = database)
2915
# update list of tables
2916
tableList = self.addLayerWidgets['table'][1]
2917
tableList.SetItems(self._getTables(driver, database))
2918
tableList.SetStringSelection(table)
2920
# update key column selection
2921
keyList = self.addLayerWidgets['key'][1]
2922
keyList.SetItems(self._getColumns(driver, database, table))
2923
keyList.SetStringSelection(key)
2927
def OnAddLayer(self, event):
2928
"""!Add new layer to vector map"""
2929
layer = int(self.addLayerWidgets['layer'][1].GetValue())
2930
layerWin = self.addLayerWidgets['layer'][1]
2931
driver = self.addLayerWidgets['driver'][1].GetStringSelection()
2932
database = self.addLayerWidgets['database'][1].GetValue()
2933
table = self.addLayerWidgets['table'][1].GetStringSelection()
2934
key = self.addLayerWidgets['key'][1].GetStringSelection()
2936
if layer in self.mapDBInfo.layers.keys():
2937
GError(parent = self,
2938
message = _("Unable to add new layer to vector map <%(vector)s>. "
2939
"Layer %(layer)d already exists.") % \
2940
{'vector' : self.mapDBInfo.map, 'layer' : layer})
2944
ret = RunCommand('v.db.connect',
2947
map = self.mapDBInfo.map,
2949
database = database,
2954
# insert records into table if required
2955
if self.addLayerWidgets['addCat'][0].IsChecked():
2956
RunCommand('v.to.db',
2959
map = self.mapDBInfo.map,
2966
# update dialog (only for new layer)
2967
self.parentDialog.UpdateDialog(layer = layer)
2969
self.mapDBInfo = self.parentDialog.mapDBInfo
2970
# increase layer number
2971
layerWin.SetValue(layer+1)
2973
if len(self.mapDBInfo.layers.keys()) == 1:
2974
# first layer add --- enable previously disabled widgets
2975
self.deleteLayer.Enable()
2976
self.deleteTable.Enable()
2977
for label in self.modifyLayerWidgets.keys():
2978
self.modifyLayerWidgets[label][1].Enable()
2980
def OnDeleteLayer(self, event):
2983
layer = int(self.deleteLayer.GetValue())
2987
RunCommand('v.db.connect',
2990
map = self.mapDBInfo.map,
2993
# drop also table linked to layer which is deleted
2994
if self.deleteTable.IsChecked():
2995
driver = self.addLayerWidgets['driver'][1].GetStringSelection()
2996
database = self.addLayerWidgets['database'][1].GetValue()
2997
table = self.mapDBInfo.layers[layer]['table']
2998
sql = 'DROP TABLE %s' % (table)
3000
RunCommand('db.execute',
3005
database = database)
3007
# update list of tables
3008
tableList = self.addLayerWidgets['table'][1]
3009
tableList.SetItems(self._getTables(driver, database))
3010
tableList.SetStringSelection(table)
3013
self.parentDialog.UpdateDialog(layer = layer)
3015
self.mapDBInfo = self.parentDialog.mapDBInfo
3017
if len(self.mapDBInfo.layers.keys()) == 0:
3018
# disable selected widgets
3019
self.deleteLayer.Enable(False)
3020
self.deleteTable.Enable(False)
3021
for label in self.modifyLayerWidgets.keys():
3022
self.modifyLayerWidgets[label][1].Enable(False)
3026
def OnChangeLayer(self, event):
3027
"""!Layer number of layer to be deleted is changed"""
3029
layer = int(event.GetString())
3032
layer = self.mapDBInfo.layers.keys()[0]
3036
if self.GetCurrentPage() == self.modifyPanel:
3037
driver = self.mapDBInfo.layers[layer]['driver']
3038
database = self.mapDBInfo.layers[layer]['database']
3039
table = self.mapDBInfo.layers[layer]['table']
3040
listOfColumns = self._getColumns(driver, database, table)
3041
self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
3042
self.modifyLayerWidgets['database'][1].SetValue(database)
3043
self.modifyLayerWidgets['table'][1].SetStringSelection(table)
3044
self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
3045
self.modifyLayerWidgets['key'][1].SetSelection(0)
3047
self.deleteTable.SetLabel(_('Drop also linked attribute table (%s)') % \
3048
self.mapDBInfo.layers[layer]['table'])
3052
def OnModifyLayer(self, event):
3053
"""!Modify layer connection settings"""
3055
layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
3058
if self.modifyLayerWidgets['driver'][1].GetStringSelection() != \
3059
self.mapDBInfo.layers[layer]['driver'] or \
3060
self.modifyLayerWidgets['database'][1].GetStringSelection() != \
3061
self.mapDBInfo.layers[layer]['database'] or \
3062
self.modifyLayerWidgets['table'][1].GetStringSelection() != \
3063
self.mapDBInfo.layers[layer]['table'] or \
3064
self.modifyLayerWidgets['key'][1].GetStringSelection() != \
3065
self.mapDBInfo.layers[layer]['key']:
3070
RunCommand('v.db.connect',
3074
map = self.mapDBInfo.map,
3077
# add modified layer
3078
RunCommand('v.db.connect',
3080
map = self.mapDBInfo.map,
3081
driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(),
3082
database = self.modifyLayerWidgets['database'][1].GetValue(),
3083
table = self.modifyLayerWidgets['table'][1].GetStringSelection(),
3084
key = self.modifyLayerWidgets['key'][1].GetStringSelection(),
3087
# update dialog (only for new layer)
3088
self.parentDialog.UpdateDialog(layer = layer)
3090
self.mapDBInfo = self.parentDialog.mapDBInfo
3094
def main(argv = None):
3096
gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
3102
print >> sys.stderr, __doc__
3105
#some applications might require image handlers
3106
wx.InitAllImageHandlers()
3108
app = wx.PySimpleApp()
3109
f = AttributeManager(parent = None, id = wx.ID_ANY,
3110
title = "%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
3112
size = (900,600), vectorName = argv[1])
3117
if __name__ == '__main__':