GRASS Programmer's Manual
6.4.2(2012)
|
00001 """ 00002 @package colorrules.py 00003 00004 @brief Dialog for interactive management of raster color tables and 00005 vector rgb_column attributes. 00006 00007 Classes: 00008 - ColorTable 00009 - BuferedWindow 00010 00011 (C) 2008, 2010 by the GRASS Development Team 00012 This program is free software under the GNU General Public License 00013 (>=v2). Read the file COPYING that comes with GRASS for details. 00014 00015 @author Michael Barton (Arizona State University) 00016 @author Martin Landa <landa.martin gmail.com> (various updates) 00017 @author Anna Kratochvilova (load/save raster color tables) 00018 """ 00019 00020 import os 00021 import sys 00022 import shutil 00023 00024 import wx 00025 import wx.lib.colourselect as csel 00026 import wx.lib.scrolledpanel as scrolled 00027 00028 import grass.script as grass 00029 00030 import dbm 00031 import gcmd 00032 import globalvar 00033 import gselect 00034 import render 00035 import utils 00036 from debug import Debug as Debug 00037 from preferences import globalSettings as UserSettings 00038 00039 class ColorTable(wx.Frame): 00040 def __init__(self, parent, raster, id=wx.ID_ANY, title = _("Set color table"), 00041 style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER, 00042 **kwargs): 00043 """!Dialog for interactively entering rules for map management 00044 commands 00045 00046 @param raster True to raster otherwise vector 00047 """ 00048 self.parent = parent # GMFrame 00049 self.raster = raster 00050 00051 wx.Frame.__init__(self, parent, id, title, style = style, **kwargs) 00052 00053 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00054 00055 # input map to change 00056 self.inmap = '' 00057 00058 if self.raster: 00059 # raster properties 00060 self.properties = { 00061 # min cat in raster map 00062 'min' : None, 00063 # max cat in raster map 00064 'max' : None, 00065 } 00066 else: 00067 # vector properties 00068 self.properties = { 00069 # list of database layers for vector (minimum of 1) 00070 'layers' : ['1'], 00071 # list of database columns for vector 00072 'columns' : [], 00073 # vector layer for attribute table to use for setting color 00074 'layer' : 1, 00075 # vector attribute table used for setting color 00076 'table' : '', 00077 # vector attribute column for assigning colors 00078 'column' : '', 00079 # vector attribute column to use for storing colors 00080 'rgb' : '', 00081 } 00082 00083 # rules for creating colortable 00084 self.ruleslines = {} 00085 00086 # instance of render.Map to be associated with display 00087 self.Map = render.Map() 00088 00089 # reference to layer with preview 00090 self.layer = None 00091 00092 if self.raster: 00093 self.SetTitle(_('Create new color table for raster map')) 00094 crlabel = _('Enter raster category values or percents') 00095 else: 00096 self.SetTitle(_('Create new color table for vector map')) 00097 crlabel = _('Enter vector attribute values or ranges (n or n1 to n2)') 00098 00099 # top controls 00100 if self.raster: 00101 maplabel = _('Select raster map:') 00102 else: 00103 maplabel = _('Select vector map:') 00104 inputBox = wx.StaticBox(parent=self, id=wx.ID_ANY, 00105 label=" %s " % maplabel) 00106 self.inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL) 00107 if self.raster: 00108 elem = 'cell' 00109 else: 00110 elem = 'vector' 00111 self.selectionInput = gselect.Select(parent=self, id=wx.ID_ANY, 00112 size=globalvar.DIALOG_GSELECT_SIZE, 00113 type=elem) 00114 00115 self.ovrwrtcheck = wx.CheckBox(parent=self, id=wx.ID_ANY, 00116 label=_('replace existing color table')) 00117 self.ovrwrtcheck.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled')) 00118 00119 if self.raster: 00120 self.btnSave = wx.Button(parent=self, id=wx.ID_SAVE) 00121 self.btnSave.SetToolTipString(_('Save color table to file')) 00122 00123 if not self.raster: 00124 self.cb_vl_label = wx.StaticText(parent=self, id=wx.ID_ANY, 00125 label=_('Layer:')) 00126 self.cb_vc_label = wx.StaticText(parent=self, id=wx.ID_ANY, 00127 label=_('Attribute column:')) 00128 self.cb_vrgb_label = wx.StaticText(parent=self, id=wx.ID_ANY, 00129 label=_('RGB color column:')) 00130 self.cb_vlayer = gselect.LayerSelect(self) 00131 self.cb_vcol = gselect.ColumnSelect(self) 00132 self.cb_vrgb = gselect.ColumnSelect(self) 00133 00134 # color table and preview window 00135 self.cr_label = wx.StaticText(parent=self, id=wx.ID_ANY, 00136 label=crlabel) 00137 self.cr_panel = self._colorRulesPanel() 00138 # add two rules as default 00139 self.AddRules(2) 00140 00141 self.numRules = wx.SpinCtrl(parent=self, id=wx.ID_ANY, 00142 min=1, max=1e6) 00143 00144 # initialize preview display 00145 self.InitDisplay() 00146 self.preview = BufferedWindow(self, id=wx.ID_ANY, size=(400, 300), 00147 Map=self.Map) 00148 self.preview.EraseMap() 00149 00150 self.btnCancel = wx.Button(parent=self, id=wx.ID_CANCEL) 00151 self.btnApply = wx.Button(parent=self, id=wx.ID_APPLY) 00152 self.btnOK = wx.Button(parent=self, id=wx.ID_OK) 00153 self.btnOK.SetDefault() 00154 self.btnOK.Enable(False) 00155 self.btnApply.Enable(False) 00156 00157 self.btnPreview = wx.Button(parent=self, id=wx.ID_ANY, 00158 label=_("Preview")) 00159 self.btnPreview.Enable(False) 00160 self.btnAdd = wx.Button(parent=self, id=wx.ID_ADD) 00161 self.helpbtn = wx.Button(parent=self, id=wx.ID_HELP) 00162 00163 00164 # bindings 00165 self.Bind(wx.EVT_BUTTON, self.OnHelp, self.helpbtn) 00166 self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput) 00167 self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel) 00168 self.Bind(wx.EVT_BUTTON, self.OnApply, self.btnApply) 00169 self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK) 00170 self.Bind(wx.EVT_BUTTON, self.OnPreview, self.btnPreview) 00171 self.Bind(wx.EVT_BUTTON, self.OnAddRules, self.btnAdd) 00172 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) 00173 00174 # additional bindings for raster/vector color management 00175 if self.raster: 00176 self.Bind(wx.EVT_BUTTON, self.OnSaveTable, self.btnSave) 00177 else: 00178 self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.cb_vlayer) 00179 self.Bind(wx.EVT_COMBOBOX, self.OnColumnSelection, self.cb_vcol) 00180 self.Bind(wx.EVT_COMBOBOX, self.OnRGBColSelection, self.cb_vrgb) 00181 00182 # set map layer from layer tree 00183 try: 00184 layer = self.parent.curr_page.maptree.layer_selected 00185 except: 00186 layer = None 00187 if layer: 00188 mapLayer = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer'] 00189 name = mapLayer.GetName() 00190 type = mapLayer.GetType() 00191 self.selectionInput.SetValue(name) 00192 self.inmap = name 00193 self.OnSelectionInput(None) 00194 00195 # layout 00196 self.__doLayout() 00197 self.SetMinSize(self.GetSize()) 00198 00199 self.CentreOnScreen() 00200 self.Show() 00201 00202 def __doLayout(self): 00203 sizer = wx.BoxSizer(wx.VERTICAL) 00204 00205 # 00206 # input 00207 # 00208 self.inputSizer.Add(item=self.selectionInput, 00209 flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=5) 00210 replaceSizer = wx.BoxSizer(wx.HORIZONTAL) 00211 replaceSizer.Add(item=self.ovrwrtcheck, proportion=1, 00212 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1) 00213 if self.raster: 00214 replaceSizer.Add(item=self.btnSave, proportion=0, 00215 flag=wx.ALIGN_RIGHT | wx.ALL, border=5) 00216 00217 self.inputSizer.Add(item=replaceSizer, proportion=1, 00218 flag=wx.ALL | wx.EXPAND, border=0) 00219 00220 # 00221 # body & preview 00222 # 00223 bodySizer = wx.GridBagSizer(hgap=5, vgap=5) 00224 row = 0 00225 bodySizer.Add(item=self.cr_label, pos=(row, 0), span=(1, 3), 00226 flag=wx.ALL, border=5) 00227 00228 if not self.raster: 00229 vSizer = wx.GridBagSizer(hgap=5, vgap=5) 00230 vSizer.Add(self.cb_vl_label, pos=(0, 0), 00231 flag=wx.ALIGN_CENTER_VERTICAL) 00232 vSizer.Add(self.cb_vlayer, pos=(0, 1), 00233 flag=wx.ALIGN_CENTER_VERTICAL) 00234 vSizer.Add(self.cb_vc_label, pos=(0, 2), 00235 flag=wx.ALIGN_CENTER_VERTICAL) 00236 vSizer.Add(self.cb_vcol, pos=(0, 3), 00237 flag=wx.ALIGN_CENTER_VERTICAL) 00238 vSizer.Add(self.cb_vrgb_label, pos=(1, 2), 00239 flag=wx.ALIGN_CENTER_VERTICAL) 00240 vSizer.Add(self.cb_vrgb, pos=(1, 3), 00241 flag=wx.ALIGN_CENTER_VERTICAL) 00242 row += 1 00243 bodySizer.Add(item=vSizer, pos=(row, 0), span=(1, 3)) 00244 00245 row += 1 00246 bodySizer.Add(item=self.cr_panel, pos=(row, 0), span=(1, 2)) 00247 00248 bodySizer.Add(item=self.preview, pos=(row, 2), 00249 flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10) 00250 bodySizer.AddGrowableRow(row) 00251 bodySizer.AddGrowableCol(2) 00252 00253 row += 1 00254 bodySizer.Add(item=self.numRules, pos=(row, 0), 00255 flag=wx.ALIGN_CENTER_VERTICAL) 00256 00257 bodySizer.Add(item=self.btnAdd, pos=(row, 1)) 00258 bodySizer.Add(item=self.btnPreview, pos=(row, 2), 00259 flag=wx.ALIGN_RIGHT) 00260 00261 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00262 btnSizer.Add(self.helpbtn, 00263 flag=wx.LEFT | wx.RIGHT, border=5) 00264 btnSizer.Add(self.btnCancel, 00265 flag=wx.LEFT | wx.RIGHT, border=5) 00266 btnSizer.Add(self.btnApply, 00267 flag=wx.LEFT | wx.RIGHT, border=5) 00268 btnSizer.Add(self.btnOK, 00269 flag=wx.LEFT | wx.RIGHT, border=5) 00270 00271 sizer.Add(item=self.inputSizer, proportion=0, 00272 flag=wx.ALL | wx.EXPAND, border=5) 00273 00274 sizer.Add(item=bodySizer, proportion=1, 00275 flag=wx.ALL | wx.EXPAND, border=5) 00276 00277 sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY, 00278 style=wx.LI_HORIZONTAL), 00279 proportion=0, 00280 flag=wx.EXPAND | wx.ALL, border=5) 00281 00282 sizer.Add(item=btnSizer, proportion=0, 00283 flag=wx.ALL | wx.ALIGN_RIGHT, border=5) 00284 00285 self.SetSizer(sizer) 00286 sizer.Fit(self) 00287 self.Layout() 00288 00289 def _colorRulesPanel(self): 00290 """!Create rules panel""" 00291 cr_panel = scrolled.ScrolledPanel(parent=self, id=wx.ID_ANY, 00292 size=(180, 300), 00293 style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) 00294 cr_panel.SetupScrolling(scroll_x = False) 00295 self.cr_sizer = wx.GridBagSizer(vgap=2, hgap=4) 00296 00297 cr_panel.SetSizer(self.cr_sizer) 00298 cr_panel.SetAutoLayout(True) 00299 00300 return cr_panel 00301 00302 def OnAddRules(self, event): 00303 """!Add rules button pressed""" 00304 nrules = self.numRules.GetValue() 00305 self.AddRules(nrules) 00306 00307 def AddRules(self, nrules): 00308 """!Add rules""" 00309 snum = len(self.ruleslines.keys()) 00310 for num in range(snum, snum + nrules): 00311 # enable 00312 enable = wx.CheckBox(parent=self.cr_panel, id=num) 00313 enable.SetValue(True) 00314 self.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable, enable) 00315 # value 00316 txt_ctrl = wx.TextCtrl(parent=self.cr_panel, id=1000 + num, 00317 size=(90, -1), 00318 style=wx.TE_NOHIDESEL) 00319 self.Bind(wx.EVT_TEXT, self.OnRuleValue, txt_ctrl) 00320 # color 00321 color_ctrl = csel.ColourSelect(self.cr_panel, id=2000 + num, 00322 size = globalvar.DIALOG_COLOR_SIZE) 00323 self.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor, color_ctrl) 00324 self.ruleslines[enable.GetId()] = { 'value' : '', 00325 'color': "0:0:0" } 00326 00327 self.cr_sizer.Add(item=enable, pos=(num, 0), 00328 flag=wx.ALIGN_CENTER_VERTICAL) 00329 self.cr_sizer.Add(item=txt_ctrl, pos=(num, 1), 00330 flag=wx.ALIGN_CENTER | wx.RIGHT, border=5) 00331 self.cr_sizer.Add(item=color_ctrl, pos=(num, 2), 00332 flag=wx.ALIGN_CENTER | wx.RIGHT, border=10) 00333 00334 self.cr_panel.Layout() 00335 self.cr_panel.SetupScrolling(scroll_x = False) 00336 00337 def InitDisplay(self): 00338 """!Initialize preview display, set dimensions and region 00339 """ 00340 self.width = self.Map.width = 400 00341 self.height = self.Map.height = 300 00342 self.Map.geom = self.width, self.height 00343 00344 def OnErase(self, event): 00345 """!Erase the histogram display 00346 """ 00347 self.PreviewWindow.Draw(self.HistWindow.pdc, pdctype='clear') 00348 00349 def OnCloseWindow(self, event): 00350 """!Window closed 00351 Also remove associated rendered images 00352 """ 00353 self.Map.Clean() 00354 self.Destroy() 00355 00356 def OnSelectionInput(self, event): 00357 """!Raster/vector map selected""" 00358 if event: 00359 self.inmap = event.GetString() 00360 00361 if self.inmap: 00362 if self.raster: 00363 mapType = 'cell' 00364 else: 00365 mapType = 'vector' 00366 if not grass.find_file(name = self.inmap, element = mapType)['file']: 00367 self.inmap = None 00368 00369 if not self.inmap: 00370 self.btnPreview.Enable(False) 00371 self.btnOK.Enable(False) 00372 self.btnApply.Enable(False) 00373 self.OnLoadTable(event) 00374 return 00375 00376 if self.raster: 00377 info = grass.raster_info(map = self.inmap) 00378 00379 if info: 00380 self.properties['min'] = info['min'] 00381 self.properties['max'] = info['max'] 00382 self.OnLoadTable(event) 00383 else: 00384 self.inmap = '' 00385 self.properties['min'] = self.properties['max'] = None 00386 self.btnPreview.Enable(False) 00387 self.btnOK.Enable(False) 00388 self.btnApply.Enable(False) 00389 self.preview.EraseMap() 00390 self.cr_label.SetLabel(_('Enter raster category values or percents')) 00391 return 00392 00393 if info['datatype'] == 'CELL': 00394 mapRange = _('range') 00395 else: 00396 mapRange = _('fp range') 00397 self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') % 00398 { 'range' : mapRange, 00399 'min' : self.properties['min'], 00400 'max' : self.properties['max'] }) 00401 00402 else: 00403 # initialize layer selection combobox 00404 self.cb_vlayer.InsertLayers(self.inmap) 00405 # initialize attribute table for layer=1 00406 layer = int(self.properties['layer']) 00407 self.properties['table'] = gselect.VectorDBInfo(self.inmap).layers[layer]['table'] 00408 # initialize column selection comboboxes 00409 self.cb_vcol.InsertColumns(vector=self.inmap, layer=layer) 00410 self.cb_vrgb.InsertColumns(vector=self.inmap, layer=layer) 00411 self.Update() 00412 00413 self.btnPreview.Enable(True) 00414 self.btnOK.Enable(True) 00415 self.btnApply.Enable(True) 00416 00417 def OnLayerSelection(self, event): 00418 # reset choices in column selection comboboxes if layer changes 00419 self.vlayer = int(event.GetString()) 00420 self.vtable = gselect.VectorDBInfo(self.inmap).layers[str(self.vlayer)] 00421 self.cb_vcol.InsertColumns(vector=self.inmap, layer=self.vlayer) 00422 self.cb_vrgb.InsertColumns(vector=self.inmap, layer=self.vlayer) 00423 self.Update() 00424 00425 def OnColumnSelection(self, event): 00426 self.properties['column'] = event.GetString() 00427 00428 def OnRGBColSelection(self, event): 00429 self.properties['rgb'] = event.GetString() 00430 00431 def OnRuleEnable(self, event): 00432 """!Rule enabled/disabled""" 00433 id = event.GetId() 00434 00435 if event.IsChecked(): 00436 value = self.FindWindowById(id+1000).GetValue() 00437 color = self.FindWindowById(id+2000).GetValue() 00438 color_str = str(color[0]) + ':' \ 00439 + str(color[1]) + ':' + \ 00440 str(color[2]) 00441 00442 self.ruleslines[id] = { 00443 'value' : value, 00444 'color' : color_str } 00445 else: 00446 del self.ruleslines[id] 00447 00448 def OnRuleValue(self, event): 00449 """!Rule value changed""" 00450 num = event.GetId() 00451 vals = event.GetString().strip() 00452 00453 if vals == '': 00454 return 00455 00456 tc = self.FindWindowById(num) 00457 00458 if self.raster: 00459 self.ruleslines[num-1000]['value'] = vals 00460 00461 else: 00462 if self.properties['column'] == '' or self.properties['rgb'] == '': 00463 tc.SetValue('') 00464 gcmd.GMessage(parent=self, 00465 message=_("Please select attribute column " 00466 "and RGB color column first")) 00467 else: 00468 try: 00469 self.ruleslines[num-1000]['value'] = self.SQLConvert(vals) 00470 except ValueError: 00471 tc.SetValue('') 00472 self.ruleslines[num-1000]['value'] = '' 00473 return 00474 00475 def OnRuleColor(self, event): 00476 """!Rule color changed""" 00477 num = event.GetId() 00478 00479 rgba_color = event.GetValue() 00480 00481 rgb_string = str(rgba_color[0]) + ':' \ 00482 + str(rgba_color[1]) + ':' + \ 00483 str(rgba_color[2]) 00484 00485 self.ruleslines[num-2000]['color'] = rgb_string 00486 00487 def SQLConvert(self, vals): 00488 valslist = [] 00489 valslist = vals.split('to') 00490 if len(valslist) == 1: 00491 sqlrule = '%s=%s' % (self.properties['column'], valslist[0]) 00492 elif len(valslist) > 1: 00493 sqlrule = '%s>=%s AND %s<=%s' % (self.properties['column'], valslist[0], 00494 self.properties['column'], valslist[1]) 00495 else: 00496 return None 00497 00498 return sqlrule 00499 00500 def OnLoadTable(self, event): 00501 """!Load current color table (using `r.colors.out`)""" 00502 self.ruleslines.clear() 00503 self.cr_panel.DestroyChildren() 00504 if self.inmap: 00505 ctable = gcmd.RunCommand('r.colors.out', 00506 parent = self, 00507 read = True, 00508 map = self.inmap, 00509 rules = '-') 00510 else: 00511 self.OnPreview(event) 00512 return 00513 00514 rulesNumber = len(ctable.splitlines()) 00515 self.AddRules(rulesNumber) 00516 00517 count = 0 00518 for line in ctable.splitlines(): 00519 value, color = map(lambda x: x.strip(), line.split(' ')) 00520 self.ruleslines[count]['value'] = value 00521 self.ruleslines[count]['color'] = color 00522 self.FindWindowById(count + 1000).SetValue(value) 00523 rgb = list() 00524 for c in color.split(':'): 00525 rgb.append(int(c)) 00526 self.FindWindowById(count + 2000).SetColour(rgb) 00527 count += 1 00528 00529 self.OnPreview(tmp = False) 00530 00531 def OnSaveTable(self, event): 00532 """!Save color table to file""" 00533 rulestxt = '' 00534 for rule in self.ruleslines.itervalues(): 00535 if not rule['value']: 00536 continue 00537 rulestxt += rule['value'] + ' ' + rule['color'] + '\n' 00538 if not rulestxt: 00539 gcmd.GMessage(message = _("Nothing to save."), 00540 parent = self) 00541 return 00542 00543 dlg = wx.FileDialog(parent = self, 00544 message = _("Save color table to file"), 00545 defaultDir = os.getcwd(), style = wx.SAVE | wx.OVERWRITE_PROMPT) 00546 if dlg.ShowModal() == wx.ID_OK: 00547 path = dlg.GetPath() 00548 fd = open(path, 'w') 00549 fd.write(rulestxt) 00550 fd.close() 00551 dlg.Destroy() 00552 00553 def OnApply(self, event): 00554 """!Apply selected color table 00555 00556 @return True on success otherwise False 00557 """ 00558 ret = self.CreateColorTable() 00559 display = self.parent.GetLayerTree().GetMapDisplay() 00560 if display and display.IsAutoRendered(): 00561 display.GetWindow().UpdateMap(render = True) 00562 00563 return ret 00564 00565 def OnOK(self, event): 00566 """!Apply selected color table and close the dialog""" 00567 if self.OnApply(event): 00568 self.Destroy() 00569 00570 def OnCancel(self, event): 00571 """!Do not apply any changes and close the dialog""" 00572 self.Destroy() 00573 00574 def OnPreview(self, event = None, tmp = True): 00575 """!Update preview (based on computational region)""" 00576 if not self.inmap: 00577 self.preview.EraseMap() 00578 return 00579 00580 # raster 00581 if self.raster: 00582 cmdlist = ['d.rast', 00583 'map=%s' % self.inmap] 00584 ltype = 'raster' 00585 00586 # find existing color table and copy to temp file 00587 try: 00588 name, mapset = self.inmap.split('@') 00589 except ValueError: 00590 name = self.inmap 00591 mapset = grass.find_file(self.inmap, element = 'cell')['mapset'] 00592 if not mapset: 00593 return 00594 old_colrtable = None 00595 if mapset == grass.gisenv()['MAPSET']: 00596 old_colrtable = grass.find_file(name=name, element='colr')['file'] 00597 else: 00598 old_colrtable = grass.find_file(name=name, element='colr2/' + mapset)['file'] 00599 00600 if old_colrtable: 00601 colrtemp = utils.GetTempfile() 00602 shutil.copyfile(old_colrtable, colrtemp) 00603 # vector 00604 else: 00605 cmdlist = ['d.vect', 00606 '-a', 00607 'map=%s' % self.inmap, 00608 'rgb_column=%s' % self.properties["rgb"], 00609 'type=point,line,boundary,area'] 00610 ltype = 'vector' 00611 00612 if not self.layer: 00613 self.layer = self.Map.AddLayer(type=ltype, name='preview', command=cmdlist, 00614 l_active=True, l_hidden=False, l_opacity=1.0, 00615 l_render=False) 00616 else: 00617 self.layer.SetCmd(cmdlist) 00618 00619 # apply new color table and display preview 00620 self.CreateColorTable(force = True) 00621 self.preview.UpdatePreview() 00622 00623 # restore previous color table 00624 if self.raster and tmp: 00625 if old_colrtable: 00626 shutil.copyfile(colrtemp, old_colrtable) 00627 os.remove(colrtemp) 00628 else: 00629 gcmd.RunCommand('r.colors', 00630 parent = self, 00631 flags = 'r', 00632 map = self.inmap) 00633 00634 def OnHelp(self, event): 00635 """!Show GRASS manual page""" 00636 if self.raster: 00637 cmd = 'r.colors' 00638 else: 00639 cmd = 'vcolors' 00640 gcmd.RunCommand('g.manual', 00641 quiet = True, 00642 parent = self, 00643 entry = cmd) 00644 00645 def _IsNumber(self, s): 00646 """!Check if 's' is a number""" 00647 try: 00648 float(s) 00649 return True 00650 except ValueError: 00651 return False 00652 00653 def CreateColorTable(self, force = False): 00654 """!Creates color table 00655 00656 @return True on success 00657 @return False on failure 00658 """ 00659 rulestxt = '' 00660 00661 for rule in self.ruleslines.itervalues(): 00662 if not rule['value']: # skip empty rules 00663 continue 00664 00665 if self.raster: 00666 if rule['value'] not in ('nv', 'default') and \ 00667 rule['value'][-1] != '%' and \ 00668 not self._IsNumber(rule['value']): 00669 gcmd.GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'], 00670 parent = self) 00671 return False 00672 00673 rulestxt += rule['value'] + ' ' + rule['color'] + '\n' 00674 else: 00675 rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'], 00676 self.properties['rgb'], 00677 rule['color'], 00678 rule['value']) 00679 if not rulestxt: 00680 return False 00681 00682 gtemp = utils.GetTempfile() 00683 output = open(gtemp, "w") 00684 try: 00685 output.write(rulestxt) 00686 finally: 00687 output.close() 00688 00689 if self.raster: 00690 if not force and \ 00691 not self.ovrwrtcheck.IsChecked(): 00692 flags = 'w' 00693 else: 00694 flags = '' 00695 00696 ret = gcmd.RunCommand('r.colors', 00697 flags = flags, 00698 map = self.inmap, 00699 rules = gtemp) 00700 if ret != 0: 00701 gcmd.GMessage(_("Color table already exists. " 00702 "Check out 'replace existing color table' to " 00703 "overwrite it."), 00704 parent = self) 00705 return False 00706 00707 else: 00708 gcmd.RunCommand('db.execute', 00709 parent = self, 00710 input = gtemp) 00711 00712 return True 00713 00714 class BufferedWindow(wx.Window): 00715 """!A Buffered window class""" 00716 def __init__(self, parent, id, 00717 style=wx.NO_FULL_REPAINT_ON_RESIZE, 00718 Map=None, **kwargs): 00719 00720 wx.Window.__init__(self, parent, id, style = style, **kwargs) 00721 00722 self.parent = parent 00723 self.Map = Map 00724 00725 # re-render the map from GRASS or just redraw image 00726 self.render = True 00727 # indicates whether or not a resize event has taken place 00728 self.resize = False 00729 00730 # 00731 # event bindings 00732 # 00733 self.Bind(wx.EVT_PAINT, self.OnPaint) 00734 self.Bind(wx.EVT_IDLE, self.OnIdle) 00735 self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) 00736 00737 # 00738 # render output objects 00739 # 00740 # image file to be rendered 00741 self.mapfile = None 00742 # wx.Image object (self.mapfile) 00743 self.img = None 00744 00745 self.pdc = wx.PseudoDC() 00746 # will store an off screen empty bitmap for saving to file 00747 self._Buffer = None 00748 00749 # make sure that extents are updated at init 00750 self.Map.region = self.Map.GetRegion() 00751 self.Map.SetRegion() 00752 00753 def Draw(self, pdc, img=None, pdctype='image'): 00754 """!Draws preview or clears window""" 00755 pdc.BeginDrawing() 00756 00757 Debug.msg (3, "BufferedWindow.Draw(): pdctype=%s" % (pdctype)) 00758 00759 if pdctype == 'clear': # erase the display 00760 bg = wx.WHITE_BRUSH 00761 pdc.SetBackground(bg) 00762 pdc.Clear() 00763 self.Refresh() 00764 pdc.EndDrawing() 00765 return 00766 00767 if pdctype == 'image' and img: 00768 bg = wx.TRANSPARENT_BRUSH 00769 pdc.SetBackground(bg) 00770 bitmap = wx.BitmapFromImage(img) 00771 w, h = bitmap.GetSize() 00772 pdc.DrawBitmap(bitmap, 0, 0, True) # draw the composite map 00773 00774 pdc.EndDrawing() 00775 self.Refresh() 00776 00777 def OnPaint(self, event): 00778 """!Draw pseudo DC to buffer""" 00779 self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height) 00780 dc = wx.BufferedPaintDC(self, self._Buffer) 00781 00782 # use PrepareDC to set position correctly 00783 self.PrepareDC(dc) 00784 00785 # we need to clear the dc BEFORE calling PrepareDC 00786 bg = wx.Brush(self.GetBackgroundColour()) 00787 dc.SetBackground(bg) 00788 dc.Clear() 00789 00790 # create a clipping rect from our position and size 00791 # and the Update Region 00792 rgn = self.GetUpdateRegion() 00793 r = rgn.GetBox() 00794 00795 # draw to the dc using the calculated clipping rect 00796 self.pdc.DrawToDCClipped(dc, r) 00797 00798 def OnSize(self, event): 00799 """!Init image size to match window size""" 00800 # set size of the input image 00801 self.Map.width, self.Map.height = self.GetClientSize() 00802 00803 # Make new off screen bitmap: this bitmap will always have the 00804 # current drawing in it, so it can be used to save the image to 00805 # a file, or whatever. 00806 self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height) 00807 00808 # get the image to be rendered 00809 self.img = self.GetImage() 00810 00811 # update map display 00812 if self.img and self.Map.width + self.Map.height > 0: # scale image during resize 00813 self.img = self.img.Scale(self.Map.width, self.Map.height) 00814 self.render = False 00815 self.UpdatePreview() 00816 00817 # re-render image on idle 00818 self.resize = True 00819 00820 def OnIdle(self, event): 00821 """!Only re-render a preview image from GRASS during 00822 idle time instead of multiple times during resizing. 00823 """ 00824 if self.resize: 00825 self.render = True 00826 self.UpdatePreview() 00827 event.Skip() 00828 00829 def GetImage(self): 00830 """!Converts files to wx.Image""" 00831 if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \ 00832 os.path.getsize(self.Map.mapfile): 00833 img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY) 00834 else: 00835 img = None 00836 00837 return img 00838 00839 def UpdatePreview(self, img=None): 00840 """!Update canvas if window changes geometry""" 00841 Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render)) 00842 oldfont = "" 00843 oldencoding = "" 00844 00845 if self.render: 00846 # make sure that extents are updated 00847 self.Map.region = self.Map.GetRegion() 00848 self.Map.SetRegion() 00849 00850 # render new map images 00851 self.mapfile = self.Map.Render(force=self.render) 00852 self.img = self.GetImage() 00853 self.resize = False 00854 00855 if not self.img: 00856 return 00857 00858 # paint images to PseudoDC 00859 self.pdc.Clear() 00860 self.pdc.RemoveAll() 00861 # draw map image background 00862 self.Draw(self.pdc, self.img, pdctype='image') 00863 00864 self.resize = False 00865 00866 def EraseMap(self): 00867 """!Erase preview""" 00868 self.Draw(self.pdc, pdctype='clear') 00869