ソースを参照

wxGUI synchronized with devbr6 (relevant parts)

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@32789 15284696-431f-4ddb-bdfa-cd5b030d7da7
Martin Landa 16 年 前
コミット
49905a0c15

+ 509 - 0
gui/wxpython/gui_modules/colorrules.py

@@ -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')
+

+ 2 - 36
gui/wxpython/gui_modules/menuform.py

@@ -44,7 +44,6 @@ Classes:
  - verify option value types
  - use DOM instead of SAX
 """
-__version__ ="$Revision$"
 
 import sys
 import re
@@ -589,22 +588,7 @@ class mainFrame(wx.Frame):
 
         # icon
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_dialog.ico'), wx.BITMAP_TYPE_ICO))
-
-        # menu
-        #         menu = wx.Menu()
-        #         menu.Append(wx.ID_ABOUT, _("&About GrassGUI"),
-        #             _("Information about GrassGUI") )
-        #         menu.Append(ID_ABOUT_COMMAND, _("&About %s") % self.task.name,
-        #             _("Short descripton of GRASS command %s") % self.task.name)
-        #         menu.AppendSeparator()
-        #         menu.Append(wx.ID_EXIT, _("E&xit"), _("Terminate the program") )
-        #         menuBar = wx.MenuBar()
-        #         menuBar.Append(menu, "&File");
-        #         self.SetMenuBar(menuBar)
-        #wx.EVT_MENU(self, wx.ID_ABOUT, self.OnAbout)
-        #wx.EVT_MENU(self, ID_ABOUT_COMMAND, self.OnAboutCommand)
-        #wx.EVT_MENU(self, wx.ID_EXIT,  self.OnCancel)
-
+        
         guisizer = wx.BoxSizer(wx.VERTICAL)
 
         # set apropriate output window
@@ -857,25 +841,7 @@ class mainFrame(wx.Frame):
             self.notebookpanel.OnPageChange(None)
             
         event.Skip()
-
-    def OnAbout(self, event):
-        """General 'about' information"""
-        dlg = wx.MessageDialog(self, _("This is a sample program for\n"
-                                       "GRASS command interface parsing\n"
-                                       "and automatic GUI building.\n%s") %(__version__),
-                               _("About wxPython GRASS GUI"), wx.OK | wx.ICON_INFORMATION)
-        dlg.ShowModal()
-        dlg.Destroy()
-
-    def OnAboutCommand(self, event):
-        """About command"""
-        dlg = wx.MessageDialog(self,
-            self.task.name+": "+self.task.description,
-            "About " + self.task.name,
-            wx.OK | wx.ICON_INFORMATION)
-        dlg.ShowModal()
-        dlg.Destroy()
-
+        
     def createCmd(self, ignoreErrors = False):
         """Create command string (python list)"""
         return self.notebookpanel.createCmd(ignoreErrors=ignoreErrors)

ファイルの差分が大きいため隠しています
+ 14 - 3624
gui/wxpython/gui_modules/nviz.py


ファイルの差分が大きいため隠しています
+ 1201 - 0
gui/wxpython/gui_modules/nviz_mapdisp.py


ファイルの差分が大きいため隠しています
+ 2618 - 0
gui/wxpython/gui_modules/nviz_tools.py


+ 5 - 1
gui/wxpython/gui_modules/vdigit.py

@@ -1722,7 +1722,7 @@ class VDigitSettingsDialog(wx.Dialog):
         text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Snapping threshold"))
         self.snappingValue = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(75, -1),
                                          initial=UserSettings.Get(group='vdigit', key="snapping", subkey='value'),
-                                         min=0, max=1e6)
+                                         min=-1, max=1e6)
         self.snappingValue.Bind(wx.EVT_SPINCTRL, self.OnChangeSnappingValue)
         self.snappingUnit = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
                                       choices=["screen pixels", "map units"])
@@ -2073,6 +2073,10 @@ class VDigitSettingsDialog(wx.Dialog):
         """Change snapping value - update static text"""
         value = self.snappingValue.GetValue()
         
+        if value < 0:
+            self.snappingInfo.SetLabel(_("No limit for snapping"))
+            return
+        
         if self.snappingUnit.GetStringSelection() == "map units":
             threshold = value
         else:

+ 8 - 5
gui/wxpython/gui_modules/wxgui_utils.py

@@ -344,18 +344,21 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
         """Set computational region from selected raster/vector map"""
         mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
         mltype = self.GetPyData(self.layer_selected)[0]['type']
-
-        cmd = ['g.region',
-               '-p'] # print by default
-
+        
+        cmd = ['g.region']
+        
         # TODO: other elements
         if mltype == 'raster':
             cmd.append('rast=%s' % mapLayer.name)
         elif mltype == 'vector':
             cmd.append('vect=%s' % mapLayer.name)
+        elif mltype == '3d-raster':
+            cmd.append('rast3d=%s' % mapLayer.name)
 
         # print output to command log area
-        self.gismgr.goutput.RunCmd(cmd)
+        if len(cmd) > 1:
+            cmd.append('-p')
+            self.gismgr.goutput.RunCmd(cmd)
         
     def OnProfile(self, event):
         """Plot profile of given raster map layer"""

+ 23 - 22
gui/wxpython/wxgui.py

@@ -85,6 +85,7 @@ import gui_modules.dbm as dbm
 import gui_modules.workspace as workspace
 import gui_modules.goutput as goutput
 import gui_modules.gdialogs as gdialogs
+import gui_modules.colorrules as colorrules
 from   gui_modules.debug import Debug as Debug
 from   icons.icon import Icons as Icons
 
@@ -868,6 +869,7 @@ class GMFrame(wx.Frame):
 
         self.disp_idx = 0
         self.curr_page = None
+        
 
     def RulesCmd(self, event):
         """
@@ -875,32 +877,31 @@ class GMFrame(wx.Frame):
         input and processes rules
         """
         command = self.GetMenuCmd(event)
-
-        dlg = rules.RulesText(self, cmd=command)
-        if dlg.ShowModal() == wx.ID_OK:
-            gtemp = utils.GetTempfile()
-            output = open(gtemp, "w")
-            try:
-                output.write(dlg.rules)
-            finally:
-                output.close()
-
-            if command[0] == 'r.colors':
-                cmdlist = [command[0],
-                           'map=%s' % dlg.inmap,
-                           'rules=%s' % gtemp]
-            else:
+        
+        if command[0] == 'r.colors':
+            ctable = colorrules.ColorTable(self, cmd=command)
+            ctable.Show()      
+        else:
+            dlg = rules.RulesText(self, cmd=command)
+            if dlg.ShowModal() == wx.ID_OK:
+                gtemp = utils.GetTempfile()
+                output = open(gtemp, "w")
+                try:
+                    output.write(dlg.rules)
+                finally:
+                    output.close()
+    
                 cmdlist = [command[0],
                            'input=%s' % dlg.inmap,
                            'output=%s' % dlg.outmap,
                            'rules=%s' % gtemp]
-
-            if dlg.overwrite == True:
-                cmdlist.append('--o')
-
-            dlg.Destroy()
-
-            self.goutput.RunCmd(cmdlist)
+    
+                if dlg.overwrite == True:
+                    cmdlist.append('--o')
+    
+                dlg.Destroy()
+    
+                self.goutput.RunCmd(cmdlist)
 
     def OnXTerm(self, event):
         """