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