GRASS Programmer's Manual
6.4.2(2012)
|
00001 """! 00002 @package dbm.py 00003 00004 @brief GRASS Attribute Table Manager 00005 00006 This program is based on FileHunter, published in 'The wxPython Linux 00007 Tutorial' on wxPython WIKI pages. 00008 00009 It also uses some functions at 00010 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426407 00011 00012 @code 00013 python dbm.py vector@mapset 00014 @endcode 00015 00016 List of classes: 00017 - Log 00018 - VirtualAttributeList 00019 - AttributeManager 00020 00021 (C) 2007-2009, 2011 by the GRASS Development Team 00022 00023 This program is free software under the GNU General Public 00024 License (>=v2). Read the file COPYING that comes with GRASS 00025 for details. 00026 00027 @author Jachym Cepicky <jachym.cepicky gmail.com> 00028 @author Martin Landa <landa.martin gmail.com> 00029 """ 00030 00031 import sys 00032 import os 00033 import locale 00034 import tempfile 00035 import copy 00036 import types 00037 00038 ### i18N 00039 import gettext 00040 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True) 00041 00042 import globalvar 00043 import wx 00044 import wx.lib.mixins.listctrl as listmix 00045 import wx.lib.flatnotebook as FN 00046 00047 import grass.script as grass 00048 00049 import sqlbuilder 00050 import gcmd 00051 import utils 00052 import gdialogs 00053 import dbm_base 00054 from debug import Debug 00055 from dbm_dialogs import ModifyTableRecord 00056 from preferences import globalSettings as UserSettings 00057 00058 class Log: 00059 """ 00060 The log output is redirected to the status bar of the containing frame. 00061 """ 00062 def __init__(self, parent): 00063 self.parent = parent 00064 00065 def write(self, text_string): 00066 """!Update status bar""" 00067 self.parent.SetStatusText(text_string.strip()) 00068 00069 00070 class VirtualAttributeList(wx.ListCtrl, 00071 listmix.ListCtrlAutoWidthMixin, 00072 listmix.ColumnSorterMixin): 00073 """ 00074 Support virtual list class 00075 """ 00076 def __init__(self, parent, log, mapDBInfo, layer): 00077 # 00078 # initialize variables 00079 # 00080 self.parent = parent 00081 self.log = log 00082 self.mapDBInfo = mapDBInfo 00083 self.layer = layer 00084 00085 self.columns = {} # <- LoadData() 00086 00087 wx.ListCtrl.__init__(self, parent=parent, id=wx.ID_ANY, 00088 style=wx.LC_REPORT | wx.LC_HRULES | 00089 wx.LC_VRULES | wx.LC_VIRTUAL | wx.LC_SORT_ASCENDING) 00090 00091 try: 00092 keyColumn = self.LoadData(layer) 00093 except gcmd.GException, e: 00094 GError(parent = self, 00095 message = e.value) 00096 return 00097 00098 # 00099 # add some attributes (colourful background for each item rows) 00100 # 00101 self.attr1 = wx.ListItemAttr() 00102 self.attr1.SetBackgroundColour(wx.Colour(238,238,238)) 00103 self.attr2 = wx.ListItemAttr() 00104 self.attr2.SetBackgroundColour("white") 00105 self.il = wx.ImageList(16, 16) 00106 self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP, wx.ART_TOOLBAR, 00107 (16,16))) 00108 self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR, 00109 (16,16))) 00110 self.SetImageList(self.il, wx.IMAGE_LIST_SMALL) 00111 00112 # setup mixins 00113 listmix.ListCtrlAutoWidthMixin.__init__(self) 00114 listmix.ColumnSorterMixin.__init__(self, len(self.columns)) 00115 00116 # sort item by category (id) 00117 if keyColumn > -1: 00118 self.SortListItems(col=keyColumn, ascending=True) 00119 else: 00120 self.SortListItems(col=0, ascending=True) 00121 00122 # events 00123 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) 00124 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) 00125 self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColumnSort) 00126 self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu) 00127 00128 def Update(self, mapDBInfo): 00129 """!Update list according new mapDBInfo description""" 00130 self.mapDBInfo = mapDBInfo 00131 self.LoadData(self.layer) 00132 00133 def LoadData(self, layer, columns=None, where=None, sql=None): 00134 """!Load data into list 00135 00136 @param layer layer number 00137 @param columns list of columns for output (-> v.db.select) 00138 @param where where statement (-> v.db.select) 00139 @param sql full sql statement (-> db.select) 00140 00141 @return id of key column 00142 @return -1 if key column is not displayed 00143 """ 00144 self.log.write(_("Loading data...")) 00145 00146 tableName = self.mapDBInfo.layers[layer]['table'] 00147 keyColumn = self.mapDBInfo.layers[layer]['key'] 00148 try: 00149 self.columns = self.mapDBInfo.tables[tableName] 00150 except KeyError: 00151 raise gcmd.GException(_("Attribute table <%s> not found. " 00152 "For creating the table switch to " 00153 "'Manage layers' tab.") % tableName) 00154 00155 if not columns: 00156 columns = self.mapDBInfo.GetColumns(tableName) 00157 else: 00158 all = self.mapDBInfo.GetColumns(tableName) 00159 for col in columns: 00160 if col not in all: 00161 wx.MessageBox(parent=self, 00162 message=_("Column <%(column)s> not found in " 00163 "in the table <%(table)s>.") % \ 00164 { 'column' : col, 'table' : tableName }, 00165 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 00166 return 00167 00168 try: 00169 # for maps connected via v.external 00170 keyId = columns.index(keyColumn) 00171 except: 00172 keyId = -1 00173 00174 # 00175 # read data 00176 # 00177 # FIXME: Max. number of rows, while the GUI is still usable 00178 00179 # stdout can be very large, do not use PIPE, redirect to temp file 00180 # TODO: more effective way should be implemented... 00181 outFile = tempfile.NamedTemporaryFile(mode='w+b') 00182 00183 if sql: 00184 ret = gcmd.RunCommand('db.select', 00185 quiet = True, 00186 parent = self, 00187 flags = 'c', 00188 sql = sql, 00189 output = outFile.name) 00190 else: 00191 if columns: 00192 ret = gcmd.RunCommand('v.db.select', 00193 quiet = True, 00194 flags = 'c', 00195 map = self.mapDBInfo.map, 00196 layer = layer, 00197 columns = ','.join(columns), 00198 where = where, 00199 stdout=outFile) 00200 else: 00201 ret = gcmd.RunCommand('v.db.select', 00202 quiet = True, 00203 flags = 'c', 00204 map = self.mapDBInfo.map, 00205 layer = layer, 00206 where = where, 00207 stdout=outFile) 00208 00209 # These two should probably be passed to init more cleanly 00210 # setting the numbers of items = number of elements in the dictionary 00211 self.itemDataMap = {} 00212 self.itemIndexMap = [] 00213 self.itemCatsMap = {} 00214 00215 self.DeleteAllItems() 00216 00217 # self.ClearAll() 00218 for i in range(self.GetColumnCount()): 00219 self.DeleteColumn(0) 00220 00221 i = 0 00222 info = wx.ListItem() 00223 info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT 00224 info.m_image = -1 00225 info.m_format = 0 00226 for column in columns: 00227 info.m_text = column 00228 self.InsertColumnInfo(i, info) 00229 i += 1 00230 00231 if i >= 256: 00232 self.log.write(_("Can display only 256 columns.")) 00233 00234 i = 0 00235 outFile.seek(0) 00236 00237 while True: 00238 # os.linesep doesn't work here (MSYS) 00239 record = outFile.readline().replace('\n', '') 00240 00241 if not record: 00242 break 00243 00244 self.AddDataRow(i, record, columns, keyId) 00245 00246 i += 1 00247 if i >= 100000: 00248 self.log.write(_("Limit 100000 records.")) 00249 break 00250 00251 self.SetItemCount(i) 00252 00253 i = 0 00254 for col in columns: 00255 width = self.columns[col]['length'] * 6 # FIXME 00256 if width < 60: 00257 width = 60 00258 if width > 300: 00259 width = 300 00260 self.SetColumnWidth(col=i, width=width) 00261 i += 1 00262 00263 self.SendSizeEvent() 00264 00265 self.log.write(_("Number of loaded records: %d") % \ 00266 self.GetItemCount()) 00267 00268 return keyId 00269 00270 def AddDataRow(self, i, record, columns, keyId): 00271 """!Add row to the data list""" 00272 self.itemDataMap[i] = [] 00273 keyColumn = self.mapDBInfo.layers[self.layer]['key'] 00274 j = 0 00275 cat = None 00276 00277 if keyColumn == 'OGC_FID': 00278 self.itemDataMap[i].append(i+1) 00279 j += 1 00280 cat = i + 1 00281 00282 for value in record.split('|'): 00283 if self.columns[columns[j]]['ctype'] != types.StringType: 00284 try: 00285 ### casting disabled (2009/03) 00286 ### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value)) 00287 self.itemDataMap[i].append(value) 00288 except ValueError: 00289 self.itemDataMap[i].append(_('Unknown value')) 00290 else: 00291 # encode string values 00292 try: 00293 self.itemDataMap[i].append(dbm_base.unicodeValue(value)) 00294 except UnicodeDecodeError: 00295 self.itemDataMap[i].append(_("Unable to decode value. " 00296 "Set encoding in GUI preferences ('Attributes').")) 00297 00298 if not cat and keyId > -1 and keyId == j: 00299 try: 00300 cat = self.columns[columns[j]]['ctype'] (value) 00301 except ValueError, e: 00302 cat = -1 00303 gcmd.GError(parent = self, 00304 message=_("Error loading attribute data. " 00305 "Record number: %(rec)d. Unable to convert value '%(val)s' in " 00306 "key column (%(key)s) to integer.\n\n" 00307 "Details: %(detail)s") % \ 00308 { 'rec' : i + 1, 'val' : value, 00309 'key' : keyColumn, 'detail' : e}) 00310 j += 1 00311 00312 self.itemIndexMap.append(i) 00313 if keyId > -1: # load cats only when LoadData() is called first time 00314 self.itemCatsMap[i] = cat 00315 00316 def OnItemSelected(self, event): 00317 """!Item selected. Add item to selected cats...""" 00318 # cat = int(self.GetItemText(event.m_itemIndex)) 00319 # if cat not in self.selectedCats: 00320 # self.selectedCats.append(cat) 00321 # self.selectedCats.sort() 00322 00323 event.Skip() 00324 00325 def OnItemDeselected(self, event): 00326 """!Item deselected. Remove item from selected cats...""" 00327 # cat = int(self.GetItemText(event.m_itemIndex)) 00328 # if cat in self.selectedCats: 00329 # self.selectedCats.remove(cat) 00330 # self.selectedCats.sort() 00331 00332 event.Skip() 00333 00334 def GetSelectedItems(self): 00335 """!Return list of selected items (category numbers)""" 00336 cats = [] 00337 item = self.GetFirstSelected() 00338 while item != -1: 00339 cats.append(self.GetItemText(item)) 00340 item = self.GetNextSelected(item) 00341 00342 return cats 00343 00344 def GetColumnText(self, index, col): 00345 """!Return column text""" 00346 item = self.GetItem(index, col) 00347 return item.GetText() 00348 00349 def GetListCtrl(self): 00350 """!Returt list""" 00351 return self 00352 00353 def OnGetItemText(self, item, col): 00354 """!Get item text""" 00355 index = self.itemIndexMap[item] 00356 s = self.itemDataMap[index][col] 00357 return s 00358 00359 def OnGetItemAttr(self, item): 00360 """!Get item attributes""" 00361 if ( item % 2) == 0: 00362 return self.attr2 00363 else: 00364 return self.attr1 00365 00366 def OnColumnMenu(self, event): 00367 """!Column heading right mouse button -> pop-up menu""" 00368 self._col = event.GetColumn() 00369 00370 popupMenu = wx.Menu() 00371 00372 if not hasattr (self, "popupID1"): 00373 self.popupID1 = wx.NewId() 00374 self.popupID2 = wx.NewId() 00375 self.popupID3 = wx.NewId() 00376 self.popupID4 = wx.NewId() 00377 self.popupID5 = wx.NewId() 00378 self.popupID6 = wx.NewId() 00379 self.popupID7 = wx.NewId() 00380 self.popupID8 = wx.NewId() 00381 self.popupID9 = wx.NewId() 00382 self.popupID10 = wx.NewId() 00383 self.popupID11 = wx.NewId() 00384 self.popupID12 = wx.NewId() 00385 00386 popupMenu.Append(self.popupID1, text=_("Sort ascending")) 00387 popupMenu.Append(self.popupID2, text=_("Sort descending")) 00388 popupMenu.AppendSeparator() 00389 subMenu = wx.Menu() 00390 popupMenu.AppendMenu(self.popupID3, _("Calculate (only numeric columns)"), 00391 subMenu) 00392 if not self.log.parent.editable or \ 00393 self.columns[self.GetColumn(self._col).GetText()]['ctype'] not in (types.IntType, types.FloatType): 00394 popupMenu.Enable(self.popupID3, False) 00395 00396 subMenu.Append(self.popupID4, text=_("Area size")) 00397 subMenu.Append(self.popupID5, text=_("Line length")) 00398 subMenu.Append(self.popupID6, text=_("Compactness of an area")) 00399 subMenu.Append(self.popupID7, text=_("Fractal dimension of boundary defining a polygon")) 00400 subMenu.Append(self.popupID8, text=_("Perimeter length of an area")) 00401 subMenu.Append(self.popupID9, text=_("Number of features for each category")) 00402 subMenu.Append(self.popupID10, text=_("Slope steepness of 3D line")) 00403 subMenu.Append(self.popupID11, text=_("Line sinuousity")) 00404 subMenu.Append(self.popupID12, text=_("Line azimuth")) 00405 00406 self.Bind (wx.EVT_MENU, self.OnColumnSortAsc, id=self.popupID1) 00407 self.Bind (wx.EVT_MENU, self.OnColumnSortDesc, id=self.popupID2) 00408 for id in (self.popupID4, self.popupID5, self.popupID6, 00409 self.popupID7, self.popupID8, self.popupID9, 00410 self.popupID10, self.popupID11, self.popupID12): 00411 self.Bind(wx.EVT_MENU, self.OnColumnCompute, id = id) 00412 00413 self.PopupMenu(popupMenu) 00414 popupMenu.Destroy() 00415 00416 def OnColumnSort(self, event): 00417 """!Column heading left mouse button -> sorting""" 00418 self._col = event.GetColumn() 00419 00420 self.ColumnSort() 00421 00422 event.Skip() 00423 00424 def OnColumnSortAsc(self, event): 00425 """!Sort values of selected column (ascending)""" 00426 self.SortListItems(col = self._col, ascending = True) 00427 event.Skip() 00428 00429 def OnColumnSortDesc(self, event): 00430 """!Sort values of selected column (descending)""" 00431 self.SortListItems(col = self._col, ascending = False) 00432 event.Skip() 00433 00434 def OnColumnCompute(self, event): 00435 """!Compute values of selected column""" 00436 id = event.GetId() 00437 00438 option = None 00439 if id == self.popupID4: 00440 option = 'area' 00441 elif id == self.popupID5: 00442 option = 'length' 00443 elif id == self.popupID6: 00444 option = 'compact' 00445 elif id == self.popupID7: 00446 option = 'fd' 00447 elif id == self.popupID8: 00448 option = 'perimeter' 00449 elif id == self.popupID9: 00450 option = 'count' 00451 elif id == self.popupID10: 00452 option = 'slope' 00453 elif id == self.popupID11: 00454 option = 'sinuous' 00455 elif id == self.popupID12: 00456 option = 'azimuth' 00457 00458 if not option: 00459 return 00460 00461 gcmd.RunCommand('v.to.db', 00462 parent = self.parent, 00463 map = self.mapDBInfo.map, 00464 layer = self.layer, 00465 option = option, 00466 columns = self.GetColumn(self._col).GetText()) 00467 00468 self.LoadData(self.layer) 00469 00470 def ColumnSort(self): 00471 """!Sort values of selected column (self._col)""" 00472 # remove duplicated arrow symbol from column header 00473 # FIXME: should be done automatically 00474 info = wx.ListItem() 00475 info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE 00476 info.m_image = -1 00477 for column in range(self.GetColumnCount()): 00478 info.m_text = self.GetColumn(column).GetText() 00479 self.SetColumn(column, info) 00480 00481 def SortItems(self, sorter=cmp): 00482 """!Sort items""" 00483 items = list(self.itemDataMap.keys()) 00484 items.sort(self.Sorter) 00485 self.itemIndexMap = items 00486 00487 # redraw the list 00488 self.Refresh() 00489 00490 def Sorter(self, key1, key2): 00491 colName = self.GetColumn(self._col).GetText() 00492 ascending = self._colSortFlag[self._col] 00493 try: 00494 item1 = self.columns[colName]["ctype"](self.itemDataMap[key1][self._col]) 00495 item2 = self.columns[colName]["ctype"](self.itemDataMap[key2][self._col]) 00496 except ValueError: 00497 item1 = self.itemDataMap[key1][self._col] 00498 item2 = self.itemDataMap[key2][self._col] 00499 00500 if type(item1) == type('') or type(item2) == type(''): 00501 cmpVal = locale.strcoll(str(item1), str(item2)) 00502 else: 00503 cmpVal = cmp(item1, item2) 00504 00505 00506 # If the items are equal then pick something else to make the sort value unique 00507 if cmpVal == 0: 00508 cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2)) 00509 00510 if ascending: 00511 return cmpVal 00512 else: 00513 return -cmpVal 00514 00515 def GetSortImages(self): 00516 """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py""" 00517 return (self.sm_dn, self.sm_up) 00518 00519 def IsEmpty(self): 00520 """!Check if list if empty""" 00521 if self.columns: 00522 return False 00523 00524 return True 00525 00526 class AttributeManager(wx.Frame): 00527 """ 00528 GRASS Attribute manager main window 00529 """ 00530 def __init__(self, parent, id=wx.ID_ANY, 00531 size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, 00532 title=None, vectorName=None, item=None, log=None, selection = 0): 00533 00534 self.vectorName = vectorName 00535 self.parent = parent # GMFrame 00536 self.treeItem = item # item in layer tree 00537 if self.parent and self.parent.GetName() == "LayerManager" and \ 00538 self.treeItem and not self.vectorName: 00539 maptree = self.parent.curr_page.maptree 00540 name = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetName() 00541 self.vectorName = name 00542 00543 # vector attributes can be changed only if vector map is in 00544 # the current mapset 00545 if grass.find_file(name = self.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']: 00546 self.editable = True 00547 else: 00548 self.editable = False 00549 00550 # FIXME: editing is currently broken on wingrass (bug #1270) 00551 if sys.platform == 'win32': 00552 self.editable = False 00553 00554 self.cmdLog = log # self.parent.goutput 00555 00556 wx.Frame.__init__(self, parent, id, style=style) 00557 00558 # title 00559 if not title: 00560 self.SetTitle("%s - <%s>" % (_("GRASS GIS Attribute Table Manager"), 00561 self.vectorName)) 00562 else: 00563 self.SetTitle(title) 00564 00565 # icon 00566 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO)) 00567 00568 self.panel = wx.Panel(parent=self, id=wx.ID_ANY) 00569 00570 try: 00571 self.map = self.parent.curr_page.maptree.Map 00572 self.mapdisplay = self.parent.curr_page.maptree.mapdisplay 00573 except: 00574 self.map = self.mapdisplay = None 00575 00576 # status bar log class 00577 self.log = Log(self) # -> statusbar 00578 00579 # query map layer (if parent (GMFrame) is given) 00580 self.qlayer = None 00581 00582 # -> layers / tables description 00583 self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName) 00584 00585 # sqlbuilder 00586 self.builder = None 00587 00588 if len(self.mapDBInfo.layers.keys()) == 0: 00589 wx.MessageBox(parent=self.parent, 00590 message=_("Database connection for vector map <%s> " 00591 "is not defined in DB file. " 00592 "You can define new connection in " 00593 "'Manage layers' tab.") % self.vectorName, 00594 caption=_("Attribute Table Manager"), 00595 style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE) 00596 00597 # 00598 # list of command/SQL statements to be performed 00599 # 00600 self.listOfCommands = [] 00601 self.listOfSQLStatements = [] 00602 00603 self.CreateStatusBar(number=1) 00604 00605 # set up virtual lists (each layer) 00606 ### {layer: list, widgets...} 00607 self.layerPage = {} 00608 00609 # auinotebook (browse, manage, settings) 00610 #self.notebook = wx.aui.AuiNotebook(parent=self, id=wx.ID_ANY, 00611 # style=wx.aui.AUI_NB_BOTTOM) 00612 # really needed (ML) 00613 # self.notebook.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, '')) 00614 00615 if globalvar.hasAgw: 00616 self.notebook = FN.FlatNotebook(parent = self.panel, id = wx.ID_ANY, 00617 agwStyle = FN.FNB_BOTTOM | 00618 FN.FNB_NO_NAV_BUTTONS | 00619 FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON) 00620 else: 00621 self.notebook = FN.FlatNotebook(parent = self.panel, id = wx.ID_ANY, 00622 style = FN.FNB_BOTTOM | 00623 FN.FNB_NO_NAV_BUTTONS | 00624 FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON) 00625 00626 if globalvar.hasAgw: 00627 dbmStyle = { 'agwStyle' : globalvar.FNPageStyle } 00628 else: 00629 dbmStyle = { 'style' : globalvar.FNPageStyle } 00630 00631 self.browsePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY, 00632 **dbmStyle) 00633 # self.notebook.AddPage(self.browsePage, caption=_("Browse data")) 00634 self.notebook.AddPage(self.browsePage, text=_("Browse data")) # FN 00635 self.browsePage.SetTabAreaColour(globalvar.FNPageColor) 00636 00637 self.manageTablePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY, 00638 **dbmStyle) 00639 #self.notebook.AddPage(self.manageTablePage, caption=_("Manage tables")) 00640 self.notebook.AddPage(self.manageTablePage, text=_("Manage tables")) # FN 00641 if not self.editable: 00642 self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False) 00643 self.manageTablePage.SetTabAreaColour(globalvar.FNPageColor) 00644 00645 self.manageLayerPage = FN.FlatNotebook(self.panel, id = wx.ID_ANY, 00646 **dbmStyle) 00647 #self.notebook.AddPage(self.manageLayerPage, caption=_("Manage layers")) 00648 self.notebook.AddPage(self.manageLayerPage, text=_("Manage layers")) # FN 00649 self.manageLayerPage.SetTabAreaColour(globalvar.FNPageColor) 00650 if not self.editable: 00651 self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False) 00652 00653 self.__createBrowsePage() 00654 self.__createManageTablePage() 00655 self.__createManageLayerPage() 00656 wx.CallAfter(self.notebook.SetSelection, selection) 00657 00658 # 00659 # buttons 00660 # 00661 self.btnQuit = wx.Button(parent=self.panel, id=wx.ID_EXIT) 00662 self.btnQuit.SetToolTipString(_("Close Attribute Table Manager")) 00663 self.btnReload = wx.Button(parent=self.panel, id=wx.ID_REFRESH) 00664 self.btnReload.SetToolTipString(_("Reload attribute data (selected layer only)")) 00665 00666 # events 00667 self.btnQuit.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 00668 self.btnReload.Bind(wx.EVT_BUTTON, self.OnDataReload) 00669 self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged) 00670 self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.browsePage) 00671 self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.manageTablePage) 00672 00673 # do layout 00674 self.__layout() 00675 00676 # self.SetMinSize(self.GetBestSize()) 00677 self.SetSize((680, 550)) # FIXME hard-coded size 00678 self.SetMinSize(self.GetSize()) 00679 00680 def __createBrowsePage(self, onlyLayer=-1): 00681 """!Create browse tab page""" 00682 for layer in self.mapDBInfo.layers.keys(): 00683 if onlyLayer > 0 and layer != onlyLayer: 00684 continue 00685 00686 panel = wx.Panel(parent=self.browsePage, id=wx.ID_ANY) 00687 00688 #IMPORTANT NOTE: wx.StaticBox MUST be defined BEFORE any of the 00689 # controls that are placed IN the wx.StaticBox, or it will freeze 00690 # on the Mac 00691 00692 listBox = wx.StaticBox(parent=panel, id=wx.ID_ANY, 00693 label=" %s " % _("Attribute data - right-click to edit/manage records")) 00694 listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL) 00695 00696 win = VirtualAttributeList(panel, self.log, 00697 self.mapDBInfo, layer) 00698 if win.IsEmpty(): 00699 del panel 00700 continue 00701 00702 win.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDataItemActivated) 00703 00704 self.layerPage[layer] = {'browsePage': panel.GetId()} 00705 00706 self.browsePage.AddPage(page=panel, text=" %d / %s %s" % \ 00707 (layer, _("Table"), self.mapDBInfo.layers[layer]['table'])) 00708 00709 pageSizer = wx.BoxSizer(wx.VERTICAL) 00710 00711 # attribute data 00712 sqlBox = wx.StaticBox(parent=panel, id=wx.ID_ANY, 00713 label=" %s " % _("SQL Query")) 00714 00715 sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL) 00716 00717 win.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnDataRightUp) #wxMSW 00718 win.Bind(wx.EVT_RIGHT_UP, self.OnDataRightUp) #wxGTK 00719 if UserSettings.Get(group='atm', key='leftDbClick', subkey='selection') == 0: 00720 win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataItemEdit) 00721 win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataItemEdit) 00722 else: 00723 win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataDrawSelected) 00724 win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataDrawSelected) 00725 00726 00727 listSizer.Add(item=win, proportion=1, 00728 flag=wx.EXPAND | wx.ALL, 00729 border=3) 00730 00731 # sql statement box 00732 btnApply = wx.Button(parent=panel, id=wx.ID_APPLY) 00733 btnApply.SetToolTipString(_("Apply SELECT statement and reload data records")) 00734 btnApply.Bind(wx.EVT_BUTTON, self.OnApplySqlStatement) 00735 btnSqlBuilder = wx.Button(parent=panel, id=wx.ID_ANY, label=_("SQL Builder")) 00736 btnSqlBuilder.Bind(wx.EVT_BUTTON, self.OnBuilder) 00737 00738 sqlSimple = wx.RadioButton(parent=panel, id=wx.ID_ANY, 00739 label=_("Simple")) 00740 sqlSimple.SetValue(True) 00741 sqlAdvanced = wx.RadioButton(parent=panel, id=wx.ID_ANY, 00742 label=_("Advanced")) 00743 sqlSimple.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql) 00744 sqlAdvanced.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql) 00745 00746 sqlWhereColumn = wx.Choice(parent=panel, id=wx.ID_ANY, 00747 size=(100,-1), 00748 choices=self.mapDBInfo.GetColumns(self.mapDBInfo.layers[layer]['table'])) 00749 sqlWhereValue = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value="", 00750 style=wx.TE_PROCESS_ENTER) 00751 sqlWhereValue.SetToolTipString(_("Example: %s") % "MULTILANE = 'no' AND OBJECTID < 10") 00752 00753 sqlStatement = wx.TextCtrl(parent=panel, id=wx.ID_ANY, 00754 value="SELECT * FROM %s" % \ 00755 self.mapDBInfo.layers[layer]['table'], 00756 style=wx.TE_PROCESS_ENTER) 00757 sqlStatement.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' AND OBJECTID < 10") 00758 sqlWhereValue.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement) 00759 sqlStatement.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement) 00760 00761 sqlLabel = wx.StaticText(parent=panel, id=wx.ID_ANY, 00762 label="SELECT * FROM %s WHERE " % \ 00763 self.mapDBInfo.layers[layer]['table']) 00764 label_query = wx.StaticText(parent=panel, id=wx.ID_ANY, 00765 label="") 00766 00767 sqlFlexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5) 00768 sqlFlexSizer.AddGrowableCol(1) 00769 00770 sqlFlexSizer.Add(item=sqlSimple, 00771 flag=wx.ALIGN_CENTER_VERTICAL) 00772 sqlSimpleSizer = wx.BoxSizer(wx.HORIZONTAL) 00773 sqlSimpleSizer.Add(item=sqlLabel, 00774 flag=wx.ALIGN_CENTER_VERTICAL) 00775 sqlSimpleSizer.Add(item=sqlWhereColumn, 00776 flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL) 00777 sqlSimpleSizer.Add(item=sqlWhereValue, proportion=1, 00778 flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL) 00779 sqlFlexSizer.Add(item=sqlSimpleSizer, 00780 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) 00781 sqlFlexSizer.Add(item=btnApply, 00782 flag=wx.ALIGN_RIGHT) 00783 sqlFlexSizer.Add(item=sqlAdvanced, 00784 flag=wx.ALIGN_CENTER_VERTICAL) 00785 sqlFlexSizer.Add(item=sqlStatement, 00786 flag=wx.EXPAND) 00787 sqlFlexSizer.Add(item=btnSqlBuilder, 00788 flag=wx.ALIGN_RIGHT) 00789 00790 sqlSizer.Add(item=sqlFlexSizer, 00791 flag=wx.ALL | wx.EXPAND, 00792 border=3) 00793 00794 pageSizer.Add(item=listSizer, 00795 proportion=1, 00796 flag=wx.ALL | wx.EXPAND, 00797 border=5) 00798 00799 pageSizer.Add(item=sqlSizer, 00800 proportion=0, 00801 flag=wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND, 00802 border=5) 00803 00804 panel.SetSizer(pageSizer) 00805 00806 self.layerPage[layer]['data'] = win.GetId() 00807 self.layerPage[layer]['simple'] = sqlSimple.GetId() 00808 self.layerPage[layer]['advanced'] = sqlAdvanced.GetId() 00809 self.layerPage[layer]['whereColumn'] = sqlWhereColumn.GetId() 00810 self.layerPage[layer]['where'] = sqlWhereValue.GetId() 00811 self.layerPage[layer]['builder'] = btnSqlBuilder.GetId() 00812 self.layerPage[layer]['statement'] = sqlStatement.GetId() 00813 00814 00815 self.browsePage.SetSelection(0) # select first layer 00816 try: 00817 self.layer = self.mapDBInfo.layers.keys()[0] 00818 self.OnChangeSql(None) 00819 self.log.write(_("Number of loaded records: %d") % \ 00820 self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount()) 00821 except (IndexError, KeyError): 00822 self.layer = None 00823 00824 def __createManageTablePage(self, onlyLayer=-1): 00825 """!Create manage page (create/link and alter tables)""" 00826 for layer in self.mapDBInfo.layers.keys(): 00827 if onlyLayer > 0 and layer != onlyLayer: 00828 continue 00829 00830 if not layer in self.layerPage: 00831 continue 00832 00833 panel = wx.Panel(parent=self.manageTablePage, id=wx.ID_ANY) 00834 self.layerPage[layer]['tablePage'] = panel.GetId() 00835 self.manageTablePage.AddPage(page=panel, 00836 text=" %d / %s %s" % (layer, 00837 _("Table"), 00838 self.mapDBInfo.layers[layer]['table'])) 00839 00840 pageSizer = wx.BoxSizer(wx.VERTICAL) 00841 00842 # 00843 # dbInfo 00844 # 00845 dbBox = wx.StaticBox(parent=panel, id=wx.ID_ANY, 00846 label=" %s " % _("Database connection")) 00847 dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL) 00848 dbSizer.Add(item=dbm_base.createDbInfoDesc(panel, self.mapDBInfo, layer), 00849 proportion=1, 00850 flag=wx.EXPAND | wx.ALL, 00851 border=3) 00852 00853 # 00854 # table description 00855 # 00856 table = self.mapDBInfo.layers[layer]['table'] 00857 tableBox = wx.StaticBox(parent=panel, id=wx.ID_ANY, 00858 label=" %s " % _("Table <%s> - right-click to delete column(s)") % table) 00859 00860 tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL) 00861 00862 list = self.__createTableDesc(panel, table) 00863 list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnTableRightUp) #wxMSW 00864 list.Bind(wx.EVT_RIGHT_UP, self.OnTableRightUp) #wxGTK 00865 self.layerPage[layer]['tableData'] = list.GetId() 00866 00867 # manage columns (add) 00868 addBox = wx.StaticBox(parent = panel, id = wx.ID_ANY, 00869 label = " %s " % _("Add column")) 00870 addSizer = wx.StaticBoxSizer(addBox, wx.HORIZONTAL) 00871 00872 column = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '', 00873 size=(150, -1), style = wx.TE_PROCESS_ENTER) 00874 column.Bind(wx.EVT_TEXT, self.OnTableAddColumnName) 00875 column.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemAdd) 00876 self.layerPage[layer]['addColName'] = column.GetId() 00877 addSizer.Add(item= wx.StaticText(parent = panel, id = wx.ID_ANY, label=_("Column")), 00878 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00879 border = 5) 00880 addSizer.Add(item = column, proportion = 1, 00881 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00882 border = 5) 00883 00884 ctype = wx.Choice (parent=panel, id=wx.ID_ANY, 00885 choices = ["integer", 00886 "double", 00887 "varchar", 00888 "date"]) # FIXME 00889 ctype.SetSelection(0) 00890 ctype.Bind(wx.EVT_CHOICE, self.OnTableChangeType) 00891 self.layerPage[layer]['addColType'] = ctype.GetId() 00892 addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label=_("Type")), 00893 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00894 border = 5) 00895 addSizer.Add(item = ctype, 00896 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00897 border = 5) 00898 00899 length = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), 00900 initial = 250, 00901 min = 1, max = 1e6) 00902 length.Enable(False) 00903 self.layerPage[layer]['addColLength'] = length.GetId() 00904 addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Length")), 00905 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00906 border = 5) 00907 addSizer.Add(item = length, 00908 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00909 border = 5) 00910 00911 btnAddCol = wx.Button(parent = panel, id = wx.ID_ADD) 00912 btnAddCol.Bind(wx.EVT_BUTTON, self.OnTableItemAdd) 00913 btnAddCol.Enable(False) 00914 self.layerPage[layer]['addColButton'] = btnAddCol.GetId() 00915 addSizer.Add(item = btnAddCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND, 00916 border = 3) 00917 00918 # manage columns (rename) 00919 renameBox = wx.StaticBox(parent = panel, id = wx.ID_ANY, 00920 label = " %s " % _("Rename column")) 00921 renameSizer = wx.StaticBoxSizer(renameBox, wx.HORIZONTAL) 00922 00923 column = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (150, -1), 00924 style = wx.CB_SIMPLE | wx.CB_READONLY, 00925 choices = self.mapDBInfo.GetColumns(table)) 00926 column.SetSelection(0) 00927 self.layerPage[layer]['renameCol'] = column.GetId() 00928 renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")), 00929 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00930 border = 5) 00931 renameSizer.Add(item = column, proportion = 1, 00932 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00933 border = 5) 00934 00935 columnTo = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '', 00936 size = (150, -1), style = wx.TE_PROCESS_ENTER) 00937 columnTo.Bind(wx.EVT_TEXT, self.OnTableRenameColumnName) 00938 columnTo.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemChange) 00939 self.layerPage[layer]['renameColTo'] = columnTo.GetId() 00940 renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("To")), 00941 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00942 border = 5) 00943 renameSizer.Add(item = columnTo, proportion = 1, 00944 flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 00945 border = 5) 00946 00947 btnRenameCol = wx.Button(parent = panel, id = wx.ID_ANY, label = _("&Rename")) 00948 btnRenameCol.Bind(wx.EVT_BUTTON, self.OnTableItemChange) 00949 btnRenameCol.Enable(False) 00950 self.layerPage[layer]['renameColButton'] = btnRenameCol.GetId() 00951 renameSizer.Add(item = btnRenameCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND, 00952 border = 3) 00953 00954 tableSizer.Add(item = list, 00955 flag = wx.ALL | wx.EXPAND, 00956 proportion = 1, 00957 border = 3) 00958 00959 pageSizer.Add(item=dbSizer, 00960 flag = wx.ALL | wx.EXPAND, 00961 proportion = 0, 00962 border = 3) 00963 00964 pageSizer.Add(item = tableSizer, 00965 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 00966 proportion = 1, 00967 border = 3) 00968 00969 pageSizer.Add(item = addSizer, 00970 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 00971 proportion = 0, 00972 border = 3) 00973 pageSizer.Add(item = renameSizer, 00974 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 00975 proportion = 0, 00976 border = 3) 00977 00978 panel.SetSizer(pageSizer) 00979 00980 self.manageTablePage.SetSelection(0) # select first layer 00981 try: 00982 self.layer = self.mapDBInfo.layers.keys()[0] 00983 except IndexError: 00984 self.layer = None 00985 00986 def __createTableDesc(self, parent, table): 00987 """!Create list with table description""" 00988 list = TableListCtrl(parent=parent, id=wx.ID_ANY, 00989 table=self.mapDBInfo.tables[table], 00990 columns=self.mapDBInfo.GetColumns(table)) 00991 list.Populate() 00992 # sorter 00993 # itemDataMap = list.Populate() 00994 # listmix.ColumnSorterMixin.__init__(self, 2) 00995 00996 return list 00997 00998 def __createManageLayerPage(self): 00999 """!Create manage page""" 01000 splitterWin = wx.SplitterWindow(parent=self.manageLayerPage, id=wx.ID_ANY) 01001 splitterWin.SetMinimumPaneSize(100) 01002 01003 self.manageLayerPage.AddPage(page=splitterWin, 01004 text=_("Layers of vector map")) # dummy page 01005 01006 # 01007 # list of layers 01008 # 01009 panelList = wx.Panel(parent=splitterWin, id=wx.ID_ANY) 01010 01011 panelListSizer = wx.BoxSizer(wx.VERTICAL) 01012 layerBox = wx.StaticBox(parent=panelList, id=wx.ID_ANY, 01013 label=" %s " % _("List of layers")) 01014 layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL) 01015 01016 self.layerList = self.__createLayerDesc(panelList) 01017 self.layerList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnLayerRightUp) #wxMSW 01018 self.layerList.Bind(wx.EVT_RIGHT_UP, self.OnLayerRightUp) #wxGTK 01019 01020 layerSizer.Add(item=self.layerList, 01021 flag=wx.ALL | wx.EXPAND, 01022 proportion=1, 01023 border=3) 01024 01025 panelListSizer.Add(item=layerSizer, 01026 flag=wx.ALL | wx.EXPAND, 01027 proportion=1, 01028 border=3) 01029 01030 panelList.SetSizer(panelListSizer) 01031 01032 # 01033 # manage part 01034 # 01035 panelManage = wx.Panel(parent=splitterWin, id=wx.ID_ANY) 01036 01037 manageSizer = wx.BoxSizer(wx.VERTICAL) 01038 01039 self.manageLayerBook = LayerBook(parent=panelManage, id=wx.ID_ANY, 01040 parentDialog=self) 01041 01042 manageSizer.Add(item=self.manageLayerBook, 01043 proportion=1, 01044 flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 01045 border=5) 01046 01047 panelManage.SetSizer(manageSizer) 01048 splitterWin.SplitHorizontally(panelList, panelManage, 100) 01049 splitterWin.Fit() 01050 01051 def __createLayerDesc(self, parent): 01052 """!Create list of linked layers""" 01053 list = LayerListCtrl(parent=parent, id=wx.ID_ANY, 01054 layers=self.mapDBInfo.layers) 01055 01056 list.Populate() 01057 # sorter 01058 # itemDataMap = list.Populate() 01059 # listmix.ColumnSorterMixin.__init__(self, 2) 01060 01061 return list 01062 01063 def __layout(self): 01064 """!Do layout""" 01065 # frame body 01066 mainSizer = wx.BoxSizer(wx.VERTICAL) 01067 01068 # buttons 01069 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 01070 btnSizer.Add(item=self.btnReload, proportion=1, 01071 flag=wx.ALL | wx.ALIGN_RIGHT, border=5) 01072 btnSizer.Add(item=self.btnQuit, proportion=1, 01073 flag=wx.ALL | wx.ALIGN_RIGHT, border=5) 01074 01075 mainSizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND) 01076 mainSizer.Add(item=btnSizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) 01077 01078 self.panel.SetAutoLayout(True) 01079 self.panel.SetSizer(mainSizer) 01080 mainSizer.Fit(self.panel) 01081 self.Layout() 01082 01083 def OnDataRightUp(self, event): 01084 """!Table description area, context menu""" 01085 if not hasattr(self, "popupDataID1"): 01086 self.popupDataID1 = wx.NewId() 01087 self.popupDataID2 = wx.NewId() 01088 self.popupDataID3 = wx.NewId() 01089 self.popupDataID4 = wx.NewId() 01090 self.popupDataID5 = wx.NewId() 01091 self.popupDataID6 = wx.NewId() 01092 self.popupDataID7 = wx.NewId() 01093 self.popupDataID8 = wx.NewId() 01094 self.popupDataID9 = wx.NewId() 01095 self.popupDataID10 = wx.NewId() 01096 self.popupDataID11 = wx.NewId() 01097 01098 self.Bind(wx.EVT_MENU, self.OnDataItemEdit, id=self.popupDataID1) 01099 self.Bind(wx.EVT_MENU, self.OnDataItemAdd, id=self.popupDataID2) 01100 self.Bind(wx.EVT_MENU, self.OnDataItemDelete, id=self.popupDataID3) 01101 self.Bind(wx.EVT_MENU, self.OnDataItemDeleteAll, id=self.popupDataID4) 01102 self.Bind(wx.EVT_MENU, self.OnDataSelectAll, id=self.popupDataID5) 01103 self.Bind(wx.EVT_MENU, self.OnDataSelectNone, id=self.popupDataID6) 01104 self.Bind(wx.EVT_MENU, self.OnDataDrawSelected, id=self.popupDataID7) 01105 self.Bind(wx.EVT_MENU, self.OnDataDrawSelectedZoom, id=self.popupDataID8) 01106 self.Bind(wx.EVT_MENU, self.OnExtractSelected, id=self.popupDataID9) 01107 self.Bind(wx.EVT_MENU, self.OnDeleteSelected, id=self.popupDataID11) 01108 self.Bind(wx.EVT_MENU, self.OnDataReload, id=self.popupDataID10) 01109 01110 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01111 # generate popup-menu 01112 menu = wx.Menu() 01113 menu.Append(self.popupDataID1, _("Edit selected record")) 01114 selected = list.GetFirstSelected() 01115 if not self.editable or selected == -1 or list.GetNextSelected(selected) != -1: 01116 menu.Enable(self.popupDataID1, False) 01117 menu.Append(self.popupDataID2, _("Insert new record")) 01118 menu.Append(self.popupDataID3, _("Delete selected record(s)")) 01119 menu.Append(self.popupDataID4, _("Delete all records")) 01120 if not self.editable: 01121 menu.Enable(self.popupDataID2, False) 01122 menu.Enable(self.popupDataID3, False) 01123 menu.Enable(self.popupDataID4, False) 01124 menu.AppendSeparator() 01125 menu.Append(self.popupDataID5, _("Select all")) 01126 menu.Append(self.popupDataID6, _("Deselect all")) 01127 menu.AppendSeparator() 01128 menu.Append(self.popupDataID7, _("Highlight selected features")) 01129 menu.Append(self.popupDataID8, _("Highlight selected features and zoom")) 01130 if not self.map or len(list.GetSelectedItems()) == 0: 01131 menu.Enable(self.popupDataID7, False) 01132 menu.Enable(self.popupDataID8, False) 01133 menu.Append(self.popupDataID9, _("Extract selected features")) 01134 menu.Append(self.popupDataID11, _("Delete selected features")) 01135 if not self.editable: 01136 menu.Enable(self.popupDataID11, False) 01137 if list.GetFirstSelected() == -1: 01138 menu.Enable(self.popupDataID3, False) 01139 menu.Enable(self.popupDataID9, False) 01140 menu.Enable(self.popupDataID11, False) 01141 menu.AppendSeparator() 01142 menu.Append(self.popupDataID10, _("Reload")) 01143 01144 self.PopupMenu(menu) 01145 menu.Destroy() 01146 01147 # update statusbar 01148 self.log.write(_("Number of loaded records: %d") % \ 01149 list.GetItemCount()) 01150 01151 def OnDataItemDelete(self, event): 01152 """!Delete selected item(s) from the list (layer/category pair)""" 01153 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01154 item = list.GetFirstSelected() 01155 01156 table = self.mapDBInfo.layers[self.layer]["table"] 01157 key = self.mapDBInfo.layers[self.layer]["key"] 01158 01159 indeces = [] 01160 # collect SQL statements 01161 while item != -1: 01162 index = list.itemIndexMap[item] 01163 indeces.append(index) 01164 01165 cat = list.itemCatsMap[index] 01166 01167 self.listOfSQLStatements.append('DELETE FROM %s WHERE %s=%d' % \ 01168 (table, key, cat)) 01169 01170 item = list.GetNextSelected(item) 01171 01172 if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'): 01173 deleteDialog = wx.MessageBox(parent=self, 01174 message=_("Selected data records (%d) will be permanently deleted " 01175 "from table. Do you want to delete them?") % \ 01176 (len(self.listOfSQLStatements)), 01177 caption=_("Delete records"), 01178 style=wx.YES_NO | wx.CENTRE) 01179 if deleteDialog != wx.YES: 01180 self.listOfSQLStatements = [] 01181 return False 01182 01183 # restore maps 01184 i = 0 01185 indexTemp = copy.copy(list.itemIndexMap) 01186 list.itemIndexMap = [] 01187 dataTemp = copy.deepcopy(list.itemDataMap) 01188 list.itemDataMap = {} 01189 catsTemp = copy.deepcopy(list.itemCatsMap) 01190 list.itemCatsMap = {} 01191 01192 i = 0 01193 for index in indexTemp: 01194 if index in indeces: 01195 continue 01196 list.itemIndexMap.append(i) 01197 list.itemDataMap[i] = dataTemp[index] 01198 list.itemCatsMap[i] = catsTemp[index] 01199 01200 i += 1 01201 01202 list.SetItemCount(len(list.itemIndexMap)) 01203 01204 # deselect items 01205 item = list.GetFirstSelected() 01206 while item != -1: 01207 list.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED) 01208 item = list.GetNextSelected(item) 01209 01210 # submit SQL statements 01211 self.ApplyCommands() 01212 01213 return True 01214 01215 def OnDataItemDeleteAll(self, event): 01216 """!Delete all items from the list""" 01217 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01218 if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'): 01219 deleteDialog = wx.MessageBox(parent=self, 01220 message=_("All data records (%d) will be permanently deleted " 01221 "from table. Do you want to delete them?") % \ 01222 (len(list.itemIndexMap)), 01223 caption=_("Delete records"), 01224 style=wx.YES_NO | wx.CENTRE) 01225 if deleteDialog != wx.YES: 01226 return 01227 01228 list.DeleteAllItems() 01229 list.itemDataMap = {} 01230 list.itemIndexMap = [] 01231 list.SetItemCount(0) 01232 01233 table = self.mapDBInfo.layers[self.layer]["table"] 01234 self.listOfSQLStatements.append('DELETE FROM %s' % table) 01235 01236 self.ApplyCommands() 01237 01238 event.Skip() 01239 01240 def _drawSelected(self, zoom): 01241 """!Highlight selected features""" 01242 if not self.map or not self.mapdisplay: 01243 return 01244 01245 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01246 cats = map(int, list.GetSelectedItems()) 01247 01248 digitToolbar = self.mapdisplay.toolbars['vdigit'] 01249 if digitToolbar and digitToolbar.GetLayer() and \ 01250 digitToolbar.GetLayer().GetName() == self.vectorName: 01251 01252 self.mapdisplay.digit.driver.SetSelected(cats, field=self.layer) 01253 if zoom: 01254 n, s, w, e = self.mapdisplay.digit.driver.GetRegionSelected() 01255 self.mapdisplay.Map.GetRegion(n=n, s=s, w=w, e=e, 01256 update=True) 01257 else: 01258 # add map layer with higlighted vector features 01259 self.AddQueryMapLayer() # -> self.qlayer 01260 01261 # set opacity based on queried layer 01262 if self.parent and self.parent.GetName() == "LayerManager" and \ 01263 self.treeItem: 01264 maptree = self.parent.curr_page.maptree 01265 opacity = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetOpacity(float=True) 01266 self.qlayer.SetOpacity(opacity) 01267 if zoom: 01268 keyColumn = self.mapDBInfo.layers[self.layer]['key'] 01269 where = '' 01270 for range in utils.ListOfCatsToRange(cats).split(','): 01271 if '-' in range: 01272 min, max = range.split('-') 01273 where += '%s >= %d and %s <= %d or ' % \ 01274 (keyColumn, int(min), 01275 keyColumn, int(max)) 01276 else: 01277 where += '%s = %d or ' % (keyColumn, int(range)) 01278 where = where.rstrip('or ') 01279 01280 select = gcmd.RunCommand('v.db.select', 01281 parent = self, 01282 read = True, 01283 quiet = True, 01284 flags = 'r', 01285 map = self.mapDBInfo.map, 01286 layer = int(self.layer), 01287 where = where) 01288 01289 region = {} 01290 for line in select.splitlines(): 01291 key, value = line.split('=') 01292 region[key.strip()] = float(value.strip()) 01293 01294 self.mapdisplay.Map.GetRegion(n=region['n'], s=region['s'], 01295 w=region['w'], e=region['e'], 01296 update=True) 01297 01298 if zoom: 01299 self.mapdisplay.Map.AdjustRegion() # adjust resolution 01300 self.mapdisplay.Map.AlignExtentFromDisplay() # adjust extent 01301 self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True) 01302 else: 01303 self.mapdisplay.MapWindow.UpdateMap(render=False, renderVector=True) 01304 01305 def OnDataDrawSelected(self, event): 01306 """!Reload table description""" 01307 self._drawSelected(zoom=False) 01308 event.Skip() 01309 01310 def OnDataDrawSelectedZoom(self, event): 01311 self._drawSelected(zoom=True) 01312 event.Skip() 01313 01314 def OnDataItemAdd(self, event): 01315 """!Add new record to the attribute table""" 01316 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01317 table = self.mapDBInfo.layers[self.layer]['table'] 01318 keyColumn = self.mapDBInfo.layers[self.layer]['key'] 01319 01320 # (column name, value) 01321 data = [] 01322 01323 # collect names of all visible columns 01324 columnName = [] 01325 for i in range(list.GetColumnCount()): 01326 columnName.append(list.GetColumn(i).GetText()) 01327 01328 # maximal category number 01329 if len(list.itemCatsMap.values()) > 0: 01330 maxCat = max(list.itemCatsMap.values()) 01331 else: 01332 maxCat = 0 # starting category '1' 01333 01334 # key column must be always presented 01335 if keyColumn not in columnName: 01336 columnName.insert(0, keyColumn) # insert key column on first position 01337 data.append((keyColumn, str(maxCat + 1))) 01338 missingKey = True 01339 else: 01340 missingKey = False 01341 01342 # add other visible columns 01343 colIdx = 0 01344 keyId = -1 01345 for col in columnName: 01346 if col == keyColumn: # key 01347 if missingKey is False: 01348 data.append((col, str(maxCat + 1))) 01349 keyId = colIdx 01350 else: 01351 data.append((col, '')) 01352 colIdx += 1 01353 01354 dlg = ModifyTableRecord(parent = self, 01355 title = _("Insert new record"), 01356 data = data, keyEditable = (keyId, True)) 01357 01358 if dlg.ShowModal() == wx.ID_OK: 01359 try: # get category number 01360 cat = int(dlg.GetValues(columns=[keyColumn])[0]) 01361 except: 01362 cat = -1 01363 01364 try: 01365 if cat in list.itemCatsMap.values(): 01366 raise ValueError(_("Record with category number %d " 01367 "already exists in the table.") % cat) 01368 01369 values = dlg.GetValues() # values (need to be casted) 01370 columnsString = '' 01371 valuesString = '' 01372 01373 for i in range(len(values)): 01374 if len(values[i]) == 0: # NULL 01375 if columnName[i] == keyColumn: 01376 raise ValueError(_("Category number (column %s)" 01377 " is missing.") % keyColumn) 01378 else: 01379 continue 01380 01381 try: 01382 if list.columns[columnName[i]]['ctype'] == int: 01383 # values[i] is stored as text. 01384 value = float(values[i]) 01385 else: 01386 value = values[i] 01387 values[i] = list.columns[columnName[i]]['ctype'] (value) 01388 01389 except: 01390 raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % 01391 {'value' : str(values[i]), 01392 'type' : list.columns[columnName[i]]['type']}) 01393 columnsString += '%s,' % columnName[i] 01394 if list.columns[columnName[i]]['ctype'] == str: 01395 valuesString += "'%s'," % values[i] 01396 else: 01397 valuesString += "%s," % values[i] 01398 01399 except ValueError, err: 01400 wx.MessageBox(parent=self, 01401 message="%s%s%s" % (_("Unable to insert new record."), 01402 os.linesep, err), 01403 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 01404 return 01405 01406 # remove category if need 01407 if missingKey is True: 01408 del values[0] 01409 01410 # add new item to the list 01411 if len(list.itemIndexMap) > 0: 01412 index = max(list.itemIndexMap) + 1 01413 else: 01414 index = 0 01415 01416 list.itemIndexMap.append(index) 01417 list.itemDataMap[index] = values 01418 list.itemCatsMap[index] = cat 01419 list.SetItemCount(list.GetItemCount() + 1) 01420 01421 self.listOfSQLStatements.append('INSERT INTO %s (%s) VALUES(%s)' % \ 01422 (table, 01423 columnsString.strip(','), 01424 valuesString.strip(','))) 01425 self.ApplyCommands() 01426 01427 def OnDataItemEdit(self, event): 01428 """!Edit selected record of the attribute table""" 01429 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01430 item = list.GetFirstSelected() 01431 if item == -1: 01432 return 01433 01434 table = self.mapDBInfo.layers[self.layer]['table'] 01435 keyColumn = self.mapDBInfo.layers[self.layer]['key'] 01436 cat = list.itemCatsMap[list.itemIndexMap[item]] 01437 01438 # (column name, value) 01439 data = [] 01440 01441 # collect names of all visible columns 01442 columnName = [] 01443 for i in range(list.GetColumnCount()): 01444 columnName.append(list.GetColumn(i).GetText()) 01445 01446 01447 # key column must be always presented 01448 if keyColumn not in columnName: 01449 columnName.insert(0, keyColumn) # insert key column on first position 01450 data.append((keyColumn, str(cat))) 01451 keyId = 0 01452 missingKey = True 01453 else: 01454 missingKey = False 01455 01456 # add other visible columns 01457 for i in range(len(columnName)): 01458 if columnName[i] == keyColumn: # key 01459 if missingKey is False: 01460 data.append((columnName[i], str(cat))) 01461 keyId = i 01462 else: 01463 if missingKey is True: 01464 value = list.GetItem(item, i-1).GetText() 01465 else: 01466 value = list.GetItem(item, i).GetText() 01467 data.append((columnName[i], value)) 01468 01469 dlg = ModifyTableRecord(parent = self, 01470 title = _("Update existing record"), 01471 data = data, keyEditable = (keyId, False)) 01472 01473 if dlg.ShowModal() == wx.ID_OK: 01474 values = dlg.GetValues() # string 01475 updateString = '' 01476 try: 01477 for i in range(len(values)): 01478 if i == keyId: # skip key column 01479 continue 01480 if list.GetItem(item, i).GetText() != values[i]: 01481 if len(values[i]) > 0: 01482 try: 01483 if missingKey is True: 01484 idx = i - 1 01485 else: 01486 idx = i 01487 if list.columns[columnName[i]]['ctype'] != type(''): 01488 if list.columns[columnName[i]]['ctype'] == int: 01489 value = float(values[i]) 01490 else: 01491 value = values[i] 01492 list.itemDataMap[item][idx] = \ 01493 list.columns[columnName[i]]['ctype'] (value) 01494 else: 01495 list.itemDataMap[item][idx] = values[i] 01496 except: 01497 raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % \ 01498 {'value' : str(values[i]), 01499 'type' : list.columns[columnName[i]]['type']}) 01500 01501 if list.columns[columnName[i]]['ctype'] == str: 01502 updateString += "%s='%s'," % (columnName[i], values[i]) 01503 else: 01504 updateString += "%s=%s," % (columnName[i], values[i]) 01505 else: # NULL 01506 updateString += "%s=NULL," % (columnName[i]) 01507 01508 except ValueError, err: 01509 wx.MessageBox(parent=self, 01510 message="%s%s%s" % (_("Unable to update existing record."), 01511 os.linesep, err), 01512 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 01513 return 01514 01515 if len(updateString) > 0: 01516 self.listOfSQLStatements.append('UPDATE %s SET %s WHERE %s=%d' % \ 01517 (table, updateString.strip(','), 01518 keyColumn, cat)) 01519 self.ApplyCommands() 01520 01521 list.Update(self.mapDBInfo) 01522 01523 def OnDataReload(self, event): 01524 """!Reload list of records""" 01525 self.OnApplySqlStatement(None) 01526 self.listOfSQLStatements = [] 01527 01528 def OnDataSelectAll(self, event): 01529 """!Select all items""" 01530 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01531 item = -1 01532 01533 while True: 01534 item = list.GetNextItem(item) 01535 if item == -1: 01536 break 01537 list.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) 01538 01539 event.Skip() 01540 01541 def OnDataSelectNone(self, event): 01542 """!Deselect items""" 01543 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01544 item = -1 01545 01546 while True: 01547 item = list.GetNextItem(item, wx.LIST_STATE_SELECTED) 01548 if item == -1: 01549 break 01550 list.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED) 01551 01552 event.Skip() 01553 01554 01555 def OnTableChangeType(self, event): 01556 """!Data type for new column changed. Enable or disable 01557 data length widget""" 01558 win = self.FindWindowById(self.layerPage[self.layer]['addColLength']) 01559 if event.GetString() == "varchar": 01560 win.Enable(True) 01561 else: 01562 win.Enable(False) 01563 01564 def OnTableRenameColumnName(self, event): 01565 """!Editing column name to be added to the table""" 01566 btn = self.FindWindowById(self.layerPage[self.layer]['renameColButton']) 01567 col = self.FindWindowById(self.layerPage[self.layer]['renameCol']) 01568 colTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']) 01569 if len(col.GetValue()) > 0 and len(colTo.GetValue()) > 0: 01570 btn.Enable(True) 01571 else: 01572 btn.Enable(False) 01573 01574 event.Skip() 01575 01576 def OnTableAddColumnName(self, event): 01577 """!Editing column name to be added to the table""" 01578 btn = self.FindWindowById(self.layerPage[self.layer]['addColButton']) 01579 if len(event.GetString()) > 0: 01580 btn.Enable(True) 01581 else: 01582 btn.Enable(False) 01583 01584 event.Skip() 01585 01586 def OnTableItemChange(self, event): 01587 """!Rename column in the table""" 01588 list = self.FindWindowById(self.layerPage[self.layer]['tableData']) 01589 name = self.FindWindowById(self.layerPage[self.layer]['renameCol']).GetValue() 01590 nameTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']).GetValue() 01591 01592 table = self.mapDBInfo.layers[self.layer]["table"] 01593 01594 if not name or not nameTo: 01595 wx.MessageBox(self=self, 01596 message=_("Unable to rename column. " 01597 "No column name defined."), 01598 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 01599 return 01600 else: 01601 item = list.FindItem(start=-1, str=name) 01602 if item > -1: 01603 if list.FindItem(start=-1, str=nameTo) > -1: 01604 wx.MessageBox(parent=self, 01605 message=_("Unable to rename column <%(column)s> to " 01606 "<%(columnTo)s>. Column already exists " 01607 "in the table <%(table)s>.") % \ 01608 {'column' : name, 'columnTo' : nameTo, 01609 'table' : table}, 01610 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 01611 return 01612 else: 01613 list.SetItemText(item, nameTo) 01614 01615 self.listOfCommands.append(('v.db.renamecol', 01616 { 'map' : self.vectorName, 01617 'layer' : self.layer, 01618 'column' : '%s,%s' % (name, nameTo) } 01619 )) 01620 else: 01621 wx.MessageBox(parent=self, 01622 message=_("Unable to rename column. " 01623 "Column <%(column)s> doesn't exist in the table <%(table)s>.") % 01624 {'column' : name, 'table' : table}, 01625 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 01626 return 01627 01628 # apply changes 01629 self.ApplyCommands() 01630 01631 # update widgets 01632 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table)) 01633 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0) 01634 self.FindWindowById(self.layerPage[self.layer]['renameColTo']).SetValue('') 01635 01636 event.Skip() 01637 01638 def OnTableRightUp(self, event): 01639 """!Table description area, context menu""" 01640 if not hasattr(self, "popupTableID"): 01641 self.popupTableID1 = wx.NewId() 01642 self.popupTableID2 = wx.NewId() 01643 self.popupTableID3 = wx.NewId() 01644 self.Bind(wx.EVT_MENU, self.OnTableItemDelete, id=self.popupTableID1) 01645 self.Bind(wx.EVT_MENU, self.OnTableItemDeleteAll, id=self.popupTableID2) 01646 self.Bind(wx.EVT_MENU, self.OnTableReload, id=self.popupTableID3) 01647 01648 # generate popup-menu 01649 menu = wx.Menu() 01650 menu.Append(self.popupTableID1, _("Drop selected column")) 01651 if self.FindWindowById(self.layerPage[self.layer]['tableData']).GetFirstSelected() == -1: 01652 menu.Enable(self.popupTableID1, False) 01653 menu.Append(self.popupTableID2, _("Drop all columns")) 01654 menu.AppendSeparator() 01655 menu.Append(self.popupTableID3, _("Reload")) 01656 01657 self.PopupMenu(menu) 01658 menu.Destroy() 01659 01660 def OnTableItemDelete(self, event): 01661 """!Delete selected item(s) from the list""" 01662 list = self.FindWindowById(self.layerPage[self.layer]['tableData']) 01663 01664 item = list.GetFirstSelected() 01665 01666 if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'): 01667 deleteDialog = wx.MessageBox(parent=self, 01668 message=_("Selected column '%s' will PERMANENTLY removed " 01669 "from table. Do you want to drop the column?") % \ 01670 (list.GetItemText(item)), 01671 caption=_("Drop column(s)"), 01672 style=wx.YES_NO | wx.CENTRE) 01673 if deleteDialog != wx.YES: 01674 return False 01675 01676 while item != -1: 01677 self.listOfCommands.append(('v.db.dropcol', 01678 { 'map' : self.vectorName, 01679 'layer' : self.layer, 01680 'column' : list.GetItemText(item) } 01681 )) 01682 list.DeleteItem(item) 01683 item = list.GetFirstSelected() 01684 01685 # apply changes 01686 self.ApplyCommands() 01687 01688 # update widgets 01689 table = self.mapDBInfo.layers[self.layer]['table'] 01690 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table)) 01691 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0) 01692 01693 event.Skip() 01694 01695 def OnTableItemDeleteAll(self, event): 01696 """!Delete all items from the list""" 01697 table = self.mapDBInfo.layers[self.layer]['table'] 01698 cols = self.mapDBInfo.GetColumns(table) 01699 keyColumn = self.mapDBInfo.layers[self.layer]['key'] 01700 if keyColumn in cols: 01701 cols.remove(keyColumn) 01702 01703 if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'): 01704 deleteDialog = wx.MessageBox(parent=self, 01705 message=_("Selected columns\n%s\nwill PERMANENTLY removed " 01706 "from table. Do you want to drop the columns?") % \ 01707 ('\n'.join(cols)), 01708 caption=_("Drop column(s)"), 01709 style=wx.YES_NO | wx.CENTRE) 01710 if deleteDialog != wx.YES: 01711 return False 01712 01713 for col in cols: 01714 self.listOfCommands.append(('v.db.dropcol', 01715 { 'map' : self.vectorName, 01716 'layer' : self.layer, 01717 'column' : col } 01718 )) 01719 self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems() 01720 01721 # apply changes 01722 self.ApplyCommands() 01723 01724 # update widgets 01725 table = self.mapDBInfo.layers[self.layer]['table'] 01726 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table)) 01727 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0) 01728 01729 event.Skip() 01730 01731 def OnTableReload(self, event=None): 01732 """!Reload table description""" 01733 self.FindWindowById(self.layerPage[self.layer]['tableData']).Populate(update=True) 01734 self.listOfCommands = [] 01735 01736 def OnTableItemAdd(self, event): 01737 """!Add new column to the table""" 01738 table = self.mapDBInfo.layers[self.layer]['table'] 01739 name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue() 01740 01741 if not name: 01742 gcmd.GError(parent = self, 01743 message = _("Unable to add column to the table. " 01744 "No column name defined.")) 01745 return 01746 01747 ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \ 01748 GetStringSelection() 01749 01750 # cast type if needed 01751 if ctype == 'double': 01752 ctype = 'double precision' 01753 if ctype == 'varchar': 01754 length = int(self.FindWindowById(self.layerPage[self.layer]['addColLength']). \ 01755 GetValue()) 01756 else: 01757 length = '' # FIXME 01758 01759 # add item to the list of table columns 01760 tlist = self.FindWindowById(self.layerPage[self.layer]['tableData']) 01761 # check for duplicate items 01762 if tlist.FindItem(start=-1, str=name) > -1: 01763 gcmd.GError(parent = self, 01764 message = _("Column <%(column)s> already exists in table <%(table)s>.") % \ 01765 {'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]} 01766 ) 01767 return 01768 index = tlist.InsertStringItem(sys.maxint, str(name)) 01769 tlist.SetStringItem(index, 0, str(name)) 01770 tlist.SetStringItem(index, 1, str(ctype)) 01771 tlist.SetStringItem(index, 2, str(length)) 01772 01773 # add v.db.addcol command to the list 01774 if ctype == 'varchar': 01775 ctype += ' (%d)' % length 01776 self.listOfCommands.append(('v.db.addcol', 01777 { 'map' : self.vectorName, 01778 'layer' : self.layer, 01779 'columns' : '%s %s' % (name, ctype) } 01780 )) 01781 # apply changes 01782 self.ApplyCommands() 01783 01784 # update widgets 01785 self.FindWindowById(self.layerPage[self.layer]['addColName']).SetValue('') 01786 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table)) 01787 self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0) 01788 01789 event.Skip() 01790 01791 def OnLayerPageChanged(self, event): 01792 """!Layer tab changed""" 01793 pageNum = event.GetSelection() 01794 self.layer = self.mapDBInfo.layers.keys()[pageNum] 01795 01796 try: 01797 idCol = self.layerPage[self.layer]['whereColumn'] 01798 except KeyError: 01799 idCol = None 01800 01801 try: 01802 self.OnChangeSql(None) 01803 # update statusbar 01804 self.log.write(_("Number of loaded records: %d") % \ 01805 self.FindWindowById(self.layerPage[self.layer]['data']).\ 01806 GetItemCount()) 01807 except: 01808 pass 01809 01810 if idCol: 01811 winCol = self.FindWindowById(idCol) 01812 table = self.mapDBInfo.layers[self.layer]["table"] 01813 self.mapDBInfo.GetColumns(table) 01814 01815 event.Skip() 01816 01817 def OnPageChanged(self, event): 01818 try: 01819 id = self.layerPage[self.layer]['data'] 01820 except KeyError: 01821 id = None 01822 01823 if event.GetSelection() == 0 and id: 01824 win = self.FindWindowById(id) 01825 if win: 01826 self.log.write(_("Number of loaded records: %d") % win.GetItemCount()) 01827 else: 01828 self.log.write("") 01829 self.btnReload.Enable() 01830 else: 01831 self.log.write("") 01832 self.btnReload.Enable(False) 01833 01834 event.Skip() 01835 01836 def OnLayerRightUp(self, event): 01837 """!Layer description area, context menu""" 01838 pass 01839 01840 def OnChangeSql(self, event): 01841 """!Switch simple/advanced sql statement""" 01842 if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue(): 01843 self.FindWindowById(self.layerPage[self.layer]['where']).Enable(True) 01844 self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(False) 01845 self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(False) 01846 else: 01847 self.FindWindowById(self.layerPage[self.layer]['where']).Enable(False) 01848 self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(True) 01849 self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True) 01850 01851 def ApplyCommands(self): 01852 """!Apply changes""" 01853 # perform GRASS commands (e.g. v.db.addcol) 01854 wx.BeginBusyCursor() 01855 01856 if len(self.listOfCommands) > 0: 01857 for cmd in self.listOfCommands: 01858 gcmd.RunCommand(prog = cmd[0], 01859 quiet = True, 01860 parent = self, 01861 **cmd[1]) 01862 01863 self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName) 01864 table = self.mapDBInfo.layers[self.layer]['table'] 01865 01866 # update table description 01867 list = self.FindWindowById(self.layerPage[self.layer]['tableData']) 01868 list.Update(table=self.mapDBInfo.tables[table], 01869 columns=self.mapDBInfo.GetColumns(table)) 01870 self.OnTableReload(None) 01871 01872 # update data list 01873 list = self.FindWindowById(self.layerPage[self.layer]['data']) 01874 list.Update(self.mapDBInfo) 01875 01876 # reset list of commands 01877 self.listOfCommands = [] 01878 01879 # perform SQL non-select statements (e.g. 'delete from table where cat=1') 01880 if len(self.listOfSQLStatements) > 0: 01881 sqlFile = tempfile.NamedTemporaryFile(mode="wt") 01882 for sql in self.listOfSQLStatements: 01883 enc = UserSettings.Get(group='atm', key='encoding', subkey='value') 01884 if not enc and 'GRASS_DB_ENCODING' in os.environ: 01885 enc = os.environ['GRASS_DB_ENCODING'] 01886 if enc: 01887 sqlFile.file.write(sql.encode(enc) + ';') 01888 else: 01889 sqlFile.file.write(sql + ';') 01890 sqlFile.file.write(os.linesep) 01891 sqlFile.file.flush() 01892 01893 driver = self.mapDBInfo.layers[self.layer]["driver"] 01894 database = self.mapDBInfo.layers[self.layer]["database"] 01895 01896 Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' % 01897 ';'.join(["%s" % s for s in self.listOfSQLStatements])) 01898 01899 gcmd.RunCommand('db.execute', 01900 parent = self, 01901 input = sqlFile.name, 01902 driver = driver, 01903 database = database) 01904 01905 # reset list of statements 01906 self.listOfSQLStatements = [] 01907 01908 wx.EndBusyCursor() 01909 01910 def OnApplySqlStatement(self, event): 01911 """!Apply simple/advanced sql statement""" 01912 keyColumn = -1 # index of key column 01913 listWin = self.FindWindowById(self.layerPage[self.layer]['data']) 01914 sql = None 01915 01916 wx.BeginBusyCursor() 01917 01918 if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue(): 01919 # simple sql statement 01920 whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection() 01921 whereVal = self.FindWindowById(self.layerPage[self.layer]['where']).GetValue().strip() 01922 try: 01923 if len(whereVal) > 0: 01924 keyColumn = listWin.LoadData(self.layer, where=whereCol + whereVal) 01925 else: 01926 keyColumn = listWin.LoadData(self.layer) 01927 except gcmd.GException, e: 01928 gcmd.GError(parent = self, 01929 message = _("Loading attribute data failed.\n\n%s") % e.value) 01930 self.FindWindowById(self.layerPage[self.layer]['where']).SetValue('') 01931 else: 01932 # advanced sql statement 01933 win = self.FindWindowById(self.layerPage[self.layer]['statement']) 01934 try: 01935 cols, where = self.ValidateSelectStatement(win.GetValue()) 01936 if cols is None and where is None: 01937 sql = win.GetValue() 01938 except TypeError: 01939 wx.MessageBox(parent=self, 01940 message=_("Loading attribute data failed.\n" 01941 "Invalid SQL select statement.\n\n%s") % win.GetValue(), 01942 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 01943 win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table']) 01944 cols = None 01945 where = None 01946 01947 if cols or where or sql: 01948 try: 01949 keyColumn = listWin.LoadData(self.layer, columns=cols, 01950 where=where, sql=sql) 01951 except gcmd.GException, e: 01952 gcmd.GError(parent = self, 01953 message = _("Loading attribute data failed.\n\n%s") % e.value) 01954 win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table']) 01955 01956 # sort by key column 01957 if sql and 'order by' in sql.lower(): 01958 pass # don't order by key column 01959 else: 01960 if keyColumn > -1: 01961 listWin.SortListItems(col=keyColumn, ascending=True) 01962 else: 01963 listWin.SortListItems(col=0, ascending=True) 01964 01965 wx.EndBusyCursor() 01966 01967 # update statusbar 01968 self.log.write(_("Number of loaded records: %d") % \ 01969 self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount()) 01970 01971 def ValidateSelectStatement(self, statement): 01972 """!Validate SQL select statement 01973 01974 @return (columns, where) 01975 @return None on error 01976 """ 01977 if statement[0:7].lower() != 'select ': 01978 return None 01979 01980 cols = '' 01981 index = 7 01982 for c in statement[index:]: 01983 if c == ' ': 01984 break 01985 cols += c 01986 index += 1 01987 if cols == '*': 01988 cols = None 01989 else: 01990 cols = cols.split(',') 01991 01992 tablelen = len(self.mapDBInfo.layers[self.layer]['table']) 01993 01994 if statement[index+1:index+6].lower() != 'from ' or \ 01995 statement[index+6:index+6+tablelen] != '%s' % \ 01996 (self.mapDBInfo.layers[self.layer]['table']): 01997 return None 01998 01999 if len(statement[index+7+tablelen:]) > 0: 02000 index = statement.lower().find('where ') 02001 if index > -1: 02002 where = statement[index+6:] 02003 else: 02004 where = None 02005 else: 02006 where = None 02007 02008 return (cols, where) 02009 02010 def OnCloseWindow(self, event): 02011 """!Cancel button pressed""" 02012 self.Close() 02013 if self.parent and self.parent.GetName() == 'LayerManager': 02014 # deregister ATM 02015 self.parent.dialogs['atm'].remove(self) 02016 02017 event.Skip() 02018 02019 def OnBuilder(self,event): 02020 """!SQL Builder button pressed -> show the SQLBuilder dialog""" 02021 if not self.builder: 02022 self.builder = sqlbuilder.SQLFrame(parent = self, id = wx.ID_ANY, 02023 title = _("SQL Builder"), 02024 vectmap = self.vectorName, 02025 evtheader = self.OnBuilderEvt) 02026 self.builder.Show() 02027 else: 02028 self.builder.Raise() 02029 02030 def OnBuilderEvt(self, event): 02031 if event == 'apply': 02032 sqlstr = self.builder.GetSQLStatement() 02033 self.FindWindowById(self.layerPage[self.layer]['statement']).SetValue(sqlstr) 02034 if self.builder.CloseOnApply(): 02035 self.builder = None 02036 elif event == 'close': 02037 self.builder = None 02038 02039 def OnTextEnter(self, event): 02040 pass 02041 02042 def OnDataItemActivated(self, event): 02043 """!Item activated, highlight selected item""" 02044 self.OnDataDrawSelected(event) 02045 02046 event.Skip() 02047 02048 def OnExtractSelected(self, event): 02049 """!Extract vector objects selected in attribute browse window 02050 to new vector map 02051 """ 02052 list = self.FindWindowById(self.layerPage[self.layer]['data']) 02053 # cats = list.selectedCats[:] 02054 cats = list.GetSelectedItems() 02055 if len(cats) == 0: 02056 wx.MessageBox(parent=self, 02057 message=_('Nothing to extract.'), 02058 caption=_('Message'), style=wx.CENTRE) 02059 return 02060 else: 02061 # dialog to get file name 02062 dlg = gdialogs.CreateNewVector(parent = self, title = _('Extract selected features'), 02063 log = self.cmdLog, 02064 cmd = (('v.extract', 02065 { 'input' : self.vectorName, 02066 'list' : utils.ListOfCatsToRange(cats) }, 02067 'output')), 02068 disableTable = True) 02069 if not dlg: 02070 return 02071 02072 name = dlg.GetName(full = True) 02073 if name and dlg.IsChecked('add'): 02074 # add layer to map layer tree 02075 self.parent.curr_page.maptree.AddLayer(ltype = 'vector', 02076 lname = name, 02077 lcmd = ['d.vect', 'map=%s' % name]) 02078 dlg.Destroy() 02079 02080 def OnDeleteSelected(self, event): 02081 """ 02082 Delete vector objects selected in attribute browse window 02083 (attribures and geometry) 02084 """ 02085 list = self.FindWindowById(self.layerPage[self.layer]['data']) 02086 cats = list.GetSelectedItems() 02087 if len(cats) == 0: 02088 wx.MessageBox(parent=self, 02089 message=_('Nothing to delete.'), 02090 caption=_('Message'), style=wx.CENTRE) 02091 02092 if self.OnDataItemDelete(None): 02093 digitToolbar = self.mapdisplay.toolbars['vdigit'] 02094 if digitToolbar and digitToolbar.GetLayer() and \ 02095 digitToolbar.GetLayer().GetName() == self.vectorName: 02096 self.mapdisplay.digit.driver.SetSelected(map(int, cats), field=self.layer) 02097 self.mapdisplay.digit.DeleteSelectedLines() 02098 else: 02099 gcmd.RunCommand('v.edit', 02100 parent = self, 02101 quiet = True, 02102 map = self.vectorName, 02103 tool = 'delete', 02104 cats = utils.ListOfCatsToRange(cats)) 02105 02106 self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True) 02107 02108 def AddQueryMapLayer(self): 02109 """!Redraw a map 02110 02111 Return True if map has been redrawn, False if no map is given 02112 """ 02113 list = self.FindWindowById(self.layerPage[self.layer]['data']) 02114 cats = { 02115 self.layer : list.GetSelectedItems() 02116 } 02117 02118 if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0: 02119 self.qlayer = None 02120 02121 if self.qlayer: 02122 self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer=False)) 02123 else: 02124 self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats) 02125 02126 return self.qlayer 02127 02128 def UpdateDialog(self, layer): 02129 """!Updates dialog layout for given layer""" 02130 # 02131 # delete page 02132 # 02133 if layer in self.mapDBInfo.layers.keys(): 02134 # delete page 02135 # draging pages disallowed 02136 # if self.browsePage.GetPageText(page).replace('Layer ', '').strip() == str(layer): 02137 # self.browsePage.DeletePage(page) 02138 # break 02139 self.browsePage.DeletePage(self.mapDBInfo.layers.keys().index(layer)) 02140 self.manageTablePage.DeletePage(self.mapDBInfo.layers.keys().index(layer)) 02141 # set current page selection 02142 self.notebook.SetSelection(2) 02143 02144 # fetch fresh db info 02145 self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName) 02146 02147 # 02148 # add new page 02149 # 02150 if layer in self.mapDBInfo.layers.keys(): 02151 # 'browse data' page 02152 self.__createBrowsePage(layer) 02153 # 'manage tables' page 02154 self.__createManageTablePage(layer) 02155 # set current page selection 02156 self.notebook.SetSelection(2) 02157 02158 # 02159 # 'manage layers' page 02160 # 02161 # update list of layers 02162 self.layerList.Update(self.mapDBInfo.layers) 02163 self.layerList.Populate(update=True) 02164 # update selected widgets 02165 listOfLayers = map(str, self.mapDBInfo.layers.keys()) 02166 ### delete layer page 02167 self.manageLayerBook.deleteLayer.SetItems(listOfLayers) 02168 if len(listOfLayers) > 0: 02169 self.manageLayerBook.deleteLayer.SetStringSelection(listOfLayers[0]) 02170 tableName = self.mapDBInfo.layers[int(listOfLayers[0])]['table'] 02171 maxLayer = max(self.mapDBInfo.layers.keys()) 02172 else: 02173 tableName = '' 02174 maxLayer = 0 02175 self.manageLayerBook.deleteTable.SetLabel( \ 02176 _('Drop also linked attribute table (%s)') % \ 02177 tableName) 02178 ### add layer page 02179 self.manageLayerBook.addLayerWidgets['layer'][1].SetValue(\ 02180 maxLayer+1) 02181 ### modify layer 02182 self.manageLayerBook.modifyLayerWidgets['layer'][1].SetItems(listOfLayers) 02183 self.manageLayerBook.OnChangeLayer(event=None) 02184 02185 def GetVectorName(self): 02186 """!Get vector name""" 02187 return self.vectorName 02188 02189 def LoadData(self, layer, columns=None, where=None, sql=None): 02190 """!Load data into list 02191 02192 @param layer layer number 02193 @param columns list of columns for output 02194 @param where where statement 02195 @param sql full sql statement 02196 02197 @return id of key column 02198 @return -1 if key column is not displayed 02199 """ 02200 listWin = self.FindWindowById(self.layerPage[layer]['data']) 02201 return listWin.LoadData(layer, columns, where, sql) 02202 02203 class TableListCtrl(wx.ListCtrl, 02204 listmix.ListCtrlAutoWidthMixin): 02205 # listmix.TextEditMixin): 02206 """!Table description list""" 02207 02208 def __init__(self, parent, id, table, columns, pos=wx.DefaultPosition, 02209 size=wx.DefaultSize): 02210 02211 self.parent = parent 02212 self.table = table 02213 self.columns = columns 02214 wx.ListCtrl.__init__(self, parent, id, pos, size, 02215 style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES | 02216 wx.BORDER_NONE) 02217 02218 listmix.ListCtrlAutoWidthMixin.__init__(self) 02219 # listmix.TextEditMixin.__init__(self) 02220 02221 def Update(self, table, columns): 02222 """!Update column description""" 02223 self.table = table 02224 self.columns = columns 02225 02226 def Populate(self, update=False): 02227 """!Populate the list""" 02228 itemData = {} # requested by sorter 02229 02230 if not update: 02231 headings = [_("Column name"), _("Data type"), _("Data length")] 02232 i = 0 02233 for h in headings: 02234 self.InsertColumn(col=i, heading=h) 02235 self.SetColumnWidth(col=i, width=150) 02236 i += 1 02237 else: 02238 self.DeleteAllItems() 02239 02240 i = 0 02241 for column in self.columns: 02242 index = self.InsertStringItem(sys.maxint, str(column)) 02243 self.SetStringItem(index, 0, str(column)) 02244 self.SetStringItem(index, 1, str(self.table[column]['type'])) 02245 self.SetStringItem(index, 2, str(self.table[column]['length'])) 02246 self.SetItemData(index, i) 02247 itemData[i] = (str(column), 02248 str(self.table[column]['type']), 02249 int(self.table[column]['length'])) 02250 i = i + 1 02251 02252 self.SendSizeEvent() 02253 02254 return itemData 02255 02256 class LayerListCtrl(wx.ListCtrl, 02257 listmix.ListCtrlAutoWidthMixin): 02258 # listmix.ColumnSorterMixin): 02259 # listmix.TextEditMixin): 02260 """!Layer description list""" 02261 02262 def __init__(self, parent, id, layers, 02263 pos=wx.DefaultPosition, 02264 size=wx.DefaultSize): 02265 02266 self.parent = parent 02267 self.layers = layers 02268 wx.ListCtrl.__init__(self, parent, id, pos, size, 02269 style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES | 02270 wx.BORDER_NONE) 02271 02272 listmix.ListCtrlAutoWidthMixin.__init__(self) 02273 # listmix.TextEditMixin.__init__(self) 02274 02275 def Update(self, layers): 02276 """!Update description""" 02277 self.layers = layers 02278 02279 def Populate(self, update=False): 02280 """!Populate the list""" 02281 itemData = {} # requested by sorter 02282 02283 if not update: 02284 headings = [_("Layer"), _("Driver"), _("Database"), _("Table"), _("Key")] 02285 i = 0 02286 for h in headings: 02287 self.InsertColumn(col=i, heading=h) 02288 i += 1 02289 else: 02290 self.DeleteAllItems() 02291 02292 i = 0 02293 for layer in self.layers.keys(): 02294 index = self.InsertStringItem(sys.maxint, str(layer)) 02295 self.SetStringItem(index, 0, str(layer)) 02296 database = str(self.layers[layer]['database']) 02297 driver = str(self.layers[layer]['driver']) 02298 table = str(self.layers[layer]['table']) 02299 key = str(self.layers[layer]['key']) 02300 self.SetStringItem(index, 1, driver) 02301 self.SetStringItem(index, 2, database) 02302 self.SetStringItem(index, 3, table) 02303 self.SetStringItem(index, 4, key) 02304 self.SetItemData(index, i) 02305 itemData[i] = (str(layer), 02306 driver, 02307 database, 02308 table, 02309 key) 02310 i += 1 02311 02312 for i in range(self.GetColumnCount()): 02313 self.SetColumnWidth(col=i, width=wx.LIST_AUTOSIZE) 02314 if self.GetColumnWidth(col=i) < 60: 02315 self.SetColumnWidth(col=i, width=60) 02316 02317 self.SendSizeEvent() 02318 02319 return itemData 02320 02321 class LayerBook(wx.Notebook): 02322 """!Manage layers (add, delete, modify)""" 02323 def __init__(self, parent, id, 02324 parentDialog, 02325 style=wx.BK_DEFAULT): 02326 wx.Notebook.__init__(self, parent, id, style=style) 02327 02328 self.parent = parent 02329 self.parentDialog = parentDialog 02330 self.mapDBInfo = self.parentDialog.mapDBInfo 02331 02332 # 02333 # drivers 02334 # 02335 drivers = gcmd.RunCommand('db.drivers', 02336 quiet = True, 02337 read = True, 02338 flags = 'p') 02339 02340 self.listOfDrivers = [] 02341 for drv in drivers.splitlines(): 02342 self.listOfDrivers.append(drv.strip()) 02343 02344 # 02345 # get default values 02346 # 02347 self.defaultConnect = {} 02348 connect = gcmd.RunCommand('db.connect', 02349 flags = 'p', 02350 read = True, 02351 quiet = True) 02352 02353 for line in connect.splitlines(): 02354 item, value = line.split(':', 1) 02355 self.defaultConnect[item.strip()] = value.strip() 02356 02357 if len(self.defaultConnect['driver']) == 0 or \ 02358 len(self.defaultConnect['database']) == 0: 02359 wx.MessageBox(parent=self.parent, 02360 message=_("Unknown default DB connection. " 02361 "Please define DB connection using db.connect module."), 02362 caption=_("Warning"), 02363 style=wx.OK | wx.ICON_WARNING | wx.CENTRE) 02364 02365 self.defaultTables = self.__getTables(self.defaultConnect['driver'], 02366 self.defaultConnect['database']) 02367 try: 02368 self.defaultColumns = self.__getColumns(self.defaultConnect['driver'], 02369 self.defaultConnect['database'], 02370 self.defaultTables[0]) 02371 except IndexError: 02372 self.defaultColumns = [] 02373 02374 self.__createAddPage() 02375 self.__createDeletePage() 02376 self.__createModifyPage() 02377 02378 def __createAddPage(self): 02379 """!Add new layer""" 02380 self.addPanel = wx.Panel(parent=self, id=wx.ID_ANY) 02381 self.AddPage(page=self.addPanel, text=_("Add layer")) 02382 02383 try: 02384 maxLayer = max(self.mapDBInfo.layers.keys()) 02385 except ValueError: 02386 maxLayer = 0 02387 02388 # layer description 02389 02390 layerBox = wx.StaticBox (parent=self.addPanel, id=wx.ID_ANY, 02391 label=" %s " % (_("Layer description"))) 02392 layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL) 02393 02394 # 02395 # list of layer widgets (label, value) 02396 # 02397 self.addLayerWidgets = {'layer': 02398 (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02399 label='%s:' % _("Layer")), 02400 wx.SpinCtrl(parent=self.addPanel, id=wx.ID_ANY, size=(65, -1), 02401 initial=maxLayer+1, 02402 min=1, max=1e6)), 02403 'driver': 02404 (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02405 label='%s:' % _("Driver")), 02406 wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1), 02407 choices=self.listOfDrivers)), 02408 'database': 02409 (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02410 label='%s:' % _("Database")), 02411 wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY, 02412 value='', 02413 style=wx.TE_PROCESS_ENTER)), 02414 'table': 02415 (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02416 label='%s:' % _("Table")), 02417 wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1), 02418 choices=self.defaultTables)), 02419 'key': 02420 (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02421 label='%s:' % _("Key column")), 02422 wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1), 02423 choices=self.defaultColumns)), 02424 'addCat': 02425 (wx.CheckBox(parent=self.addPanel, id=wx.ID_ANY, 02426 label=_("Insert record for each category into table")), 02427 None), 02428 } 02429 02430 # set default values for widgets 02431 self.addLayerWidgets['driver'][1].SetStringSelection(self.defaultConnect['driver']) 02432 self.addLayerWidgets['database'][1].SetValue(self.defaultConnect['database']) 02433 self.addLayerWidgets['table'][1].SetSelection(0) 02434 self.addLayerWidgets['key'][1].SetSelection(0) 02435 # events 02436 self.addLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged) 02437 self.addLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged) 02438 self.addLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged) 02439 02440 # tooltips 02441 self.addLayerWidgets['addCat'][0].SetToolTipString(_("You need to add categories " 02442 "by v.category module.")) 02443 # 02444 # list of table widgets 02445 # 02446 keyCol = UserSettings.Get(group='atm', key='keycolumn', subkey='value') 02447 self.tableWidgets = {'table': (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02448 label='%s:' % _("Table name")), 02449 wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY, 02450 value='', 02451 style=wx.TE_PROCESS_ENTER)), 02452 'key': (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY, 02453 label='%s:' % _("Key column")), 02454 wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY, 02455 value=keyCol, 02456 style=wx.TE_PROCESS_ENTER))} 02457 # events 02458 self.tableWidgets['table'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable) 02459 self.tableWidgets['key'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable) 02460 02461 btnTable = wx.Button(self.addPanel, wx.ID_ANY, _("&Create table"), 02462 size=(125,-1)) 02463 btnTable.Bind(wx.EVT_BUTTON, self.OnCreateTable) 02464 02465 btnLayer = wx.Button(self.addPanel, wx.ID_ANY, _("&Add layer"), 02466 size=(125,-1)) 02467 btnLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer) 02468 02469 btnDefault = wx.Button(self.addPanel, wx.ID_ANY, _("&Set default"), 02470 size=(125,-1)) 02471 btnDefault.Bind(wx.EVT_BUTTON, self.OnSetDefault) 02472 02473 # do layout 02474 02475 pageSizer = wx.BoxSizer(wx.HORIZONTAL) 02476 02477 # data area 02478 dataSizer = wx.GridBagSizer(hgap=5, vgap=5) 02479 dataSizer.AddGrowableCol(1) 02480 row = 0 02481 for key in ('layer', 'driver', 'database', 'table', 'key', 'addCat'): 02482 label, value = self.addLayerWidgets[key] 02483 if not value: 02484 span = (1, 2) 02485 else: 02486 span = (1, 1) 02487 dataSizer.Add(item=label, 02488 flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0), 02489 span=span) 02490 02491 if not value: 02492 row += 1 02493 continue 02494 02495 if label.GetLabel() == "Layer:": 02496 style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT 02497 else: 02498 style = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND 02499 02500 dataSizer.Add(item=value, 02501 flag=style, pos=(row, 1)) 02502 02503 row += 1 02504 02505 layerSizer.Add(item=dataSizer, 02506 proportion=1, 02507 flag=wx.ALL | wx.EXPAND, 02508 border=5) 02509 02510 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 02511 btnSizer.Add(item=btnDefault, 02512 proportion=0, 02513 flag=wx.ALL | wx.ALIGN_LEFT, 02514 border=5) 02515 02516 btnSizer.Add(item=(5, 5), 02517 proportion=1, 02518 flag=wx.ALL | wx.EXPAND, 02519 border=5) 02520 02521 btnSizer.Add(item=btnLayer, 02522 proportion=0, 02523 flag=wx.ALL | wx.ALIGN_RIGHT, 02524 border=5) 02525 02526 layerSizer.Add(item=btnSizer, 02527 proportion=0, 02528 flag=wx.ALL | wx.EXPAND, 02529 border=0) 02530 02531 # table description 02532 tableBox = wx.StaticBox (parent=self.addPanel, id=wx.ID_ANY, 02533 label=" %s " % (_("Table description"))) 02534 tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL) 02535 02536 # data area 02537 dataSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) 02538 dataSizer.AddGrowableCol(1) 02539 for key in ['table', 'key']: 02540 label, value = self.tableWidgets[key] 02541 dataSizer.Add(item=label, 02542 flag=wx.ALIGN_CENTER_VERTICAL) 02543 dataSizer.Add(item=value, 02544 flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) 02545 02546 tableSizer.Add(item=dataSizer, 02547 proportion=1, 02548 flag=wx.ALL | wx.EXPAND, 02549 border=5) 02550 02551 tableSizer.Add(item=btnTable, 02552 proportion=0, 02553 flag=wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT, 02554 border=5) 02555 02556 pageSizer.Add(item=layerSizer, 02557 proportion=3, 02558 flag=wx.ALL | wx.EXPAND, 02559 border=3) 02560 02561 pageSizer.Add(item=tableSizer, 02562 proportion=2, 02563 flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, 02564 border=3) 02565 02566 layerSizer.SetVirtualSizeHints(self.addPanel) 02567 self.addPanel.SetAutoLayout(True) 02568 self.addPanel.SetSizer(pageSizer) 02569 pageSizer.Fit(self.addPanel) 02570 02571 def __createDeletePage(self): 02572 """!Delete layer""" 02573 self.deletePanel = wx.Panel(parent=self, id=wx.ID_ANY) 02574 self.AddPage(page=self.deletePanel, text=_("Remove layer")) 02575 02576 label = wx.StaticText(parent=self.deletePanel, id=wx.ID_ANY, 02577 label='%s:' % _("Layer to remove")) 02578 02579 self.deleteLayer = wx.ComboBox(parent=self.deletePanel, id=wx.ID_ANY, size=(100, -1), 02580 style=wx.CB_SIMPLE | wx.CB_READONLY, 02581 choices=map(str, self.mapDBInfo.layers.keys())) 02582 self.deleteLayer.SetSelection(0) 02583 self.deleteLayer.Bind(wx.EVT_COMBOBOX, self.OnChangeLayer) 02584 02585 try: 02586 tableName = self.mapDBInfo.layers[int(self.deleteLayer.GetStringSelection())]['table'] 02587 except ValueError: 02588 tableName = '' 02589 02590 self.deleteTable = wx.CheckBox(parent=self.deletePanel, id=wx.ID_ANY, 02591 label=_('Drop also linked attribute table (%s)') % \ 02592 tableName) 02593 02594 if tableName == '': 02595 self.deleteLayer.Enable(False) 02596 self.deleteTable.Enable(False) 02597 02598 btnDelete = wx.Button(self.deletePanel, wx.ID_DELETE, _("&Remove layer"), 02599 size=(125,-1)) 02600 btnDelete.Bind(wx.EVT_BUTTON, self.OnDeleteLayer) 02601 02602 # 02603 # do layout 02604 # 02605 pageSizer = wx.BoxSizer(wx.VERTICAL) 02606 02607 dataSizer = wx.BoxSizer(wx.VERTICAL) 02608 02609 flexSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) 02610 flexSizer.AddGrowableCol(2) 02611 02612 flexSizer.Add(item=label, 02613 flag=wx.ALIGN_CENTER_VERTICAL) 02614 flexSizer.Add(item=self.deleteLayer, 02615 flag=wx.ALIGN_CENTER_VERTICAL) 02616 02617 dataSizer.Add(item=flexSizer, 02618 proportion=0, 02619 flag=wx.ALL | wx.EXPAND, 02620 border=1) 02621 02622 dataSizer.Add(item=self.deleteTable, 02623 proportion=0, 02624 flag=wx.ALL | wx.EXPAND, 02625 border=1) 02626 02627 pageSizer.Add(item=dataSizer, 02628 proportion=1, 02629 flag=wx.ALL | wx.EXPAND, 02630 border=5) 02631 02632 pageSizer.Add(item=btnDelete, 02633 proportion=0, 02634 flag=wx.ALL | wx.ALIGN_RIGHT, 02635 border=5) 02636 02637 self.deletePanel.SetSizer(pageSizer) 02638 02639 def __createModifyPage(self): 02640 """!Modify layer""" 02641 self.modifyPanel = wx.Panel(parent=self, id=wx.ID_ANY) 02642 self.AddPage(page=self.modifyPanel, text=_("Modify layer")) 02643 02644 # 02645 # list of layer widgets (label, value) 02646 # 02647 self.modifyLayerWidgets = {'layer': 02648 (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY, 02649 label='%s:' % _("Layer")), 02650 wx.ComboBox(parent=self.modifyPanel, id=wx.ID_ANY, 02651 size=(100, -1), 02652 style=wx.CB_SIMPLE | wx.CB_READONLY, 02653 choices=map(str, 02654 self.mapDBInfo.layers.keys()))), 02655 'driver': 02656 (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY, 02657 label='%s:' % _("Driver")), 02658 wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY, 02659 size=(200, -1), 02660 choices=self.listOfDrivers)), 02661 'database': 02662 (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY, 02663 label='%s:' % _("Database")), 02664 wx.TextCtrl(parent=self.modifyPanel, id=wx.ID_ANY, 02665 value='', size=(350, -1), 02666 style=wx.TE_PROCESS_ENTER)), 02667 'table': 02668 (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY, 02669 label='%s:' % _("Table")), 02670 wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY, 02671 size=(200, -1), 02672 choices=self.defaultTables)), 02673 'key': 02674 (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY, 02675 label='%s:' % _("Key column")), 02676 wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY, 02677 size=(200, -1), 02678 choices=self.defaultColumns))} 02679 02680 # set default values for widgets 02681 self.modifyLayerWidgets['layer'][1].SetSelection(0) 02682 try: 02683 layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection()) 02684 except ValueError: 02685 layer = None 02686 for label in self.modifyLayerWidgets.keys(): 02687 self.modifyLayerWidgets[label][1].Enable(False) 02688 02689 if layer: 02690 driver = self.mapDBInfo.layers[layer]['driver'] 02691 database = self.mapDBInfo.layers[layer]['database'] 02692 table = self.mapDBInfo.layers[layer]['table'] 02693 02694 listOfColumns = self.__getColumns(driver, database, table) 02695 self.modifyLayerWidgets['driver'][1].SetStringSelection(driver) 02696 self.modifyLayerWidgets['database'][1].SetValue(database) 02697 if table in self.modifyLayerWidgets['table'][1].GetItems(): 02698 self.modifyLayerWidgets['table'][1].SetStringSelection(table) 02699 else: 02700 if self.defaultConnect['schema'] != '': 02701 table = self.defaultConnect['schema'] + table # try with default schema 02702 else: 02703 table = 'public.' + table # try with 'public' schema 02704 self.modifyLayerWidgets['table'][1].SetStringSelection(table) 02705 self.modifyLayerWidgets['key'][1].SetItems(listOfColumns) 02706 self.modifyLayerWidgets['key'][1].SetSelection(0) 02707 02708 # events 02709 self.modifyLayerWidgets['layer'][1].Bind(wx.EVT_COMBOBOX, self.OnChangeLayer) 02710 # self.modifyLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged) 02711 # self.modifyLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged) 02712 # self.modifyLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged) 02713 02714 btnModify = wx.Button(self.modifyPanel, wx.ID_DELETE, _("&Modify layer"), 02715 size=(125,-1)) 02716 btnModify.Bind(wx.EVT_BUTTON, self.OnModifyLayer) 02717 02718 # 02719 # do layout 02720 # 02721 pageSizer = wx.BoxSizer(wx.VERTICAL) 02722 02723 # data area 02724 dataSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) 02725 dataSizer.AddGrowableCol(1) 02726 for key in ('layer', 'driver', 'database', 'table', 'key'): 02727 label, value = self.modifyLayerWidgets[key] 02728 dataSizer.Add(item=label, 02729 flag=wx.ALIGN_CENTER_VERTICAL) 02730 if label.GetLabel() == "Layer:": 02731 dataSizer.Add(item=value, 02732 flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) 02733 else: 02734 dataSizer.Add(item=value, 02735 flag=wx.ALIGN_CENTER_VERTICAL) 02736 02737 pageSizer.Add(item=dataSizer, 02738 proportion=1, 02739 flag=wx.ALL | wx.EXPAND, 02740 border=5) 02741 02742 pageSizer.Add(item=btnModify, 02743 proportion=0, 02744 flag=wx.ALL | wx.ALIGN_RIGHT, 02745 border=5) 02746 02747 self.modifyPanel.SetSizer(pageSizer) 02748 02749 def __getTables(self, driver, database): 02750 """!Get list of tables for given driver and database""" 02751 tables = [] 02752 02753 ret = gcmd.RunCommand('db.tables', 02754 parent = self, 02755 read = True, 02756 flags = 'p', 02757 driver = driver, 02758 database = database) 02759 02760 if ret is None: 02761 wx.MessageBox(parent=self, 02762 message=_("Unable to get list of tables.\n" 02763 "Please use db.connect to set database parameters."), 02764 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 02765 02766 return tables 02767 02768 for table in ret.splitlines(): 02769 tables.append(table) 02770 02771 return tables 02772 02773 def __getColumns(self, driver, database, table): 02774 """!Get list of column of given table""" 02775 columns = [] 02776 02777 ret = gcmd.RunCommand('db.columns', 02778 parent = self, 02779 quiet = True, 02780 read = True, 02781 driver = driver, 02782 database = database, 02783 table = table) 02784 02785 if ret == None: 02786 return columns 02787 02788 for column in ret.splitlines(): 02789 columns.append(column) 02790 02791 return columns 02792 02793 def OnDriverChanged(self, event): 02794 """!Driver selection changed, update list of tables""" 02795 driver = event.GetString() 02796 database = self.addLayerWidgets['database'][1].GetValue() 02797 02798 winTable = self.addLayerWidgets['table'][1] 02799 winKey = self.addLayerWidgets['key'][1] 02800 tables = self.__getTables(driver, database) 02801 02802 winTable.SetItems(tables) 02803 winTable.SetSelection(0) 02804 02805 if len(tables) == 0: 02806 winKey.SetItems([]) 02807 02808 event.Skip() 02809 02810 def OnDatabaseChanged(self, event): 02811 """!Database selection changed, update list of tables""" 02812 event.Skip() 02813 02814 def OnTableChanged(self, event): 02815 """!Table name changed, update list of columns""" 02816 driver = self.addLayerWidgets['driver'][1].GetStringSelection() 02817 database = self.addLayerWidgets['database'][1].GetValue() 02818 table = event.GetString() 02819 02820 win = self.addLayerWidgets['key'][1] 02821 cols = self.__getColumns(driver, database, table) 02822 win.SetItems(cols) 02823 win.SetSelection(0) 02824 02825 event.Skip() 02826 02827 def OnSetDefault(self, event): 02828 """!Set default values""" 02829 driver = self.addLayerWidgets['driver'][1] 02830 database = self.addLayerWidgets['database'][1] 02831 table = self.addLayerWidgets['table'][1] 02832 key = self.addLayerWidgets['key'][1] 02833 02834 driver.SetStringSelection(self.defaultConnect['driver']) 02835 database.SetValue(self.defaultConnect['database']) 02836 tables = self.__getTables(self.defaultConnect['driver'], 02837 self.defaultConnect['database']) 02838 table.SetItems(tables) 02839 table.SetSelection(0) 02840 if len(tables) == 0: 02841 key.SetItems([]) 02842 else: 02843 cols = self.__getColumns(self.defaultConnect['driver'], 02844 self.defaultConnect['database'], 02845 tables[0]) 02846 key.SetItems(cols) 02847 key.SetSelection(0) 02848 02849 event.Skip() 02850 02851 def OnCreateTable(self, event): 02852 """!Create new table (name and key column given)""" 02853 driver = self.addLayerWidgets['driver'][1].GetStringSelection() 02854 database = self.addLayerWidgets['database'][1].GetValue() 02855 table = self.tableWidgets['table'][1].GetValue() 02856 key = self.tableWidgets['key'][1].GetValue() 02857 02858 if not table or not key: 02859 wx.MessageBox(parent=self, 02860 message=_("Unable to create new table. " 02861 "Table name or key column name is missing."), 02862 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 02863 return 02864 02865 if table in self.addLayerWidgets['table'][1].GetItems(): 02866 wx.MessageBox(parent=self, 02867 message=_("Unable to create new table. " 02868 "Table <%s> already exists in the database.") % table, 02869 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 02870 return 02871 02872 # create table 02873 sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key) 02874 02875 gcmd.RunCommand('db.execute', 02876 quiet = True, 02877 parent = self, 02878 stdin = sql, 02879 driver = driver, 02880 database = database) 02881 02882 # update list of tables 02883 tableList = self.addLayerWidgets['table'][1] 02884 tableList.SetItems(self.__getTables(driver, database)) 02885 tableList.SetStringSelection(table) 02886 02887 # update key column selection 02888 keyList = self.addLayerWidgets['key'][1] 02889 keyList.SetItems(self.__getColumns(driver, database, table)) 02890 keyList.SetStringSelection(key) 02891 02892 event.Skip() 02893 02894 def OnAddLayer(self, event): 02895 """!Add new layer to vector map""" 02896 layer = int(self.addLayerWidgets['layer'][1].GetValue()) 02897 layerWin = self.addLayerWidgets['layer'][1] 02898 driver = self.addLayerWidgets['driver'][1].GetStringSelection() 02899 database = self.addLayerWidgets['database'][1].GetValue() 02900 table = self.addLayerWidgets['table'][1].GetStringSelection() 02901 key = self.addLayerWidgets['key'][1].GetStringSelection() 02902 02903 if layer in self.mapDBInfo.layers.keys(): 02904 wx.MessageBox(parent=self, 02905 message=_("Unable to add new layer to vector map <%(vector)s>. " 02906 "Layer %(layer)d already exists.") % 02907 {'vector' : self.mapDBInfo.map, 'layer' : layer}, 02908 caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) 02909 return 02910 02911 # add new layer 02912 ret = gcmd.RunCommand('v.db.connect', 02913 parent = self, 02914 quiet = True, 02915 map = self.mapDBInfo.map, 02916 driver = driver, 02917 database = database, 02918 table = table, 02919 key = key, 02920 layer = layer) 02921 02922 # insert records into table if required 02923 if self.addLayerWidgets['addCat'][0].IsChecked(): 02924 gcmd.RunCommand('v.to.db', 02925 parent = self, 02926 quiet = True, 02927 map = self.mapDBInfo.map, 02928 layer = layer, 02929 qlayer = layer, 02930 option = 'cat', 02931 columns = key) 02932 02933 if ret == 0: 02934 # update dialog (only for new layer) 02935 self.parentDialog.UpdateDialog(layer=layer) 02936 # update db info 02937 self.mapDBInfo = self.parentDialog.mapDBInfo 02938 # increase layer number 02939 layerWin.SetValue(layer+1) 02940 02941 if len(self.mapDBInfo.layers.keys()) == 1: 02942 # first layer add --- enable previously disabled widgets 02943 self.deleteLayer.Enable() 02944 self.deleteTable.Enable() 02945 for label in self.modifyLayerWidgets.keys(): 02946 self.modifyLayerWidgets[label][1].Enable() 02947 02948 def OnDeleteLayer(self, event): 02949 """!Delete layer""" 02950 try: 02951 layer = int(self.deleteLayer.GetValue()) 02952 except: 02953 return 02954 02955 gcmd.RunCommand('v.db.connect', 02956 parent = self, 02957 flags = 'd', 02958 map = self.mapDBInfo.map, 02959 layer = layer) 02960 02961 # drop also table linked to layer which is deleted 02962 if self.deleteTable.IsChecked(): 02963 driver = self.addLayerWidgets['driver'][1].GetStringSelection() 02964 database = self.addLayerWidgets['database'][1].GetValue() 02965 table = self.mapDBInfo.layers[layer]['table'] 02966 sql = 'DROP TABLE %s' % (table) 02967 02968 gcmd.RunCommand('db.execute', 02969 parent = self, 02970 stdin = sql, 02971 quiet = True, 02972 driver = driver, 02973 database = database) 02974 02975 # update list of tables 02976 tableList = self.addLayerWidgets['table'][1] 02977 tableList.SetItems(self.__getTables(driver, database)) 02978 tableList.SetStringSelection(table) 02979 02980 # update dialog 02981 self.parentDialog.UpdateDialog(layer=layer) 02982 # update db info 02983 self.mapDBInfo = self.parentDialog.mapDBInfo 02984 02985 if len(self.mapDBInfo.layers.keys()) == 0: 02986 # disable selected widgets 02987 self.deleteLayer.Enable(False) 02988 self.deleteTable.Enable(False) 02989 for label in self.modifyLayerWidgets.keys(): 02990 self.modifyLayerWidgets[label][1].Enable(False) 02991 02992 event.Skip() 02993 02994 def OnChangeLayer(self, event): 02995 """!Layer number of layer to be deleted is changed""" 02996 try: 02997 layer = int(event.GetString()) 02998 except: 02999 try: 03000 layer = self.mapDBInfo.layers.keys()[0] 03001 except: 03002 return 03003 03004 if self.GetCurrentPage() == self.modifyPanel: 03005 driver = self.mapDBInfo.layers[layer]['driver'] 03006 database = self.mapDBInfo.layers[layer]['database'] 03007 table = self.mapDBInfo.layers[layer]['table'] 03008 listOfColumns = self.__getColumns(driver, database, table) 03009 self.modifyLayerWidgets['driver'][1].SetStringSelection(driver) 03010 self.modifyLayerWidgets['database'][1].SetValue(database) 03011 self.modifyLayerWidgets['table'][1].SetStringSelection(table) 03012 self.modifyLayerWidgets['key'][1].SetItems(listOfColumns) 03013 self.modifyLayerWidgets['key'][1].SetSelection(0) 03014 else: 03015 self.deleteTable.SetLabel(_('Drop also linked attribute table (%s)') % \ 03016 self.mapDBInfo.layers[layer]['table']) 03017 if event: 03018 event.Skip() 03019 03020 def OnModifyLayer(self, event): 03021 """!Modify layer connection settings""" 03022 03023 layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection()) 03024 03025 modify = False 03026 if self.modifyLayerWidgets['driver'][1].GetStringSelection() != \ 03027 self.mapDBInfo.layers[layer]['driver'] or \ 03028 self.modifyLayerWidgets['database'][1].GetStringSelection() != \ 03029 self.mapDBInfo.layers[layer]['database'] or \ 03030 self.modifyLayerWidgets['table'][1].GetStringSelection() != \ 03031 self.mapDBInfo.layers[layer]['table'] or \ 03032 self.modifyLayerWidgets['key'][1].GetStringSelection() != \ 03033 self.mapDBInfo.layers[layer]['key']: 03034 modify = True 03035 03036 if modify: 03037 # delete layer 03038 gcmd.RunCommand('v.db.connect', 03039 parent = self, 03040 quiet = True, 03041 flag = 'd', 03042 map = self.mapDBInfo.map, 03043 layer = layer) 03044 03045 # add modified layer 03046 gcmd.RunCommand('v.db.connect', 03047 quiet = True, 03048 map = self.mapDBInfo.map, 03049 driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(), 03050 database = self.modifyLayerWidgets['database'][1].GetValue(), 03051 table = self.modifyLayerWidgets['table'][1].GetStringSelection(), 03052 key = self.modifyLayerWidgets['key'][1].GetStringSelection(), 03053 layer = int(layer)) 03054 03055 # update dialog (only for new layer) 03056 self.parentDialog.UpdateDialog(layer=layer) 03057 # update db info 03058 self.mapDBInfo = self.parentDialog.mapDBInfo 03059 03060 event.Skip() 03061 03062 def main(argv=None): 03063 if argv is None: 03064 argv = sys.argv 03065 03066 if len(argv) != 2: 03067 print >> sys.stderr, __doc__ 03068 sys.exit() 03069 03070 # Command line arguments of the script to be run are preserved by the 03071 # hotswap.py wrapper but hotswap.py and its options are removed that 03072 # sys.argv looks as if no wrapper was present. 03073 #print "argv:", `argv` 03074 03075 #some applications might require image handlers 03076 wx.InitAllImageHandlers() 03077 03078 app = wx.PySimpleApp() 03079 f = AttributeManager(parent=None, id=wx.ID_ANY, 03080 title="%s - <%s>" % (_("GRASS GIS Attribute Table Manager"), 03081 argv[1]), 03082 size=(900,600), vectorName=argv[1]) 03083 f.Show() 03084 03085 app.MainLoop() 03086 03087 if __name__ == '__main__': 03088 main()