123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- """
- MODULE: histogram
- CLASSES:
- * BufferedWindow
- * HistFrame
- PURPOSE: Plotting histogram
- AUTHORS: The GRASS Development Team
- Michael Barton
- 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 wx
- import wx.aui
- import os, sys, time, glob, math
- from threading import Thread
- import globalvar
- try:
- import subprocess
- except:
- CompatPath = os.path.join(globalvar.ETCWXDIR)
- sys.path.append(CompatPath)
- from compat import subprocess
- gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
- sys.path.append(gmpath)
- import render
- import menuform
- import disp_print
- import utils
- from gui_modules.preferences import DefaultFontDialog as DefaultFontDialog
- from debug import Debug as Debug
- from icon import Icons as Icons
- import images
- imagepath = images.__path__[0]
- sys.path.append(imagepath)
- os.environ["GRASS_BACKGROUNDCOLOR"] = "blue"
- 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
- """
- 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)
- 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.UpdateHist()
- # 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.UpdateHist()
- event.Skip()
- def SaveToFile(self, FileName, FileType):
- """
- This will save the contents of the buffer
- to the specified file. See the wx.Windows docs for
- wx.Bitmap::SaveFile for the details
- """
- dc = wx.BufferedPaintDC(self, self._Buffer)
- self.pdc.DrawToDC(dc)
- self._Buffer.SaveFile(FileName, FileType)
- 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 UpdateHist(self, img=None):
- """
- Update canvas if histogram options changes or window changes geometry
- """
- Debug.msg (2, "BufferedWindow.UpdateHist(%s): render=%s" % (img, self.render))
- oldfont = ""
- oldencoding = ""
- if self.render:
- # render new map images
- # set default font and encoding environmental variables
- if "GRASS_FONT" in os.environ:
- oldfont = os.environ["GRASS_FONT"]
- if self.parent.font != "": os.environ["GRASS_FONT"] = self.parent.font
- if "GRASS_ENCODING" in os.environ:
- oldencoding = os.environ["GRASS_ENCODING"]
- if self.parent.encoding != None and self.parent.encoding != "ISO-8859-1":
- os.environ[GRASS_ENCODING] = self.parent.encoding
-
- # using active comp region
- self.Map.GetRegion(update=True)
-
- self.Map.width, self.Map.height = self.GetClientSize()
- 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()
- self.parent.statusbar.SetStatusText("Raster/Image map layer <%s>" % self.parent.mapname)
- # set default font and encoding environmental variables
- if oldfont != "":
- os.environ["GRASS_FONT"] = oldfont
- if oldencoding != "":
- os.environ["GRASS_ENCODING"] = oldencoding
- def EraseMap(self):
- """
- Erase the map display
- """
- self.Draw(self.pdc, pdctype='clear')
- class HistFrame(wx.Frame):
- """
- Main frame for hisgram display window.
- Uses d.histogram rendered onto canvas
- """
- def __init__(self, parent=None, id = wx.ID_ANY, title="Histogram of image or raster map",
- pos=wx.DefaultPosition, size=wx.DefaultSize,
- style=wx.DEFAULT_FRAME_STYLE):
- wx.Frame.__init__(self, parent, id, title, pos, size, style)
- toolbar = self.__createToolBar()
- self.Map = render.Map() # instance of render.Map to be associated with display
- self.layer = None # reference to layer with histogram
- #
- # Set the size & cursor
- #
- self.SetClientSize(size)
- self.iconsize = (16, 16)
- # Init variables
- self.params = {} # previously set histogram parameters
- self.propwin = '' # ID of properties dialog
- self.font = ""
- self.encoding = 'ISO-8859-1' # default encoding for display fonts
- #
- # Add statusbar
- #
- self.mapname = ''
- self.statusbar = self.CreateStatusBar(number=1, style=0)
- # self.statusbar.SetStatusWidths([-2, -1])
- hist_frame_statusbar_fields = ["Histogramming %s" % self.mapname]
- for i in range(len(hist_frame_statusbar_fields)):
- self.statusbar.SetStatusText(hist_frame_statusbar_fields[i], i)
- #
- # Init map display
- #
- self.InitDisplay() # initialize region values
- # initialize buffered DC
- self.HistWindow = BufferedWindow(self, id = wx.ID_ANY, Map=self.Map) # initialize buffered DC
- #
- # Bind various events
- #
- self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
- #
- # Init print module and classes
- #
- self.printopt = disp_print.PrintOptions(self, self.HistWindow)
- #
- # Add layer to the map
- #
- self.layer = self.Map.AddLayer(type="command", name='histogram', command=['d.histogram'],
- l_active=False, l_hidden=False, l_opacity=1, l_render=False)
- def __createToolBar(self):
- """Creates toolbar"""
- toolbar = self.CreateToolBar()
- for each in self.toolbarData():
- self.AddToolbarButton(toolbar, *each)
- toolbar.Realize()
- def AddToolbarButton(self, toolbar, label, icon, help, handler):
- """Adds buttons to the toolbar"""
- if not label:
- toolbar.AddSeparator()
- return
- tool = toolbar.AddLabelTool(id=wx.ID_ANY, label=label, bitmap=icon, shortHelp=help)
- self.Bind(wx.EVT_TOOL, handler, tool)
- def toolbarData(self):
- return (
- ('histogram',
- Icons["histogram"].GetBitmap(),
- Icons["histogram"].GetLabel(),
- self.OnOptions),
- ('rendermap',
- Icons["rendermap"].GetBitmap(),
- Icons["rendermap"].GetLabel(),
- self.OnRender),
- ('erase',
- Icons["erase"].GetBitmap(),
- Icons["erase"].GetLabel(),
- self.OnErase),
- ('font',
- Icons["font"].GetBitmap(),
- Icons["font"].GetLabel(),
- self.SetHistFont),
- ('', '', '', ''),
- ('save',
- Icons["savefile"].GetBitmap(),
- Icons["savefile"].GetLabel(),
- self.SaveToFile),
- ('print',
- Icons["printmap"].GetBitmap(),
- Icons["printmap"].GetLabel(),
- self.PrintMenu),
- ('quit',
- Icons["quit"].GetBitmap(),
- Icons["quit"].GetLabel(),
- self.OnQuit))
- def InitDisplay(self):
- """
- Initialize histogram display, set dimensions and region
- """
- self.width, self.height = self.GetClientSize()
- self.Map.geom = self.width, self.height
- def OnOptions(self, event):
- """Change histogram settings"""
- cmd = ['d.histogram']
- if self.mapname != '':
- cmd.append('map=%s' % self.mapname)
- menuform.GUI().ParseCommand(cmd,
- completed=(self.GetOptData, None, self.params),
- parentframe=self)
-
- def GetOptData(self, dcmd, layer, params, propwin):
- """
- Callback method for histogram command generated by
- dialog created in menuform.py
- """
- if dcmd:
- name = utils.GetLayerNameFromCmd(dcmd, fullyQualified=True)
- self.SetHistLayer(name)
- self.params = params
- self.propwin = propwin
- self.HistWindow.UpdateHist()
- def SetHistLayer(self, name):
- """
- Set histogram layer
- """
- self.mapname = name
- self.layer = self.Map.ChangeLayer(layer=self.layer,
- command=[['d.histogram', 'map=%s' % self.mapname],],
- active=True)
- return self.layer
- def SetHistFont(self, event):
- """
- Set font for histogram. If not
- set, font will be default display font.
- """
- dlg = DefaultFontDialog(parent=self, id=wx.ID_ANY,
- title=_('Select font for histogram text'))
- dlg.fontlb.SetStringSelection(self.font, True)
-
- if dlg.ShowModal() == wx.ID_CANCEL:
- dlg.Destroy()
- return
- # set default font type, font, and encoding to whatever selected in dialog
- if dlg.font != None:
- self.font = dlg.font
- if dlg.encoding != None:
- self.encoding = dlg.encoding
- dlg.Destroy()
- self.HistWindow.UpdateHist()
- def OnErase(self, event):
- """
- Erase the histogram display
- """
- self.HistWindow.Draw(self.HistWindow.pdc, pdctype='clear')
- def OnRender(self, event):
- """
- Re-render histogram
- """
- self.HistWindow.UpdateHist()
- def SaveToFile(self, event):
- """
- Save to file
- """
- filetype = "PNG file (*.png)|*.png|"\
- "TIF file (*.tif)|*.tif|"\
- "GIF file (*.gif)|*.gif"
- dlg = wx.FileDialog(self, "Choose a file name to save the image as a PNG to",
- defaultDir = "",
- defaultFile = "",
- wildcard = filetype,
- style=wx.SAVE|wx.FD_OVERWRITE_PROMPT)
- if dlg.ShowModal() == wx.ID_OK:
- base = os.path.splitext(dlg.GetPath())[0]
- ext = os.path.splitext(dlg.GetPath())[1]
- if dlg.GetFilterIndex() == 0:
- type = wx.BITMAP_TYPE_PNG
- path = dlg.GetPath()
- if ext != '.png': path = base+'.png'
- elif dlg.GetFilterIndex() == 1:
- type = wx.BITMAP_TYPE_TIF
- if ext != '.tif': path = base+'.tif'
- elif dlg.GetFilterIndex() == 2:
- type = wx.BITMAP_TYPE_TIF
- if ext != '.gif': path = base+'.gif'
- self.HistWindow.SaveToFile(path, type)
- dlg.Destroy()
- def PrintMenu(self, event):
- """
- Print options and output menu
- """
- point = wx.GetMousePosition()
- printmenu = wx.Menu()
- # Add items to the menu
- setup = wx.MenuItem(printmenu, -1,'Page setup')
- printmenu.AppendItem(setup)
- self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
- preview = wx.MenuItem(printmenu, -1,'Print preview')
- printmenu.AppendItem(preview)
- self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
- doprint = wx.MenuItem(printmenu, -1,'Print display')
- printmenu.AppendItem(doprint)
- self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
- reset = wx.MenuItem(printmenu, -1,'Reset')
- printmenu.AppendItem(reset)
- self.Bind(wx.EVT_MENU, self.printopt.OnReset, reset)
- # Popup the menu. If an item is selected then its handler
- # will be called before PopupMenu returns.
- self.PopupMenu(printmenu)
- printmenu.Destroy()
- def OnQuit(self, event):
- self.Close(True)
- def OnCloseWindow(self, event):
- """
- Window closed
- Also remove associated rendered images
- """
- try:
- self.propwin.Close(True)
- except:
- pass
- self.Map.Clean()
- self.Destroy()
|