GRASS Programmer's Manual  6.4.4(2014)-r
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
colorrules.py
Go to the documentation of this file.
1 """
2 @package module.colorrules
3 
4 @brief Dialog for interactive management of raster/vector color tables
5 and color rules.
6 
7 Classes:
8  - colorrules::RulesPanel
9  - colorrules::ColorTable
10  - colorrules::RasterColorTable
11  - colorrules::VectorColorTable
12  - colorrules::BufferedWindow
13 
14 (C) 2008, 2010-2011 by the GRASS Development Team
15 
16 This program is free software under the GNU General Public License
17 (>=v2). Read the file COPYING that comes with GRASS for details.
18 
19 @author Michael Barton (Arizona State University)
20 @author Martin Landa <landa.martin gmail.com> (various updates)
21 @author Anna Kratochvilova <kratochanna gmail.com> (split to base and derived classes)
22 """
23 
24 import os
25 import shutil
26 import copy
27 import tempfile
28 
29 import wx
30 import wx.lib.colourselect as csel
31 import wx.lib.scrolledpanel as scrolled
32 import wx.lib.filebrowsebutton as filebrowse
33 
34 import grass.script as grass
35 
36 from core import globalvar
37 from core import utils
38 from core.gcmd import GMessage, RunCommand, GError
39 from gui_core.gselect import Select, LayerSelect, ColumnSelect, VectorDBInfo
40 from core.render import Map
41 from gui_core.forms import GUI
42 from core.debug import Debug as Debug
43 from core.settings import UserSettings
44 
45 class RulesPanel:
46  def __init__(self, parent, mapType, attributeType, properties, panelWidth = 180):
47  """!Create rules panel
48 
49  @param mapType raster/vector
50  @param attributeType color/size for choosing widget type
51  @param properties properties of classes derived from ColorTable
52  @param panelWidth width of scroll panel"""
53 
54  self.ruleslines = {}
55  self.mapType = mapType
56  self.attributeType = attributeType
57  self.properties = properties
58  self.parent = parent
59  self.panelWidth = panelWidth
60 
61  self.mainSizer = wx.FlexGridSizer(cols = 3, vgap = 6, hgap = 4)
62  # put small border at the top of panel
63  for i in range(3):
64  self.mainSizer.Add(item = wx.Size(3, 3))
65 
66  self.mainPanel = scrolled.ScrolledPanel(parent, id = wx.ID_ANY,
67  size = (self.panelWidth, 300),
68  style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
69 
70  # (un)check all
71  self.checkAll = wx.CheckBox(parent, id = wx.ID_ANY, label = _("Check all"))
72  self.checkAll.SetValue(True)
73  # clear button
74  self.clearAll = wx.Button(parent, id = wx.ID_ANY, label = _("Clear all"))
75  # determines how many rules should be added
76  self.numRules = wx.SpinCtrl(parent, id = wx.ID_ANY,
77  min = 1, max = 1e6, initial = 1)
78  # add rules
79  self.btnAdd = wx.Button(parent, id = wx.ID_ADD)
80 
81  self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAddRules)
82  self.checkAll.Bind(wx.EVT_CHECKBOX, self.OnCheckAll)
83  self.clearAll.Bind(wx.EVT_BUTTON, self.OnClearAll)
84 
85  self.mainPanel.SetSizer(self.mainSizer)
86  self.mainPanel.SetAutoLayout(True)
87  self.mainPanel.SetupScrolling()
88 
89  def Clear(self):
90  """!Clear and widgets and delete information"""
91  self.ruleslines.clear()
92  self.mainSizer.Clear(deleteWindows=True)
93 
94  def OnCheckAll(self, event):
95  """!(Un)check all rules"""
96  check = event.GetInt()
97  for child in self.mainPanel.GetChildren():
98  if child.GetName() == 'enable':
99  child.SetValue(check)
100  else:
101  child.Enable(check)
102 
103  def OnClearAll(self, event):
104  """!Delete all widgets in panel"""
105  self.Clear()
106 
107  def OnAddRules(self, event):
108  """!Add rules button pressed"""
109  nrules = self.numRules.GetValue()
110  self.AddRules(nrules)
111 
112  def AddRules(self, nrules, start = False):
113  """!Add rules
114  @param start set widgets (not append)"""
115 
116  snum = len(self.ruleslines.keys())
117  if start:
118  snum = 0
119  for num in range(snum, snum + nrules):
120  # enable
121  enable = wx.CheckBox(parent = self.mainPanel, id = num)
122  enable.SetValue(True)
123  enable.SetName('enable')
124  enable.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable)
125  # value
126  txt_ctrl = wx.TextCtrl(parent = self.mainPanel, id = 1000 + num,
127  size = (80, -1),
128  style = wx.TE_NOHIDESEL)
129  if self.mapType == 'vector':
130  txt_ctrl.SetToolTipString(_("Enter vector attribute values"))
131  txt_ctrl.Bind(wx.EVT_TEXT, self.OnRuleValue)
132  txt_ctrl.SetName('source')
133  if self.attributeType == 'color':
134  # color
135  columnCtrl = csel.ColourSelect(self.mainPanel, id = 2000 + num,
136  size = globalvar.DIALOG_COLOR_SIZE)
137  columnCtrl.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor)
138  columnCtrl.SetName('target')
139  if not start:
140  self.ruleslines[enable.GetId()] = { 'value' : '',
141  'color': "0:0:0" }
142  else:
143  # size or width
144  init = 2
145  if self.attributeType == 'size':
146  init = 100
147  columnCtrl = wx.SpinCtrl(self.mainPanel, id = 2000 + num,
148  size = (50, -1), min = 1, max = 1e4,
149  initial = init)
150  columnCtrl.Bind(wx.EVT_SPINCTRL, self.OnRuleSize)
151  columnCtrl.Bind(wx.EVT_TEXT, self.OnRuleSize)
152  columnCtrl.SetName('target')
153  if not start:
154  self.ruleslines[enable.GetId()] = { 'value' : '',
155  self.attributeType: init }
156 
157  self.mainSizer.Add(item = enable, proportion = 0,
158  flag = wx.ALIGN_CENTER_VERTICAL)
159  self.mainSizer.Add(item = txt_ctrl, proportion = 0,
160  flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
161  self.mainSizer.Add(item = columnCtrl, proportion = 0,
162  flag = wx.ALIGN_CENTER | wx.RIGHT, border = 10)
163 
164  self.mainPanel.Layout()
165  self.mainPanel.SetupScrolling(scroll_x = False)
166 
167  def OnRuleEnable(self, event):
168  """!Rule enabled/disabled"""
169  id = event.GetId()
170 
171  if event.IsChecked():
172  self.mainPanel.FindWindowById(id + 1000).Enable()
173  self.mainPanel.FindWindowById(id + 2000).Enable()
174  if self.mapType == 'vector' and not self.parent.GetParent().colorTable:
175  vals = []
176  vals.append(self.mainPanel.FindWindowById(id + 1000).GetValue())
177  try:
178  vals.append(self.mainPanel.FindWindowById(id + 1 + 1000).GetValue())
179  except AttributeError:
180  vals.append(None)
181  value = self.SQLConvert(vals)
182  else:
183  value = self.mainPanel.FindWindowById(id + 1000).GetValue()
184  color = self.mainPanel.FindWindowById(id + 2000).GetValue()
185 
186  if self.attributeType == 'color':
187  # color
188  color_str = str(color[0]) + ':' \
189  + str(color[1]) + ':' \
190  + str(color[2])
191  self.ruleslines[id] = {'value' : value,
192  'color' : color_str }
193 
194  else:
195  # size or width
196  self.ruleslines[id] = {'value' : value,
197  self.attributeType : float(color) }
198 
199  else:
200  self.mainPanel.FindWindowById(id + 1000).Disable()
201  self.mainPanel.FindWindowById(id + 2000).Disable()
202  del self.ruleslines[id]
203 
204  def OnRuleColor(self, event):
205  """!Rule color changed"""
206  num = event.GetId()
207 
208  rgba_color = event.GetValue()
209 
210  rgb_string = str(rgba_color[0]) + ':' \
211  + str(rgba_color[1]) + ':' \
212  + str(rgba_color[2])
213 
214  self.ruleslines[num-2000]['color'] = rgb_string
215 
216  def OnRuleSize(self, event):
217  """!Rule size changed"""
218  num = event.GetId()
219  size = event.GetInt()
220 
221  self.ruleslines[num - 2000][self.attributeType] = size
222 
223  def OnRuleValue(self, event):
224  """!Rule value changed"""
225  num = event.GetId()
226  val = event.GetString().strip()
227 
228  if val == '':
229  return
230  try:
231  table = self.parent.colorTable
232  except AttributeError:
233  # due to panel/scrollpanel in vector dialog
234  if isinstance(self.parent.GetParent(), RasterColorTable):
235  table = self.parent.GetParent().colorTable
236  else:
237  table = self.parent.GetParent().GetParent().colorTable
238  if table:
239  self.SetRasterRule(num, val)
240  else:
241  self.SetVectorRule(num, val)
242 
243  def SetRasterRule(self, num, val):
244  """!Set raster rule"""
245  self.ruleslines[num - 1000]['value'] = val
246 
247  def SetVectorRule(self, num, val):
248  """!Set vector rule"""
249  vals = []
250  vals.append(val)
251  try:
252  vals.append(self.mainPanel.FindWindowById(num + 1).GetValue())
253  except AttributeError:
254  vals.append(None)
255  self.ruleslines[num - 1000]['value'] = self.SQLConvert(vals)
256 
257  def Enable(self, enable = True):
258  """!Enable/Disable all widgets"""
259  for child in self.mainPanel.GetChildren():
260  child.Enable(enable)
261  sql = True
262  self.LoadRulesline(sql)# todo
263  self.btnAdd.Enable(enable)
264  self.numRules.Enable(enable)
265  self.checkAll.Enable(enable)
266  self.clearAll.Enable(enable)
267 
268 
269  def LoadRules(self):
270  message = ""
271  for item in range(len(self.ruleslines)):
272  try:
273  self.mainPanel.FindWindowById(item + 1000).SetValue(self.ruleslines[item]['value'])
274  r, g, b = (0, 0, 0) # default
275  if not self.ruleslines[item][self.attributeType]:
276  if self.attributeType == 'color':
277  self.ruleslines[item][self.attributeType] = '%d:%d:%d' % (r, g, b)
278  elif self.attributeType == 'size':
279  self.ruleslines[item][self.attributeType] = 100
280  elif self.attributeType == 'width':
281  self.ruleslines[item][self.attributeType] = 2
282 
283  if self.attributeType == 'color':
284  try:
285  r, g, b = map(int, self.ruleslines[item][self.attributeType].split(':'))
286  except ValueError, e:
287  message = _("Bad color format. Use color format '0:0:0'")
288  self.mainPanel.FindWindowById(item + 2000).SetValue((r, g, b))
289  else:
290  value = float(self.ruleslines[item][self.attributeType])
291  self.mainPanel.FindWindowById(item + 2000).SetValue(value)
292  except:
293  continue
294 
295  if message:
296  GMessage(parent = self.parent, message = message)
297  return False
298 
299  return True
300 
301  def SQLConvert(self, vals):
302  """!Prepare value for SQL query"""
303  if vals[0].isdigit():
304  sqlrule = '%s=%s' % (self.properties['sourceColumn'], vals[0])
305  if vals[1]:
306  sqlrule += ' AND %s<%s' % (self.properties['sourceColumn'], vals[1])
307  else:
308  sqlrule = '%s=%s' % (self.properties['sourceColumn'], vals[0])
309 
310  return sqlrule
311 
312 class ColorTable(wx.Frame):
313  def __init__(self, parent, title, id = wx.ID_ANY,
314  style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
315  **kwargs):
316  """!Dialog for interactively entering rules for map management
317  commands
318  """
319  self.parent = parent # GMFrame
320  wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
321 
322  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
323 
324  self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
325 
326  # instance of render.Map to be associated with display
327  self.Map = Map()
328 
329  # input map to change
330  self.inmap = ''
331  # reference to layer with preview
332  self.layer = None
333  # layout
334  self._doLayout()
335 
336  # bindings
337  self.Bind(wx.EVT_BUTTON, self.OnHelp, self.btnHelp)
338  self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput)
339  self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
340  self.Bind(wx.EVT_BUTTON, self.OnApply, self.btnApply)
341  self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
342  self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
343 
344  self.Bind(wx.EVT_BUTTON, self.OnPreview, self.btnPreview)
345 
346  def _initLayer(self):
347  """!Set initial layer when opening dialog"""
348  # set map layer from layer tree, first selected,
349  # if not the right type, than select another
350  try:
351  sel = self.parent.curr_page.maptree.layer_selected
352  if sel and self.parent.curr_page.maptree.GetPyData(sel)[0]['type'] == self.mapType:
353  layer = sel
354  else:
355  layer = self.parent.curr_page.maptree.FindItemByData(key = 'type', value = self.mapType)
356  except:
357  layer = None
358  if layer:
359  mapLayer = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer']
360  name = mapLayer.GetName()
361  type = mapLayer.GetType()
362  self.selectionInput.SetValue(name)
363  self.inmap = name
364 
365  def _createMapSelection(self, parent):
366  """!Create map selection part of dialog"""
367  # top controls
368  if self.mapType == 'raster':
369  maplabel = _('Select raster map:')
370  else:
371  maplabel = _('Select vector map:')
372  inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
373  label = " %s " % maplabel)
374  inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
375 
376  self.selectionInput = Select(parent = parent, id = wx.ID_ANY,
377  size = globalvar.DIALOG_GSELECT_SIZE,
378  type = self.mapType)
379  # layout
380  inputSizer.Add(item = self.selectionInput,
381  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
382 
383  return inputSizer
384 
385  def _createFileSelection(self, parent):
386  """!Create file (open/save rules) selection part of dialog"""
387  inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
388  label = " %s " % _("Import or export color table:"))
389  inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
390 
391  self.loadRules = filebrowse.FileBrowseButton(parent = parent, id = wx.ID_ANY, fileMask = '*',
392  size = globalvar.DIALOG_GSELECT_SIZE,
393  labelText = _('Load color table from file:'),
394  dialogTitle = _('Choose file to load color table'),
395  buttonText = _('Load'),
396  toolTip = _("Type filename or click to choose "
397  "file and load color table"),
398  startDirectory = os.getcwd(), fileMode = wx.FD_OPEN,
399  changeCallback = self.OnLoadRulesFile)
400  self.saveRules = filebrowse.FileBrowseButton(parent = parent, id = wx.ID_ANY, fileMask = '*',
401  size = globalvar.DIALOG_GSELECT_SIZE,
402  labelText = _('Save color table to file:'),
403  dialogTitle = _('Choose file to save color table'),
404  toolTip = _("Type filename or click to choose "
405  "file and save color table"),
406  buttonText = _('Save'),
407  startDirectory = os.getcwd(), fileMode = wx.FD_SAVE,
408  changeCallback = self.OnSaveRulesFile)
409 
410  default = wx.Button(parent = parent, id = wx.ID_ANY, label = _("Reload default table"))
411  # layout
412  sizer = wx.BoxSizer(wx.HORIZONTAL)
413  sizer.Add(item = self.loadRules, proportion = 1,
414  flag = wx.RIGHT | wx.EXPAND, border = 10)
415  sizer.Add(item = default, flag = wx.ALIGN_CENTER_VERTICAL)
416  inputSizer.Add(item = sizer,
417  flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
418  sizer = wx.BoxSizer(wx.HORIZONTAL)
419  sizer.Add(item = self.saveRules, proportion = 1,
420  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
421  inputSizer.Add(item = sizer, proportion = 1,
422  flag = wx.ALL | wx.EXPAND, border = 5)
423 
424  default.Bind(wx.EVT_BUTTON, self.OnLoadDefaultTable)
425 
426  if self.mapType == 'vector':
427  # parent is collapsible pane
428  parent.SetSizer(inputSizer)
429 
430  return inputSizer
431 
432  def _createPreview(self, parent):
433  """!Create preview"""
434  # initialize preview display
435  self.InitDisplay()
436  self.preview = BufferedWindow(parent, id = wx.ID_ANY, size = (400, 300),
437  Map = self.Map)
438  self.preview.EraseMap()
439 
440  def _createButtons(self, parent):
441  """!Create buttons for leaving dialog"""
442  self.btnHelp = wx.Button(parent, id = wx.ID_HELP)
443  self.btnCancel = wx.Button(parent, id = wx.ID_CANCEL)
444  self.btnApply = wx.Button(parent, id = wx.ID_APPLY)
445  self.btnOK = wx.Button(parent, id = wx.ID_OK)
446 
447  self.btnOK.SetDefault()
448  self.btnOK.Enable(False)
449  self.btnApply.Enable(False)
450 
451  # layout
452  btnSizer = wx.BoxSizer(wx.HORIZONTAL)
453  btnSizer.Add(wx.Size(-1, -1), proportion = 1)
454  btnSizer.Add(self.btnHelp,
455  flag = wx.LEFT | wx.RIGHT, border = 5)
456  btnSizer.Add(self.btnCancel,
457  flag = wx.LEFT | wx.RIGHT, border = 5)
458  btnSizer.Add(self.btnApply,
459  flag = wx.LEFT | wx.RIGHT, border = 5)
460  btnSizer.Add(self.btnOK,
461  flag = wx.LEFT | wx.RIGHT, border = 5)
462 
463  return btnSizer
464 
465  def _createBody(self, parent):
466  """!Create dialog body consisting of rules and preview"""
467  bodySizer = wx.GridBagSizer(hgap = 5, vgap = 5)
468  bodySizer.AddGrowableRow(1)
469  bodySizer.AddGrowableCol(2)
470 
471  row = 0
472  # label with range
473  self.cr_label = wx.StaticText(parent, id = wx.ID_ANY)
474  bodySizer.Add(item = self.cr_label, pos = (row, 0), span = (1, 3),
475  flag = wx.ALL, border = 5)
476 
477  row += 1
478  # color table
479  self.rulesPanel = RulesPanel(parent = parent, mapType = self.mapType,
480  attributeType = self.attributeType, properties = self.properties)
481 
482  bodySizer.Add(item = self.rulesPanel.mainPanel, pos = (row, 0),
483  span = (1, 2), flag = wx.EXPAND)
484  # add two rules as default
485  self.rulesPanel.AddRules(2)
486 
487  # preview window
488  self._createPreview(parent = parent)
489  bodySizer.Add(item = self.preview, pos = (row, 2),
490  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER)
491 
492  row += 1
493  # add ckeck all and clear all
494  bodySizer.Add(item = self.rulesPanel.checkAll, flag = wx.ALIGN_CENTER_VERTICAL,
495  pos = (row, 0))
496  bodySizer.Add(item = self.rulesPanel.clearAll, pos = (row, 1))
497 
498  # preview button
499  self.btnPreview = wx.Button(parent, id = wx.ID_ANY,
500  label = _("Preview"))
501  bodySizer.Add(item = self.btnPreview, pos = (row, 2),
502  flag = wx.ALIGN_RIGHT)
503  self.btnPreview.Enable(False)
504  self.btnPreview.SetToolTipString(_("Show preview of map "
505  "(current Map Display extent is used)."))
506 
507  row +=1
508  # add rules button and spin to sizer
509  bodySizer.Add(item = self.rulesPanel.numRules, pos = (row, 0),
510  flag = wx.ALIGN_CENTER_VERTICAL)
511  bodySizer.Add(item = self.rulesPanel.btnAdd, pos = (row, 1))
512 
513  return bodySizer
514 
515  def InitDisplay(self):
516  """!Initialize preview display, set dimensions and region
517  """
518  self.width = self.Map.width = 400
519  self.height = self.Map.height = 300
520  self.Map.geom = self.width, self.height
521 
522  def OnCloseWindow(self, event):
523  """!Window closed
524  """
525  self.OnCancel(event)
526 
527  def OnApply(self, event):
528  """!Apply selected color table
529 
530  @return True on success otherwise False
531  """
532  ret = self.CreateColorTable()
533  if not ret:
534  GMessage(parent = self, message = _("No valid color rules given."))
535  else:
536  # re-render preview and current map window
537  self.OnPreview(None)
538  display = self.parent.GetLayerTree().GetMapDisplay()
539  if display and display.IsAutoRendered():
540  display.GetWindow().UpdateMap(render = True)
541 
542  return ret
543 
544  def OnOK(self, event):
545  """!Apply selected color table and close the dialog"""
546  if self.OnApply(event):
547  self.OnCancel(event)
548 
549  def OnCancel(self, event):
550  """!Do not apply any changes, remove associated
551  rendered images and close the dialog"""
552  self.Map.Clean()
553  self.Destroy()
554 
555  def OnSaveRulesFile(self, event):
556  """!Save color table to file"""
557  path = event.GetString()
558  if not os.path.exists(path):
559  return
560 
561  rulestxt = ''
562  for rule in self.rulesPanel.ruleslines.itervalues():
563  if 'value' not in rule:
564  continue
565  rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
566  if not rulestxt:
567  GMessage(message = _("Nothing to save."),
568  parent = self)
569  return
570 
571  fd = open(path, 'w')
572  fd.write(rulestxt)
573  fd.close()
574 
575  def OnLoadRulesFile(self, event):
576  """!Load color table from file"""
577  path = event.GetString()
578  if not os.path.exists(path):
579  return
580 
581  self.rulesPanel.Clear()
582 
583  file = open(path, 'r')
584  ctable = file.read()
585  self.ReadColorTable(ctable = ctable)
586 
587  def ReadColorTable(self, ctable):
588  """!Read color table
589 
590  @param table color table in format coming from r.colors.out"""
591 
592  rulesNumber = len(ctable.splitlines())
593  self.rulesPanel.AddRules(rulesNumber)
594 
595  minim = maxim = count = 0
596  for line in ctable.splitlines():
597  try:
598  value, color = map(lambda x: x.strip(), line.split(' '))
599  except ValueError:
600  GMessage(parent = self, message = _("Invalid color table format"))
601  self.rulesPanel.Clear()
602  return
603 
604  self.rulesPanel.ruleslines[count]['value'] = value
605  self.rulesPanel.ruleslines[count]['color'] = color
606  self.rulesPanel.mainPanel.FindWindowById(count + 1000).SetValue(value)
607  rgb = list()
608  for c in color.split(':'):
609  rgb.append(int(c))
610  self.rulesPanel.mainPanel.FindWindowById(count + 2000).SetColour(rgb)
611  # range
612  try:
613  if float(value) < minim:
614  minim = float(value)
615  if float(value) > maxim:
616  maxim = float(value)
617  except ValueError: # nv, default
618  pass
619  count += 1
620 
621  if self.mapType == 'vector':
622  # raster min, max is known from r.info
623  self.properties['min'], self.properties['max'] = minim, maxim
624  self.SetRangeLabel()
625 
626  self.OnPreview(tmp = True)
627 
628  def OnLoadDefaultTable(self, event):
629  """!Load internal color table"""
630  self.LoadTable()
631 
632  def LoadTable(self, mapType = 'raster'):
633  """!Load current color table (using `r(v).colors.out`)
634 
635  @param mapType map type (raster or vector)"""
636  self.rulesPanel.Clear()
637 
638  if mapType == 'raster':
639  cmd = ['r.colors.out',
640  'read=True',
641  'map=%s' % self.inmap,
642  'rules=-']
643  else:
644  cmd = ['v.colors.out',
645  'read=True',
646  'map=%s' % self.inmap,
647  'rules=-']
648 
649  if self.properties['sourceColumn'] and self.properties['sourceColumn'] != 'cat':
650  cmd.append('column=%s' % self.properties['sourceColumn'])
651 
652  cmd = utils.CmdToTuple(cmd)
653 
654  if self.inmap:
655  ctable = RunCommand(cmd[0], **cmd[1])
656  else:
657  self.OnPreview()
658  return
659 
660  self.ReadColorTable(ctable = ctable)
661 
662  def CreateColorTable(self, tmp = False):
663  """!Creates color table
664 
665  @return True on success
666  @return False on failure
667  """
668  rulestxt = ''
669 
670  for rule in self.rulesPanel.ruleslines.itervalues():
671  if 'value' not in rule: # skip empty rules
672  continue
673 
674  if rule['value'] not in ('nv', 'default') and \
675  rule['value'][-1] != '%' and \
676  not self._IsNumber(rule['value']):
677  GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
678  parent = self)
679  return False
680 
681  rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
682 
683  if not rulestxt:
684  return False
685 
686  gtemp = utils.GetTempfile()
687  output = open(gtemp, "w")
688  try:
689  output.write(rulestxt)
690  finally:
691  output.close()
692 
693  cmd = ['%s.colors' % self.mapType[0], #r.colors/v.colors
694  'map=%s' % self.inmap,
695  'rules=%s' % gtemp]
696  if self.mapType == 'vector' and self.properties['sourceColumn'] \
697  and self.properties['sourceColumn'] != 'cat':
698  cmd.append('column=%s' % self.properties['sourceColumn'])
699  cmd = utils.CmdToTuple(cmd)
700  ret = RunCommand(cmd[0], **cmd[1])
701  if ret != 0:
702  return False
703 
704  return True
705 
706  def DoPreview(self, ltype, cmdlist):
707  """!Update preview (based on computational region)"""
708 
709  if not self.layer:
710  self.layer = self.Map.AddLayer(type = ltype, name = 'preview', command = cmdlist,
711  l_active = True, l_hidden = False, l_opacity = 1.0,
712  l_render = False)
713  else:
714  self.layer.SetCmd(cmdlist)
715 
716  # apply new color table and display preview
717  self.CreateColorTable(tmp = True)
718  self.preview.UpdatePreview()
719 
720  def RunHelp(self, cmd):
721  """!Show GRASS manual page"""
722  RunCommand('g.manual',
723  quiet = True,
724  parent = self,
725  entry = cmd)
726 
727  def _IsNumber(self, s):
728  """!Check if 's' is a number"""
729  try:
730  float(s)
731  return True
732  except ValueError:
733  return False
734 
735 
737  def __init__(self, parent, **kwargs):
738  """!Dialog for interactively entering color rules for raster maps"""
739 
740  self.mapType = 'raster'
741  self.attributeType = 'color'
742  self.colorTable = True
743  # raster properties
744  self.properties = {
745  # min cat in raster map
746  'min' : None,
747  # max cat in raster map
748  'max' : None,
749  }
750 
751  ColorTable.__init__(self, parent,
752  title = _('Create new color table for raster map'), **kwargs)
753 
754  self._initLayer()
755 
756  # self.SetMinSize(self.GetSize())
757  self.SetMinSize((650, 700))
758 
759  self.CentreOnScreen()
760  self.Show()
761 
762  def _doLayout(self):
763  """!Do main layout"""
764  sizer = wx.BoxSizer(wx.VERTICAL)
765  #
766  # map selection
767  #
768  mapSelection = self._createMapSelection(parent = self.panel)
769  sizer.Add(item = mapSelection, proportion = 0,
770  flag = wx.ALL | wx.EXPAND, border = 5)
771  #
772  # manage extern tables
773  #
774  fileSelection = self._createFileSelection(parent = self.panel)
775  sizer.Add(item = fileSelection, proportion = 0,
776  flag = wx.ALL | wx.EXPAND, border = 5)
777  #
778  # body & preview
779  #
780  bodySizer = self._createBody(parent = self.panel)
781  sizer.Add(item = bodySizer, proportion = 1,
782  flag = wx.ALL | wx.EXPAND, border = 5)
783  #
784  # buttons
785  #
786  btnSizer = self._createButtons(parent = self.panel)
787  sizer.Add(item = wx.StaticLine(parent = self.panel, id = wx.ID_ANY,
788  style = wx.LI_HORIZONTAL), proportion = 0,
789  flag = wx.EXPAND | wx.ALL, border = 5)
790 
791  sizer.Add(item = btnSizer, proportion = 0,
792  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
793 
794  self.panel.SetSizer(sizer)
795  sizer.Layout()
796  sizer.Fit(self.panel)
797  self.Layout()
798 
799  def OnSelectionInput(self, event):
800  """!Raster map selected"""
801  if event:
802  self.inmap = event.GetString()
803 
804  self.loadRules.SetValue('')
805  self.saveRules.SetValue('')
806  if self.inmap:
807  if not grass.find_file(name = self.inmap, element = 'cell')['file']:
808  self.inmap = None
809 
810  if not self.inmap:
811  self.btnPreview.Enable(False)
812  self.btnOK.Enable(False)
813  self.btnApply.Enable(False)
814  self.LoadTable()
815  return
816 
817  info = grass.raster_info(map = self.inmap)
818 
819  if info:
820  self.properties['min'] = info['min']
821  self.properties['max'] = info['max']
822  self.LoadTable()
823  else:
824  self.inmap = ''
825  self.properties['min'] = self.properties['max'] = None
826  self.btnPreview.Enable(False)
827  self.btnOK.Enable(False)
828  self.btnApply.Enable(False)
829  self.preview.EraseMap()
830  self.cr_label.SetLabel(_('Enter raster category values or percents'))
831  return
832 
833  if info['datatype'] == 'CELL':
834  mapRange = _('range')
835  else:
836  mapRange = _('fp range')
837  self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
838  { 'range' : mapRange,
839  'min' : self.properties['min'],
840  'max' : self.properties['max'] })
841 
842  self.btnPreview.Enable()
843  self.btnOK.Enable()
844  self.btnApply.Enable()
845 
846 
847  def OnPreview(self, tmp = True):
848  """!Update preview (based on computational region)"""
849  if not self.inmap:
850  self.preview.EraseMap()
851  return
852 
853  cmdlist = ['d.rast',
854  'map=%s' % self.inmap]
855  ltype = 'raster'
856 
857  # find existing color table and copy to temp file
858  try:
859  name, mapset = self.inmap.split('@')
860  except ValueError:
861  name = self.inmap
862  mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
863  if not mapset:
864  return
865  old_colrtable = None
866  if mapset == grass.gisenv()['MAPSET']:
867  old_colrtable = grass.find_file(name = name, element = 'colr')['file']
868  else:
869  old_colrtable = grass.find_file(name = name, element = 'colr2/' + mapset)['file']
870 
871  if old_colrtable:
872  colrtemp = utils.GetTempfile()
873  shutil.copyfile(old_colrtable, colrtemp)
874 
875  ColorTable.DoPreview(self, ltype, cmdlist)
876 
877  # restore previous color table
878  if tmp:
879  if old_colrtable:
880  shutil.copyfile(colrtemp, old_colrtable)
881  os.remove(colrtemp)
882  else:
883  RunCommand('r.colors',
884  parent = self,
885  flags = 'r',
886  map = self.inmap)
887 
888  def OnHelp(self, event):
889  """!Show GRASS manual page"""
890  cmd = 'r.colors'
891  ColorTable.RunHelp(self, cmd = cmd)
892 
894  def __init__(self, parent, attributeType, **kwargs):
895  """!Dialog for interactively entering color rules for vector maps"""
896  # dialog attributes
897  self.mapType = 'vector'
898  self.attributeType = attributeType # color, size, width
899  # in version 7 v.colors used, otherwise color column only
900  self.version7 = int(grass.version()['version'].split('.')[0]) >= 7
901  self.colorTable = False
902  self.updateColumn = True
903  # vector properties
904  self.properties = {
905  # vector layer for attribute table to use for setting color
906  'layer' : 1,
907  # vector attribute table used for setting color
908  'table' : '',
909  # vector attribute column for assigning colors
910  'sourceColumn' : '',
911  # vector attribute column to use for loading colors
912  'loadColumn' : '',
913  # vector attribute column to use for storing colors
914  'storeColumn' : '',
915  # vector attribute column for temporary storing colors
916  'tmpColumn' : 'tmp_0',
917  # min value of attribute column/vector color table
918  'min': None,
919  # max value of attribute column/vector color table
920  'max': None
921  }
922  self.columnsProp = {'color': {'name': 'GRASSRGB', 'type1': 'varchar(11)', 'type2': ['character']},
923  'size' : {'name': 'GRASSSIZE', 'type1': 'integer', 'type2': ['integer']},
924  'width': {'name': 'GRASSWIDTH', 'type1': 'integer', 'type2': ['integer']}}
925  ColorTable.__init__(self, parent = parent,
926  title = _('Create new color rules for vector map'), **kwargs)
927 
928  # additional bindings for vector color management
929  self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.layerSelect)
930  self.Bind(wx.EVT_COMBOBOX, self.OnSourceColumnSelection, self.sourceColumn)
931  self.Bind(wx.EVT_COMBOBOX, self.OnFromColSelection, self.fromColumn)
932  self.Bind(wx.EVT_COMBOBOX, self.OnToColSelection, self.toColumn)
933  self.Bind(wx.EVT_BUTTON, self.OnAddColumn, self.addColumn)
934 
935  self._initLayer()
936  if self.colorTable:
937  self.cr_label.SetLabel(_("Enter vector attribute values or percents:"))
938  else:
939  self.cr_label.SetLabel(_("Enter vector attribute values:"))
940 
941  #self.SetMinSize(self.GetSize())
942  self.SetMinSize((650, 700))
943 
944  self.CentreOnScreen()
945  self.Show()
946 
947  def _createVectorAttrb(self, parent):
948  """!Create part of dialog with layer/column selection"""
949  inputBox = wx.StaticBox(parent = parent, id = wx.ID_ANY,
950  label = " %s " % _("Select vector columns"))
951  cb_vl_label = wx.StaticText(parent, id = wx.ID_ANY,
952  label = _('Layer:'))
953  cb_vc_label = wx.StaticText(parent, id = wx.ID_ANY,
954  label = _('Attribute column:'))
955 
956  if self.attributeType == 'color':
957  labels = [_("Load color from column:"), _("Save color to column:")]
958  elif self.attributeType == 'size':
959  labels = [_("Load size from column:"), _("Save size to column:")]
960  elif self.attributeType == 'width':
961  labels = [_("Load width from column:"), _("Save width to column:")]
962 
963  if self.version7 and self.attributeType == 'color':
964  self.useColumn = wx.CheckBox(parent, id = wx.ID_ANY,
965  label = _("Use color column instead of color table:"))
966  self.useColumn.Bind(wx.EVT_CHECKBOX, self.OnCheckColumn)
967 
968  fromColumnLabel = wx.StaticText(parent, id = wx.ID_ANY,
969  label = labels[0])
970  toColumnLabel = wx.StaticText(parent, id = wx.ID_ANY,
971  label = labels[1])
972 
973  self.rgb_range_label = wx.StaticText(parent, id = wx.ID_ANY)
974  self.layerSelect = LayerSelect(parent)
975  self.sourceColumn = ColumnSelect(parent)
976  self.fromColumn = ColumnSelect(parent)
977  self.toColumn = ColumnSelect(parent)
978  self.addColumn = wx.Button(parent, id = wx.ID_ANY,
979  label = _('Add column'))
980  self.addColumn.SetToolTipString(_("Add GRASSRGB column to current attribute table."))
981 
982  # layout
983  inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
984  vSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
985  row = 0
986  vSizer.Add(cb_vl_label, pos = (row, 0),
987  flag = wx.ALIGN_CENTER_VERTICAL)
988  vSizer.Add(self.layerSelect, pos = (row, 1),
989  flag = wx.ALIGN_CENTER_VERTICAL)
990  row += 1
991  vSizer.Add(cb_vc_label, pos = (row, 0),
992  flag = wx.ALIGN_CENTER_VERTICAL)
993  vSizer.Add(self.sourceColumn, pos = (row, 1),
994  flag = wx.ALIGN_CENTER_VERTICAL)
995  vSizer.Add(self.rgb_range_label, pos = (row, 2),
996  flag = wx.ALIGN_CENTER_VERTICAL)
997  row += 1
998  if self.version7 and self.attributeType == 'color':
999  vSizer.Add(self.useColumn, pos = (row, 0), span = (1, 2),
1000  flag = wx.ALIGN_CENTER_VERTICAL)
1001  row += 1
1002 
1003  vSizer.Add(fromColumnLabel, pos = (row, 0),
1004  flag = wx.ALIGN_CENTER_VERTICAL)
1005  vSizer.Add(self.fromColumn, pos = (row, 1),
1006  flag = wx.ALIGN_CENTER_VERTICAL)
1007  row += 1
1008  vSizer.Add(toColumnLabel, pos = (row, 0),
1009  flag = wx.ALIGN_CENTER_VERTICAL)
1010  vSizer.Add(self.toColumn, pos = (row, 1),
1011  flag = wx.ALIGN_CENTER_VERTICAL)
1012  vSizer.Add(self.addColumn, pos = (row, 2),
1013  flag = wx.ALIGN_CENTER_VERTICAL)
1014  inputSizer.Add(item = vSizer,
1015  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
1016  self.colorColumnSizer = vSizer
1017  return inputSizer
1018 
1019  def _doLayout(self):
1020  """!Do main layout"""
1021  scrollPanel = scrolled.ScrolledPanel(parent = self.panel, id = wx.ID_ANY,
1022  style = wx.TAB_TRAVERSAL)
1023  scrollPanel.SetupScrolling()
1024  sizer = wx.BoxSizer(wx.VERTICAL)
1025  #
1026  # map selection
1027  #
1028  mapSelection = self._createMapSelection(parent = scrollPanel)
1029  sizer.Add(item = mapSelection, proportion = 0,
1030  flag = wx.ALL | wx.EXPAND, border = 5)
1031  #
1032  # manage extern tables
1033  #
1034  if self.version7 and self.attributeType == 'color':
1035  self.cp = wx.CollapsiblePane(scrollPanel, label = _("Import or export color table"),
1036  winid = wx.ID_ANY,
1037  style = wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE)
1038  self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, self.cp)
1039 
1040  self._createFileSelection(parent = self.cp.GetPane())
1041  sizer.Add(item = self.cp, proportion = 0,
1042  flag = wx.ALL | wx.EXPAND, border = 5)
1043  #
1044  # set vector attributes
1045  #
1046  vectorAttrb = self._createVectorAttrb(parent = scrollPanel)
1047  sizer.Add(item = vectorAttrb, proportion = 0,
1048  flag = wx.ALL | wx.EXPAND, border = 5)
1049  #
1050  # body & preview
1051  #
1052  bodySizer = self._createBody(parent = scrollPanel)
1053  sizer.Add(item = bodySizer, proportion = 1,
1054  flag = wx.ALL | wx.EXPAND, border = 5)
1055 
1056  scrollPanel.SetSizer(sizer)
1057  scrollPanel.Fit()
1058 
1059  #
1060  # buttons
1061  #
1062  btnSizer = self._createButtons(self.panel)
1063 
1064  mainsizer = wx.BoxSizer(wx.VERTICAL)
1065  mainsizer.Add(scrollPanel, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
1066  mainsizer.Add(item = wx.StaticLine(parent = self.panel, id = wx.ID_ANY,
1067  style = wx.LI_HORIZONTAL), proportion = 0,
1068  flag = wx.EXPAND | wx.ALL, border = 5)
1069  mainsizer.Add(item = btnSizer, proportion = 0,
1070  flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND, border = 5)
1071 
1072  self.panel.SetSizer(mainsizer)
1073  mainsizer.Layout()
1074  mainsizer.Fit(self.panel)
1075  self.Layout()
1076 
1077  def OnPaneChanged(self, event = None):
1078  # redo the layout
1079  self.Layout()
1080  # and also change the labels
1081  if self.cp.IsExpanded():
1082  self.cp.SetLabel('')
1083  else:
1084  self.cp.SetLabel(_("Import or export color table"))
1085 
1086  def CheckMapset(self):
1087  """!Check if current vector is in current mapset"""
1088  if grass.find_file(name = self.inmap,
1089  element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
1090  return True
1091  else:
1092  return False
1093 
1094  def NoConnection(self, vectorName):
1095  dlg = wx.MessageDialog(parent = self,
1096  message = _("Database connection for vector map <%s> "
1097  "is not defined in DB file. Do you want to create and "
1098  "connect new attribute table?") % vectorName,
1099  caption = _("No database connection defined"),
1100  style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
1101  if dlg.ShowModal() == wx.ID_YES:
1102  dlg.Destroy()
1103  GUI(parent = self).ParseCommand(['v.db.addtable', 'map=' + self.inmap],
1104  completed = (self.CreateAttrTable, self.inmap, ''))
1105  else:
1106  dlg.Destroy()
1107 
1108  def OnCheckColumn(self, event):
1109  """!Use color column instead of color table"""
1110  if self.useColumn.GetValue():
1111  self.properties['loadColumn'] = self.fromColumn.GetStringSelection()
1112  self.properties['storeColumn'] = self.toColumn.GetStringSelection()
1113  self.fromColumn.Enable(True)
1114  self.toColumn.Enable(True)
1115  self.colorTable = False
1116 
1117  if self.properties['loadColumn']:
1118  self.LoadTable()
1119  else:
1120  self.rulesPanel.Clear()
1121  else:
1122  self.properties['loadColumn'] = ''
1123  self.properties['storeColumn'] = ''
1124  self.fromColumn.Enable(False)
1125  self.toColumn.Enable(False)
1126  self.colorTable = True
1127  self.LoadTable()
1128 
1129  def EnableVectorAttributes(self, enable):
1130  """!Enable/disable part of dialog connected with db"""
1131  for child in self.colorColumnSizer.GetChildren():
1132  child.GetWindow().Enable(enable)
1133 
1134  def DisableClearAll(self):
1135  """!Enable, disable the whole dialog"""
1136  self.rulesPanel.Clear()
1137  self.EnableVectorAttributes(False)
1138  self.btnPreview.Enable(False)
1139  self.btnOK.Enable(False)
1140  self.btnApply.Enable(False)
1141  self.preview.EraseMap()
1142 
1143  def OnSelectionInput(self, event):
1144  """!Vector map selected"""
1145  if event:
1146  if self.inmap:
1147  # switch to another map -> delete temporary column
1148  self.DeleteTemporaryColumn()
1149  self.inmap = event.GetString()
1150 
1151  if self.version7 and self.attributeType == 'color':
1152  self.loadRules.SetValue('')
1153  self.saveRules.SetValue('')
1154 
1155  if self.inmap:
1156  if not grass.find_file(name = self.inmap, element = 'vector')['file']:
1157  self.inmap = None
1158 
1159  self.UpdateDialog()
1160 
1161  def UpdateDialog(self):
1162  """!Update dialog after map selection"""
1163 
1164  if not self.inmap:
1165  self.DisableClearAll()
1166  return
1167 
1168  if self.inmap and not self.CheckMapset():
1169  # currently v.colors need the map to be in current mapset
1170  if self.version7 and self.attributeType == 'color':
1171  message = _("Selected map <%(map)s> is not in current mapset <%(mapset)s>. "
1172  "Color rules cannot be edited.") % \
1173  { 'map' : self.inmap,
1174  'mapset' : grass.gisenv()['MAPSET'] }
1175  else:
1176  message = _("Selected map <%(map)s> is not in current mapset <%(mapset)s>. "
1177  "Attribute table cannot be edited.") % \
1178  { 'map' : self.inmap,
1179  'mapset' : grass.gisenv()['MAPSET'] }
1180  wx.CallAfter(GMessage, parent = self, message = message)
1181  self.DisableClearAll()
1182  return
1183 
1184  # check for db connection
1185  self.dbInfo = VectorDBInfo(self.inmap)
1186  enable = True
1187  if not len(self.dbInfo.layers): # no connection
1188  if not (self.version7 and self.attributeType == 'color'): # otherwise it doesn't matter
1189  wx.CallAfter(self.NoConnection, self.inmap)
1190  enable = False
1191  for combo in (self.layerSelect, self.sourceColumn, self.fromColumn, self.toColumn):
1192  combo.SetValue("")
1193  combo.Clear()
1194  for prop in ('sourceColumn', 'loadColumn', 'storeColumn'):
1195  self.properties[prop] = ''
1196  self.EnableVectorAttributes(False)
1197  else: # db connection exist
1198  # initialize layer selection combobox
1199  self.EnableVectorAttributes(True)
1200  self.layerSelect.InsertLayers(self.inmap)
1201  # initialize attribute table for layer=1
1202  self.properties['layer'] = self.layerSelect.GetString(0)
1203  self.layerSelect.SetStringSelection(self.properties['layer'])
1204  layer = int(self.properties['layer'])
1205  self.properties['table'] = self.dbInfo.layers[layer]['table']
1206 
1207  if self.attributeType == 'color':
1208  self.AddTemporaryColumn(type = 'varchar(11)')
1209  else:
1210  self.AddTemporaryColumn(type = 'integer')
1211 
1212  # initialize column selection comboboxes
1213 
1214  self.OnLayerSelection(event = None)
1215 
1216  if self.version7 and self.attributeType == 'color':
1217  self.useColumn.SetValue(False)
1218  self.OnCheckColumn(event = None)
1219 
1220  self.LoadTable()
1221 
1222  self.btnPreview.Enable(enable)
1223  self.btnOK.Enable(enable)
1224  self.btnApply.Enable(enable)
1225 
1226  def AddTemporaryColumn(self, type):
1227  """!Add temporary column to not overwrite the original values,
1228  need to be deleted when closing dialog and unloading map
1229 
1230  @param type type of column (e.g. vachar(11))"""
1231  # because more than one dialog with the same map can be opened we must test column name and
1232  # create another one
1233  while self.properties['tmpColumn'] in self.dbInfo.GetTableDesc(self.properties['table']).keys():
1234  name, idx = self.properties['tmpColumn'].split('_')
1235  idx = int(idx)
1236  idx += 1
1237  self.properties['tmpColumn'] = name + '_' + str(idx)
1238 
1239  if self.version7:
1240  modul = 'v.db.addcolumn'
1241  else:
1242  modul = 'v.db.addcol'
1243  ret = RunCommand(modul,
1244  parent = self,
1245  map = self.inmap,
1246  layer = self.properties['layer'],
1247  column = '%s %s' % (self.properties['tmpColumn'], type))
1248 
1250  """!Delete temporary column"""
1251  if self.inmap:
1252  if self.version7:
1253  modul = 'v.db.dropcolumn'
1254  else:
1255  modul = 'v.db.dropcol'
1256  ret = RunCommand(modul,
1257  map = self.inmap,
1258  layer = self.properties['layer'],
1259  column = self.properties['tmpColumn'])
1260 
1261  def OnLayerSelection(self, event):
1262  # reset choices in column selection comboboxes if layer changes
1263  vlayer = int(self.layerSelect.GetStringSelection())
1264  self.sourceColumn.InsertColumns(vector = self.inmap, layer = vlayer,
1265  type = ['integer', 'double precision'], dbInfo = self.dbInfo,
1266  excludeCols = ['tmpColumn'])
1267  self.sourceColumn.SetStringSelection('cat')
1268  self.properties['sourceColumn'] = self.sourceColumn.GetString(0)
1269 
1270  if self.attributeType == 'color':
1271  type = ['character']
1272  else:
1273  type = ['integer']
1274  self.fromColumn.InsertColumns(vector = self.inmap, layer = vlayer, type = type,
1275  dbInfo = self.dbInfo, excludeCols = ['tmpColumn'])
1276  self.toColumn.InsertColumns(vector = self.inmap, layer = vlayer, type = type,
1277  dbInfo = self.dbInfo, excludeCols = ['tmpColumn'])
1278 
1279  found = self.fromColumn.FindString(self.columnsProp[self.attributeType]['name'])
1280  if found != wx.NOT_FOUND:
1281  self.fromColumn.SetSelection(found)
1282  self.toColumn.SetSelection(found)
1283  self.properties['loadColumn'] = self.fromColumn.GetString(found)
1284  self.properties['storeColumn'] = self.toColumn.GetString(found)
1285  else:
1286  self.properties['loadColumn'] = ''
1287  self.properties['storeColumn'] = ''
1288 
1289  if event:
1290  self.LoadTable()
1291  self.Update()
1292 
1293  def OnSourceColumnSelection(self, event):
1294  self.properties['sourceColumn'] = event.GetString()
1295 
1296  self.LoadTable()
1297 
1298  def OnAddColumn(self, event):
1299  """!Add GRASS(RGB,SIZE,WIDTH) column if it doesn't exist"""
1300  if self.columnsProp[self.attributeType]['name'] not in self.fromColumn.GetItems():
1301  if self.version7:
1302  modul = 'v.db.addcolumn'
1303  else:
1304  modul = 'v.db.addcol'
1305  ret = RunCommand(modul,
1306  map = self.inmap,
1307  layer = self.properties['layer'],
1308  columns = '%s %s' % (self.columnsProp[self.attributeType]['name'],
1309  self.columnsProp[self.attributeType]['type1']))
1310  self.toColumn.InsertColumns(self.inmap, self.properties['layer'],
1311  type = self.columnsProp[self.attributeType]['type2'])
1312  self.toColumn.SetStringSelection(self.columnsProp[self.attributeType]['name'])
1313  self.properties['storeColumn'] = self.toColumn.GetStringSelection()
1314 
1315  self.LoadTable()
1316  else:
1317  GMessage(parent = self,
1318  message = _("%s column already exists.") % \
1319  self.columnsProp[self.attributeType]['name'])
1320 
1321  def CreateAttrTable(self, dcmd, layer, params, propwin):
1322  """!Create attribute table"""
1323  if dcmd:
1324  cmd = utils.CmdToTuple(dcmd)
1325  ret = RunCommand(cmd[0], **cmd[1])
1326  if ret == 0:
1327  self.OnSelectionInput(None)
1328  return True
1329 
1330  for combo in (self.layerSelect, self.sourceColumn, self.fromColumn, self.toColumn):
1331  combo.SetValue("")
1332  combo.Disable()
1333  return False
1334 
1335  def LoadTable(self):
1336  """!Load table"""
1337  if self.colorTable:
1338  ColorTable.LoadTable(self, mapType = 'vector')
1339  else:
1340  self.LoadRulesFromColumn()
1341 
1343  """!Load current column (GRASSRGB, size column)"""
1344 
1345  self.rulesPanel.Clear()
1346  if not self.properties['sourceColumn']:
1347  self.preview.EraseMap()
1348  return
1349 
1350  busy = wx.BusyInfo(message = _("Please wait, loading data from attribute table..."),
1351  parent = self)
1352  wx.Yield()
1353 
1354  columns = self.properties['sourceColumn']
1355  if self.properties['loadColumn']:
1356  columns += ',' + self.properties['loadColumn']
1357 
1358  sep = ';'
1359  if self.inmap:
1360  outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
1361  ret = RunCommand('v.db.select',
1362  quiet = True,
1363  flags = 'c',
1364  map = self.inmap,
1365  layer = self.properties['layer'],
1366  columns = columns,
1367  fs = sep,
1368  stdout = outFile)
1369  else:
1370  self.preview.EraseMap()
1371  busy.Destroy()
1372  return
1373 
1374  outFile.seek(0)
1375  i = 0
1376  minim = maxim = 0.0
1377  limit = 1000
1378 
1379  colvallist = []
1380  readvals = False
1381 
1382  while True:
1383  # os.linesep doesn't work here (MSYS)
1384  record = outFile.readline().replace('\n', '')
1385  if not record:
1386  break
1387  self.rulesPanel.ruleslines[i] = {}
1388 
1389  if not self.properties['loadColumn']:
1390  col1 = record
1391  col2 = None
1392  else:
1393  col1, col2 = record.split(sep)
1394 
1395  if float(col1) < minim:
1396  minim = float(col1)
1397  if float(col1) > maxim:
1398  maxim = float(col1)
1399 
1400 
1401  # color rules list should only have unique values of col1, not all records
1402  if col1 not in colvallist:
1403  self.rulesPanel.ruleslines[i]['value'] = col1
1404  self.rulesPanel.ruleslines[i][self.attributeType] = col2
1405 
1406  colvallist.append(col1)
1407  i += 1
1408 
1409  if i > limit and readvals == False:
1410  dlg = wx.MessageDialog (parent = self, message = _(
1411  "Number of loaded records reached %d, "
1412  "displaying all the records will be time-consuming "
1413  "and may lead to computer freezing, "
1414  "do you still want to continue?") % i,
1415  caption = _("Too many records"),
1416  style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
1417  if dlg.ShowModal() == wx.ID_YES:
1418  readvals = True
1419  dlg.Destroy()
1420  else:
1421  busy.Destroy()
1422  dlg.Destroy()
1423  self.updateColumn = False
1424  return
1425 
1426  self.rulesPanel.AddRules(i, start = True)
1427  ret = self.rulesPanel.LoadRules()
1428 
1429  self.properties['min'], self.properties['max'] = minim, maxim
1430  self.SetRangeLabel()
1431 
1432  if ret:
1433  self.OnPreview()
1434  else:
1435  self.rulesPanel.Clear()
1436 
1437  busy.Destroy()
1438 
1439  def SetRangeLabel(self):
1440  """!Set labels with info about attribute column range"""
1441 
1442  if self.properties['sourceColumn']:
1443  ctype = self.dbInfo.GetTableDesc(self.properties['table'])[self.properties['sourceColumn']]['ctype']
1444  else:
1445  ctype = int
1446 
1447  range = ''
1448  if self.properties['min'] or self.properties['max']:
1449  if ctype == float:
1450  range = "%s: %.1f - %.1f)" % (_("range"),
1451  self.properties['min'], self.properties['max'])
1452  elif ctype == int:
1453  range = "%s: %d - %d)" % (_("range"),
1454  self.properties['min'], self.properties['max'])
1455  if range:
1456  if self.colorTable:
1457  self.cr_label.SetLabel(_("Enter vector attribute values or percents %s:") % range)
1458  else:
1459  self.cr_label.SetLabel(_("Enter vector attribute values %s:") % range)
1460  else:
1461  if self.colorTable:
1462  self.cr_label.SetLabel(_("Enter vector attribute values or percents:"))
1463  else:
1464  self.cr_label.SetLabel(_("Enter vector attribute values:"))
1465 
1466  def OnFromColSelection(self, event):
1467  """!Selection in combobox (for loading values) changed"""
1468  self.properties['loadColumn'] = event.GetString()
1469 
1470  self.LoadTable()
1471 
1472  def OnToColSelection(self, event):
1473  """!Selection in combobox (for storing values) changed"""
1474  self.properties['storeColumn'] = event.GetString()
1475 
1476  def OnPreview(self, event = None, tmp = True):
1477  """!Update preview (based on computational region)"""
1478  if self.colorTable:
1479  self.OnTablePreview(tmp)
1480  else:
1481  self.OnColumnPreview()
1482 
1483  def OnTablePreview(self, tmp):
1484  """!Update preview (based on computational region)"""
1485  if not self.inmap:
1486  self.preview.EraseMap()
1487  return
1488 
1489  ltype = 'vector'
1490  cmdlist = ['d.vect',
1491  'map=%s' % self.inmap]
1492 
1493  # find existing color table and copy to temp file
1494  old_colrtable = None
1495  path = grass.find_file(name = self.inmap, element = 'vector')['file']
1496 
1497  if os.path.exists(os.path.join(path, 'colr')):
1498  old_colrtable = os.path.join(path, 'colr')
1499  colrtemp = utils.GetTempfile()
1500  shutil.copyfile(old_colrtable, colrtemp)
1501 
1502  ColorTable.DoPreview(self, ltype, cmdlist)
1503 
1504  # restore previous color table
1505  if tmp:
1506  if old_colrtable:
1507  shutil.copyfile(colrtemp, old_colrtable)
1508  os.remove(colrtemp)
1509  else:
1510  RunCommand('v.colors',
1511  parent = self,
1512  flags = 'r',
1513  map = self.inmap)
1514 
1515  def OnColumnPreview(self):
1516  """!Update preview (based on computational region)"""
1517  if not self.inmap or not self.properties['tmpColumn']:
1518  self.preview.EraseMap()
1519  return
1520 
1521  cmdlist = ['d.vect',
1522  'map=%s' % self.inmap,
1523  'type=point,line,boundary,area']
1524 
1525  if self.attributeType == 'color':
1526  cmdlist.append('flags=a')
1527  cmdlist.append('rgb_column=%s' % self.properties['tmpColumn'])
1528  elif self.attributeType == 'size':
1529  cmdlist.append('size_column=%s' % self.properties['tmpColumn'])
1530  elif self.attributeType == 'width':
1531  cmdlist.append('width_column=%s' % self.properties['tmpColumn'])
1532 
1533  ltype = 'vector'
1534 
1535  ColorTable.DoPreview(self, ltype, cmdlist)
1536 
1537  def OnHelp(self, event):
1538  """!Show GRASS manual page"""
1539  cmd = 'v.colors'
1540  ColorTable.RunHelp(self, cmd = cmd)
1541 
1542  def UseAttrColumn(self, useAttrColumn):
1543  """!Find layers and apply the changes in d.vect command"""
1544  layers = self.parent.curr_page.maptree.FindItemByData(key = 'name', value = self.inmap)
1545  if not layers:
1546  return
1547  for layer in layers:
1548  if self.parent.curr_page.maptree.GetPyData(layer)[0]['type'] != 'vector':
1549  continue
1550  cmdlist = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer'].GetCmd()
1551 
1552  if self.attributeType == 'color':
1553  if useAttrColumn:
1554  cmdlist[1].update({'flags': 'a'})
1555  cmdlist[1].update({'rgb_column': self.properties['storeColumn']})
1556  else:
1557  if 'flags' in cmdlist[1]:
1558  cmdlist[1]['flags'] = cmdlist[1]['flags'].replace('a', '')
1559  cmdlist[1].pop('rgb_column', None)
1560  elif self.attributeType == 'size':
1561  cmdlist[1].update({'size_column': self.properties['storeColumn']})
1562  elif self.attributeType == 'width':
1563  cmdlist[1].update({'width_column' :self.properties['storeColumn']})
1564  self.parent.curr_page.maptree.GetPyData(layer)[0]['cmd'] = cmdlist
1565 
1566  def CreateColorTable(self, tmp = False):
1567  """!Create color rules (color table or color column)"""
1568  if self.colorTable:
1569  ret = ColorTable.CreateColorTable(self)
1570  else:
1571  if self.updateColumn:
1572  ret = self.UpdateColorColumn(tmp)
1573  else:
1574  ret = True
1575 
1576  return ret
1577 
1578  def UpdateColorColumn(self, tmp):
1579  """!Creates color table
1580 
1581  @return True on success
1582  @return False on failure
1583  """
1584  rulestxt = ''
1585 
1586  for rule in self.rulesPanel.ruleslines.itervalues():
1587  if 'value' not in rule: # skip empty rules
1588  break
1589 
1590  if tmp:
1591  rgb_col = self.properties['tmpColumn']
1592  else:
1593  rgb_col = self.properties['storeColumn']
1594  if not self.properties['storeColumn']:
1595  GMessage(parent = self.parent,
1596  message = _("Please select column to save values to."))
1597 
1598  rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
1599  rgb_col,
1600  rule[self.attributeType],
1601  rule['value'])
1602  if not rulestxt:
1603  return False
1604 
1605  gtemp = utils.GetTempfile()
1606  output = open(gtemp, "w")
1607  try:
1608  output.write(rulestxt)
1609  finally:
1610  output.close()
1611 
1612  RunCommand('db.execute',
1613  parent = self,
1614  input = gtemp)
1615 
1616  return True
1617 
1618  def OnCancel(self, event):
1619  """!Do not apply any changes and close the dialog"""
1620  self.DeleteTemporaryColumn()
1621  self.Map.Clean()
1622  self.Destroy()
1623 
1624  def OnApply(self, event):
1625  """!Apply selected color table
1626 
1627  @return True on success otherwise False
1628  """
1629  if self.colorTable:
1630  self.UseAttrColumn(False)
1631  else:
1632  if not self.properties['storeColumn']:
1633  GError(_("No color column defined. Operation canceled."),
1634  parent = self)
1635  return
1636 
1637  self.UseAttrColumn(True)
1638 
1639  return ColorTable.OnApply(self, event)
1640 
1641 class BufferedWindow(wx.Window):
1642  """!A Buffered window class"""
1643  def __init__(self, parent, id,
1644  style = wx.NO_FULL_REPAINT_ON_RESIZE,
1645  Map = None, **kwargs):
1646 
1647  wx.Window.__init__(self, parent, id, style = style, **kwargs)
1648 
1649  self.parent = parent
1650  self.Map = Map
1651 
1652  # re-render the map from GRASS or just redraw image
1653  self.render = True
1654  # indicates whether or not a resize event has taken place
1655  self.resize = False
1656 
1657  #
1658  # event bindings
1659  #
1660  self.Bind(wx.EVT_PAINT, self.OnPaint)
1661  self.Bind(wx.EVT_IDLE, self.OnIdle)
1662  self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
1663 
1664  #
1665  # render output objects
1666  #
1667  # image file to be rendered
1668  self.mapfile = None
1669  # wx.Image object (self.mapfile)
1670  self.img = None
1671 
1672  self.pdc = wx.PseudoDC()
1673  # will store an off screen empty bitmap for saving to file
1674  self._Buffer = None
1675 
1676  # make sure that extents are updated at init
1677  self.Map.region = self.Map.GetRegion()
1678  self.Map.SetRegion()
1679 
1680  def Draw(self, pdc, img = None, pdctype = 'image'):
1681  """!Draws preview or clears window"""
1682  pdc.BeginDrawing()
1683 
1684  Debug.msg (3, "BufferedWindow.Draw(): pdctype=%s" % (pdctype))
1685 
1686  if pdctype == 'clear': # erase the display
1687  bg = wx.WHITE_BRUSH
1688  pdc.SetBackground(bg)
1689  pdc.Clear()
1690  self.Refresh()
1691  pdc.EndDrawing()
1692  return
1693 
1694  if pdctype == 'image' and img:
1695  bg = wx.TRANSPARENT_BRUSH
1696  pdc.SetBackground(bg)
1697  bitmap = wx.BitmapFromImage(img)
1698  w, h = bitmap.GetSize()
1699  pdc.DrawBitmap(bitmap, 0, 0, True) # draw the composite map
1700 
1701  pdc.EndDrawing()
1702  self.Refresh()
1703 
1704  def OnPaint(self, event):
1705  """!Draw pseudo DC to buffer"""
1706  self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
1707  dc = wx.BufferedPaintDC(self, self._Buffer)
1708 
1709  # use PrepareDC to set position correctly
1710  # probably does nothing, removed from wxPython 2.9
1711  # self.PrepareDC(dc)
1712 
1713  # we need to clear the dc BEFORE calling PrepareDC
1714  bg = wx.Brush(self.GetBackgroundColour())
1715  dc.SetBackground(bg)
1716  dc.Clear()
1717 
1718  # create a clipping rect from our position and size
1719  # and the Update Region
1720  rgn = self.GetUpdateRegion()
1721  r = rgn.GetBox()
1722 
1723  # draw to the dc using the calculated clipping rect
1724  self.pdc.DrawToDCClipped(dc, r)
1725 
1726  def OnSize(self, event):
1727  """!Init image size to match window size"""
1728  # set size of the input image
1729  self.Map.width, self.Map.height = self.GetClientSize()
1730 
1731  # Make new off screen bitmap: this bitmap will always have the
1732  # current drawing in it, so it can be used to save the image to
1733  # a file, or whatever.
1734  self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
1735 
1736  # get the image to be rendered
1737  self.img = self.GetImage()
1738 
1739  # update map display
1740  if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
1741  self.img = self.img.Scale(self.Map.width, self.Map.height)
1742  self.render = False
1743  self.UpdatePreview()
1744 
1745  # re-render image on idle
1746  self.resize = True
1747 
1748  def OnIdle(self, event):
1749  """!Only re-render a preview image from GRASS during
1750  idle time instead of multiple times during resizing.
1751  """
1752  if self.resize:
1753  self.render = True
1754  self.UpdatePreview()
1755  event.Skip()
1756 
1757  def GetImage(self):
1758  """!Converts files to wx.Image"""
1759  if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
1760  os.path.getsize(self.Map.mapfile):
1761  img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
1762  else:
1763  img = None
1764 
1765  return img
1766 
1767  def UpdatePreview(self, img = None):
1768  """!Update canvas if window changes geometry"""
1769  Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render))
1770  oldfont = ""
1771  oldencoding = ""
1772 
1773  if self.render:
1774  # extent is taken from current map display
1775  try:
1776  self.Map.region = copy.deepcopy(self.parent.parent.curr_page.maptree.Map.region)
1777  except AttributeError:
1778  self.Map.region = self.Map.GetRegion()
1779  # render new map images
1780  self.mapfile = self.Map.Render(force = self.render)
1781  self.img = self.GetImage()
1782  self.resize = False
1783 
1784  if not self.img:
1785  return
1786 
1787  # paint images to PseudoDC
1788  self.pdc.Clear()
1789  self.pdc.RemoveAll()
1790  # draw map image background
1791  self.Draw(self.pdc, self.img, pdctype = 'image')
1792 
1793  self.resize = False
1794 
1795  def EraseMap(self):
1796  """!Erase preview"""
1797  self.Draw(self.pdc, pdctype = 'clear')
def CmdToTuple
Convert command list to tuple for gcmd.RunCommand()
Definition: core/utils.py:527
def __init__
Dialog for interactively entering color rules for vector maps.
Definition: colorrules.py:894
def AddTemporaryColumn
Add temporary column to not overwrite the original values, need to be deleted when closing dialog and...
Definition: colorrules.py:1226
def GetValue
Definition: widgets.py:118
def OnLoadRulesFile
Load color table from file.
Definition: colorrules.py:575
wxGUI command interface
def OnPreview
Update preview (based on computational region)
Definition: colorrules.py:1476
def OnRuleColor
Rule color changed.
Definition: colorrules.py:204
def UseAttrColumn
Find layers and apply the changes in d.vect command.
Definition: colorrules.py:1542
def Draw
Draws preview or clears window.
Definition: colorrules.py:1680
def OnSelectionInput
Vector map selected.
Definition: colorrules.py:1143
def ReadColorTable
Read color table.
Definition: colorrules.py:587
def UpdatePreview
Update canvas if window changes geometry.
Definition: colorrules.py:1767
def OnCheckColumn
Use color column instead of color table.
Definition: colorrules.py:1108
def OnPaint
Draw pseudo DC to buffer.
Definition: colorrules.py:1704
wxGUI debugging
def OnIdle
Only re-render a preview image from GRASS during idle time instead of multiple times during resizing...
Definition: colorrules.py:1748
def CreateColorTable
Create color rules (color table or color column)
Definition: colorrules.py:1566
def OnApply
Apply selected color table.
Definition: colorrules.py:527
def OnClearAll
Delete all widgets in panel.
Definition: colorrules.py:103
def OnRuleEnable
Rule enabled/disabled.
Definition: colorrules.py:167
def DeleteTemporaryColumn
Delete temporary column.
Definition: colorrules.py:1249
def OnCloseWindow
Window closed.
Definition: colorrules.py:522
def UpdateDialog
Update dialog after map selection.
Definition: colorrules.py:1161
def AddRules
Add rules.
Definition: colorrules.py:112
def OnTablePreview
Update preview (based on computational region)
Definition: colorrules.py:1483
def DoPreview
Update preview (based on computational region)
Definition: colorrules.py:706
def _createVectorAttrb
Create part of dialog with layer/column selection.
Definition: colorrules.py:947
def InitDisplay
Initialize preview display, set dimensions and region.
Definition: colorrules.py:515
def CheckMapset
Check if current vector is in current mapset.
Definition: colorrules.py:1086
def LoadTable
Load current color table (using r(v).colors.out)
Definition: colorrules.py:632
def SetValue
Definition: widgets.py:115
def OnCancel
Do not apply any changes, remove associated rendered images and close the dialog. ...
Definition: colorrules.py:549
def OnHelp
Show GRASS manual page.
Definition: colorrules.py:1537
def OnSelectionInput
Raster map selected.
Definition: colorrules.py:799
def CreateColorTable
Creates color table.
Definition: colorrules.py:662
Custom control that selects elements.
Rendering map layers and overlays into map composition image.
def Clear
Clear and widgets and delete information.
Definition: colorrules.py:89
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def DisableClearAll
Enable, disable the whole dialog.
Definition: colorrules.py:1134
def SetRangeLabel
Set labels with info about attribute column range.
Definition: colorrules.py:1439
def __init__
Create rules panel.
Definition: colorrules.py:46
def _createButtons
Create buttons for leaving dialog.
Definition: colorrules.py:440
def _createPreview
Create preview.
Definition: colorrules.py:432
def _createFileSelection
Create file (open/save rules) selection part of dialog.
Definition: colorrules.py:385
def __init__
Dialog for interactively entering color rules for raster maps.
Definition: colorrules.py:737
A Buffered window class.
Definition: colorrules.py:1641
def _createMapSelection
Create map selection part of dialog.
Definition: colorrules.py:365
def _createBody
Create dialog body consisting of rules and preview.
Definition: colorrules.py:465
def EnableVectorAttributes
Enable/disable part of dialog connected with db.
Definition: colorrules.py:1129
def CreateAttrTable
Create attribute table.
Definition: colorrules.py:1321
def OnApply
Apply selected color table.
Definition: colorrules.py:1624
def UpdateColorColumn
Creates color table.
Definition: colorrules.py:1578
def SetRasterRule
Set raster rule.
Definition: colorrules.py:243
def GetTempfile
Creates GRASS temporary file using defined prefix.
Definition: core/utils.py:44
def OnLoadDefaultTable
Load internal color table.
Definition: colorrules.py:628
def OnOK
Apply selected color table and close the dialog.
Definition: colorrules.py:544
def OnSaveRulesFile
Save color table to file.
Definition: colorrules.py:555
def OnRuleSize
Rule size changed.
Definition: colorrules.py:216
def OnFromColSelection
Selection in combobox (for loading values) changed.
Definition: colorrules.py:1466
def OnHelp
Show GRASS manual page.
Definition: colorrules.py:888
def OnCancel
Do not apply any changes and close the dialog.
Definition: colorrules.py:1618
def OnSize
Init image size to match window size.
Definition: colorrules.py:1726
def GetImage
Converts files to wx.Image.
Definition: colorrules.py:1757
def __init__
Dialog for interactively entering rules for map management commands.
Definition: colorrules.py:315
def OnAddRules
Add rules button pressed.
Definition: colorrules.py:107
def LoadRulesFromColumn
Load current column (GRASSRGB, size column)
Definition: colorrules.py:1342
def OnToColSelection
Selection in combobox (for storing values) changed.
Definition: colorrules.py:1472
def OnAddColumn
Add GRASS(RGB,SIZE,WIDTH) column if it doesn't exist.
Definition: colorrules.py:1298
def OnRuleValue
Rule value changed.
Definition: colorrules.py:223
def _IsNumber
Check if 's' is a number.
Definition: colorrules.py:727
def RunHelp
Show GRASS manual page.
Definition: colorrules.py:720
Default GUI settings.
def _initLayer
Set initial layer when opening dialog.
Definition: colorrules.py:346
def SetVectorRule
Set vector rule.
Definition: colorrules.py:247
tuple range
Definition: tools.py:1406
def OnColumnPreview
Update preview (based on computational region)
Definition: colorrules.py:1515
def EraseMap
Erase preview.
Definition: colorrules.py:1795
def OnPreview
Update preview (based on computational region)
Definition: colorrules.py:847
def LoadTable
Load table.
Definition: colorrules.py:1335
def OnCheckAll
(Un)check all rules
Definition: colorrules.py:94
def RunCommand
Run GRASS command.
Definition: gcmd.py:633
def SQLConvert
Prepare value for SQL query.
Definition: colorrules.py:301
def Enable
Enable/Disable all widgets.
Definition: colorrules.py:257