GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
wxplot/base.py
Go to the documentation of this file.
1 """!
2 @package wxplot.base
3 
4 @brief Base classes for iinteractive plotting using PyPlot
5 
6 Classes:
7  - base::PlotIcons
8  - base::BasePlotFrame
9 
10 (C) 2011 by the GRASS Development Team
11 
12 This program is free software under the GNU General Public License
13 (>=v2). Read the file COPYING that comes with GRASS for details.
14 
15 @author Michael Barton, Arizona State University
16 """
17 
18 import os
19 import sys
20 
21 import wx
22 import wx.lib.plot as plot
23 
24 from core.globalvar import ETCICONDIR
25 from core.settings import UserSettings
26 from wxplot.dialogs import TextDialog, OptDialog
27 from core.render import Map
28 from icons.icon import MetaIcon
29 from gui_core.toolbars import BaseIcons
30 
31 import grass.script as grass
32 
33 PlotIcons = {
34  'draw' : MetaIcon(img = 'show',
35  label = _('Draw/re-draw plot')),
36  'transect' : MetaIcon(img = 'layer-raster-profile',
37  label = _('Draw transect in map display window to profile')),
38  'options' : MetaIcon(img = 'settings',
39  label = _('Plot options')),
40  'statistics' : MetaIcon(img = 'check',
41  label = _('Plot statistics')),
42  'save' : MetaIcon(img = 'save',
43  label = _('Save profile data to CSV file')),
44  'quit' : BaseIcons['quit'].SetLabel(_('Quit plot tool')),
45  }
46 
47 class BasePlotFrame(wx.Frame):
48  """!Abstract PyPlot display frame class"""
49  def __init__(self, parent = None, id = wx.ID_ANY, size = wx.Size(700, 400),
50  style = wx.DEFAULT_FRAME_STYLE, rasterList = [], **kwargs):
51 
52  wx.Frame.__init__(self, parent, id, size = size, style = style, **kwargs)
53 
54  self.parent = parent # MapFrame for a plot type
55  self.mapwin = self.parent.MapWindow
56  self.Map = Map() # instance of render.Map to be associated with display
57  self.rasterList = rasterList #list of rasters to plot
58  self.raster = {} # dictionary of raster maps and their plotting parameters
59  self.plottype = ''
60 
61  self.linestyledict = { 'solid' : wx.SOLID,
62  'dot' : wx.DOT,
63  'long-dash' : wx.LONG_DASH,
64  'short-dash' : wx.SHORT_DASH,
65  'dot-dash' : wx.DOT_DASH }
66 
67  self.ptfilldict = { 'transparent' : wx.TRANSPARENT,
68  'solid' : wx.SOLID }
69 
70  #
71  # Icon
72  #
73  self.SetIcon(wx.Icon(os.path.join(ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
74 
75  #
76  # Add statusbar
77  #
78  self.statusbar = self.CreateStatusBar(number = 2, style = 0)
79  self.statusbar.SetStatusWidths([-2, -1])
80 
81  #
82  # Define canvas and settings
83  #
84  #
85  self.client = plot.PlotCanvas(self)
86 
87  #define the function for drawing pointLabels
88  self.client.SetPointLabelFunc(self.DrawPointLabel)
89 
90  # Create mouse event for showing cursor coords in status bar
91  self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
92 
93  # Show closest point when enabled
94  self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
95 
96  self.plotlist = [] # list of things to plot
97  self.plot = None # plot draw object
98  self.ptitle = "" # title of window
99  self.xlabel = "" # default X-axis label
100  self.ylabel = "" # default Y-axis label
101 
102  #
103  # Bind various events
104  #
105  self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
106 
107  self.CentreOnScreen()
108 
109  self._createColorDict()
110 
111 
112  def _createColorDict(self):
113  """!Create color dictionary to return wx.Colour tuples
114  for assigning colors to images in imagery groups"""
115 
116  self.colorDict = {}
117  for clr in grass.named_colors.iterkeys():
118  if clr == 'white' or clr == 'black': continue
119  r = grass.named_colors[clr][0] * 255
120  g = grass.named_colors[clr][1] * 255
121  b = grass.named_colors[clr][2] * 255
122  self.colorDict[clr] = (r,g,b,255)
123 
124  def InitPlotOpts(self, plottype):
125  """!Initialize options for entire plot
126  """
127  self.plottype = plottype # profile
128 
129  self.properties = {} # plot properties
130  self.properties['font'] = {}
131  self.properties['font']['prop'] = UserSettings.Get(group = self.plottype, key = 'font')
132  self.properties['font']['wxfont'] = wx.Font(11, wx.FONTFAMILY_SWISS,
133  wx.FONTSTYLE_NORMAL,
134  wx.FONTWEIGHT_NORMAL)
135 
136  self.properties['raster'] = {}
137  self.properties['raster'] = UserSettings.Get(group = self.plottype, key = 'raster')
138  colstr = str(self.properties['raster']['pcolor'])
139  self.properties['raster']['pcolor'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
140 
141  if self.plottype == 'profile':
142  self.properties['marker'] = UserSettings.Get(group = self.plottype, key = 'marker')
143  # changing color string to tuple for markers/points
144  colstr = str(self.properties['marker']['color'])
145  self.properties['marker']['color'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
146 
147 
148  self.properties['grid'] = UserSettings.Get(group = self.plottype, key = 'grid')
149  colstr = str(self.properties['grid']['color']) # changing color string to tuple
150  self.properties['grid']['color'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
151 
152  self.properties['x-axis'] = {}
153  self.properties['x-axis']['prop'] = UserSettings.Get(group = self.plottype, key = 'x-axis')
154  self.properties['x-axis']['axis'] = None
155 
156  self.properties['y-axis'] = {}
157  self.properties['y-axis']['prop'] = UserSettings.Get(group = self.plottype, key = 'y-axis')
158  self.properties['y-axis']['axis'] = None
159 
160  self.properties['legend'] = UserSettings.Get(group = self.plottype, key = 'legend')
161 
162  self.zoom = False # zooming disabled
163  self.drag = False # draging disabled
164  self.client.SetShowScrollbars(True) # vertical and horizontal scrollbars
165 
166  # x and y axis set to normal (non-log)
167  self.client.setLogScale((False, False))
168  if self.properties['x-axis']['prop']['type']:
169  self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
170  else:
171  self.client.SetXSpec('auto')
172 
173  if self.properties['y-axis']['prop']['type']:
174  self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
175  else:
176  self.client.SetYSpec('auto')
177 
178  def InitRasterOpts(self, rasterList, plottype):
179  """!Initialize or update raster dictionary for plotting
180  """
181 
182  rdict = {} # initialize a dictionary
183 
184  self.properties['raster'] = UserSettings.Get(group = self.plottype, key = 'raster')
185 
186  for r in rasterList:
187  idx = rasterList.index(r)
188 
189  try:
190  ret = grass.raster_info(r)
191  except:
192  continue
193  # if r.info cannot parse map, skip it
194 
195  self.raster[r] = self.properties['raster'] # some default settings
196  rdict[r] = {} # initialize sub-dictionaries for each raster in the list
197 
198  rdict[r]['units'] = ''
199  if ret['units'] not in ('(none)', '"none"', '', None):
200  rdict[r]['units'] = ret['units']
201 
202  rdict[r]['plegend'] = r.split('@')[0]
203  rdict[r]['datalist'] = [] # list of cell value,frequency pairs for plotting
204  rdict[r]['pline'] = None
205  rdict[r]['datatype'] = ret['datatype']
206 
207  #
208  #initialize with saved values
209  #
210  if self.properties['raster']['pwidth'] != None:
211  rdict[r]['pwidth'] = self.properties['raster']['pwidth']
212  else:
213  rdict[r]['pwidth'] = 1
214 
215  if self.properties['raster']['pstyle'] != None and \
216  self.properties['raster']['pstyle'] != '':
217  rdict[r]['pstyle'] = self.properties['raster']['pstyle']
218  else:
219  rdict[r]['pstyle'] = 'solid'
220 
221  if idx <= len(self.colorList):
222  if idx == 0:
223  # use saved color for first plot
224  if self.properties['raster']['pcolor'] != None:
225  rdict[r]['pcolor'] = self.properties['raster']['pcolor']
226  else:
227  rdict[r]['pcolor'] = self.colorDict[self.colorList[idx]]
228  else:
229  rdict[r]['pcolor'] = self.colorDict[self.colorList[idx]]
230  else:
231  r = randint(0, 255)
232  b = randint(0, 255)
233  g = randint(0, 255)
234  rdict[r]['pcolor'] = ((r,g,b,255))
235 
236  return rdict
237 
238  def SetGraphStyle(self):
239  """!Set plot and text options
240  """
241  self.client.SetFont(self.properties['font']['wxfont'])
242  self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
243  self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
244 
245  self.client.SetEnableZoom(self.zoom)
246  self.client.SetEnableDrag(self.drag)
247 
248  #
249  # axis settings
250  #
251  if self.properties['x-axis']['prop']['type'] == 'custom':
252  self.client.SetXSpec('min')
253  else:
254  self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
255 
256  if self.properties['y-axis']['prop']['type'] == 'custom':
257  self.client.SetYSpec('min')
258  else:
259  self.client.SetYSpec(self.properties['y-axis']['prop'])
260 
261  if self.properties['x-axis']['prop']['type'] == 'custom' and \
262  self.properties['x-axis']['prop']['min'] < self.properties['x-axis']['prop']['max']:
263  self.properties['x-axis']['axis'] = (self.properties['x-axis']['prop']['min'],
264  self.properties['x-axis']['prop']['max'])
265  else:
266  self.properties['x-axis']['axis'] = None
267 
268  if self.properties['y-axis']['prop']['type'] == 'custom' and \
269  self.properties['y-axis']['prop']['min'] < self.properties['y-axis']['prop']['max']:
270  self.properties['y-axis']['axis'] = (self.properties['y-axis']['prop']['min'],
271  self.properties['y-axis']['prop']['max'])
272  else:
273  self.properties['y-axis']['axis'] = None
274 
275  if self.properties['x-axis']['prop']['log'] == True:
276  self.properties['x-axis']['axis'] = None
277  self.client.SetXSpec('min')
278  if self.properties['y-axis']['prop']['log'] == True:
279  self.properties['y-axis']['axis'] = None
280  self.client.SetYSpec('min')
281 
282  self.client.setLogScale((self.properties['x-axis']['prop']['log'],
283  self.properties['y-axis']['prop']['log']))
284 
285  #
286  # grid settings
287  #
288  self.client.SetEnableGrid(self.properties['grid']['enabled'])
289 
290  self.client.SetGridColour(wx.Colour(self.properties['grid']['color'][0],
291  self.properties['grid']['color'][1],
292  self.properties['grid']['color'][2],
293  255))
294 
295  #
296  # legend settings
297  #
298  self.client.SetFontSizeLegend(self.properties['font']['prop']['legendSize'])
299  self.client.SetEnableLegend(self.properties['legend']['enabled'])
300 
301  def DrawPlot(self, plotlist):
302  """!Draw line and point plot from list plot elements.
303  """
304  self.plot = plot.PlotGraphics(plotlist,
305  self.ptitle,
306  self.xlabel,
307  self.ylabel)
308 
309  if self.properties['x-axis']['prop']['type'] == 'custom':
310  self.client.SetXSpec('min')
311  else:
312  self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
313 
314  if self.properties['y-axis']['prop']['type'] == 'custom':
315  self.client.SetYSpec('min')
316  else:
317  self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
318 
319  self.client.Draw(self.plot, self.properties['x-axis']['axis'],
320  self.properties['y-axis']['axis'])
321 
322  def DrawPointLabel(self, dc, mDataDict):
323  """!This is the fuction that defines how the pointLabels are
324  plotted dc - DC that will be passed mDataDict - Dictionary
325  of data that you want to use for the pointLabel
326 
327  As an example I have decided I want a box at the curve
328  point with some text information about the curve plotted
329  below. Any wxDC method can be used.
330  """
331  dc.SetPen(wx.Pen(wx.BLACK))
332  dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
333 
334  sx, sy = mDataDict["scaledXY"] #scaled x,y of closest point
335  dc.DrawRectangle( sx-5,sy-5, 10, 10) #10by10 square centered on point
336  px,py = mDataDict["pointXY"]
337  cNum = mDataDict["curveNum"]
338  pntIn = mDataDict["pIndex"]
339  legend = mDataDict["legend"]
340  #make a string to display
341  s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
342  dc.DrawText(s, sx , sy+1)
343 
344  def OnZoom(self, event):
345  """!Enable zooming and disable dragging
346  """
347  self.zoom = True
348  self.drag = False
349  self.client.SetEnableZoom(self.zoom)
350  self.client.SetEnableDrag(self.drag)
351 
352  def OnDrag(self, event):
353  """!Enable dragging and disable zooming
354  """
355  self.zoom = False
356  self.drag = True
357  self.client.SetEnableDrag(self.drag)
358  self.client.SetEnableZoom(self.zoom)
359 
360  def OnRedraw(self, event):
361  """!Redraw the plot window. Unzoom to original size
362  """
363  self.client.Reset()
364  self.client.Redraw()
365 
366  def OnErase(self, event):
367  """!Erase the plot window
368  """
369  self.client.Clear()
370  self.mapwin.ClearLines(self.mapwin.pdc)
371  self.mapwin.ClearLines(self.mapwin.pdcTmp)
372  self.mapwin.polycoords = []
373  self.mapwin.Refresh()
374 
375  def SaveToFile(self, event):
376  """!Save plot to graphics file
377  """
378  self.client.SaveFile()
379 
380  def OnMouseLeftDown(self,event):
381  self.SetStatusText(_("Left Mouse Down at Point:") + \
382  " (%.4f, %.4f)" % self.client._getXY(event))
383  event.Skip() # allows plotCanvas OnMouseLeftDown to be called
384 
385  def OnMotion(self, event):
386  """!Indicate when mouse is outside the plot area
387  """
388  if self.client.OnLeave(event): print 'out of area'
389  #show closest point (when enbled)
390  if self.client.GetEnablePointLabel() == True:
391  #make up dict with info for the pointLabel
392  #I've decided to mark the closest point on the closest curve
393  dlst = self.client.GetClosetPoint( self.client._getXY(event), pointScaled = True)
394  if dlst != []: #returns [] if none
395  curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
396  #make up dictionary to pass to my user function (see DrawPointLabel)
397  mDataDict = {"curveNum":curveNum, "legend":legend, "pIndex":pIndex,\
398  "pointXY":pointXY, "scaledXY":scaledXY}
399  #pass dict to update the pointLabel
400  self.client.UpdatePointLabel(mDataDict)
401  event.Skip() #go to next handler
402 
403 
404  def PlotOptionsMenu(self, event):
405  """!Popup menu for plot and text options
406  """
407  point = wx.GetMousePosition()
408  popt = wx.Menu()
409 
410  # Add items to the menu
411  settext = wx.MenuItem(popt, wx.ID_ANY, _('Text settings'))
412  popt.AppendItem(settext)
413  self.Bind(wx.EVT_MENU, self.PlotText, settext)
414 
415  setgrid = wx.MenuItem(popt, wx.ID_ANY, _('Plot settings'))
416  popt.AppendItem(setgrid)
417  self.Bind(wx.EVT_MENU, self.PlotOptions, setgrid)
418 
419  # Popup the menu. If an item is selected then its handler
420  # will be called before PopupMenu returns.
421  self.PopupMenu(popt)
422  popt.Destroy()
423 
424  def NotFunctional(self):
425  """!Creates a 'not functional' message dialog
426  """
427  dlg = wx.MessageDialog(parent = self,
428  message = _('This feature is not yet functional'),
429  caption = _('Under Construction'),
430  style = wx.OK | wx.ICON_INFORMATION)
431  dlg.ShowModal()
432  dlg.Destroy()
433 
434  def OnPlotText(self, dlg):
435  """!Custom text settings.
436  """
437  self.ptitle = dlg.ptitle
438  self.xlabel = dlg.xlabel
439  self.ylabel = dlg.ylabel
440 
441  self.client.SetFont(self.properties['font']['wxfont'])
442  self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
443  self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
444 
445  if self.plot:
446  self.plot.setTitle(dlg.ptitle)
447  self.plot.setXLabel(dlg.xlabel)
448  self.plot.setYLabel(dlg.ylabel)
449 
450  self.OnRedraw(event = None)
451 
452  def PlotText(self, event):
453  """!Set custom text values for profile title and axis labels.
454  """
455  dlg = TextDialog(parent = self, id = wx.ID_ANY,
456  plottype = self.plottype,
457  title = _('Text settings'))
458 
459  btnval = dlg.ShowModal()
460  if btnval == wx.ID_SAVE or btnval == wx.ID_OK or btnval == wx.ID_CANCEL:
461  dlg.Destroy()
462 
463  def PlotOptions(self, event):
464  """!Set various profile options, including: line width, color,
465  style; marker size, color, fill, and style; grid and legend
466  options. Calls OptDialog class.
467  """
468 
469  dlg = OptDialog(parent = self, id = wx.ID_ANY,
470  plottype = self.plottype,
471  title = _('Plot settings'))
472 
473  btnval = dlg.ShowModal()
474 
475  if btnval == wx.ID_SAVE or btnval == wx.ID_OK or btnval == wx.ID_CANCEL:
476  dlg.Destroy()
477 
478  def PrintMenu(self, event):
479  """!Print options and output menu
480  """
481  point = wx.GetMousePosition()
482  printmenu = wx.Menu()
483  for title, handler in ((_("Page setup"), self.OnPageSetup),
484  (_("Print preview"), self.OnPrintPreview),
485  (_("Print display"), self.OnDoPrint)):
486  item = wx.MenuItem(printmenu, wx.ID_ANY, title)
487  printmenu.AppendItem(item)
488  self.Bind(wx.EVT_MENU, handler, item)
489 
490  # Popup the menu. If an item is selected then its handler
491  # will be called before PopupMenu returns.
492  self.PopupMenu(printmenu)
493  printmenu.Destroy()
494 
495  def OnPageSetup(self, event):
496  self.client.PageSetup()
497 
498  def OnPrintPreview(self, event):
499  self.client.PrintPreview()
500 
501  def OnDoPrint(self, event):
502  self.client.Printout()
503 
504  def OnQuit(self, event):
505  self.Close(True)
506 
507  def OnCloseWindow(self, event):
508  """!Close plot window and clean up
509  """
510  try:
511  self.mapwin.ClearLines()
512  self.mapwin.mouse['begin'] = self.mapwin.mouse['end'] = (0.0, 0.0)
513  self.mapwin.mouse['use'] = 'pointer'
514  self.mapwin.mouse['box'] = 'point'
515  self.mapwin.polycoords = []
516  self.mapwin.UpdateMap(render = False, renderVector = False)
517  except:
518  pass
519 
520  self.mapwin.SetCursor(self.Parent.cursors["default"])
521  self.Destroy()
522 
def DrawPointLabel
This is the fuction that defines how the pointLabels are plotted dc - DC that will be passed mDataDic...
Definition: wxplot/base.py:322
def InitRasterOpts
Initialize or update raster dictionary for plotting.
Definition: wxplot/base.py:178
def PlotText
Set custom text values for profile title and axis labels.
Definition: wxplot/base.py:452
def _createColorDict
Create color dictionary to return wx.Colour tuples for assigning colors to images in imagery groups...
Definition: wxplot/base.py:112
Dialogs for different plotting routines.
def PlotOptionsMenu
Popup menu for plot and text options.
Definition: wxplot/base.py:404
def SaveToFile
Save plot to graphics file.
Definition: wxplot/base.py:375
def OnErase
Erase the plot window.
Definition: wxplot/base.py:366
def OnCloseWindow
Close plot window and clean up.
Definition: wxplot/base.py:507
def OnPlotText
Custom text settings.
Definition: wxplot/base.py:434
Rendering map layers and overlays into map composition image.
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def SetGraphStyle
Set plot and text options.
Definition: wxplot/base.py:238
def OnZoom
Enable zooming and disable dragging.
Definition: wxplot/base.py:344
def NotFunctional
Creates a &#39;not functional&#39; message dialog.
Definition: wxplot/base.py:424
Base classes toolbar widgets.
Abstract PyPlot display frame class.
Definition: wxplot/base.py:47
def OnRedraw
Redraw the plot window.
Definition: wxplot/base.py:360
def OnMotion
Indicate when mouse is outside the plot area.
Definition: wxplot/base.py:385
def PlotOptions
Set various profile options, including: line width, color, style; marker size, color, fill, and style; grid and legend options.
Definition: wxplot/base.py:463
def DrawPlot
Draw line and point plot from list plot elements.
Definition: wxplot/base.py:301
Global variables used by wxGUI.
def PrintMenu
Print options and output menu.
Definition: wxplot/base.py:478
Default GUI settings.
def InitPlotOpts
Initialize options for entire plot.
Definition: wxplot/base.py:124
def OnDrag
Enable dragging and disable zooming.
Definition: wxplot/base.py:352