2
@package dbm_dialogs.py
4
@brief DBM-related dialogs
7
- DisplayAttributesDialog
10
(C) 2007-2010 by the GRASS Development Team
12
This program is free software under the GNU General Public
13
License (>=v2). Read the file COPYING that comes with GRASS
16
@author Martin Landa <landa.martin gmail.com>
22
if not os.getenv("GRASS_WXBUNDLED"):
23
globalvar.CheckForWx()
26
import wx.lib.scrolledpanel as scrolled
29
from debug import Debug
30
from preferences import globalSettings as UserSettings
31
from dbm_base import VectorDBInfo
33
class DisplayAttributesDialog(wx.Dialog):
35
Standard dialog used to add/update/display attributes linked
38
Attribute data can be selected based on layer and category number
43
@param query query coordinates and distance (used for v.edit)
44
@param cats {layer: cats}
45
@param line feature id (requested for cats)
48
@param action (add, update, display)
50
def __init__(self, parent, map,
51
query=None, cats=None, line=None,
52
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
53
pos=wx.DefaultPosition,
55
self.parent = parent # mapdisplay.BufferedWindow
59
# ids/cats of selected features
60
# fid : {layer : cats}
62
self.fid = -1 # feature id
64
# get layer/table/column information
65
self.mapDBInfo = VectorDBInfo(self.map)
67
layers = self.mapDBInfo.layers.keys() # get available layers
69
# check if db connection / layer exists
71
label = _("Database connection "
72
"is not defined in DB file.")
74
wx.MessageBox(parent=self.parent,
75
message=_("No attribute table linked to "
76
"vector map <%(vector)s> found. %(msg)s"
77
"\nYou can disable this message from digitization settings. Or "
78
"you can create and link attribute table to the vector map "
79
"using Attribute Table Manager.") %
80
{'vector' : self.map, 'msg' : label},
81
caption=_("Message"), style=wx.OK | wx.ICON_EXCLAMATION | wx.CENTRE)
84
wx.Dialog.__init__(self, parent=self.parent, id=wx.ID_ANY,
85
title="", style=style, pos=pos)
88
mainSizer = wx.BoxSizer(wx.VERTICAL)
91
self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
93
self.closeDialog = wx.CheckBox(parent=self, id=wx.ID_ANY,
94
label=_("Close dialog on submit"))
95
self.closeDialog.SetValue(True)
97
# feature id (text/choice for duplicates)
98
self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY,
100
self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
101
self.fidText = wx.StaticText(parent=self, id=wx.ID_ANY)
103
self.noFoundMsg = wx.StaticText(parent=self, id=wx.ID_ANY,
104
label=_("No attributes found"))
106
self.UpdateDialog(query=query, cats=cats)
109
if self.action == "update":
110
self.SetTitle(_("Update attributes"))
111
elif self.action == "add":
112
self.SetTitle(_("Add attributes"))
114
self.SetTitle(_("Display attributes"))
117
btnCancel = wx.Button(self, wx.ID_CANCEL)
118
btnReset = wx.Button(self, wx.ID_UNDO, _("&Reload"))
119
btnSubmit = wx.Button(self, wx.ID_OK, _("&Submit"))
121
btnSizer = wx.StdDialogButtonSizer()
122
btnSizer.AddButton(btnCancel)
123
btnSizer.AddButton(btnReset)
124
btnSizer.SetNegativeButton(btnReset)
125
btnSubmit.SetDefault()
126
btnSizer.AddButton(btnSubmit)
129
mainSizer.Add(item=self.noFoundMsg, proportion=0,
130
flag=wx.EXPAND | wx.ALL, border=5)
131
mainSizer.Add(item=self.notebook, proportion=1,
132
flag=wx.EXPAND | wx.ALL, border=5)
133
fidSizer = wx.BoxSizer(wx.HORIZONTAL)
134
fidSizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY,
135
label=_("Feature id:")),
136
proportion=0, border=5,
137
flag=wx.ALIGN_CENTER_VERTICAL)
138
fidSizer.Add(item=self.fidMulti, proportion=0,
139
flag=wx.EXPAND | wx.ALL, border=5)
140
fidSizer.Add(item=self.fidText, proportion=0,
141
flag=wx.EXPAND | wx.ALL, border=5)
142
mainSizer.Add(item=fidSizer, proportion=0,
143
flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
144
mainSizer.Add(item=self.closeDialog, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
146
mainSizer.Add(item=btnSizer, proportion=0,
147
flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
150
btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
151
btnSubmit.Bind(wx.EVT_BUTTON, self.OnSubmit)
152
btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
154
self.SetSizer(mainSizer)
157
# set min size for dialog
158
w, h = self.GetBestSize()
160
self.SetMinSize((w, 200))
162
self.SetMinSize(self.GetBestSize())
164
if self.notebook.GetPageCount() == 0:
165
Debug.msg(2, "DisplayAttributesDialog(): Nothing found!")
166
### self.mapDBInfo = None
168
def __SelectAttributes(self, layer):
169
"""!Select attributes"""
172
def OnSQLStatement(self, event):
173
"""!Update SQL statement"""
176
def GetSQLString(self, updateValues=False):
177
"""!Create SQL statement string based on self.sqlStatement
179
If updateValues is True, update dataFrame according to values
183
# find updated values for each layer/category
184
for layer in self.mapDBInfo.layers.keys(): # for each layer
185
table = self.mapDBInfo.GetTable(layer)
186
key = self.mapDBInfo.GetKeyColumn(layer)
187
columns = self.mapDBInfo.GetTableDesc(table)
188
for idx in range(len(columns[key]['values'])): # for each category
191
for name in columns.keys():
193
cat = columns[name]['values'][idx]
195
type = columns[name]['type']
196
value = columns[name]['values'][idx]
197
id = columns[name]['ids'][idx]
199
newvalue = self.FindWindowById(id).GetValue()
201
newvalue = self.FindWindowById(id).GetLabel()
206
if newvalue != value:
207
updatedColumns.append(name)
209
updatedValues.append('NULL')
211
if type != 'character':
212
updatedValues.append(newvalue)
214
updatedValues.append("'" + newvalue + "'")
215
columns[name]['values'][idx] = newvalue
217
if self.action != "add" and len(updatedValues) == 0:
220
if self.action == "add":
221
sqlString = "INSERT INTO %s (%s," % (table, key)
223
sqlString = "UPDATE %s SET " % table
225
for idx in range(len(updatedColumns)):
226
name = updatedColumns[idx]
227
if self.action == "add":
228
sqlString += name + ","
230
sqlString += name + "=" + updatedValues[idx] + ","
232
sqlString = sqlString[:-1] # remove last comma
234
if self.action == "add":
235
sqlString += ") VALUES (%s," % cat
236
for value in updatedValues:
237
sqlString += str(value) + ","
238
sqlString = sqlString[:-1] # remove last comma
241
sqlString += " WHERE cat=%s" % cat
242
sqlCommands.append(sqlString)
246
Debug.msg(3, "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)
250
def OnReset(self, event = None):
252
for layer in self.mapDBInfo.layers.keys():
253
table = self.mapDBInfo.layers[layer]["table"]
254
key = self.mapDBInfo.layers[layer]["key"]
255
columns = self.mapDBInfo.tables[table]
256
for idx in range(len(columns[key]['values'])):
257
for name in columns.keys():
258
type = columns[name]['type']
259
value = columns[name]['values'][idx]
263
id = columns[name]['ids'][idx]
267
if name != key and id != wx.NOT_FOUND:
268
self.FindWindowById(id).SetValue(str(value))
270
def OnCancel(self, event):
271
"""!Cancel button pressed"""
272
self.parent.parent.dialogs['attributes'] = None
273
if self.parent.parent.digit:
274
self.parent.parent.digit.driver.SetSelected([])
275
self.parent.UpdateMap(render=False)
277
self.parent.parent.OnRender(None)
281
def OnSubmit(self, event):
282
"""!Submit records"""
283
for sql in self.GetSQLString(updateValues=True):
284
enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
286
os.environ.has_key('GRASS_DB_ENCODING'):
287
enc = os.environ['GRASS_DB_ENCODING']
289
sql = sql.encode(enc)
291
gcmd.RunCommand('db.execute',
295
if self.closeDialog.IsChecked():
298
def OnFeature(self, event):
299
self.fid = int(event.GetString())
300
self.UpdateDialog(cats=self.cats, fid=self.fid)
303
"""!Get id of selected vector object or 'None' if nothing selected
305
@param id if true return ids otherwise cats
310
return self.cats[self.fid]
313
"""!Get selected feature id"""
316
def UpdateDialog(self, map=None, query=None, cats=None, fid=-1):
319
Return True if updated otherwise False
323
# get layer/table/column information
324
self.mapDBInfo = VectorDBInfo(self.map)
326
if not self.mapDBInfo:
329
self.mapDBInfo.Reset()
331
layers = self.mapDBInfo.layers.keys() # get available layers
333
# id of selected line
334
if query: # select by position
335
data = self.mapDBInfo.SelectByPoint(query[0],
338
if data and data.has_key('Layer'):
340
for layer in data['Layer']:
342
if data.has_key('Id'):
343
tfid = int(data['Id'][idx])
345
tfid = 0 # Area / Volume
346
if not self.cats.has_key(tfid):
348
if not self.cats[tfid].has_key(layer):
349
self.cats[tfid][layer] = []
350
cat = int(data['Category'][idx])
351
self.cats[tfid][layer].append(cat)
358
elif len(self.cats.keys()) > 0:
359
self.fid = self.cats.keys()[0]
363
if len(self.cats.keys()) == 1:
364
self.fidMulti.Show(False)
365
self.fidText.Show(True)
367
self.fidText.SetLabel("%d" % self.fid)
369
self.fidText.SetLabel(_("Unknown"))
371
self.fidMulti.Show(True)
372
self.fidText.Show(False)
374
for tfid in self.cats.keys():
375
choices.append(str(tfid))
376
self.fidMulti.SetItems(choices)
377
self.fidMulti.SetStringSelection(str(self.fid))
380
self.notebook.DeleteAllPages()
382
for layer in layers: # for each layer
383
if not query: # select by layer/cat
384
if self.fid > 0 and self.cats[self.fid].has_key(layer):
385
for cat in self.cats[self.fid][layer]:
386
nselected = self.mapDBInfo.SelectFromTable(layer,
388
(self.mapDBInfo.layers[layer]['key'],
393
# if nselected <= 0 and self.action != "add":
394
# continue # nothing selected ...
396
if self.action == "add":
398
if self.cats[self.fid].has_key(layer):
399
table = self.mapDBInfo.layers[layer]["table"]
400
key = self.mapDBInfo.layers[layer]["key"]
401
columns = self.mapDBInfo.tables[table]
402
for name in columns.keys():
404
for cat in self.cats[self.fid][layer]:
405
self.mapDBInfo.tables[table][name]['values'].append(cat)
407
self.mapDBInfo.tables[table][name]['values'].append(None)
408
else: # change status 'add' -> 'update'
409
self.action = "update"
411
table = self.mapDBInfo.layers[layer]["table"]
412
key = self.mapDBInfo.layers[layer]["key"]
413
columns = self.mapDBInfo.tables[table]
415
for idx in range(len(columns[key]['values'])):
416
for name in columns.keys():
418
cat = int(columns[name]['values'][idx])
421
# use scrolled panel instead (and fix initial max height of the window to 480px)
422
panel = scrolled.ScrolledPanel(parent=self.notebook, id=wx.ID_ANY,
424
panel.SetupScrolling(scroll_x=False)
426
self.notebook.AddPage(page=panel, text=" %s %d / %s %d" % (_("Layer"), layer,
430
border = wx.BoxSizer(wx.VERTICAL)
432
flexSizer = wx.FlexGridSizer (cols=4, hgap=3, vgap=3)
433
flexSizer.AddGrowableCol(3)
434
# columns (sorted by index)
435
names = [''] * len(columns.keys())
436
for name in columns.keys():
437
names[columns[name]['index']] = name
440
if name == key: # skip key column (category)
443
vtype = columns[name]['type']
445
if columns[name]['values'][idx] is not None:
446
if columns[name]['ctype'] != type(''):
447
value = str(columns[name]['values'][idx])
449
value = columns[name]['values'][idx]
453
colName = wx.StaticText(parent=panel, id=wx.ID_ANY,
455
colType = wx.StaticText(parent=panel, id=wx.ID_ANY,
456
label="[" + vtype.lower() + "]")
457
delimiter = wx.StaticText(parent=panel, id=wx.ID_ANY, label=":")
459
colValue = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value=value)
461
colValue.SetName(name)
462
self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
464
flexSizer.Add(colName, proportion=0,
465
flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
466
flexSizer.Add(colType, proportion=0,
467
flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
468
flexSizer.Add(delimiter, proportion=0,
469
flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
470
flexSizer.Add(colValue, proportion=1,
471
flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
472
# add widget reference to self.columns
473
columns[name]['ids'].append(colValue.GetId()) # name, type, values, id
474
# for each attribute (including category) END
475
border.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
476
panel.SetSizer(border)
477
# for each category END
480
if self.notebook.GetPageCount() == 0:
481
self.noFoundMsg.Show(True)
483
self.noFoundMsg.Show(False)
489
def SetColumnValue(self, layer, column, value):
490
"""!Set attrbute value
492
@param column column name
495
table = self.mapDBInfo.GetTable(layer)
496
columns = self.mapDBInfo.GetTableDesc(table)
498
for key, col in columns.iteritems():
500
col['values'] = [col['ctype'](value),]
503
class ModifyTableRecord(wx.Dialog):
504
"""!Dialog for inserting/updating table record"""
505
def __init__(self, parent, id, title, data, keyEditable=(-1, True),
506
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
509
'Data' is a list: [(column, value)]
510
'KeyEditable' (id, editable?) indicates if textarea for key column
511
is editable(True) or not.
513
wx.Dialog.__init__(self, parent, id, title, style=style)
515
self.CenterOnParent()
517
self.keyId = keyEditable[0]
519
self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
521
box = wx.StaticBox(parent=self.panel, id=wx.ID_ANY, label='')
523
self.dataPanel = scrolled.ScrolledPanel(parent=self.panel, id=wx.ID_ANY,
524
style=wx.TAB_TRAVERSAL)
525
self.dataPanel.SetupScrolling(scroll_x=False)
530
self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
531
self.btnSubmit = wx.Button(self.panel, wx.ID_OK, _("Submit"))
532
self.btnSubmit.SetDefault()
541
for column, value in data:
542
if keyEditable[0] == id:
543
self.cat = int(value)
544
if keyEditable[1] == False:
546
box.SetLabel =" %s %d " % (_("Category"), self.cat)
547
self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
551
valueWin = wx.SpinCtrl(parent=self.dataPanel, id=wx.ID_ANY,
552
value=value, min=-1e9, max=1e9, size=(250, -1))
554
valueWin = wx.TextCtrl(parent=self.dataPanel, id=wx.ID_ANY,
555
value=value, size=(250, -1))
557
label = wx.StaticText(parent=self.dataPanel, id=wx.ID_ANY,
560
self.widgets.append((label.GetId(),
567
# winSize = self.GetSize()
568
# fix height of window frame if needed
569
# if winSize[1] > 480:
571
# self.SetSize(winSize)
572
# self.SetMinSize(winSize)
576
sizer = wx.BoxSizer(wx.VERTICAL)
579
dataSizer = wx.FlexGridSizer (cols=2, hgap=3, vgap=3)
580
dataSizer.AddGrowableCol(1)
582
for labelId, valueId in self.widgets:
583
label = self.FindWindowById(labelId)
584
value = self.FindWindowById(valueId)
586
dataSizer.Add(label, proportion=0,
587
flag=wx.ALIGN_CENTER_VERTICAL)
588
dataSizer.Add(value, proportion=0,
589
flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
591
self.dataPanel.SetAutoLayout(True)
592
self.dataPanel.SetSizer(dataSizer)
593
dataSizer.Fit(self.dataPanel)
596
self.boxSizer.Add(item=self.dataPanel, proportion=1,
597
flag=wx.EXPAND | wx.ALL, border=5)
600
btnSizer = wx.StdDialogButtonSizer()
601
btnSizer.AddButton(self.btnCancel)
602
btnSizer.AddButton(self.btnSubmit)
606
sizer.Add(item=self.dataPanel, proportion=1,
607
flag=wx.EXPAND | wx.ALL, border=5)
609
sizer.Add(item=self.boxSizer, proportion=1,
610
flag=wx.EXPAND | wx.ALL, border=5)
613
sizer.Add(item=btnSizer, proportion=0,
614
flag=wx.EXPAND | wx.ALL, border=5)
616
framewidth = self.GetSize()[0]
617
self.SetMinSize((framewidth,150))
618
self.SetMaxSize((framewidth,300))
620
#sizer.SetSizeHints(self.panel)
621
self.panel.SetAutoLayout(True)
622
self.panel.SetSizer(sizer)
623
sizer.Fit(self.panel)
627
# # set window frame size (min & max)
628
# minFrameHeight = 150
629
# maxFrameHeight = 2 * minFrameHeight
630
# if self.GetSize()[1] > minFrameHeight:
631
# print 'size ='+str(self.GetSize()[1])
633
# self.SetMinSize((self.GetSize()[0], minFrameHeight))
636
# self.SetMinSize(self.GetSize())
638
# if self.GetSize()[1] > maxFrameHeight:
640
# self.SetSize((self.GetSize()[0], maxFrameHeight))
643
# self.SetSize(self.panel.GetSize())
647
def GetValues(self, columns=None):
648
"""!Return list of values (casted to string).
650
If columns is given (list), return only values of given columns.
653
for labelId, valueId in self.widgets:
654
column = self.FindWindowById(labelId).GetLabel().replace(':', '')
655
if columns is None or column in columns:
656
value = str(self.FindWindowById(valueId).GetValue())
657
valueList.append(value)
661
valueList.insert(self.keyId, str(self.cat))