GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
histogram.py
Go to the documentation of this file.
1 """!
2 @package modules.histogram
3 
4 Plotting histogram based on d.histogram
5 
6 Classes:
7  - histogram::BufferedWindow
8  - histogram::HistogramFrame
9  - histogram::HistogramToolbar
10 
11 (C) 2007, 2010-2011 by the GRASS Development Team
12 
13 This program is free software under the GNU General Public License
14 (>=v2). Read the file COPYING that comes with GRASS for details.
15 
16 @author Michael Barton
17 @author Various updates by Martin Landa <landa.martin gmail.com>
18 """
19 
20 import os
21 
22 import wx
23 
24 from core import globalvar
25 from core.render import Map
26 from gui_core.forms import GUI
27 from mapdisp.gprint import PrintOptions
28 from core.utils import GetLayerNameFromCmd
29 from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
30 from gui_core.preferences import DefaultFontDialog
31 from core.debug import Debug
32 from core.gcmd import GError
33 from gui_core.toolbars import BaseToolbar, BaseIcons
34 
35 class BufferedWindow(wx.Window):
36  """!A Buffered window class.
37 
38  When the drawing needs to change, you app needs to call the
39  UpdateHist() method. Since the drawing is stored in a bitmap, you
40  can also save the drawing to file by calling the
41  SaveToFile(self,file_name,file_type) method.
42  """
43  def __init__(self, parent, id = wx.ID_ANY,
44  style = wx.NO_FULL_REPAINT_ON_RESIZE,
45  Map = None, **kwargs):
46 
47  wx.Window.__init__(self, parent, id = id, style = style, **kwargs)
48 
49  self.parent = parent
50  self.Map = Map
51  self.mapname = self.parent.mapname
52 
53  #
54  # Flags
55  #
56  self.render = True # re-render the map from GRASS or just redraw image
57  self.resize = False # indicates whether or not a resize event has taken place
58  self.dragimg = None # initialize variable for map panning
59  self.pen = None # pen for drawing zoom boxes, etc.
60 
61  #
62  # Event bindings
63  #
64  self.Bind(wx.EVT_PAINT, self.OnPaint)
65  self.Bind(wx.EVT_SIZE, self.OnSize)
66  self.Bind(wx.EVT_IDLE, self.OnIdle)
67 
68  #
69  # Render output objects
70  #
71  self.mapfile = None # image file to be rendered
72  self.img = "" # wx.Image object (self.mapfile)
73 
74  self.imagedict = {} # images and their PseudoDC ID's for painting and dragging
75 
76  self.pdc = wx.PseudoDC()
77  self._buffer = '' # will store an off screen empty bitmap for saving to file
78 
79  # make sure that extents are updated at init
80  self.Map.region = self.Map.GetRegion()
81  self.Map.SetRegion()
82 
83  self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
84 
85  def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0,0,0,0]):
86  """!Draws histogram or clears window
87  """
88  if drawid == None:
89  if pdctype == 'image' :
90  drawid = imagedict[img]
91  elif pdctype == 'clear':
92  drawid == None
93  else:
94  drawid = wx.NewId()
95  else:
96  pdc.SetId(drawid)
97 
98  pdc.BeginDrawing()
99 
100  Debug.msg (3, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % (drawid, pdctype, coords))
101 
102  if pdctype == 'clear': # erase the display
103  bg = wx.WHITE_BRUSH
104  pdc.SetBackground(bg)
105  pdc.Clear()
106  self.Refresh()
107  pdc.EndDrawing()
108  return
109 
110  if pdctype == 'image':
111  bg = wx.TRANSPARENT_BRUSH
112  pdc.SetBackground(bg)
113  bitmap = wx.BitmapFromImage(img)
114  w,h = bitmap.GetSize()
115  pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
116  pdc.SetIdBounds(drawid, (coords[0],coords[1],w,h))
117 
118  pdc.EndDrawing()
119  self.Refresh()
120 
121  def OnPaint(self, event):
122  """!Draw psuedo DC to buffer
123  """
124  dc = wx.BufferedPaintDC(self, self._buffer)
125 
126  # use PrepareDC to set position correctly
127  # probably does nothing, removed from wxPython 2.9
128  # self.PrepareDC(dc)
129  # we need to clear the dc BEFORE calling PrepareDC
130  bg = wx.Brush(self.GetBackgroundColour())
131  dc.SetBackground(bg)
132  dc.Clear()
133  # create a clipping rect from our position and size
134  # and the Update Region
135  rgn = self.GetUpdateRegion()
136  r = rgn.GetBox()
137  # draw to the dc using the calculated clipping rect
138  self.pdc.DrawToDCClipped(dc,r)
139 
140  def OnSize(self, event):
141  """!Init image size to match window size
142  """
143  # set size of the input image
144  self.Map.width, self.Map.height = self.GetClientSize()
145 
146  # Make new off screen bitmap: this bitmap will always have the
147  # current drawing in it, so it can be used to save the image to
148  # a file, or whatever.
149  self._buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
150 
151  # get the image to be rendered
152  self.img = self.GetImage()
153 
154  # update map display
155  if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
156  self.img = self.img.Scale(self.Map.width, self.Map.height)
157  self.render = False
158  self.UpdateHist()
159 
160  # re-render image on idle
161  self.resize = True
162 
163  def OnIdle(self, event):
164  """!Only re-render a histogram image from GRASS during idle
165  time instead of multiple times during resizing.
166  """
167  if self.resize:
168  self.render = True
169  self.UpdateHist()
170  event.Skip()
171 
172  def SaveToFile(self, FileName, FileType, width, height):
173  """!This will save the contents of the buffer to the specified
174  file. See the wx.Windows docs for wx.Bitmap::SaveFile for the
175  details
176  """
177  busy = wx.BusyInfo(message=_("Please wait, exporting image..."),
178  parent=self)
179  wx.Yield()
180 
181  self.Map.ChangeMapSize((width, height))
182  ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
183  self.Map.Render(force=True, windres = True)
184  img = self.GetImage()
185  self.Draw(self.pdc, img, drawid = 99)
186  dc = wx.BufferedPaintDC(self, ibuffer)
187  dc.Clear()
188  # probably does nothing, removed from wxPython 2.9
189  # self.PrepareDC(dc)
190  self.pdc.DrawToDC(dc)
191  ibuffer.SaveFile(FileName, FileType)
192 
193  busy.Destroy()
194 
195  def GetImage(self):
196  """!Converts files to wx.Image
197  """
198  if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
199  os.path.getsize(self.Map.mapfile):
200  img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
201  else:
202  img = None
203 
204  self.imagedict[img] = 99 # set image PeudoDC ID
205  return img
206 
207  def UpdateHist(self, img = None):
208  """!Update canvas if histogram options changes or window
209  changes geometry
210  """
211  Debug.msg (2, "BufferedWindow.UpdateHist(%s): render=%s" % (img, self.render))
212  oldfont = ""
213  oldencoding = ""
214 
215  if self.render:
216  # render new map images
217  # set default font and encoding environmental variables
218  if "GRASS_FONT" in os.environ:
219  oldfont = os.environ["GRASS_FONT"]
220  if self.parent.font != "": os.environ["GRASS_FONT"] = self.parent.font
221  if "GRASS_ENCODING" in os.environ:
222  oldencoding = os.environ["GRASS_ENCODING"]
223  if self.parent.encoding != None and self.parent.encoding != "ISO-8859-1":
224  os.environ[GRASS_ENCODING] = self.parent.encoding
225 
226  # using active comp region
227  self.Map.GetRegion(update = True)
228 
229  self.Map.width, self.Map.height = self.GetClientSize()
230  self.mapfile = self.Map.Render(force = self.render)
231  self.img = self.GetImage()
232  self.resize = False
233 
234  if not self.img: return
235  try:
236  id = self.imagedict[self.img]
237  except:
238  return
239 
240  # paint images to PseudoDC
241  self.pdc.Clear()
242  self.pdc.RemoveAll()
243  self.Draw(self.pdc, self.img, drawid = id) # draw map image background
244 
245  self.resize = False
246 
247  # update statusbar
248  # Debug.msg (3, "BufferedWindow.UpdateHist(%s): region=%s" % self.Map.region)
249  self.Map.SetRegion()
250  self.parent.statusbar.SetStatusText("Image/Raster map <%s>" % self.parent.mapname)
251 
252  # set default font and encoding environmental variables
253  if oldfont != "":
254  os.environ["GRASS_FONT"] = oldfont
255  if oldencoding != "":
256  os.environ["GRASS_ENCODING"] = oldencoding
257 
258  def EraseMap(self):
259  """!Erase the map display
260  """
261  self.Draw(self.pdc, pdctype = 'clear')
262 
263 class HistogramFrame(wx.Frame):
264  """!Main frame for hisgram display window. Uses d.histogram
265  rendered onto canvas
266  """
267  def __init__(self, parent = None, id = wx.ID_ANY,
268  title = _("GRASS GIS Histogramming Tool (d.histogram)"),
269  size = wx.Size(500, 350),
270  style = wx.DEFAULT_FRAME_STYLE, **kwargs):
271  wx.Frame.__init__(self, parent, id, title, size = size, style = style, **kwargs)
272  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
273 
274  self.Map = Map() # instance of render.Map to be associated with display
275  self.layer = None # reference to layer with histogram
276 
277  # Init variables
278  self.params = {} # previously set histogram parameters
279  self.propwin = '' # ID of properties dialog
280 
281  self.font = ""
282  self.encoding = 'ISO-8859-1' # default encoding for display fonts
283 
284  self.toolbar = HistogramToolbar(parent = self)
285  self.SetToolBar(self.toolbar)
286 
287  # find selected map
288  self.mapname = None
289  if parent.GetName() == "MapWindow" and not parent.IsStandalone():
290  tree = parent.GetLayerManager().GetLayerTree()
291 
292  if tree.layer_selected and tree.GetPyData(tree.layer_selected)[0]['type'] == 'raster':
293  self.mapname = tree.GetPyData(tree.layer_selected)[0]['maplayer'].name
294 
295  # Add statusbar
296  self.statusbar = self.CreateStatusBar(number = 1, style = 0)
297  # self.statusbar.SetStatusWidths([-2, -1])
298  hist_frame_statusbar_fields = ["Histogramming %s" % self.mapname]
299  for i in range(len(hist_frame_statusbar_fields)):
300  self.statusbar.SetStatusText(hist_frame_statusbar_fields[i], i)
301 
302  # Init map display
303  self.InitDisplay() # initialize region values
304 
305  # initialize buffered DC
306  self.HistWindow = BufferedWindow(self, id = wx.ID_ANY, Map = self.Map) # initialize buffered DC
307 
308  # Bind various events
309  self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
310 
311  # Init print module and classes
312  self.printopt = PrintOptions(self, self.HistWindow)
313 
314  # Add layer to the map
315  self.layer = self.Map.AddLayer(type = "command", name = 'histogram', command = [['d.histogram']],
316  l_active = False, l_hidden = False, l_opacity = 1, l_render = False)
317  if self.mapname:
318  self.SetHistLayer(self.mapname, None)
319  else:
320  self.OnErase(None)
321 
322  def InitDisplay(self):
323  """!Initialize histogram display, set dimensions and region
324  """
325  self.width, self.height = self.GetClientSize()
326  self.Map.geom = self.width, self.height
327 
328  def OnOptions(self, event):
329  """!Change histogram settings"""
330  cmd = ['d.histogram']
331  if self.mapname != '':
332  cmd.append('map=%s' % self.mapname)
333  module = GUI(parent = self)
334  module.ParseCommand(cmd, completed = (self.GetOptData, None, self.params))
335 
336  def GetOptData(self, dcmd, layer, params, propwin):
337  """!Callback method for histogram command generated by dialog
338  created in menuform.py
339  """
340  if dcmd:
341  name, found = GetLayerNameFromCmd(dcmd, fullyQualified = True,
342  layerType = 'raster')
343  if not found:
344  GError(parent = propwin,
345  message = _("Raster map <%s> not found") % name)
346  return
347 
348  self.SetHistLayer(name, dcmd)
349  self.params = params
350  self.propwin = propwin
351  self.HistWindow.UpdateHist()
352 
353  def SetHistLayer(self, name, cmd = None):
354  """!Set histogram layer
355  """
356  self.mapname = name
357  if not cmd:
358  cmd = ['d.histogram',('map=%s' % self.mapname)]
359  self.layer = self.Map.ChangeLayer(layer = self.layer,
360  command = [cmd],
361  active = True)
362 
363  return self.layer
364 
365  def SetHistFont(self, event):
366  """!Set font for histogram. If not set, font will be default
367  display font.
368  """
369  dlg = DefaultFontDialog(parent = self, id = wx.ID_ANY,
370  title = _('Select font for histogram text'))
371  dlg.fontlb.SetStringSelection(self.font, True)
372 
373  if dlg.ShowModal() == wx.ID_CANCEL:
374  dlg.Destroy()
375  return
376 
377  # set default font type, font, and encoding to whatever selected in dialog
378  if dlg.font != None:
379  self.font = dlg.font
380  if dlg.encoding != None:
381  self.encoding = dlg.encoding
382 
383  dlg.Destroy()
384  self.HistWindow.UpdateHist()
385 
386  def OnErase(self, event):
387  """!Erase the histogram display
388  """
389  self.HistWindow.Draw(self.HistWindow.pdc, pdctype = 'clear')
390 
391  def OnRender(self, event):
392  """!Re-render histogram
393  """
394  self.HistWindow.UpdateHist()
395 
396  def GetWindow(self):
397  """!Get buffered window"""
398  return self.HistWindow
399 
400  def SaveToFile(self, event):
401  """!Save to file
402  """
403  filetype, ltype = GetImageHandlers(self.HistWindow.img)
404 
405  # get size
406  dlg = ImageSizeDialog(self)
407  dlg.CentreOnParent()
408  if dlg.ShowModal() != wx.ID_OK:
409  dlg.Destroy()
410  return
411  width, height = dlg.GetValues()
412  dlg.Destroy()
413 
414  # get filename
415  dlg = wx.FileDialog(parent = self,
416  message = _("Choose a file name to save the image "
417  "(no need to add extension)"),
418  wildcard = filetype,
419  style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
420 
421  if dlg.ShowModal() == wx.ID_OK:
422  path = dlg.GetPath()
423  if not path:
424  dlg.Destroy()
425  return
426 
427  base, ext = os.path.splitext(path)
428  fileType = ltype[dlg.GetFilterIndex()]['type']
429  extType = ltype[dlg.GetFilterIndex()]['ext']
430  if ext != extType:
431  path = base + '.' + extType
432 
433  self.HistWindow.SaveToFile(path, fileType,
434  width, height)
435 
436  self.HistWindow.UpdateHist()
437  dlg.Destroy()
438 
439  def PrintMenu(self, event):
440  """!Print options and output menu
441  """
442  point = wx.GetMousePosition()
443  printmenu = wx.Menu()
444  # Add items to the menu
445  setup = wx.MenuItem(printmenu, id = wx.ID_ANY, text = _('Page setup'))
446  printmenu.AppendItem(setup)
447  self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
448 
449  preview = wx.MenuItem(printmenu, id = wx.ID_ANY, text = _('Print preview'))
450  printmenu.AppendItem(preview)
451  self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
452 
453  doprint = wx.MenuItem(printmenu, id = wx.ID_ANY, text = _('Print display'))
454  printmenu.AppendItem(doprint)
455  self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
456 
457  # Popup the menu. If an item is selected then its handler
458  # will be called before PopupMenu returns.
459  self.PopupMenu(printmenu)
460  printmenu.Destroy()
461 
462  def OnQuit(self, event):
463  self.Close(True)
464 
465  def OnCloseWindow(self, event):
466  """!Window closed
467  Also remove associated rendered images
468  """
469  try:
470  self.propwin.Close(True)
471  except:
472  pass
473  self.Map.Clean()
474  self.Destroy()
475 
476 class HistogramToolbar(BaseToolbar):
477  """!Histogram toolbar (see histogram.py)
478  """
479  def __init__(self, parent):
480  BaseToolbar.__init__(self, parent)
481 
482  self.InitToolbar(self._toolbarData())
483 
484  # realize the toolbar
485  self.Realize()
486 
487  def _toolbarData(self):
488  """!Toolbar data"""
489  return self._getToolbarData((('histogram', BaseIcons["histogramD"],
490  self.parent.OnOptions),
491  ('render', BaseIcons["display"],
492  self.parent.OnRender),
493  ('erase', BaseIcons["erase"],
494  self.parent.OnErase),
495  ('font', BaseIcons["font"],
496  self.parent.SetHistFont),
497  (None, ),
498  ('save', BaseIcons["saveFile"],
499  self.parent.SaveToFile),
500  ('hprint', BaseIcons["print"],
501  self.parent.PrintMenu),
502  (None, ),
503  ('quit', BaseIcons["quit"],
504  self.parent.OnQuit))
505  )
def _toolbarData
Toolbar data.
Definition: histogram.py:487
wxGUI command interface
def GetOptData
Callback method for histogram command generated by dialog created in menuform.py. ...
Definition: histogram.py:336
wxGUI debugging
def OnPaint
Draw psuedo DC to buffer.
Definition: histogram.py:121
A Buffered window class.
Definition: histogram.py:35
def OnSize
Init image size to match window size.
Definition: histogram.py:140
def SetHistLayer
Set histogram layer.
Definition: histogram.py:353
def EraseMap
Erase the map display.
Definition: histogram.py:258
Histogram toolbar (see histogram.py)
Definition: histogram.py:476
def GetWindow
Get buffered window.
Definition: histogram.py:396
def PrintMenu
Print options and output menu.
Definition: histogram.py:439
def Draw
Draws histogram or clears window.
Definition: histogram.py:85
Main frame for hisgram display window.
Definition: histogram.py:263
#define max(x, y)
Definition: draw2.c:69
Various dialogs used in wxGUI.
Print context and utility functions for printing contents of map display window.
Rendering map layers and overlays into map composition image.
def OnCloseWindow
Window closed Also remove associated rendered images.
Definition: histogram.py:465
def SetHistFont
Set font for histogram.
Definition: histogram.py:365
def OnRender
Re-render histogram.
Definition: histogram.py:391
def OnIdle
Only re-render a histogram image from GRASS during idle time instead of multiple times during resizin...
Definition: histogram.py:163
def GetLayerNameFromCmd
Get map name from GRASS command.
Definition: core/utils.py:73
def GetImageHandlers
Get list of supported image handlers.
Base classes toolbar widgets.
Misc utilities for wxGUI.
def InitDisplay
Initialize histogram display, set dimensions and region.
Definition: histogram.py:322
User preferences dialog.
def SaveToFile
This will save the contents of the buffer to the specified file.
Definition: histogram.py:172
def SaveToFile
Save to file.
Definition: histogram.py:400
def OnErase
Erase the histogram display.
Definition: histogram.py:386
tuple range
Definition: tools.py:1406
def GetImage
Converts files to wx.Image.
Definition: histogram.py:195
def OnOptions
Change histogram settings.
Definition: histogram.py:328
def UpdateHist
Update canvas if histogram options changes or window changes geometry.
Definition: histogram.py:207