[GRASS-SVN] r32708 -
grass/branches/develbranch_6/gui/wxpython/gui_modules
svn_grass at osgeo.org
svn_grass at osgeo.org
Tue Aug 12 03:03:02 EDT 2008
Author: cmbarton
Date: 2008-08-12 03:03:01 -0400 (Tue, 12 Aug 2008)
New Revision: 32708
Added:
grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py
Log:
New raster color table creator with preview
Added: grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py (rev 0)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py 2008-08-12 07:03:01 UTC (rev 32708)
@@ -0,0 +1,509 @@
+"""
+MODULE: rules.py
+
+CLASSES:
+ * RulesText
+
+PURPOSE: Dialog for interactive entry of rules for r.colors,
+ r.reclass, r.recode, and v.reclass
+
+AUTHORS: The GRASS Development Team
+ Michael Barton (Arizona State University)
+
+COPYRIGHT: (C) 2007 by the GRASS Development Team
+ This program is free software under the GNU General Public
+ License (>=v2). Read the file COPYING that comes with GRASS
+ for details.
+
+"""
+
+import os
+import sys
+import shutil
+from debug import Debug as Debug
+
+import wx
+import wx.lib.colourselect as csel
+import wx.lib.scrolledpanel as scrolled
+
+import gcmd
+import gselect
+import globalvar
+import render
+import utils
+
+class ColorTable(wx.Frame):
+ def __init__(self, parent, id=wx.ID_ANY, title='',
+ pos=wx.DefaultPosition, size=(-1, -1),
+ style=wx.DEFAULT_FRAME_STYLE|wx.RESIZE_BORDER,
+ **kwargs):
+ wx.Frame.__init__(self, parent, id, title, pos, size, style)
+
+ """
+ Dialog for interactively entering rules
+ for map management commands
+
+ @param cmd command (given as list)
+ """
+
+ self.CentreOnParent()
+ self.parent = parent
+ self.cmd = kwargs['cmd'] # grass command
+ self.inmap = '' # input map to change
+ self.rastmin = '' # min cat in raster map
+ self.rastmax = '' # max cat in raster map
+ self.old_colrtable = '' # existing color table of raster map
+ self.vals = '' # raster category values for assigning colors
+ self.rgb_string = '' # r:g:b color string for assigning colors to cats
+ self.ruleslines = {} # rules for creating colortable
+ self.overwrite = True
+
+
+ self.Map = render.Map() # instance of render.Map to be associated with display
+ self.layer = None # reference to layer with histogram
+ self.mapname = ''
+
+ #
+ # Set the size & cursor
+ #
+ self.SetClientSize(size)
+
+ # set window frame title
+ self.SetTitle('Create new color table for raster map')
+
+ # top controls
+ self.map_label=wx.StaticText(parent=self, id=wx.ID_ANY, label='Select raster map:')
+ self.selectionInput = gselect.Select(parent=self, id=wx.ID_ANY,
+ size=globalvar.DIALOG_GSELECT_SIZE,
+ type='cell')
+ self.ovrwrtcheck = wx.CheckBox(parent=self, id=wx.ID_ANY,
+ label=_('replace existing color table'))
+ self.ovrwrtcheck.SetValue(self.overwrite)
+ self.helpbtn = wx.Button(parent=self, id=wx.ID_HELP)
+
+ # color table and preview window
+ self.cr_label = wx.StaticText(parent=self, id=wx.ID_ANY,
+ label=_('Enter raster cat values or percents'))
+ self.cr_panel = self.__colorrulesPanel()
+ self.InitDisplay() # initialize preview display
+ self.preview = BufferedWindow(self, id = wx.ID_ANY, size=(400,300), Map=self.Map)
+
+ # bottom controls
+ self.line = wx.StaticLine(parent=self, id=wx.ID_ANY, size=(-1,-1),
+ style=wx.LI_HORIZONTAL)
+
+ cancel_btn = wx.Button(self, wx.ID_CANCEL)
+ apply_btn = wx.Button(self, wx.ID_APPLY)
+ ok_btn = wx.Button(self, wx.ID_OK)
+ ok_btn.SetDefault()
+
+ self.btnsizer = wx.StdDialogButtonSizer()
+ self.btnsizer.Add(cancel_btn, flag=wx.ALL, border=5)
+ self.btnsizer.Add(apply_btn, flag=wx.ALL, border=5)
+ self.btnsizer.Add(ok_btn, flag=wx.ALL, border=5)
+ self.btnsizer.Realize()
+
+ self.preview_btn = wx.Button(self, wx.ID_ANY, _("Preview"))
+
+ # bindings
+ self.Bind(wx.EVT_BUTTON, self.OnHelp, self.helpbtn)
+ self.Bind(wx.EVT_CHECKBOX, self.OnOverwrite, self.ovrwrtcheck)
+ self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput)
+ self.Bind(wx.EVT_BUTTON, self.OnCancel, cancel_btn)
+ self.Bind(wx.EVT_BUTTON, self.OnApply, apply_btn)
+ self.Bind(wx.EVT_BUTTON, self.OnOK, ok_btn)
+ self.Bind(wx.EVT_BUTTON, self.OnPreview, self.preview_btn)
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+ # layout
+ self.__doLayout()
+ self.Show()
+
+ def __doLayout(self):
+ sizer = wx.GridBagSizer(hgap=5, vgap=5)
+ sizer.AddGrowableCol(2)
+ sizer.AddGrowableRow(4)
+ sizer.Add(self.map_label, pos=(0,0),
+ flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.ALL, border=5)
+ sizer.Add(item=self.selectionInput, pos=(1,0), span=(1,3),
+ flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND | wx.ALL, border=5)
+ sizer.Add(item=self.ovrwrtcheck, pos=(2, 0), span=(1,2),
+ flag=wx.ALL, border=5)
+ sizer.Add(item=self.helpbtn, pos=(2,2), flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
+ sizer.Add(item=self.cr_label, pos=(3,0), span=(1,2), flag=wx.ALL, border=5)
+ sizer.Add(item=self.cr_panel, pos=(4,0),
+ flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=10)
+ sizer.Add(item=self.preview, pos=(4,1), span=(1,2),
+ flag=wx.ALIGN_LEFT|wx.EXPAND|wx.LEFT|wx.RIGHT, border=10)
+ sizer.Add(item=self.line, pos=(5,0), span=(1,3),
+ flag=wx.GROW|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|
+ wx.TOP|wx.BOTTOM, border=5)
+ sizer.Add(self.btnsizer, pos=(6,0), span=(1,2),
+ flag=wx.ALIGN_CENTER|wx.FIXED_MINSIZE)
+ sizer.Add(self.preview_btn, pos=(6,2),
+ flag=wx.ALIGN_BOTTOM|wx.ALIGN_CENTER|wx.FIXED_MINSIZE|wx.ALL, border=5)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def __previewPanel(self):
+ preview = wx.Panel(self, -1, pos=(-1,-1), size=(-1,-1), style=wx.SUNKEN_BORDER)
+ preview.SetBackgroundColour(wx.WHITE)
+ #preview.SetAutoLayout(1)
+ return preview
+
+ def __colorrulesPanel(self):
+ cr_panel = scrolled.ScrolledPanel(self, -1, size=(150,300),
+ style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER,
+ name="cr_panel" )
+ cr_sizer = wx.GridBagSizer(vgap=2, hgap=4)
+
+ for num in range(100):
+ txt_ctrl = wx.TextCtrl(parent=cr_panel, id=num, value='',
+ pos=wx.DefaultPosition, size=(50,-1),
+ style=wx.TE_NOHIDESEL)
+ self.Bind(wx.EVT_TEXT, self.OnVals, txt_ctrl)
+ color_ctrl = csel.ColourSelect(cr_panel, id=num)
+ self.Bind(csel.EVT_COLOURSELECT, self.OnSelectColor, color_ctrl)
+ self.ruleslines[num] = ["","0:0:0"]
+
+ cr_sizer.Add(item=txt_ctrl, pos=(num,0),
+ flag=wx.ALIGN_CENTER|wx.LEFT, border=10)
+ cr_sizer.Add(item=color_ctrl, pos=(num,1),
+ flag=wx.ALIGN_CENTER|wx.RIGHT, border=10)
+
+ cr_panel.SetSizer(cr_sizer)
+ cr_panel.SetAutoLayout(1)
+ cr_panel.SetupScrolling()
+
+ return cr_panel
+
+ def InitDisplay(self):
+ """
+ Initialize preview display, set dimensions and region
+ """
+ #self.width, self.height = self.GetClientSize()
+ self.width = self.Map.width = 400
+ self.height = self.Map.height = 300
+ self.Map.geom = self.width, self.height
+
+ def OnErase(self, event):
+ """
+ Erase the histogram display
+ """
+ self.PreviewWindow.Draw(self.HistWindow.pdc, pdctype='clear')
+
+ def OnCloseWindow(self, event):
+ """
+ Window closed
+ Also remove associated rendered images
+ """
+ #try:
+ # self.propwin.Close(True)
+ #except:
+ # pass
+ self.Map.Clean()
+ self.Destroy()
+
+ def OnSelectionInput(self, event):
+ self.inmap = event.GetString()
+ try:
+ cmdlist = ['r.info', 'map=%s' % self.inmap, '-r']
+
+ p = gcmd.Command(cmdlist)
+ output = p.ReadStdOutput()
+ for line in output:
+ line = line.strip('\n ')
+ if 'min' in line:
+ self.rastmin = line.split('=')[1].strip('\n ')
+ if 'max' in line:
+ self.rastmax = line.split('=')[1].strip('\n ')
+
+ self.cr_label.SetLabel('Enter raster cat values or percents (range = %s-%s)' %
+ (self.rastmin,self.rastmax))
+ except:
+ pass
+
+ def OnVals(self, event):
+ num = event.GetId()
+ vals = event.GetString().strip()
+ self.ruleslines[num][0] = vals
+
+ def OnSelectColor(self, event):
+ num = event.GetId()
+ rgba_color = event.GetValue()
+ rgb_string = str(rgba_color[0]) + ':' + str(rgba_color[1]) + ':' + str(rgba_color[2])
+ self.ruleslines[num][1] = rgb_string
+
+ def OnApply(self, event):
+ self.CreateColorTable()
+
+ def OnOK(self, event):
+ self.CreateColorTable()
+ self.Destroy()
+
+ def OnCancel(self, event):
+ self.Destroy()
+
+ def OnPreview(self, event):
+ # Add layer to the map for preview
+ #
+ cmd = ['d.rast', 'map=%s' % self.inmap]
+ self.layer = self.Map.AddLayer(type="command", name='raster', command=cmd,
+ l_active=True, l_hidden=False, l_opacity=1, l_render=False)
+
+ # Find existing color table and copy to temp file
+ p = gcmd.Command(['g.findfile', 'element=colr', 'file=%s' % self.inmap])
+ output = p.ReadStdOutput()
+ for line in output:
+ if 'file=' in line:
+ old_colrtable = line.split('=')[1].strip("'")
+ try:
+ colrtemp = utils.GetTempfile()
+ shutil.copyfile(old_colrtable,colrtemp)
+ except:
+ return
+
+ # apply new color table and display preview
+ self.CreateColorTable()
+ self.preview.UpdatePreview()
+
+ shutil.copyfile(colrtemp, old_colrtable)
+
+ try:
+ os.remove(colrtemp)
+ except:
+ pass
+
+ def OnHelp(self, event):
+ gcmd.Command(['g.manual',
+ '--quiet',
+ '%s' % self.cmd[0]])
+
+ def OnOverwrite(self, event):
+ self.overwrite = event.IsChecked()
+
+ def CreateColorTable(self):
+ rulestxt = ''
+ for num in self.ruleslines:
+ if self.ruleslines[num][0] != "":
+ rulestxt += self.ruleslines[num][0] + ' ' + self.ruleslines[num][1] + '\n'
+ if rulestxt == '': return
+
+ gtemp = utils.GetTempfile()
+ output = open(gtemp, "w")
+ try:
+ output.write(rulestxt)
+ finally:
+ output.close()
+
+ cmdlist = ['r.colors', 'map=%s' % self.inmap, 'rules=%s' % gtemp]
+
+ if self.overwrite == False:
+ cmdlist.append('-w')
+
+ gcmd.Command(cmdlist)
+
+class BufferedWindow(wx.Window):
+ """
+ A Buffered window class.
+
+ When the drawing needs to change, you app needs to call the
+ UpdateHist() method. Since the drawing is stored in a bitmap, you
+ can also save the drawing to file by calling the
+ SaveToFile(self,file_name,file_type) method.
+ """
+
+ def __init__(self, parent, id,
+ pos = wx.DefaultPosition,
+ size = wx.DefaultSize,
+ style=wx.NO_FULL_REPAINT_ON_RESIZE,
+ Map=None):
+
+ wx.Window.__init__(self, parent, id, pos, size, style)
+
+ self.parent = parent
+ self.Map = Map
+ self.mapname = self.parent.mapname
+
+ #
+ # Flags
+ #
+ self.render = True # re-render the map from GRASS or just redraw image
+ self.resize = False # indicates whether or not a resize event has taken place
+ self.dragimg = None # initialize variable for map panning
+ self.pen = None # pen for drawing zoom boxes, etc.
+
+ #
+ # Event bindings
+ #
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+ #self.Bind(wx.EVT_SIZE, self.OnSize)
+ self.Bind(wx.EVT_IDLE, self.OnIdle)
+
+ #
+ # Render output objects
+ #
+ self.mapfile = None # image file to be rendered
+ self.img = "" # wx.Image object (self.mapfile)
+
+ self.imagedict = {} # images and their PseudoDC ID's for painting and dragging
+
+ self.pdc = wx.PseudoDC()
+ self._Buffer = '' # will store an off screen empty bitmap for saving to file
+
+ # make sure that extents are updated at init
+ self.Map.region = self.Map.GetRegion()
+ self.Map.SetRegion()
+
+ self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
+
+ def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0,0,0,0]):
+ """
+ Draws histogram or clears window
+ """
+
+ if drawid == None:
+ if pdctype == 'image' :
+ drawid = imagedict[img]
+ elif pdctype == 'clear':
+ drawid == None
+ else:
+ drawid = wx.NewId()
+ else:
+ pdc.SetId(drawid)
+
+ pdc.BeginDrawing()
+
+ Debug.msg (3, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % (drawid, pdctype, coords))
+
+ if pdctype == 'clear': # erase the display
+ bg = wx.WHITE_BRUSH
+ pdc.SetBackground(bg)
+ pdc.Clear()
+ self.Refresh()
+ pdc.EndDrawing()
+ return
+
+ if pdctype == 'image':
+ bg = wx.TRANSPARENT_BRUSH
+ pdc.SetBackground(bg)
+ bitmap = wx.BitmapFromImage(img)
+ w,h = bitmap.GetSize()
+ pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
+ pdc.SetIdBounds(drawid, (coords[0],coords[1],w,h))
+
+ pdc.EndDrawing()
+ self.Refresh()
+
+ def OnPaint(self, event):
+ """
+ Draw psuedo DC to buffer
+ """
+
+ self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
+ dc = wx.BufferedPaintDC(self, self._Buffer)
+
+ # use PrepareDC to set position correctly
+ self.PrepareDC(dc)
+ # we need to clear the dc BEFORE calling PrepareDC
+ bg = wx.Brush(self.GetBackgroundColour())
+ dc.SetBackground(bg)
+ dc.Clear()
+ # create a clipping rect from our position and size
+ # and the Update Region
+ rgn = self.GetUpdateRegion()
+ r = rgn.GetBox()
+ # draw to the dc using the calculated clipping rect
+ self.pdc.DrawToDCClipped(dc,r)
+
+ #self.pdc.DrawToDC(dc)
+
+ def OnSize(self, event):
+ """
+ Init image size to match window size
+ """
+
+ # set size of the input image
+ self.Map.width, self.Map.height = self.GetClientSize()
+
+ # Make new off screen bitmap: this bitmap will always have the
+ # current drawing in it, so it can be used to save the image to
+ # a file, or whatever.
+ self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
+
+ # get the image to be rendered
+ self.img = self.GetImage()
+
+ # update map display
+ if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
+ self.img = self.img.Scale(self.Map.width, self.Map.height)
+ self.render = False
+ self.UpdatePreview()
+
+ # re-render image on idle
+ self.resize = True
+
+ def OnIdle(self, event):
+ """
+ Only re-render a histogram image from GRASS during
+ idle time instead of multiple times during resizing.
+ """
+
+ if self.resize:
+ self.render = True
+ self.UpdatePreview()
+ event.Skip()
+
+ def GetImage(self):
+ """
+ Converts files to wx.Image
+ """
+ if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
+ os.path.getsize(self.Map.mapfile):
+ img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
+ else:
+ img = None
+
+ self.imagedict[img] = 99 # set image PeudoDC ID
+ return img
+
+
+ def UpdatePreview(self, img=None):
+ """
+ Update canvas if window changes geometry
+ """
+
+ Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render))
+ oldfont = ""
+ oldencoding = ""
+
+ if self.render:
+ # render new map images
+ self.mapfile = self.Map.Render(force=self.render)
+ self.img = self.GetImage()
+ self.resize = False
+
+ if not self.img: return
+ try:
+ id = self.imagedict[self.img]
+ except:
+ return
+
+ # paint images to PseudoDC
+ self.pdc.Clear()
+ self.pdc.RemoveAll()
+ self.Draw(self.pdc, self.img, drawid=id) # draw map image background
+
+ self.resize = False
+
+ # update statusbar
+ # Debug.msg (3, "BufferedWindow.UpdateHist(%s): region=%s" % self.Map.region)
+ self.Map.SetRegion()
+
+ def EraseMap(self):
+ """
+ Erase the map display
+ """
+ self.Draw(self.pdc, pdctype='clear')
+
More information about the grass-commit
mailing list