[GRASS-SVN] r57427 - in grass/trunk/gui/wxpython: . gcp gui_core iclass mapdisp mapswipe mapwin nviz vdigit
svn_grass at osgeo.org
svn_grass at osgeo.org
Tue Aug 6 09:10:51 PDT 2013
Author: wenzeslaus
Date: 2013-08-06 09:10:51 -0700 (Tue, 06 Aug 2013)
New Revision: 57427
Added:
grass/trunk/gui/wxpython/mapwin/
grass/trunk/gui/wxpython/mapwin/__init__.py
grass/trunk/gui/wxpython/mapwin/analysis.py
grass/trunk/gui/wxpython/mapwin/base.py
grass/trunk/gui/wxpython/mapwin/buffered.py
grass/trunk/gui/wxpython/mapwin/decorations.py
grass/trunk/gui/wxpython/mapwin/graphics.py
Removed:
grass/trunk/gui/wxpython/gui_core/mapwindow.py
grass/trunk/gui/wxpython/mapdisp/analysis.py
grass/trunk/gui/wxpython/mapdisp/mapwindow.py
grass/trunk/gui/wxpython/mapdisp/overlays.py
Modified:
grass/trunk/gui/wxpython/Makefile
grass/trunk/gui/wxpython/gcp/mapdisplay.py
grass/trunk/gui/wxpython/gui_core/dialogs.py
grass/trunk/gui/wxpython/iclass/frame.py
grass/trunk/gui/wxpython/mapdisp/frame.py
grass/trunk/gui/wxpython/mapdisp/test_mapdisp.py
grass/trunk/gui/wxpython/mapswipe/frame.py
grass/trunk/gui/wxpython/mapswipe/mapwindow.py
grass/trunk/gui/wxpython/nviz/mapwindow.py
grass/trunk/gui/wxpython/vdigit/dialogs.py
grass/trunk/gui/wxpython/vdigit/main.py
grass/trunk/gui/wxpython/vdigit/mapwindow.py
grass/trunk/gui/wxpython/vdigit/wxdigit.py
Log:
wxGUI/mapwin: new mapwin subdirectory with map window related code including buffered window
Modified: grass/trunk/gui/wxpython/Makefile
===================================================================
--- grass/trunk/gui/wxpython/Makefile 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/Makefile 2013-08-06 16:10:51 UTC (rev 57427)
@@ -11,7 +11,7 @@
SRCFILES := $(wildcard icons/*.py scripts/*.py xml/*) \
$(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
- gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapdisp/*.py \
+ gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapwin/*.py mapdisp/*.py \
mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* vdigit/* \
vnet/*.py web_services/*.py wxplot/*.py) \
gis_set.py gis_set_error.py wxgui.py README
@@ -20,7 +20,7 @@
$(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
- gui_core iclass lmgr location_wizard mapdisp modules nviz psmap \
+ gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
mapswipe vdigit wxplot web_services rlisetup vnet)
DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
Modified: grass/trunk/gui/wxpython/gcp/mapdisplay.py
===================================================================
--- grass/trunk/gui/wxpython/gcp/mapdisplay.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/gcp/mapdisplay.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -31,8 +31,8 @@
from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
from gui_core.mapdisp import SingleMapFrame
from core.settings import UserSettings
-from mapdisp.mapwindow import BufferedWindow
-from gui_core.mapwindow import MapWindowProperties
+from mapwin.buffered import BufferedMapWindow
+from mapwin.base import MapWindowProperties
import mapdisp.statusbar as sb
import gcp.statusbar as sbgcp
@@ -114,14 +114,14 @@
# Init map display (buffered DC & set default cursor)
#
self.grwiz.SwitchEnv('source')
- self.SrcMapWindow = BufferedWindow(parent=self, giface=self._giface, id=wx.ID_ANY,
- properties=self.mapWindowProperties,
- Map=self.SrcMap)
+ self.SrcMapWindow = BufferedMapWindow(parent=self, giface=self._giface, id=wx.ID_ANY,
+ properties=self.mapWindowProperties,
+ Map=self.SrcMap)
self.grwiz.SwitchEnv('target')
- self.TgtMapWindow = BufferedWindow(parent=self, giface=self._giface, id=wx.ID_ANY,
- properties=self.mapWindowProperties,
- Map=self.TgtMap)
+ self.TgtMapWindow = BufferedMapWindow(parent=self, giface=self._giface, id=wx.ID_ANY,
+ properties=self.mapWindowProperties,
+ Map=self.TgtMap)
self.MapWindow = self.SrcMapWindow
self.Map = self.SrcMap
self._setUpMapWindow(self.SrcMapWindow)
Modified: grass/trunk/gui/wxpython/gui_core/dialogs.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/dialogs.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/gui_core/dialogs.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -9,8 +9,6 @@
- dialogs::MapsetDialog
- dialogs::NewVectorDialog
- dialogs::SavedRegion
- - dialogs::DecorationDialog
- - dialogs::TextLayerDialog
- dialogs::GroupDialog
- dialogs::MapLayersDialog
- dialogs::ImportDialog
@@ -49,9 +47,8 @@
from core import globalvar
from core.gcmd import GError, RunCommand, GMessage
from gui_core.gselect import LocationSelect, MapsetSelect, Select, OgrTypeSelect, GdalSelect, MapsetSelect
-from gui_core.forms import GUI
from gui_core.widgets import SingleSymbolPanel, GListCtrl, SimpleValidator
-from core.utils import GetLayerNameFromCmd, GetValidLayerName, _
+from core.utils import GetValidLayerName, _
from core.settings import UserSettings, GetDisplayVectSettings
from core.debug import Debug
@@ -510,366 +507,8 @@
def GetName(self):
"""!Return region name"""
return self.wind
-
-DECOR_DIALOG_LEGEND = 0
-DECOR_DIALOG_BARSCALE = 1
-class DecorationDialog(wx.Dialog):
- """!Controls setting options and displaying/hiding map overlay
- decorations
- """
- def __init__(self, parent, title, giface, overlayController,
- ddstyle, **kwargs):
-
- wx.Dialog.__init__(self, parent, wx.ID_ANY, title, **kwargs)
-
- self.parent = parent # MapFrame
- self._overlay = overlayController
- self._ddstyle = ddstyle
- self._giface = giface
-
- self._oldMouseUse = None
- self._oldCursor = None
-
- sizer = wx.BoxSizer(wx.VERTICAL)
-
- box = wx.BoxSizer(wx.HORIZONTAL)
- self.chkbox = wx.CheckBox(parent = self, id = wx.ID_ANY)
- self.chkbox.SetValue(True)
- if self._ddstyle == DECOR_DIALOG_LEGEND:
- self.chkbox.SetLabel("Show legend")
- else:
- self.chkbox.SetLabel("Show scale and North arrow")
-
-
- box.Add(item = self.chkbox, proportion = 0,
- flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
- sizer.Add(item = box, proportion = 0,
- flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
-
- box = wx.BoxSizer(wx.HORIZONTAL)
- optnbtn = wx.Button(parent = self, id = wx.ID_ANY, label = _("Set options"))
- box.Add(item = optnbtn, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
- sizer.Add(item = box, proportion = 0,
- flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
- if self._ddstyle == DECOR_DIALOG_LEGEND:
- box = wx.BoxSizer(wx.HORIZONTAL)
- self.resizeBtn = wx.ToggleButton(parent = self, id = wx.ID_ANY, label = _("Set size and position"))
- self.resizeBtn.SetToolTipString(_("Click and drag on the map display to set legend "
- "size and position and then press OK"))
- self.resizeBtn.Disable()
- self.resizeBtn.Bind(wx.EVT_TOGGLEBUTTON, self.OnResize)
- box.Add(item = self.resizeBtn, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
- sizer.Add(item = box, proportion = 0,
- flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
-
- box = wx.BoxSizer(wx.HORIZONTAL)
- if self._ddstyle == DECOR_DIALOG_LEGEND:
- labelText = _("Drag legend object with mouse in pointer mode to position.\n"
- "Double-click to change options.\n"
- "Define raster map name for legend in properties dialog.")
- else:
- labelText = _("Drag scale object with mouse in pointer mode to position.\n"
- "Double-click to change options.")
-
- label = wx.StaticText(parent = self, id = wx.ID_ANY,
- label = labelText)
-
- box.Add(item = label, proportion = 0,
- flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
- sizer.Add(item = box, proportion = 0,
- flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
-
- line = wx.StaticLine(parent = self, id = wx.ID_ANY, size = (20,-1), style = wx.LI_HORIZONTAL)
- sizer.Add(item = line, proportion = 0,
- flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
-
- # buttons
- btnsizer = wx.StdDialogButtonSizer()
-
- self.btnOK = wx.Button(parent = self, id = wx.ID_OK)
- self.btnOK.SetDefault()
- self.btnOK.Enable(self._ddstyle != DECOR_DIALOG_LEGEND)
- btnsizer.AddButton(self.btnOK)
-
- btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
- btnsizer.AddButton(btnCancel)
- btnsizer.Realize()
-
- sizer.Add(item = btnsizer, proportion = 0,
- flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
-
- #
- # bindings
- #
- optnbtn.Bind(wx.EVT_BUTTON, self.OnOptions)
- btnCancel.Bind(wx.EVT_BUTTON, lambda evt: self.CloseDialog())
- self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
-
- self.SetSizer(sizer)
- sizer.Fit(self)
-
- mapName, found = GetLayerNameFromCmd(self._overlay.cmd)
- if found:
- # enable 'OK' and 'Resize' button
- self.btnOK.Enable()
-
- # set title
- self.SetTitle(_('Legend of raster map <%s>') % \
- mapName)
-
- def OnOptions(self, event):
- """!Sets option for decoration map overlays
- """
- if self._overlay.propwin is None:
- # build properties dialog
- GUI(parent = self.parent).ParseCommand(cmd = self._overlay.cmd,
- completed = (self.GetOptData, self._overlay.name, ''))
-
- else:
- if self._overlay.propwin.IsShown():
- self._overlay.propwin.SetFocus()
- else:
- self._overlay.propwin.Show()
-
- def OnResize(self, event):
- window = self._giface.GetMapWindow()
- if event.GetInt():
- self._oldMouseUse = window.mouse['use']
- self._oldCursor = window.GetNamedCursor()
- window.SetNamedCursor('cross')
- window.mouse['use'] = None
- window.mouse['box'] = 'box'
- window.pen = wx.Pen(colour = 'Black', width = 2, style = wx.SHORT_DASH)
- window.mouseLeftUp.connect(self._resizeLegend)
- else:
- self.Restore()
- self.DisconnectResizing()
-
- def Restore(self):
- """!Restore conditions before resizing"""
- window = self._giface.GetMapWindow()
- if self._oldCursor:
- window.SetNamedCursor(self._oldCursor)
- if self._oldMouseUse:
- window.mouse['use'] = self._oldMouseUse
-
- def DisconnectResizing(self):
- self._giface.GetMapWindow().mouseLeftUp.disconnect(self._resizeLegend)
-
- def _resizeLegend(self, x, y):
- """!Update legend after drawing new legend size (moved from BufferedWindow)"""
- self.resizeBtn.SetValue(False)
- window = self._giface.GetMapWindow()
- self.DisconnectResizing()
- self.Restore()
- # resize legend
- screenSize = window.GetClientSizeTuple()
- self._overlay.ResizeLegend(window.mouse["begin"], window.mouse["end"], screenSize)
- # redraw
- self._giface.updateMap.emit()
-
- def CloseDialog(self):
- """!Hide dialog"""
- if self._ddstyle == DECOR_DIALOG_LEGEND and self.resizeBtn.GetValue():
- self.Restore()
- self.resizeBtn.SetValue(False)
- self.DisconnectResizing()
-
- self.Hide()
-
- def OnOK(self, event):
- """!Button 'OK' pressed"""
- # enable or disable overlay
- self._overlay.Show(self.chkbox.IsChecked())
-
- # update map
- if self.parent.IsPaneShown('3d'):
- self.parent.MapWindow.UpdateOverlays()
-
- self._giface.updateMap.emit()
-
- # hide dialog
- self.CloseDialog()
-
- def GetOptData(self, dcmd, layer, params, propwin):
- """!Process decoration layer data"""
- if dcmd:
- self._overlay.cmd = dcmd
- self._overlay.propwin = propwin
- if params:
- self.btnOK.Enable()
- if self._ddstyle == DECOR_DIALOG_LEGEND and not self.parent.IsPaneShown('3d'):
- self.resizeBtn.Enable()
-
- def Show(self, show=True):
- if show:
- self.resizeBtn.Enable(not self.parent.IsPaneShown('3d'))
- wx.Dialog.Show(self, show)
-
-class TextLayerDialog(wx.Dialog):
- """
- Controls setting options and displaying/hiding map overlay decorations
- """
-
- def __init__(self, parent, ovlId, title, name = 'text',
- pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE):
-
- wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
- from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
-
- self.ovlId = ovlId
- self.parent = parent
-
- if self.ovlId in self.parent.MapWindow.textdict.keys():
- self.currText = self.parent.MapWindow.textdict[self.ovlId]['text']
- self.currFont = self.parent.MapWindow.textdict[self.ovlId]['font']
- self.currClr = self.parent.MapWindow.textdict[self.ovlId]['color']
- self.currRot = self.parent.MapWindow.textdict[self.ovlId]['rotation']
- self.currCoords = self.parent.MapWindow.textdict[self.ovlId]['coords']
- self.currBB = self.parent.MapWindow.textdict[self.ovlId]['bbox']
- else:
- self.currClr = wx.BLACK
- self.currText = ''
- self.currFont = self.GetFont()
- self.currRot = 0.0
- self.currCoords = [10, 10]
- self.currBB = wx.Rect()
-
- self.sizer = wx.BoxSizer(wx.VERTICAL)
- box = wx.GridBagSizer(vgap = 5, hgap = 5)
-
- # show/hide
- self.chkbox = wx.CheckBox(parent = self, id = wx.ID_ANY,
- label = _('Show text object'))
- if self.parent.Map.GetOverlay(self.ovlId) is None:
- self.chkbox.SetValue(True)
- else:
- self.chkbox.SetValue(self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive())
- box.Add(item = self.chkbox, span = (1,2),
- flag = wx.ALIGN_LEFT|wx.ALL, border = 5,
- pos = (0, 0))
-
- # text entry
- label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Enter text:"))
- box.Add(item = label,
- flag = wx.ALIGN_CENTER_VERTICAL,
- pos = (1, 0))
-
- self.textentry = ExpandoTextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (300,-1))
- self.textentry.SetFont(self.currFont)
- self.textentry.SetForegroundColour(self.currClr)
- self.textentry.SetValue(self.currText)
- # get rid of unneeded scrollbar when text box first opened
- self.textentry.SetClientSize((300,-1))
-
- box.Add(item = self.textentry,
- pos = (1, 1))
-
- # rotation
- label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Rotation:"))
- box.Add(item = label,
- flag = wx.ALIGN_CENTER_VERTICAL,
- pos = (2, 0))
- self.rotation = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", pos = (30, 50),
- size = (75,-1), style = wx.SP_ARROW_KEYS)
- self.rotation.SetRange(-360, 360)
- self.rotation.SetValue(int(self.currRot))
- box.Add(item = self.rotation,
- flag = wx.ALIGN_RIGHT,
- pos = (2, 1))
-
- # font
- fontbtn = wx.Button(parent = self, id = wx.ID_ANY, label = _("Set font"))
- box.Add(item = fontbtn,
- flag = wx.ALIGN_RIGHT,
- pos = (3, 1))
-
- self.sizer.Add(item = box, proportion = 1,
- flag = wx.ALL, border = 10)
-
- # note
- box = wx.BoxSizer(wx.HORIZONTAL)
- label = wx.StaticText(parent = self, id = wx.ID_ANY,
- label = _("Drag text with mouse in pointer mode "
- "to position.\nDouble-click to change options"))
- box.Add(item = label, proportion = 0,
- flag = wx.ALIGN_CENTRE | wx.ALL, border = 5)
- self.sizer.Add(item = box, proportion = 0,
- flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER | wx.ALL, border = 5)
-
- line = wx.StaticLine(parent = self, id = wx.ID_ANY,
- size = (20,-1), style = wx.LI_HORIZONTAL)
- self.sizer.Add(item = line, proportion = 0,
- flag = wx.EXPAND | wx.ALIGN_CENTRE | wx.ALL, border = 5)
-
- btnsizer = wx.StdDialogButtonSizer()
-
- btn = wx.Button(parent = self, id = wx.ID_OK)
- btn.SetDefault()
- btnsizer.AddButton(btn)
-
- btn = wx.Button(parent = self, id = wx.ID_CANCEL)
- btnsizer.AddButton(btn)
- btnsizer.Realize()
-
- self.sizer.Add(item = btnsizer, proportion = 0,
- flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
-
- self.SetSizer(self.sizer)
- self.sizer.Fit(self)
-
- # bindings
- self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnRefit, self.textentry)
- self.Bind(wx.EVT_BUTTON, self.OnSelectFont, fontbtn)
- self.Bind(wx.EVT_TEXT, self.OnText, self.textentry)
- self.Bind(wx.EVT_SPINCTRL, self.OnRotation, self.rotation)
-
- def OnRefit(self, event):
- """!Resize text entry to match text"""
- self.sizer.Fit(self)
-
- def OnText(self, event):
- """!Change text string"""
- self.currText = event.GetString()
-
- def OnRotation(self, event):
- """!Change rotation"""
- self.currRot = event.GetInt()
-
- event.Skip()
-
- def OnSelectFont(self, event):
- """!Change font"""
- data = wx.FontData()
- data.EnableEffects(True)
- data.SetColour(self.currClr) # set colour
- data.SetInitialFont(self.currFont)
-
- dlg = wx.FontDialog(self, data)
-
- if dlg.ShowModal() == wx.ID_OK:
- data = dlg.GetFontData()
- self.currFont = data.GetChosenFont()
- self.currClr = data.GetColour()
-
- self.textentry.SetFont(self.currFont)
- self.textentry.SetForegroundColour(self.currClr)
-
- self.Layout()
-
- dlg.Destroy()
-
- def GetValues(self):
- """!Get text properties"""
- return { 'text' : self.currText,
- 'font' : self.currFont,
- 'color' : self.currClr,
- 'rotation' : self.currRot,
- 'coords' : self.currCoords,
- 'active' : self.chkbox.IsChecked() }
-
class GroupDialog(wx.Dialog):
"""!Dialog for creating/editing groups"""
def __init__(self, parent = None, defaultGroup = None,
Deleted: grass/trunk/gui/wxpython/gui_core/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/mapwindow.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/gui_core/mapwindow.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -1,356 +0,0 @@
-"""!
- at package gui_core.mapwindow
-
- at brief Map display canvas - base class for buffered window.
-
-Classes:
- - mapwindow::MapWindow
-
-(C) 2006-2012 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.
-
- at author Martin Landa <landa.martin gmail.com>
- at author Michael Barton
- at author Jachym Cepicky
- at author Vaclav Petras <wenzeslaus gmail.com> (handlers support)
- at author Stepan Turek <stepan.turek seznam.cz> (handlers support)
-"""
-
-import wx
-
-from core.settings import UserSettings
-from core.gcmd import GError
-from core.utils import _
-
-from grass.script import core as grass
-from grass.pydispatch.signal import Signal
-
-
-class MapWindowProperties(object):
- def __init__(self):
- self._resolution = None
- self.resolutionChanged = Signal('MapWindowProperties.resolutionChanged')
- self._autoRender = None
- self.autoRenderChanged = Signal('MapWindowProperties.autoRenderChanged')
- self._showRegion = None
- self.showRegionChanged = Signal('MapWindowProperties.showRegionChanged')
- self._alignExtent = None
- self.alignExtentChanged = Signal('MapWindowProperties.alignExtentChanged')
-
- def setValuesFromUserSettings(self):
- """Convenient function to get values from user settings into this object."""
- self._resolution = UserSettings.Get(group='display',
- key='compResolution',
- subkey='enabled')
- self._autoRender = UserSettings.Get(group='display',
- key='autoRendering',
- subkey='enabled')
- self._showRegion = False # in statusbar.py was not from settings
- self._alignExtent = UserSettings.Get(group='display',
- key='alignExtent',
- subkey='enabled')
- @property
- def resolution(self):
- return self._resolution
-
- @resolution.setter
- def resolution(self, value):
- if value != self._resolution:
- self._resolution = value
- self.resolutionChanged.emit(value=value)
-
- @property
- def autoRender(self):
- return self._autoRender
-
- @autoRender.setter
- def autoRender(self, value):
- if value != self._autoRender:
- self._autoRender = value
- self.autoRenderChanged.emit(value=value)
-
- @property
- def showRegion(self):
- return self._showRegion
-
- @showRegion.setter
- def showRegion(self, value):
- if value != self._showRegion:
- self._showRegion = value
- self.showRegionChanged.emit(value=value)
-
- @property
- def alignExtent(self):
- return self._alignExtent
-
- @alignExtent.setter
- def alignExtent(self, value):
- if value != self._alignExtent:
- self._alignExtent = value
- self.alignExtentChanged.emit(value=value)
-
-
-class MapWindow(object):
- """!Abstract map display window class
-
- Superclass for BufferedWindow class (2D display mode), and GLWindow
- (3D display mode).
-
- Subclasses have to define
- - _bindMouseEvents method which binds MouseEvent handlers
- - Pixel2Cell
- - Cell2Pixel (if it is possible)
- """
- def __init__(self, parent, giface, Map):
- self.parent = parent
- self.Map = Map
- self._giface = giface
-
- # Emitted when someone registers as mouse event handler
- self.mouseHandlerRegistered = Signal('MapWindow.mouseHandlerRegistered')
- # Emitted when mouse event handler is unregistered
- self.mouseHandlerUnregistered = Signal('MapWindow.mouseHandlerUnregistered')
- # emitted after double click in pointer mode on legend, text, scalebar
- self.overlayActivated = Signal('MapWindow.overlayActivated')
-
- # mouse attributes -- position on the screen, begin and end of
- # dragging, and type of drawing
- self.mouse = {
- 'begin': [0, 0], # screen coordinates
- 'end' : [0, 0],
- 'use' : "pointer",
- 'box' : "point"
- }
- # last east, north coordinates, changes on mouse motion
- self.lastEN = None
-
- # stores overridden cursor
- self._overriddenCursor = None
-
- # dictionary where event types are stored as keys and lists of
- # handlers for these types as values
- self.handlersContainer = {
- wx.EVT_LEFT_DOWN : [],
- wx.EVT_LEFT_UP : [],
- wx.EVT_LEFT_DCLICK : [],
- wx.EVT_MIDDLE_DOWN : [],
- wx.EVT_MIDDLE_UP : [],
- wx.EVT_MIDDLE_DCLICK : [],
- wx.EVT_RIGHT_DOWN : [],
- wx.EVT_RIGHT_UP : [],
- wx.EVT_RIGHT_DCLICK : [],
- wx.EVT_MOTION : [],
- wx.EVT_ENTER_WINDOW : [],
- wx.EVT_LEAVE_WINDOW : [],
- wx.EVT_MOUSEWHEEL : [],
- wx.EVT_MOUSE_EVENTS : []
- }
-
- # available cursors
- self._cursors = {
- "default": wx.StockCursor(wx.CURSOR_ARROW),
- "cross": wx.StockCursor(wx.CURSOR_CROSS),
- "hand": wx.StockCursor(wx.CURSOR_HAND),
- "pencil": wx.StockCursor(wx.CURSOR_PENCIL),
- "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
- }
-
- # default cursor for window is arrow (at least we rely on it here)
- # but we need to define attribute here
- # cannot call SetNamedCursor since it expects the instance
- # to be a wx window, so setting only the attribute
- self._cursor = 'default'
-
- wx.CallAfter(self.InitBinding)
-
- def __del__(self):
- self.UnregisterAllHandlers()
-
- def InitBinding(self):
- """!Binds helper functions, which calls all handlers
- registered to events with the events
- """
- for ev, handlers in self.handlersContainer.iteritems():
- self.Bind(ev, self.EventTypeHandler(handlers))
-
- def EventTypeHandler(self, evHandlers):
- return lambda event:self.HandlersCaller(event, evHandlers)
-
- def HandlersCaller(self, event, handlers):
- """!Hepler function which calls all handlers registered for
- event
- """
- for handler in handlers:
- try:
- handler(event)
- except:
- handlers.remove(handler)
- GError(parent = self,
- message=_("Error occured during calling of handler: %s \n"
- "Handler was unregistered.") % handler.__name__)
-
- event.Skip()
-
- def RegisterMouseEventHandler(self, event, handler, cursor = None):
- """!Binds event handler
-
- @depreciated This method is depreciated. Use Signals or drawing API instead.
- Signals do not cover all events but new Signals can be added when needed
- consider also adding generic signal. However, more interesing and useful
- is higher level API to create objects, graphics etc.
-
- Call event.Skip() in handler to allow default processing in MapWindow.
-
- If any error occures inside of handler, the handler is removed.
-
- Before handler is unregistered it is called with
- string value "unregistered" of event parameter.
-
- @code
- # your class methods
- def OnButton(self, event):
- # current map display's map window
- # expects LayerManager to be the parent
- self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
- if self.mapwin.RegisterEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
- wx.StockCursor(wx.CURSOR_CROSS)):
- self.parent.GetLayerTree().GetMapDisplay().Raise()
- else:
- # handle that you cannot get coordinates
-
- def OnMouseAction(self, event):
- # get real world coordinates of mouse click
- coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
- self.text.SetLabel('Coor: ' + str(coor))
- self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction)
- event.Skip()
- @endcode
-
- Emits mouseHandlerRegistered signal before handler is registered.
-
- @param event one of mouse events
- @param handler function to handle event
- @param cursor cursor which temporary overrides current cursor
-
- @return True if successful
- @return False if event cannot be bind
- """
- self.mouseHandlerRegistered.emit()
- # inserts handler into list
- for containerEv, handlers in self.handlersContainer.iteritems():
- if event == containerEv:
- handlers.append(handler)
-
- self.mouse['useBeforeGenericEvent'] = self.mouse['use']
- self.mouse['use'] = 'genericEvent'
-
- if cursor:
- self._overriddenCursor = self.GetNamedCursor()
- self.SetNamedCursor(cursor)
-
- return True
-
- def UnregisterAllHandlers(self):
- """!Unregisters all registered handlers
-
- @depreciated This method is depreciated. Use Signals or drawing API instead.
-
- Before each handler is unregistered it is called with string
- value "unregistered" of event parameter.
- """
- for containerEv, handlers in self.handlersContainer.iteritems():
- for handler in handlers:
- try:
- handler("unregistered")
- handlers.remove(handler)
- except:
- GError(parent = self,
- message = _("Error occured during unregistration of handler: %s \n \
- Handler was unregistered.") % handler.__name__)
- handlers.remove(handler)
-
- def UnregisterMouseEventHandler(self, event, handler):
- """!Unbinds event handler for event
-
- @depreciated This method is depreciated. Use Signals or drawing API instead.
-
- Before handler is unregistered it is called with string value
- "unregistered" of event parameter.
-
- Emits mouseHandlerUnregistered signal after handler is unregistered.
-
- @param handler handler to unbind
- @param event event from which handler will be unbinded
-
- @return True if successful
- @return False if event cannot be unbind
- """
- # removes handler from list
- for containerEv, handlers in self.handlersContainer.iteritems():
- if event != containerEv:
- continue
- try:
- handler("unregistered")
- if handler in handlers:
- handlers.remove(handler)
- else:
- grass.warning(_("Handler: %s was not registered") \
- % handler.__name__)
- except:
- GError(parent = self,
- message = _("Error occured during unregistration of handler: %s \n \
- Handler was unregistered") % handler.__name__)
- handlers.remove(handler)
-
- # restore mouse use (previous state)
- self.mouse['use'] = self.mouse['useBeforeGenericEvent']
-
- # restore overridden cursor
- if self._overriddenCursor:
- self.SetNamedCursor(self._overriddenCursor)
-
- self.mouseHandlerUnregistered.emit()
- return True
-
- def Pixel2Cell(self, xyCoords):
- raise NotImplementedError()
-
- def Cell2Pixel(self, enCoords):
- raise NotImplementedError()
-
- def OnMotion(self, event):
- """!Tracks mouse motion and update statusbar
-
- @todo remove this method when lastEN is not used
-
- @see GetLastEN
- """
- try:
- self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
- except (ValueError):
- self.lastEN = None
-
- event.Skip()
-
- def GetLastEN(self):
- """!Returns last coordinates of mouse cursor.
-
- @depreciated This method is depreciated. Use Signal with coordinates as parameters.
-
- @see OnMotion
- """
- return self.lastEN
-
- def SetNamedCursor(self, cursorName):
- """!Sets cursor defined by name."""
- cursor = self._cursors[cursorName]
- self.SetCursor(cursor)
- self._cursor = cursorName
-
- def GetNamedCursor(self):
- """!Returns current cursor name."""
- return self._cursor
-
- cursor = property(fget=GetNamedCursor, fset=SetNamedCursor)
Modified: grass/trunk/gui/wxpython/iclass/frame.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/frame.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/iclass/frame.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -45,13 +45,13 @@
import grass.script as grass
from mapdisp import statusbar as sb
-from mapdisp.mapwindow import BufferedWindow
+from mapwin.buffered import BufferedMapWindow
from vdigit.toolbars import VDigitToolbar
from gui_core.mapdisp import DoubleMapFrame
from core.render import Map, MapLayer
from core.gcmd import RunCommand, GMessage, GError, GWarning
from gui_core.dialogs import SetOpacityDialog
-from gui_core.mapwindow import MapWindowProperties
+from mapwin.base import MapWindowProperties
from dbmgr.vinfo import VectorDBInfo
import grass.script as grass
@@ -96,9 +96,9 @@
self.firstMapWindow = IClassVDigitWindow(parent = self, giface = self._giface,
properties=self.mapWindowProperties,
map = self.firstMap)
- self.secondMapWindow = BufferedWindow(parent = self, giface = self._giface,
- properties=self.mapWindowProperties,
- Map = self.secondMap)
+ self.secondMapWindow = BufferedMapWindow(parent=self, giface=self._giface,
+ properties=self.mapWindowProperties,
+ Map=self.secondMap)
self.MapWindow = self.firstMapWindow # current by default
self._bindWindowsActivation()
Deleted: grass/trunk/gui/wxpython/mapdisp/analysis.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/analysis.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapdisp/analysis.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -1,284 +0,0 @@
-# -*- coding: utf-8 -*-
-"""!
- at package mapdisp.analysis
-
- at brief Map display controllers for analyses (profiling, measuring)
-
-Classes:
- - analysis::AnalysisControllerBase
- - analysis::ProfileController
- - analysis::MeasureDistanceController
-
-(C) 2013 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.
-
- at author Anna Petrasova <kratochanna gmail.com>
-"""
-
-import os
-import math
-import wx
-
-from core.utils import _
-import core.units as units
-
-from grass.pydispatch.signal import Signal
-
-
-class AnalysisControllerBase:
- """!Base class for analysis which require drawing line in map display."""
- def __init__(self, giface, mapWindow):
- """!
-
- @param giface grass interface
- @param mapWindow instance of BufferedWindow
- """
- self._giface = giface
- self._mapWindow = mapWindow
-
- self._registeredGraphics = None
-
- self._oldMouseUse = None
- self._oldCursor = None
-
- def IsActive(self):
- """!Returns True if analysis mode is activated."""
- return bool(self._registeredGraphics)
-
- def _start(self, x, y):
- """!Handles the actual start of drawing line
- and adding each new point.
-
- @param x,y east north coordinates
- """
- if not self._registeredGraphics.GetAllItems():
- item = self._registeredGraphics.AddItem(coords=[[x, y]])
- item.SetPropertyVal('penName', 'analysisPen')
- else:
- # needed to switch mouse begin and end to draw intermediate line properly
- coords = self._registeredGraphics.GetItem(0).GetCoords()[-1]
- self._mapWindow.mouse['begin'] = self._mapWindow.Cell2Pixel(coords)
-
- def _addPoint(self, x, y):
- """!New point added.
-
- @param x,y east north coordinates
- """
- # add new point and calculate distance
- item = self._registeredGraphics.GetItem(0)
- coords = item.GetCoords() + [[x, y]]
- item.SetCoords(coords)
- # draw
- self._mapWindow.ClearLines()
- self._registeredGraphics.Draw(pdc=self._mapWindow.pdcTmp)
- wx.Yield()
-
- self._doAnalysis(coords)
-
- def _doAnalysis(self, coords):
- """!Perform the required analysis
- (compute distnace, update profile)
-
- @param coords EN coordinates
- """
- raise NotImplementedError()
-
- def _disconnectAll(self):
- """!Disconnect all mouse signals
- to stop drawing."""
- raise NotImplementedError()
-
- def _connectAll(self):
- """!Connect all mouse signals to draw."""
- raise NotImplementedError()
-
- def _getPen(self):
- """!Returns wx.Pen instance."""
- raise NotImplementedError()
-
- def Stop(self, restore=True):
- """!Analysis mode is stopped.
-
- @param restore if restore previous cursor, mouse['use']
- """
- self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
- self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
- # disconnect mouse events
- self._disconnectAll()
- # unregister
- self._mapWindow.UnregisterGraphicsToDraw(self._registeredGraphics)
- self._registeredGraphics = None
- self._mapWindow.Refresh()
-
- if restore:
- # restore mouse['use'] and cursor to the state before measuring starts
- self._mapWindow.SetNamedCursor(self._oldCursor)
- self._mapWindow.mouse['use'] = self._oldMouseUse
-
- def Start(self):
- """!Init analysis: register graphics to map window,
- connect required mouse signals.
- """
- self._oldMouseUse = self._mapWindow.mouse['use']
- self._oldCursor = self._mapWindow.GetNamedCursor()
-
- self._registeredGraphics = self._mapWindow.RegisterGraphicsToDraw(graphicsType='line')
-
- self._connectAll()
-
- # change mouse['box'] and pen to draw line during dragging
- # TODO: better solution for drawing this line
- self._mapWindow.mouse['use'] = None
- self._mapWindow.mouse['box'] = "line"
- self._mapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
-
- self._registeredGraphics.AddPen('analysisPen', self._getPen())
-
- # change the cursor
- self._mapWindow.SetNamedCursor('pencil')
-
-
-class ProfileController(AnalysisControllerBase):
- """!Class controls profiling in map display.
- It should be used inside ProfileFrame
- """
- def __init__(self, giface, mapWindow):
- AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)
-
- self.transectChanged = Signal('ProfileController.transectChanged')
-
- def _doAnalysis(self, coords):
- """!Informs profile dialog that profile changed.
-
- @param coords EN coordinates
- """
- self.transectChanged.emit(coords=coords)
-
- def _disconnectAll(self):
- self._mapWindow.mouseLeftDown.disconnect(self._start)
- self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
-
- def _connectAll(self):
- self._mapWindow.mouseLeftDown.connect(self._start)
- self._mapWindow.mouseLeftUp.connect(self._addPoint)
-
- def _getPen(self):
- return wx.Pen(colour=wx.Colour(0, 100, 0), width=2, style=wx.SHORT_DASH)
-
- def Stop(self, restore=True):
- AnalysisControllerBase.Stop(self, restore=restore)
-
- self.transectChanged.emit(coords=[])
-
-
-class MeasureDistanceController(AnalysisControllerBase):
- """!Class controls measuring distance in map display."""
- def __init__(self, giface, mapWindow):
- AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)
-
- self._projInfo = self._mapWindow.Map.projinfo
- self._totaldist = 0.0 # total measured distance
- self._useCtypes = False
-
- def _doAnalysis(self, coords):
- """!New point added.
-
- @param x,y east north coordinates
- """
- self.MeasureDist(coords[-2], coords[-1])
-
- def _disconnectAll(self):
- self._mapWindow.mouseLeftDown.disconnect(self._start)
- self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
- self._mapWindow.mouseDClick.disconnect(self.Stop)
-
- def _connectAll(self):
- self._mapWindow.mouseLeftDown.connect(self._start)
- self._mapWindow.mouseLeftUp.connect(self._addPoint)
- self._mapWindow.mouseDClick.connect(self.Stop)
-
- def _getPen(self):
- return wx.Pen(colour='green', width=2, style=wx.SHORT_DASH)
-
- def Stop(self, restore=True):
- AnalysisControllerBase.Stop(self, restore=restore)
-
- self._giface.WriteCmdLog(_('Measuring finished'))
-
- def Start(self):
- """!Init measurement routine that calculates map distance
- along transect drawn on map display
- """
- AnalysisControllerBase.Start(self)
- self._totaldist = 0.0 # total measured distance
-
- # initiating output (and write a message)
- # e.g., in Layer Manager switch to output console
- # TODO: this should be something like: write important message or write tip
- # TODO: mixed 'switching' and message? no, measuring handles 'swithing' on its own
- self._giface.WriteWarning(_('Click and drag with left mouse button '
- 'to measure.%s'
- 'Double click with left button to clear.') % \
- (os.linesep))
- if self._projInfo['proj'] != 'xy':
- mapunits = self._projInfo['units']
- self._giface.WriteCmdLog(_('Measuring distance') + ' ('
- + mapunits + '):')
- else:
- self._giface.WriteCmdLog(_('Measuring distance:'))
-
- if self._projInfo['proj'] == 'll':
- try:
- import grass.lib.gis as gislib
- gislib.G_begin_distance_calculations()
- self._useCtypes = True
- except ImportError, e:
- self._giface.WriteWarning(_('Geodesic distance calculation '
- 'is not available.\n'
- 'Reason: %s' % e))
-
- def MeasureDist(self, beginpt, endpt):
- """!Calculate distance and print to output window.
-
- @param beginpt,endpt EN coordinates
- """
- # move also Distance method?
- dist, (north, east) = self._mapWindow.Distance(beginpt, endpt, screen=False)
-
- dist = round(dist, 3)
- mapunits = self._projInfo['units']
- if mapunits == 'degrees' and self._useCtypes:
- mapunits = 'meters'
- d, dunits = units.formatDist(dist, mapunits)
-
- self._totaldist += dist
- td, tdunits = units.formatDist(self._totaldist,
- mapunits)
-
- strdist = str(d)
- strtotdist = str(td)
-
- if self._projInfo['proj'] == 'xy' or 'degree' not in self._projInfo['unit']:
- angle = int(math.degrees(math.atan2(north, east)) + 0.5)
- # uncomment below (or flip order of atan2(y,x) above) to use
- # the mathematical theta convention (CCW from +x axis)
- #angle = 90 - angle
- if angle < 0:
- angle = 360 + angle
-
- mstring = '%s = %s %s\n%s = %s %s\n%s = %d %s\n%s' \
- % (_('segment'), strdist, dunits,
- _('total distance'), strtotdist, tdunits,
- _('bearing'), angle, _('degrees (clockwise from grid-north)'),
- '-' * 60)
- else:
- mstring = '%s = %s %s\n%s = %s %s\n%s' \
- % (_('segment'), strdist, dunits,
- _('total distance'), strtotdist, tdunits,
- '-' * 60)
-
- self._giface.WriteLog(mstring, priority=2)
-
- return dist
Modified: grass/trunk/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/frame.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapdisp/frame.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -43,20 +43,21 @@
from core.gcmd import GError, GMessage
from dbmgr.dialogs import DisplayAttributesDialog
from core.utils import ListOfCatsToRange, GetLayerNameFromCmd, _
-from gui_core.dialogs import GetImageHandlers, ImageSizeDialog, DecorationDialog, TextLayerDialog, \
- DECOR_DIALOG_LEGEND, DECOR_DIALOG_BARSCALE
+from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
from core.debug import Debug
from core.settings import UserSettings
from gui_core.mapdisp import SingleMapFrame
-from gui_core.mapwindow import MapWindowProperties
+from mapwin.base import MapWindowProperties
from gui_core.query import QueryDialog, PrepareQueryResults
-from mapdisp.mapwindow import BufferedWindow
-from mapdisp.overlays import LegendController, BarscaleController
+from mapwin.buffered import BufferedMapWindow
+from mapwin.decorations import DecorationDialog, TextLayerDialog, \
+ LegendController, BarscaleController, \
+ DECOR_DIALOG_LEGEND, DECOR_DIALOG_BARSCALE
from modules.histogram import HistogramFrame
from wxplot.histogram import HistogramPlotFrame
from wxplot.profile import ProfileFrame
from wxplot.scatter import ScatterFrame
-from mapdisp.analysis import ProfileController, MeasureDistanceController
+from mapwin.analysis import ProfileController, MeasureDistanceController
from mapdisp import statusbar as sb
@@ -71,7 +72,7 @@
toolbars = ["map"], tree = None, notebook = None, lmgr = None,
page = None, Map = Map(), auimgr = None, name = 'MapWindow', **kwargs):
"""!Main map display window with toolbars, statusbar and
- BufferedWindow (map canvas)
+ 2D map window, 3D map window and digitizer.
@param toolbars array of activated toolbars, e.g. ['map', 'digit']
@param tree reference to layer tree
@@ -154,10 +155,10 @@
#
# Init map display (buffered DC & set default cursor)
#
- self.MapWindow2D = BufferedWindow(self, giface = self._giface,
- Map=self.Map,
- properties=self.mapWindowProperties,
- overlays=self.decorations)
+ self.MapWindow2D = BufferedMapWindow(self, giface = self._giface,
+ Map=self.Map,
+ properties=self.mapWindowProperties,
+ overlays=self.decorations)
self.MapWindow2D.mapQueried.connect(self.Query)
self.MapWindow2D.overlayActivated.connect(self._activateOverlay)
self._setUpMapWindow(self.MapWindow2D)
Deleted: grass/trunk/gui/wxpython/mapdisp/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/mapwindow.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapdisp/mapwindow.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -1,2086 +0,0 @@
-"""!
- at package mapdisp.mapwindow
-
- at brief Map display canvas - buffered window.
-
-Classes:
- - mapwindow::BufferedWindow
- - mapwindow::GraphicsSet
- - mapwindow::GraphicsSetItem
-
-(C) 2006-2013 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.
-
- at author Martin Landa <landa.martin gmail.com>
- at author Michael Barton
- at author Jachym Cepicky
- at author Stepan Turek <stepan.turek seznam.cz> (handlers support, GraphicsSet)
- at author Anna Petrasova <kratochanna gmail.com> (refactoring)
- at author Vaclav Petras <wenzeslaus gmail.com> (refactoring)
-"""
-
-import os
-import time
-import math
-import sys
-from copy import copy
-
-import wx
-
-from grass.pydispatch.signal import Signal
-
-import grass.script as grass
-
-from gui_core.dialogs import SavedRegion
-from core.gcmd import RunCommand, GException, GError, GMessage
-from core.debug import Debug
-from core.settings import UserSettings
-from gui_core.mapwindow import MapWindow
-from core.utils import GetGEventAttribsForHandler, _
-import core.utils as utils
-
-try:
- import grass.lib.gis as gislib
- haveCtypes = True
-except ImportError:
- haveCtypes = False
-
-class BufferedWindow(MapWindow, wx.Window):
- """!A Buffered window class (2D view mode)
-
- Superclass for VDigitWindow (vector digitizer).
-
- When the drawing needs to change, you app needs to call the
- UpdateMap() method. Since the drawing is stored in a bitmap, you
- can also save the drawing to file by calling the
- SaveToFile() method.
- """
- def __init__(self, parent, giface, Map, properties,
- id=wx.ID_ANY, overlays=None,
- style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
- """!
- @param parent parent window
- @param giface grass interface instance
- @param Map map instance
- @param properties instance of MapWindowProperties
- @param id wx window id
- @param style wx window style
- @param kwargs keyword arguments passed to MapWindow and wx.Window
- """
- MapWindow.__init__(self, parent=parent, giface=giface, Map=Map)
- wx.Window.__init__(self, parent=parent, id=id, style=style, **kwargs)
-
- self._properties = properties
-
- # flags
- self.resize = False # indicates whether or not a resize event has taken place
- self.dragimg = None # initialize variable for map panning
- self.alwaysRender = False # if it always sets render to True in self.UpdateMap()
-
- # variables for drawing on DC
- self.pen = None # pen for drawing zoom boxes, etc.
- self.polypen = None # pen for drawing polylines (measurements, profiles, etc)
- # List of wx.Point tuples defining a polyline (geographical coordinates)
- self.polycoords = []
- # ID of rubber band line
- self.lineid = None
- # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
- self.plineid = None
-
- # Emitted when zoom of a window is changed
- self.zoomChanged = Signal('BufferedWindow.zoomChanged')
-
- # Emitted when map was queried, parameters x, y are mouse coordinates
- # TODO: change pixel coordinates to map coordinates (using Pixel2Cell)
- self.mapQueried = Signal('BufferedWindow.mapQueried')
-
- # Emitted when the zoom history stack is emptied
- self.zoomHistoryUnavailable = Signal('BufferedWindow.zoomHistoryUnavailable')
- # Emitted when the zoom history stack is not empty
- self.zoomHistoryAvailable = Signal('BufferedWindow.zoomHistoryAvailable')
-
- # Emitted when map enters the window
- self.mouseEntered = Signal('BufferedWindow.mouseEntered')
- # Emitted when left mouse button is released and mouse use is 'pointer'
- # Parameters are x and y of the mouse click in map (cell) units
- # new and experimental, if the concept would be used widely,
- # it could replace register and unregister mechanism
- # and partially maybe also internal mouse use dictionary
- self.mouseLeftUpPointer = Signal('BufferedWindow.mouseLeftUpPointer')
- # Emitted when left mouse button is released
- self.mouseLeftUp = Signal('BufferedWindow.mouseLeftUp')
- # Emitted when left mouse button was pressed
- self.mouseLeftDown = Signal('BufferedWindow.mouseLeftDown')
- # Emitted after double-click
- self.mouseDClick = Signal('BufferedWindow.mouseDClick')
- # Emitted when mouse us moving (mouse motion event)
- # Parametres are x and y of the mouse position in map (cell) units
- self.mouseMoving = Signal('BufferedWindow.mouseMoving')
-
- # event bindings
- self.Bind(wx.EVT_PAINT, self.OnPaint)
- self.Bind(wx.EVT_SIZE, self.OnSize)
- self.Bind(wx.EVT_IDLE, self.OnIdle)
-
- self._bindMouseEvents()
-
- self.processMouse = True
-
- # render output objects
- self.mapfile = None # image file to be rendered
- self.img = None # wx.Image object (self.mapfile)
- # decoration overlays
- self.overlays = overlays
- # images and their PseudoDC ID's for painting and dragging
- self.imagedict = {}
- self.select = {} # selecting/unselecting decorations for dragging
- self.textdict = {} # text, font, and color indexed by id
-
- # zoom objects
- self.zoomhistory = [] # list of past zoom extents
- self.currzoom = 0 # current set of extents in zoom history being used
- self.zoomtype = 1 # 1 zoom in, 0 no zoom, -1 zoom out
- self.hitradius = 10 # distance for selecting map decorations
- self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
-
- # OnSize called to make sure the buffer is initialized.
- # This might result in OnSize getting called twice on some
- # platforms at initialization, but little harm done.
- ### self.OnSize(None)
-
- self._definePseudoDC()
- # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
- self.redrawAll = True
-
- # will store an off screen empty bitmap for saving to file
- self._buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
-
- self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
- # rerender when Map reports change
- self.Map.layerChanged.connect(lambda: self.UpdateMap())
-
- # vars for handling mouse clicks
- self.dragid = -1
- self.lastpos = (0, 0)
-
- # list for registration of graphics to draw
- self.graphicsSetList = []
-
- def _definePseudoDC(self):
- """!Define PseudoDC objects to use
- """
- # create PseudoDC used for background map, map decorations like scales and legends
- self.pdc = wx.PseudoDC()
- # used for digitization tool
- self.pdcVector = None
- # decorations (region box, etc.)
- self.pdcDec = wx.PseudoDC()
- # pseudoDC for temporal objects (select box, measurement tool, etc.)
- self.pdcTmp = wx.PseudoDC()
-
- def _bindMouseEvents(self):
- self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
- self.Bind(wx.EVT_MOTION, self.OnMotion)
- self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
-
- def OnContextMenu(self, event):
- """!Show Map Display context menu"""
- if hasattr(self, "digit"):
- event.Skip()
- return
-
- if not hasattr(self, "popupCopyCoordinates"):
- self.popupCopyCoordinates = wx.NewId()
- self.Bind(wx.EVT_MENU, self.OnCopyCoordinates, id = self.popupCopyCoordinates)
-
- # generate popup-menu
- menu = wx.Menu()
- menu.Append(self.popupCopyCoordinates, _("Copy coordinates to clipboard"))
-
- self.PopupMenu(menu)
- menu.Destroy()
-
- def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0, 0, 0, 0], pen = None):
- """!Draws map and overlay decorations
- """
- if drawid == None:
- if pdctype == 'image' and img:
- drawid = self.imagedict[img]
- elif pdctype == 'clear':
- drawid = None
- else:
- drawid = wx.NewId()
-
- # TODO: find better solution
- if not pen:
- if pdctype == 'polyline':
- pen = self.polypen
- else:
- pen = self.pen
-
- if img and pdctype == 'image':
- # self.imagedict[img]['coords'] = coords
- self.select[self.imagedict[img]['id']] = False # ?
-
- pdc.BeginDrawing()
-
- if drawid != 99:
- bg = wx.TRANSPARENT_BRUSH
- else:
- bg = wx.Brush(self.GetBackgroundColour())
-
- pdc.SetBackground(bg)
-
- Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype = %s, coord=%s" % \
- (drawid, pdctype, coords))
-
- # set PseudoDC id
- if drawid is not None:
- pdc.SetId(drawid)
-
- if pdctype == 'clear': # erase the display
- bg = wx.WHITE_BRUSH
- # bg = wx.Brush(self.GetBackgroundColour())
- pdc.SetBackground(bg)
- pdc.RemoveAll()
- pdc.Clear()
- pdc.EndDrawing()
-
- self.Refresh()
- return
-
- if pdctype == 'image': # draw selected image
- bitmap = wx.BitmapFromImage(img)
- w,h = bitmap.GetSize()
- pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
- pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
-
- elif pdctype == 'box': # draw a box on top of the map
- if pen:
- pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
- pdc.SetPen(pen)
- x2 = max(coords[0],coords[2])
- x1 = min(coords[0],coords[2])
- y2 = max(coords[1],coords[3])
- y1 = min(coords[1],coords[3])
- rwidth = x2-x1
- rheight = y2-y1
- rect = wx.Rect(x1, y1, rwidth, rheight)
- pdc.DrawRectangleRect(rect)
- pdc.SetIdBounds(drawid, rect)
-
- elif pdctype == 'line': # draw a line on top of the map
- if pen:
- pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
- pdc.SetPen(pen)
- pdc.DrawLinePoint(wx.Point(coords[0], coords[1]),wx.Point(coords[2], coords[3]))
- pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
-
- elif pdctype == 'polyline': # draw a polyline on top of the map
- if pen:
- pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
- pdc.SetPen(pen)
- if (len(coords) < 2):
- return
- i = 1
- while i < len(coords):
- pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
- wx.Point(coords[i][0], coords[i][1]))
- i += 1
-
- # get bounding rectangle for polyline
- xlist = []
- ylist = []
- if len(coords) > 0:
- for point in coords:
- x,y = point
- xlist.append(x)
- ylist.append(y)
- x1 = min(xlist)
- x2 = max(xlist)
- y1 = min(ylist)
- y2 = max(ylist)
- pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
- # self.ovlcoords[drawid] = [x1,y1,x2,y2]
-
- elif pdctype == 'point': # draw point
- if pen:
- pdc.SetPen(pen)
- pdc.DrawPoint(coords[0], coords[1])
- coordsBound = (coords[0] - 5,
- coords[1] - 5,
- coords[0] + 5,
- coords[1] + 5)
- pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
-
- elif pdctype == 'text': # draw text on top of map
- if not img['active']:
- return # only draw active text
- if 'rotation' in img:
- rotation = float(img['rotation'])
- else:
- rotation = 0.0
- w, h = self.GetFullTextExtent(img['text'])[0:2]
- pdc.SetFont(img['font'])
- pdc.SetTextForeground(img['color'])
- coords, bbox = self.TextBounds(img)
- if rotation == 0:
- pdc.DrawText(img['text'], coords[0], coords[1])
- else:
- pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
- pdc.SetIdBounds(drawid, bbox)
-
- pdc.EndDrawing()
-
- self.Refresh()
-
- return drawid
-
- def TextBounds(self, textinfo, relcoords = False):
- """!Return text boundary data
-
- @param textinfo text metadata (text, font, color, rotation)
- @param coords reference point
-
- @return coords of nonrotated text bbox (TL corner)
- @return bbox of rotated text bbox (wx.Rect)
- @return relCoords are text coord inside bbox
- """
- if 'rotation' in textinfo:
- rotation = float(textinfo['rotation'])
- else:
- rotation = 0.0
-
- coords = textinfo['coords']
- bbox = wx.Rect(coords[0], coords[1], 0, 0)
- relCoords = (0, 0)
- Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
- (textinfo['text'], rotation))
-
- self.Update()
-
- self.SetFont(textinfo['font'])
-
- w, h = self.GetTextExtent(textinfo['text'])
-
- if rotation == 0:
- bbox[2], bbox[3] = w, h
- if relcoords:
- return coords, bbox, relCoords
- else:
- return coords, bbox
-
- boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
- boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
- if rotation > 0 and rotation < 90:
- bbox[1] -= boxh
- relCoords = (0, boxh)
- elif rotation >= 90 and rotation < 180:
- bbox[0] -= boxw
- bbox[1] -= boxh
- relCoords = (boxw, boxh)
- elif rotation >= 180 and rotation < 270:
- bbox[0] -= boxw
- relCoords = (boxw, 0)
- bbox[2] = boxw
- bbox[3] = boxh
- bbox.Inflate(h,h)
- if relcoords:
- return coords, bbox, relCoords
- else:
- return coords, bbox
-
- def OnPaint(self, event):
- """!Draw PseudoDC's to buffered paint DC
-
- If self.redrawAll is False on self.pdcTmp content is re-drawn
- """
- Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
- dc = wx.BufferedPaintDC(self, self._buffer)
- dc.Clear()
-
- # use PrepareDC to set position correctly
- # probably does nothing, removed from wxPython 2.9
- # self.PrepareDC(dc)
-
- # create a clipping rect from our position and size
- # and update region
- rgn = self.GetUpdateRegion().GetBox()
- dc.SetClippingRect(rgn)
-
- switchDraw = False
- if self.redrawAll is None:
- self.redrawAll = True
- switchDraw = True
-
- if self.redrawAll: # redraw pdc and pdcVector
- # draw to the dc using the calculated clipping rect
- self.pdc.DrawToDCClipped(dc, rgn)
-
- # draw vector map layer
- if hasattr(self, "digit"):
- # decorate with GDDC (transparency)
- try:
- gcdc = wx.GCDC(dc)
- self.pdcVector.DrawToDCClipped(gcdc, rgn)
- except NotImplementedError, e:
- print >> sys.stderr, e
- self.pdcVector.DrawToDCClipped(dc, rgn)
-
- self.bufferLast = None
- else: # do not redraw pdc and pdcVector
- if self.bufferLast is None:
- # draw to the dc
- self.pdc.DrawToDC(dc)
-
- if hasattr(self, "digit"):
- # decorate with GDDC (transparency)
- try:
- gcdc = wx.GCDC(dc)
- self.pdcVector.DrawToDC(gcdc)
- except NotImplementedError, e:
- print >> sys.stderr, e
- self.pdcVector.DrawToDC(dc)
-
- # store buffered image
- # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
- self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
-
- self.pdc.DrawBitmap(self.bufferLast, 0, 0, False)
- self.pdc.DrawToDC(dc)
-
- # draw decorations (e.g. region box)
- try:
- gcdc = wx.GCDC(dc)
- self.pdcDec.DrawToDC(gcdc)
- except NotImplementedError, e:
- print >> sys.stderr, e
- self.pdcDec.DrawToDC(dc)
-
- # draw temporary object on the foreground
- ### self.pdcTmp.DrawToDCClipped(dc, rgn)
- self.pdcTmp.DrawToDC(dc)
-
- if switchDraw:
- self.redrawAll = False
-
- def OnSize(self, event):
- """!Scale map image so that it is the same size as the Window
- """
- # re-render image on idle
- self.resize = time.clock()
-
- def OnIdle(self, event):
- """!Only re-render a composite map image from GRASS during
- idle time instead of multiple times during resizing.
- """
-
- # use OnInternalIdle() instead ?
-
- if self.resize and self.resize + 0.2 < time.clock():
- Debug.msg(3, "BufferedWindow.OnSize():")
-
- # set size of the input image
- self.Map.ChangeMapSize(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.Destroy()
- self._buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
-
- # get the image to be rendered
- self.img = self.GetImage()
-
- # update map display
- updatemap = True
- if self.img and self.Map.width + self.Map.height > 0: # scale image after resize
- self.img = self.img.Scale(self.Map.width, self.Map.height)
- if len(self.Map.GetListOfLayers()) > 0:
- self.UpdateMap()
- updatemap = False
-
- if updatemap:
- self.UpdateMap(render = True)
- self.resize = False
- elif self.resize:
- event.RequestMore()
-
- event.Skip()
-
- def SaveToFile(self, FileName, FileType, width, height):
- """!This draws the pseudo DC to a buffer that can be saved to
- a file.
-
- @param FileName file name
- @param FileType type of bitmap
- @param width image width
- @param height image height
- """
- busy = wx.BusyInfo(message = _("Please wait, exporting image..."),
- parent = self)
- wx.Yield()
-
- self.Map.ChangeMapSize((width, height))
- ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
- self.Map.Render(force = True, windres = self._properties.resolution)
- img = self.GetImage()
- self.pdc.RemoveAll()
- self.Draw(self.pdc, img, drawid = 99)
-
- # compute size ratio to move overlay accordingly
- cSize = self.GetClientSizeTuple()
- ratio = float(width) / cSize[0], float(height) / cSize[1]
-
- # redraw legend, scalebar
- for img in self.GetOverlay():
- # draw any active and defined overlays
- if self.imagedict[img]['layer'].IsActive():
- id = self.imagedict[img]['id']
- coords = int(ratio[0] * self.overlays[id].coords[0]),\
- int(ratio[1] * self.overlays[id].coords[1])
- self.Draw(self.pdc, img = img, drawid = id,
- pdctype = self.overlays[id].pdcType, coords = coords)
-
- # redraw text labels
- for id in self.textdict.keys():
- textinfo = self.textdict[id]
- oldCoords = textinfo['coords']
- textinfo['coords'] = ratio[0] * textinfo['coords'][0],\
- ratio[1] * textinfo['coords'][1]
- self.Draw(self.pdc, img = self.textdict[id], drawid = id,
- pdctype = 'text')
- # set back old coordinates
- textinfo['coords'] = oldCoords
-
- dc = wx.BufferedDC(None, ibuffer)
- dc.Clear()
- # probably does nothing, removed from wxPython 2.9
- # self.PrepareDC(dc)
- self.pdc.DrawToDC(dc)
- if self.pdcVector:
- self.pdcVector.DrawToDC(dc)
- ibuffer.SaveFile(FileName, FileType)
-
- busy.Destroy()
-
- self.UpdateMap(render = True)
- self.Refresh()
-
- def GetOverlay(self):
- """!Converts rendered overlay files to wx.Image
-
- Updates self.imagedict
-
- @return list of images
- """
- imgs = []
- for overlay in self.Map.GetListOfLayers(ltype = "overlay", active = True):
- if overlay.mapfile is not None \
- and os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
- img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
-
- for key in self.imagedict.keys():
- if self.imagedict[key]['id'] == overlay.id:
- del self.imagedict[key]
-
- self.imagedict[img] = { 'id' : overlay.id,
- 'layer' : overlay }
- imgs.append(img)
-
- return imgs
-
- def GetImage(self):
- """!Converts redered map files to wx.Image
-
- Updates self.imagedict (id=99)
-
- @return wx.Image instance (map composition)
- """
- imgId = 99
- if self.mapfile and 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
-
- for key in self.imagedict.keys():
- if self.imagedict[key]['id'] == imgId:
- del self.imagedict[key]
-
- self.imagedict[img] = { 'id': imgId }
-
- return img
-
- def SetAlwaysRenderEnabled(self, alwaysRender = True):
- self.alwaysRender = alwaysRender
-
- def IsAlwaysRenderEnabled(self):
- return self.alwaysRender
-
- def UpdateMap(self, render = True, renderVector = True):
- """!Updates the canvas anytime there is a change to the
- underlaying images or to the geometry of the canvas.
-
- This method should not be called directly.
-
- @todo change direct calling of UpdateMap method to emittig grass
- interface updateMap signal
-
- @param render re-render map composition
- @param renderVector re-render vector map layer enabled for editing (used for digitizer)
- """
- start = time.clock()
- self.resize = False
-
- # was if self.Map.cmdfile and ...
- if self.IsAlwaysRenderEnabled() and self.img is None:
- render = True
-
- #
- # render background image if needed
- #
-
- # here was the change of the layertree rerender variable
- # but it is fully the problem of layertree
- # and so it is handled there
- # remove this comment when it is old enough
-
- try:
- if render:
- # update display size
- self.Map.ChangeMapSize(self.GetClientSize())
- if self._properties.resolution:
- # use computation region resolution for rendering
- windres = True
- else:
- windres = False
-
- self.mapfile = self.Map.Render(force = True,
- windres = windres)
- else:
- self.mapfile = self.Map.Render(force = False)
-
- except GException, e:
- GError(message = e.value)
- self.mapfile = None
-
- self.img = self.GetImage() # id=99
-
- #
- # clear pseudoDcs
- #
- for pdc in (self.pdc,
- self.pdcDec,
- self.pdcTmp):
- pdc.Clear()
- pdc.RemoveAll()
-
- #
- # draw background map image to PseudoDC
- #
- if not self.img:
- self.Draw(self.pdc, pdctype = 'clear')
- else:
- try:
- id = self.imagedict[self.img]['id']
- except:
- return False
-
- self.Draw(self.pdc, self.img, drawid = id)
-
- #
- # render vector map layer
- #
- if renderVector and hasattr(self, "digit"):
- self._updateMap()
- #
- # render overlays
- #
- for img in self.GetOverlay():
- # draw any active and defined overlays
- if self.imagedict[img]['layer'].IsActive():
- id = self.imagedict[img]['id']
- self.Draw(self.pdc, img = img, drawid = id,
- pdctype = self.overlays[id].pdcType, coords = self.overlays[id].coords)
-
- for id in self.textdict.keys():
- self.Draw(self.pdc, img = self.textdict[id], drawid = id,
- pdctype = 'text', coords = [10, 10, 10, 10])
-
- # optionally draw computational extent box
- self.DrawCompRegionExtent()
-
- #
- # redraw pdcTmp if needed
- #
-
- # draw registered graphics
- if len(self.graphicsSetList) > 0:
- penOrig = self.pen
- polypenOrig = self.polypen
-
- for item in self.graphicsSetList:
- try:
- item.Draw(self.pdcTmp)
- except:
- GError(parent = self,
- message = _('Unable to draw registered graphics. '
- 'The graphics was unregistered.'))
- self.UnregisterGraphicsToDraw(item)
-
- self.pen = penOrig
- self.polypen = polypenOrig
-
- if len(self.polycoords) > 0:
- self.DrawLines(self.pdcTmp)
-
- stop = time.clock()
-
- Debug.msg (1, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
- (render, renderVector, (stop-start)))
-
- return True
-
- def DrawCompRegionExtent(self):
- """!Draw computational region extent in the display
-
- Display region is drawn as a blue box inside the computational region,
- computational region inside a display region as a red box).
- """
- if self._properties.showRegion:
- compReg = self.Map.GetRegion()
- dispReg = self.Map.GetCurrentRegion()
- reg = None
- if utils.isInRegion(dispReg, compReg):
- self.polypen = wx.Pen(colour = wx.Colour(0, 0, 255, 128), width = 3, style = wx.SOLID)
- reg = dispReg
- else:
- self.polypen = wx.Pen(colour = wx.Colour(255, 0, 0, 128),
- width = 3, style = wx.SOLID)
- reg = compReg
-
- regionCoords = []
- regionCoords.append((reg['w'], reg['n']))
- regionCoords.append((reg['e'], reg['n']))
- regionCoords.append((reg['e'], reg['s']))
- regionCoords.append((reg['w'], reg['s']))
- regionCoords.append((reg['w'], reg['n']))
- # draw region extent
- self.DrawLines(pdc=self.pdcDec, polycoords=regionCoords)
-
- def EraseMap(self):
- """!Erase map canvas
- """
- self.Draw(self.pdc, pdctype = 'clear')
-
- if hasattr(self, "digit"):
- self.Draw(self.pdcVector, pdctype = 'clear')
-
- self.Draw(self.pdcDec, pdctype = 'clear')
- self.Draw(self.pdcTmp, pdctype = 'clear')
-
- self.Map.AbortAllThreads()
-
- def DragMap(self, moveto):
- """!Drag the entire map image for panning.
-
- @param moveto dx,dy
- """
- dc = wx.BufferedDC(wx.ClientDC(self))
- dc.SetBackground(wx.Brush("White"))
- dc.Clear()
-
- self.dragimg = wx.DragImage(self._buffer)
- self.dragimg.BeginDrag((0, 0), self)
- self.dragimg.GetImageRect(moveto)
- self.dragimg.Move(moveto)
-
- self.dragimg.DoDrawImage(dc, moveto)
- self.dragimg.EndDrag()
-
- def DragItem(self, id, coords):
- """!Drag an overlay decoration item
- """
- if id == 99 or id == '' or id == None: return
- Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
- x, y = self.lastpos
- dx = coords[0] - x
- dy = coords[1] - y
- self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
- r = self.pdc.GetIdBounds(id)
-
- if type(r) is list:
- r = wx.Rect(r[0], r[1], r[2], r[3])
- if id > 100: # text dragging
- rtop = (r[0],r[1]-r[3],r[2],r[3])
- r = r.Union(rtop)
- rleft = (r[0]-r[2],r[1],r[2],r[3])
- r = r.Union(rleft)
- self.pdc.TranslateId(id, dx, dy)
-
- r2 = self.pdc.GetIdBounds(id)
- if type(r2) is list:
- r2 = wx.Rect(r[0], r[1], r[2], r[3])
- if id > 100: # text
- self.textdict[id]['bbox'] = r2
- self.textdict[id]['coords'][0] += dx
- self.textdict[id]['coords'][1] += dy
- r = r.Union(r2)
- r.Inflate(4,4)
- self.RefreshRect(r, False)
- self.lastpos = (coords[0], coords[1])
-
- def MouseDraw(self, pdc = None, begin = None, end = None):
- """!Mouse box or line from 'begin' to 'end'
-
- If not given from self.mouse['begin'] to self.mouse['end'].
- """
- if not pdc:
- return
-
- if begin is None:
- begin = self.mouse['begin']
- if end is None:
- end = self.mouse['end']
-
- Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
- (self.mouse['use'], self.mouse['box'],
- begin[0], begin[1], end[0], end[1]))
-
- if self.mouse['box'] == "box":
- boxid = wx.ID_NEW
- mousecoords = [begin[0], begin[1],
- end[0], end[1]]
- r = pdc.GetIdBounds(boxid)
- if type(r) is list:
- r = wx.Rect(r[0], r[1], r[2], r[3])
- r.Inflate(4, 4)
- try:
- pdc.ClearId(boxid)
- except:
- pass
- self.RefreshRect(r, False)
- pdc.SetId(boxid)
- self.Draw(pdc, drawid = boxid, pdctype = 'box', coords = mousecoords)
-
- elif self.mouse['box'] == "line":
- self.lineid = wx.ID_NEW
- mousecoords = [begin[0], begin[1], \
- end[0], end[1]]
- x1 = min(begin[0],end[0])
- x2 = max(begin[0],end[0])
- y1 = min(begin[1],end[1])
- y2 = max(begin[1],end[1])
- r = wx.Rect(x1,y1,x2-x1,y2-y1)
- r.Inflate(4,4)
- try:
- pdc.ClearId(self.lineid)
- except:
- pass
- self.RefreshRect(r, False)
- pdc.SetId(self.lineid)
- self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = mousecoords)
-
- def DrawLines(self, pdc = None, polycoords = None):
- """!Draw polyline in PseudoDC
-
- Set self.pline to wx.NEW_ID + 1
-
- polycoords - list of polyline vertices, geographical coordinates
- (if not given, self.polycoords is used)
- """
- if not pdc:
- pdc = self.pdcTmp
-
- if not polycoords:
- polycoords = self.polycoords
-
- if len(polycoords) > 0:
- self.plineid = wx.ID_NEW + 1
- # convert from EN to XY
- coords = []
- for p in polycoords:
- coords.append(self.Cell2Pixel(p))
-
- self.Draw(pdc, drawid = self.plineid, pdctype = 'polyline', coords = coords)
-
- Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
- (coords, self.plineid))
-
- return self.plineid
-
- return -1
-
- def DrawCross(self, pdc, coords, size, rotation = 0, pen = None,
- text = None, textAlign = 'lr', textOffset = (5, 5)):
- """!Draw cross in PseudoDC
-
- @todo implement rotation
-
- @param pdc PseudoDC
- @param coords center coordinates
- @param rotation rotate symbol
- @param text draw also text (text, font, color, rotation)
- @param textAlign alignment (default 'lower-right')
- @param textOffset offset for text (from center point)
- """
- Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
- (pdc, coords, size))
- coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
- (coords[0], coords[1] - size, coords[0], coords[1] + size))
-
- self.lineid = wx.NewId()
- for lineCoords in coordsCross:
- self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = lineCoords, pen = pen)
-
- if not text:
- return self.lineid
-
- if textAlign == 'ul':
- coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
- elif textAlign == 'ur':
- coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
- elif textAlign == 'lr':
- coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
- else:
- coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
-
- self.Draw(pdc, img = text,
- pdctype = 'text', coords = coord, pen = pen)
-
- return self.lineid
-
- def _computeZoomToPointAndRecenter(self, position, zoomtype):
- """!Computes zoom parameters for recenter mode.
-
- Computes begin and end parameters for Zoom() method.
- Used for zooming by single click (not box)
- and mouse wheel zooming (zoom and recenter mode).
- """
- if zoomtype > 0:
- begin = (position[0] - self.Map.width / 4,
- position[1] - self.Map.height / 4)
- end = (position[0] + self.Map.width / 4,
- position[1] + self.Map.height / 4)
- else:
- begin = ((self.Map.width - position[0]) / 2,
- (self.Map.height - position[1]) / 2)
- end = (begin[0] + self.Map.width / 2,
- begin[1] + self.Map.height / 2)
- return begin, end
-
- def MouseActions(self, event):
- """!Mouse motion and button click notifier
- """
- if not self.processMouse:
- return
-
- # zoom with mouse wheel
- if event.GetWheelRotation() != 0:
- self.OnMouseWheel(event)
-
- # left mouse button pressed
- elif event.LeftDown():
- self.OnLeftDown(event)
-
- # left mouse button released
- elif event.LeftUp():
- self.OnLeftUp(event)
-
- # dragging
- elif event.Dragging():
- self.OnDragging(event)
-
- # double click
- elif event.ButtonDClick():
- self.OnButtonDClick(event)
-
- # middle mouse button pressed
- elif event.MiddleDown():
- self.OnMiddleDown(event)
-
- # middle mouse button relesed
- elif event.MiddleUp():
- self.OnMiddleUp(event)
-
- # right mouse button pressed
- elif event.RightDown():
- self.OnRightDown(event)
-
- # right mouse button released
- elif event.RightUp():
- self.OnRightUp(event)
-
- elif event.Entering():
- self.OnMouseEnter(event)
-
- elif event.Moving():
- pixelCoordinates = event.GetPositionTuple()[:]
- coordinates = self.Pixel2Cell(pixelCoordinates)
- self.mouseMoving.emit(x=coordinates[0], y=coordinates[1])
- self.OnMouseMoving(event)
-
- def OnMouseWheel(self, event):
- """!Mouse wheel moved
- """
- zoomBehaviour = UserSettings.Get(group = 'display',
- key = 'mouseWheelZoom',
- subkey = 'selection')
- if zoomBehaviour == 2:
- event.Skip()
- return
-
- self.processMouse = False
- current = event.GetPositionTuple()[:]
- wheel = event.GetWheelRotation()
- Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
-
- if wheel > 0:
- zoomtype = 1
- else:
- zoomtype = -1
- if UserSettings.Get(group = 'display',
- key = 'scrollDirection',
- subkey = 'selection'):
- zoomtype *= -1
- # zoom 1/2 of the screen (TODO: settings)
- if zoomBehaviour == 0: # zoom and recenter
- begin, end = self._computeZoomToPointAndRecenter(position = current, zoomtype = zoomtype)
-
- elif zoomBehaviour == 1: # zoom to current cursor position
- begin = (current[0]/2, current[1]/2)
- end = ((self.Map.width - current[0])/2 + current[0],
- (self.Map.height - current[1])/2 + current[1])
-
-
- # zoom
- self.Zoom(begin, end, zoomtype)
-
- # redraw map
- self.UpdateMap()
-
- self.Refresh()
- self.processMouse = True
-
- def OnDragging(self, event):
- """!Mouse dragging
- """
- Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
- current = event.GetPositionTuple()[:]
- previous = self.mouse['begin']
- move = (current[0] - previous[0],
- current[1] - previous[1])
-
- if hasattr(self, "digit"):
- digitToolbar = self.toolbar
- else:
- digitToolbar = None
-
- # dragging or drawing box with left button
- if self.mouse['use'] == 'pan' or \
- event.MiddleIsDown():
- self.DragMap(move)
-
- # dragging decoration overlay item
- elif (self.mouse['use'] == 'pointer' and
- not digitToolbar and
- self.dragid != None):
- coords = event.GetPositionTuple()
- self.DragItem(self.dragid, coords)
-
- # dragging anything else - rubber band box or line
- else:
- if (self.mouse['use'] == 'pointer' and
- not digitToolbar):
- return
-
- self.mouse['end'] = event.GetPositionTuple()[:]
- if (event.LeftIsDown() and
- not (digitToolbar and
- digitToolbar.GetAction() in ("moveLine",) and
- self.digit.GetDisplay().GetSelected() > 0)):
- self.MouseDraw(pdc = self.pdcTmp)
-
- def OnLeftDown(self, event):
- """!Left mouse button pressed
- """
- Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
- self.mouse["use"])
-
- self.mouse['begin'] = event.GetPositionTuple()[:]
-
- # vector digizer
- if self.mouse["use"] == "pointer" and \
- hasattr(self, "digit"):
- if event.ControlDown():
- self.OnLeftDownUndo(event)
- else:
- self._onLeftDown(event)
-
- elif self.mouse['use'] == 'pointer':
- # get decoration or text id
- idlist = []
- self.dragid = ''
- self.lastpos = self.mouse['begin']
- idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
- self.hitradius)
- if 99 in idlist:
- idlist.remove(99)
- if idlist != []:
- self.dragid = idlist[0] #drag whatever is on top
- else:
- pass
- coords = self.Pixel2Cell(self.mouse['begin'])
- self.mouseLeftDown.emit(x=coords[0], y=coords[1])
-
- event.Skip()
-
- def OnLeftUp(self, event):
- """!Left mouse button released
-
- Emits mapQueried signal when mouse use is 'query'.
- """
- Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
- self.mouse["use"])
-
- self.mouse['end'] = event.GetPositionTuple()[:]
- coordinates = self.Pixel2Cell(self.mouse['end'])
-
- if self.mouse['use'] in ["zoom", "pan"]:
- # set region in zoom or pan
- begin = self.mouse['begin']
- end = self.mouse['end']
-
- if self.mouse['use'] == 'zoom':
- # set region for click (zero-width box)
- if begin[0] - end[0] == 0 or \
- begin[1] - end[1] == 0:
- begin, end = self._computeZoomToPointAndRecenter(position = end, zoomtype = self.zoomtype)
- self.Zoom(begin, end, self.zoomtype)
-
- # redraw map
- self.UpdateMap(render = True)
-
- elif self.mouse["use"] == "query":
- self.mapQueried.emit(x=self.mouse['end'][0], y=self.mouse['end'][1])
-
- elif self.mouse["use"] == "pointer" and \
- hasattr(self, "digit"):
- self._onLeftUp(event)
-
- elif (self.mouse['use'] == 'pointer' and
- self.dragid >= 0):
- # end drag of overlay decoration
-
- if self.dragid < 99 and self.dragid in self.overlays:
- self.overlays[self.dragid].coords = self.pdc.GetIdBounds(self.dragid)
- elif self.dragid > 100 and self.dragid in self.textdict:
- self.textdict[self.dragid]['bbox'] = self.pdc.GetIdBounds(self.dragid)
- else:
- pass
- self.dragid = None
-
- self.mouseLeftUpPointer.emit(x=coordinates[0], y=coordinates[1])
-
- # TODO: decide which coordinates to send (e, n, mouse['begin'], mouse['end'])
- self.mouseLeftUp.emit(x=coordinates[0], y=coordinates[1])
-
- def OnButtonDClick(self, event):
- """!Mouse button double click
- """
- Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
- self.mouse["use"])
-
- screenCoords = event.GetPosition()
-
- if self.mouse['use'] == 'pointer':
- # select overlay decoration options dialog
- idlist = self.pdc.FindObjects(screenCoords[0], screenCoords[1], self.hitradius)
- if idlist:
- self.dragid = idlist[0]
- self.overlayActivated.emit(overlayId=self.dragid)
-
- coords = self.Pixel2Cell(screenCoords)
- self.mouseDClick.emit(x=coords[0], y=coords[1])
-
- def OnRightDown(self, event):
- """!Right mouse button pressed
- """
- Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
- self.mouse["use"])
-
- if hasattr(self, "digit"):
- self._onRightDown(event)
-
- event.Skip()
-
- def OnRightUp(self, event):
- """!Right mouse button released
- """
- Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
- self.mouse["use"])
-
- if hasattr(self, "digit"):
- self._onRightUp(event)
-
- self.redrawAll = True
- self.Refresh()
-
- event.Skip()
-
- def OnMiddleDown(self, event):
- """!Middle mouse button pressed
- """
- if not event:
- return
-
- self.mouse['begin'] = event.GetPositionTuple()[:]
-
- def OnMiddleUp(self, event):
- """!Middle mouse button released
- """
- self.mouse['end'] = event.GetPositionTuple()[:]
-
- # set region in zoom or pan
- begin = self.mouse['begin']
- end = self.mouse['end']
-
- self.Zoom(begin, end, 0) # no zoom
-
- # redraw map
- self.UpdateMap(render = True)
-
- def OnMouseEnter(self, event):
- """!Mouse entered window and no mouse buttons were pressed
-
- Emits the mouseEntered signal.
- """
- self.mouseEntered.emit()
- event.Skip()
-
- def OnMouseMoving(self, event):
- """!Motion event and no mouse buttons were pressed
- """
- if self.mouse["use"] == "pointer" and \
- hasattr(self, "digit"):
- self._onMouseMoving(event)
-
- event.Skip()
-
- def OnCopyCoordinates(self, event):
- """!Copy coordinates to cliboard"""
- e, n = self.GetLastEN()
- if wx.TheClipboard.Open():
- do = wx.TextDataObject()
- # TODO: put delimiter in settings and apply also for Go to in statusbar
- delim = ';'
- do.SetText(str(e) + delim + str(n))
- wx.TheClipboard.SetData(do)
- wx.TheClipboard.Close()
-
- def ClearLines(self, pdc = None):
- """!Clears temporary drawn lines from PseudoDC
- """
- if not pdc:
- pdc = self.pdcTmp
- try:
- pdc.ClearId(self.lineid)
- pdc.RemoveId(self.lineid)
- except:
- pass
-
- try:
- pdc.ClearId(self.plineid)
- pdc.RemoveId(self.plineid)
- except:
- pass
-
- Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
- (self.lineid, self.plineid))
-
- return True
-
- def Pixel2Cell(self, xyCoords):
- """!Convert image coordinates to real word coordinates
-
- @param x, y image coordinates
-
- @return easting, northing
- @return None on error
- """
- try:
- x = int(xyCoords[0])
- y = int(xyCoords[1])
- except:
- return None
-
- if self.Map.region["ewres"] > self.Map.region["nsres"]:
- res = self.Map.region["ewres"]
- else:
- res = self.Map.region["nsres"]
-
- w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
- n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
-
- east = w + x * res
- north = n - y * res
-
- return (east, north)
-
- def Cell2Pixel(self, enCoords):
- """!Convert real word coordinates to image coordinates
- """
- try:
- east = float(enCoords[0])
- north = float(enCoords[1])
- except:
- return None
-
- if self.Map.region["ewres"] > self.Map.region["nsres"]:
- res = self.Map.region["ewres"]
- else:
- res = self.Map.region["nsres"]
-
- w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
- n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
-
- x = (east - w) / res
- y = (n - north) / res
-
- return (x, y)
-
- def Zoom(self, begin, end, zoomtype):
- """!Calculates new region while (un)zoom/pan-ing
- """
- x1, y1 = begin
- x2, y2 = end
- newreg = {}
-
- # threshold - too small squares do not make sense
- # can only zoom to windows of > 5x5 screen pixels
- if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
- if x1 > x2:
- x1, x2 = x2, x1
- if y1 > y2:
- y1, y2 = y2, y1
-
- # zoom in
- if zoomtype > 0:
- newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
- newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
-
- # zoom out
- elif zoomtype < 0:
- newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
- newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + 2 * \
- (self.Map.width - x2),
- self.Map.height + 2 * \
- (self.Map.height - y2)))
- # pan
- elif zoomtype == 0:
- dx = x1 - x2
- dy = y1 - y2
- if dx == 0 and dy == 0:
- dx = x1 - self.Map.width / 2
- dy = y1 - self.Map.height / 2
- newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
- newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + dx,
- self.Map.height + dy))
-
- # if new region has been calculated, set the values
- if newreg != {}:
- # LL locations
- if self.Map.projinfo['proj'] == 'll':
- self.Map.region['n'] = min(self.Map.region['n'], 90.0)
- self.Map.region['s'] = max(self.Map.region['s'], -90.0)
-
- ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
- cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
-
- # calculate new center point and display resolution
- self.Map.region['center_easting'] = ce
- self.Map.region['center_northing'] = cn
- self.Map.region['ewres'] = (newreg['e'] - newreg['w']) / self.Map.width
- self.Map.region['nsres'] = (newreg['n'] - newreg['s']) / self.Map.height
- if self._properties.alignExtent:
- self.Map.AlignExtentFromDisplay()
- else:
- for k in ('n', 's', 'e', 'w'):
- self.Map.region[k] = newreg[k]
-
- if hasattr(self, "digit") and \
- hasattr(self, "moveInfo"):
- self._zoom(None)
-
- self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
- self.Map.region['e'], self.Map.region['w'])
-
- if self.redrawAll is False:
- self.redrawAll = True
-
- def ZoomBack(self):
- """!Zoom to previous extents in zoomhistory list
-
- Emits zoomChanged signal.
- Emits zoomHistoryUnavailable signal when stack is empty.
- """
- Debug.msg(4, "BufferedWindow.ZoomBack(): hist)=%s" % self.zoomhistory)
-
- zoom = list()
-
- if len(self.zoomhistory) > 1:
- self.zoomhistory.pop()
- zoom = self.zoomhistory[-1]
-
- if len(self.zoomhistory) < 2:
- self.zoomHistoryUnavailable.emit()
-
- # zoom to selected region
- self.Map.GetRegion(n = zoom[0], s = zoom[1],
- e = zoom[2], w = zoom[3],
- update = True)
- # update map
- self.UpdateMap()
-
- self.zoomChanged.emit()
-
- def ZoomHistory(self, n, s, e, w):
- """!Manages a list of last 10 zoom extents
-
- Emits zoomChanged signal.
- Emits zoomHistoryAvailable signal when stack is not empty.
- Emits zoomHistoryUnavailable signal when stack is empty.
-
- All methods which are changing zoom should call this method
- to make a record in the history. The signal zoomChanged will be
- then emitted automatically.
-
- @param n,s,e,w north, south, east, west
-
- @return removed history item if exists (or None)
- """
- removed = None
- self.zoomhistory.append((n,s,e,w))
-
- if len(self.zoomhistory) > 10:
- removed = self.zoomhistory.pop(0)
-
- if removed:
- Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
- (self.zoomhistory, removed))
- else:
- Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
- (self.zoomhistory))
-
- # update toolbar
- if len(self.zoomhistory) > 1:
- self.zoomHistoryAvailable.emit()
- else:
- self.zoomHistoryUnavailable.emit()
-
- self.zoomChanged.emit()
-
- return removed
-
- def InitZoomHistory(self):
- """Initializes zoom history.
-
- @todo First item is handled in some special way. Improve the
- documentation or fix the code.
-
- It does not emits any signals.
-
- This method can be possibly removed when the history will solve the
- fist item in different way or when GCP manager (and possibly others)
- will handle Map variable in the way that it will be prepared for
- MapWindow/BufferedWindow and thus usable to initialize history.
- """
- self.zoomhistory.append((self.Map.region['n'],
- self.Map.region['s'],
- self.Map.region['e'],
- self.Map.region['w']))
- Debug.msg(4, "BufferedWindow.InitZoomHistory(): hist=%s" %
- (self.zoomhistory))
-
- def ResetZoomHistory(self):
- """!Reset zoom history"""
- self.zoomhistory = list()
-
- def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
- """!Set display extents to match selected raster
- or vector map(s).
-
- @param layers list of layers to be zoom to
- @param ignoreNulls True to ignore null-values (valid only for rasters)
- @param render True to re-render display
- """
- if not layers:
- layers = self._giface.GetLayerList().GetSelectedLayers(checkedOnly=False)
- layers = [layer.maplayer for layer in layers]
-
- if not layers:
- return
-
- rast = []
- vect = []
- updated = False
- for l in layers:
- # only raster/vector layers are currently supported
- if l.type == 'raster':
- rast.append(l.GetName())
- elif l.type == 'vector':
- if hasattr(self, "digit") and \
- self.toolbar.GetLayer() == l:
- w, s, b, e, n, t = self.digit.GetDisplay().GetMapBoundingBox()
- self.Map.GetRegion(n = n, s = s, w = w, e = e,
- update = True)
- updated = True
- else:
- vect.append(l.name)
- elif l.type == 'rgb':
- for rname in l.GetName().splitlines():
- rast.append(rname)
-
- if not updated:
- self.Map.GetRegion(rast = rast,
- vect = vect,
- zoom = ignoreNulls,
- update = True)
-
- self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
- self.Map.region['e'], self.Map.region['w'])
-
- if render:
- self.UpdateMap()
-
- def ZoomToWind(self):
- """!Set display geometry to match computational region
- settings (set with g.region)
- """
- self.Map.region = self.Map.GetRegion()
-
- self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
- self.Map.region['e'], self.Map.region['w'])
-
- self.UpdateMap()
-
- def ZoomToDefault(self):
- """!Set display geometry to match default region settings
- """
- self.Map.region = self.Map.GetRegion(default = True)
- self.Map.AdjustRegion() # aling region extent to the display
-
- self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
- self.Map.region['e'], self.Map.region['w'])
-
- self.UpdateMap()
-
- def GoTo(self, e, n):
- region = self.Map.GetCurrentRegion()
-
- region['center_easting'], region['center_northing'] = e, n
-
- dn = (region['nsres'] * region['rows']) / 2.
- region['n'] = region['center_northing'] + dn
- region['s'] = region['center_northing'] - dn
- de = (region['ewres'] * region['cols']) / 2.
- region['e'] = region['center_easting'] + de
- region['w'] = region['center_easting'] - de
-
- self.Map.AdjustRegion()
-
- # add to zoom history
- self.ZoomHistory(region['n'], region['s'],
- region['e'], region['w'])
- self.UpdateMap()
-
- def DisplayToWind(self):
- """!Set computational region (WIND file) to match display
- extents
- """
- tmpreg = os.getenv("GRASS_REGION")
- if tmpreg:
- del os.environ["GRASS_REGION"]
-
- # We ONLY want to set extents here. Don't mess with resolution. Leave that
- # for user to set explicitly with g.region
- new = self.Map.AlignResolution()
- RunCommand('g.region',
- parent = self,
- overwrite = True,
- n = new['n'],
- s = new['s'],
- e = new['e'],
- w = new['w'],
- rows = int(new['rows']),
- cols = int(new['cols']))
-
- if tmpreg:
- os.environ["GRASS_REGION"] = tmpreg
-
- def ZoomToSaved(self):
- """!Set display geometry to match extents in
- saved region file
- """
- dlg = SavedRegion(parent = self,
- title = _("Zoom to saved region extents"),
- loadsave = 'load')
-
- if dlg.ShowModal() == wx.ID_CANCEL or not dlg.GetName():
- dlg.Destroy()
- return
-
- if not grass.find_file(name = dlg.GetName(), element = 'windows')['name']:
- wx.MessageBox(parent = self,
- message = _("Region <%s> not found. Operation canceled.") % dlg.GetName(),
- caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
- dlg.Destroy()
- return
-
- self.Map.GetRegion(regionName = dlg.GetName(),
- update = True)
-
- dlg.Destroy()
-
- self.ZoomHistory(self.Map.region['n'],
- self.Map.region['s'],
- self.Map.region['e'],
- self.Map.region['w'])
-
- self.UpdateMap()
-
- def SaveRegion(self, display = True):
- """!Save display extents/compulational region to named region
- file.
-
- @param display True for display extends otherwise computational region
- """
- if display:
- title = _("Save display extents to region file")
- else:
- title = _("Save computational region to region file")
-
- dlg = SavedRegion(parent = self, title = title, loadsave = 'save')
- if dlg.ShowModal() == wx.ID_CANCEL or not dlg.GetName():
- dlg.Destroy()
- return
-
- # test to see if it already exists and ask permission to overwrite
- if grass.find_file(name = dlg.GetName(), element = 'windows')['name']:
- overwrite = wx.MessageBox(parent = self,
- message = _("Region file <%s> already exists. "
- "Do you want to overwrite it?") % (dlg.GetName()),
- caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
- if overwrite != wx.YES:
- dlg.Destroy()
- return
-
- if display:
- self._saveDisplayRegion(dlg.GetName())
- else:
- self._saveCompRegion(dlg.GetName())
-
- dlg.Destroy()
-
- def _saveCompRegion(self, name):
- """!Save region settings to region file
-
- @param name region name
- """
- RunCommand('g.region',
- overwrite = True,
- parent = self,
- flags = 'u',
- save = name)
-
- def _saveDisplayRegion(self, name):
- """!Save display extents to region file
-
- @param name region name
- """
- new = self.Map.GetCurrentRegion()
-
- tmpreg = os.getenv("GRASS_REGION")
- if tmpreg:
- del os.environ["GRASS_REGION"]
-
- RunCommand('g.region',
- overwrite = True,
- parent = self,
- flags = 'u',
- n = new['n'],
- s = new['s'],
- e = new['e'],
- w = new['w'],
- rows = int(new['rows']),
- cols = int(new['cols']),
- save = name)
-
- if tmpreg:
- os.environ["GRASS_REGION"] = tmpreg
-
- def Distance(self, beginpt, endpt, screen = True):
- """!Calculates distance
-
- Ctypes required for LL-locations
-
- @param beginpt first point
- @param endpt second point
- @param screen True for screen coordinates otherwise EN
- """
- if screen:
- e1, n1 = self.Pixel2Cell(beginpt)
- e2, n2 = self.Pixel2Cell(endpt)
- else:
- e1, n1 = beginpt
- e2, n2 = endpt
-
- dEast = (e2 - e1)
- dNorth = (n2 - n1)
-
- if self.Map.projinfo['proj'] == 'll' and haveCtypes:
- dist = gislib.G_distance(e1, n1, e2, n2)
- else:
- dist = math.sqrt(math.pow((dEast), 2) + math.pow((dNorth), 2))
-
- return (dist, (dEast, dNorth))
-
- def GetMap(self):
- """!Get render.Map() instance"""
- return self.Map
-
- def RegisterGraphicsToDraw(self, graphicsType, setStatusFunc = None, drawFunc = None):
- """! This method registers graphics to draw.
-
- @param type (string) - graphics type: "point" or "line"
- @param setStatusFunc (function reference) - function called before drawing each item
- Status function should be in this form: setStatusFunc(item, itemOrderNum)
- item - passes instance of GraphicsSetItem which will be drawn
- itemOrderNum - number of item in drawing order (from O)
- Hidden items are also counted in drawing order.
- @param drawFunc (function reference) - defines own function for drawing
- If function is not defined DrawCross method is used for type "point"
- or DrawLines method for type "line".
-
- @return reference to GraphicsSet, which was added.
- """
- item = GraphicsSet(parentMapWin = self,
- graphicsType = graphicsType,
- setStatusFunc = setStatusFunc,
- drawFunc = drawFunc)
- self.graphicsSetList.append(item)
-
- return item
-
- def UnregisterGraphicsToDraw(self, item):
- """!Unregisteres GraphicsSet instance
-
- @param item (GraphicsSetItem) - item to unregister
-
- @return True - if item was unregistered
- @return False - if item was not found
- """
- if item in self.graphicsSetList:
- self.graphicsSetList.remove(item)
- return True
-
- return False
-
-class GraphicsSet:
- def __init__(self, parentMapWin, graphicsType, setStatusFunc = None, drawFunc = None):
- """!Class, which contains instances of GraphicsSetItem and
- draws them For description of parameters look at method
- RegisterGraphicsToDraw in BufferedWindow class.
- """
- self.pens = {
- "default" : wx.Pen(colour = wx.BLACK, width = 2, style = wx.SOLID),
- "selected" : wx.Pen(colour = wx.GREEN, width = 2, style = wx.SOLID),
- "unused" : wx.Pen(colour = wx.LIGHT_GREY, width = 2, style = wx.SOLID),
- "highest" : wx.Pen(colour = wx.RED, width = 2, style = wx.SOLID)
- }
-
- # list contains instances of GraphicsSetItem
- self.itemsList = []
-
- self.properties = {}
- self.graphicsType = graphicsType
- self.parentMapWin = parentMapWin
- self.setStatusFunc = setStatusFunc
-
- if drawFunc:
- self.drawFunc = drawFunc
-
- elif self.graphicsType == "point":
- self.properties["size"] = 5
-
- self.properties["text"] = {}
- self.properties["text"]['font'] = wx.Font(pointSize = self.properties["size"],
- family = wx.FONTFAMILY_DEFAULT,
- style = wx.FONTSTYLE_NORMAL,
- weight = wx.FONTWEIGHT_NORMAL)
- self.properties["text"]['active'] = True
-
- self.drawFunc = self.parentMapWin.DrawCross
-
- elif self.graphicsType == "line":
- self.drawFunc = self.parentMapWin.DrawLines
-
- def Draw(self, pdc):
- """!Draws all containing items.
-
- @param pdc - device context, where items are drawn
- """
- itemOrderNum = 0
- for item in self.itemsList:
- if self.setStatusFunc is not None:
- self.setStatusFunc(item, itemOrderNum)
-
- if item.GetPropertyVal("hide") == True:
- itemOrderNum += 1
- continue
-
- if self.graphicsType == "point":
- if item.GetPropertyVal("penName"):
- self.parentMapWin.pen = self.pens[item.GetPropertyVal("penName")]
- else:
- self.parentMapWin.pen = self.pens["default"]
-
- coords = self.parentMapWin.Cell2Pixel(item.GetCoords())
- size = self.properties["size"]
-
- self.properties["text"]['coords'] = [coords[0] + size, coords[1] + size, size, size]
- self.properties["text"]['color'] = self.parentMapWin.pen.GetColour()
- self.properties["text"]['text'] = item.GetPropertyVal("label")
-
- self.drawFunc(pdc = pdc,
- coords = coords,
- text = self.properties["text"],
- size = self.properties["size"])
-
- elif self.graphicsType == "line":
- if item.GetPropertyVal("penName"):
- self.parentMapWin.polypen = self.pens[item.GetPropertyVal("penName")]
- else:
- self.parentMapWin.polypen = self.pens["default"]
- coords = item.GetCoords()
-
- self.drawFunc(pdc = pdc,
- polycoords = coords)
- itemOrderNum += 1
-
- def AddItem(self, coords, penName = None, label = None, hide = False):
- """!Append item to the list.
-
- Added item is put to the last place in drawing order.
- Could be 'point' or 'line' according to graphicsType.
-
- @param coords - list of east, north coordinates (double) of item
- Example: point: [1023, 122]
- line: [[10, 12],[20,40],[23, 2334]]
- @param penName (string) - the 'default' pen is used if is not defined
- @param label (string) - label, which will be drawn with point. It is relavant just for 'point' type.
- @param hide (bool) - If it is True, the item is not drawn, when self.Draw is called.
- Hidden items are also counted in drawing order.
-
- @return (GraphicsSetItem) - added item reference
- """
- item = GraphicsSetItem(coords = coords, penName = penName, label = label, hide = hide)
- self.itemsList.append(item)
-
- return item
-
- def DeleteItem(self, item):
- """!Deletes item
-
- @param item (GraphicsSetItem) - item to remove
-
- @return True if item was removed
- @return False if item was not found
- """
- try:
- self.itemsList.remove(item)
- except ValueError:
- return False
-
- return True
-
- def GetAllItems(self):
- """!Returns list of all containing instances of GraphicsSetItem, in order
- as they are drawn. If you want to change order of drawing use: SetItemDrawOrder method.
- """
- # user can edit objects but not order in list, that is reason,
- # why is returned shallow copy of data list it should be used
- # SetItemDrawOrder for changing order
- return copy(self.itemsList)
-
- def GetItem(self, drawNum):
- """!Get given item from the list.
-
- @param drawNum (int) - drawing order (index) number of item
-
- @return instance of GraphicsSetItem which is drawn in drawNum order
- @return False if drawNum was out of range
- """
- if drawNum < len(self.itemsList) and drawNum >= 0:
- return self.itemsList[drawNum]
- else:
- return False
-
- def SetPropertyVal(self, propName, propVal):
- """!Set property value
-
- @param propName (string) - property name: "size", "text"
- - both properties are relevant for "point" type
- @param propVal - property value to be set
-
- @return True - if value was set
- @return False - if propName is not "size" or "text" or type is "line"
- """
- if self.properties.has_key(propName):
- self.properties[propName] = propVal
- return True
-
- return False
-
- def GetPropertyVal(self, propName):
- """!Get property value
-
- Raises KeyError if propName is not "size" or "text" or type is
- "line"
-
- @param propName (string) - property name: "size", "text"
- - both properties are relevant for "point" type
-
- @return value of property
- """
- if self.properties.has_key(propName):
- return self.properties[propName]
-
- raise KeyError(_("Property does not exist: %s") % (propName))
-
- def AddPen(self, penName, pen):
- """!Add pen
-
- @param penName (string) - name of added pen
- @param pen (wx.Pen) - added pen
-
- @return True - if pen was added
- @return False - if pen already exists
- """
- if self.pens.has_key(penName):
- return False
-
- self.pens[penName] = pen
- return True
-
- def GetPen(self, penName):
- """!Get existing pen
-
- @param penName (string) - name of pen
-
- @return wx.Pen reference if is found
- @return None if penName was not found
- """
- if self.pens.has_key(penName):
- return self.pens[penName]
-
- return None
-
- def SetItemDrawOrder(self, item, drawNum):
- """!Set draw order for item
-
- @param item (GraphicsSetItem)
- @param drawNum (int) - drawing order of item to be set
-
- @return True - if order was changed
- @return False - if drawNum is out of range or item was not found
- """
- if drawNum < len(self.itemsList) and drawNum >= 0 and \
- item in self.itemsList:
- self.itemsList.insert(drawNum, self.itemsList.pop(self.itemsList.index(item)))
- return True
-
- return False
-
- def GetItemDrawOrder(self, item):
- """!Get draw order for given item
-
- @param item (GraphicsSetItem)
-
- @return (int) - drawing order of item
- @return None - if item was not found
- """
- try:
- return self.itemsList.index(item)
- except ValueError:
- return None
-
-class GraphicsSetItem:
- def __init__(self, coords, penName = None, label = None, hide = False):
- """!Could be point or line according to graphicsType in
- GraphicsSet class
-
- @param coords - list of coordinates (double) of item
- Example: point: [1023, 122]
- line: [[10, 12],[20,40],[23, 2334]]
- @param penName (string) - if it is not defined 'default' pen is used
- @param label (string) - label, which will be drawn with point. It is relevant just for 'point' type
- @param hide (bool) - if it is True, item is not drawn
- Hidden items are also counted in drawing order in GraphicsSet class.
- """
- self.coords = coords
-
- self.properties = { "penName" : penName,
- "hide" : hide,
- "label" : label }
-
- def SetPropertyVal(self, propName, propVal):
- """!Set property value
-
- @param propName (string) - property name: "penName", "hide" or "label"
- - property "label" is relevant just for 'point' type
- @param propVal - property value to be set
-
- @return True - if value was set
- @return False - if propName is not "penName", "hide" or "label"
- """
- if self.properties.has_key(propName):
- self.properties[propName] = propVal
- return True
-
- return False
-
- def GetPropertyVal(self, propName):
- """!Get property value
-
- Raises KeyError if propName is not "penName", "hide" or
- "label".
-
- @param propName (string) - property name: "penName", "hide" or "label"
- - property "label" is relevant just for 'point' type
-
- @return value of property
- """
- if self.properties.has_key(propName):
- return self.properties[propName]
-
- raise KeyError(_("Property does not exist: %s") % (propName))
-
- def SetCoords(self, coords):
- """!Set coordinates of item
-
- @param coords - list of east, north coordinates (double) of item
- Example: point: [1023, 122]
- line: [[10, 12],[20,40],[23, 2334]]
- """
- self.coords = coords
-
- def GetCoords(self):
- """!Get item coordinates
-
- @returns coordinates
- """
- return self.coords
Deleted: grass/trunk/gui/wxpython/mapdisp/overlays.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/overlays.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapdisp/overlays.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -1,156 +0,0 @@
-"""!
- at package mapdisp.overlays
-
- at brief Map display overlays - barscale and legend
-
-Classes:
- - overlays::OverlayController
- - overlays::BarscaleController
- - overlays::LegendController
-
-(C) 2006-2013 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.
-
- at author Anna Kratochvilova <kratochanna gmail.com>
-"""
-
-class OverlayController(object):
- """!Base class for decorations (barscale, legend) controller."""
- def __init__(self, renderer):
- self._renderer = renderer
- self._overlay = None
- self._coords = [0, 0]
- self._pdcType = 'image'
- self._propwin = None
- self._defaultAt = ''
- self._cmd = None # to be set by user
- self._name = None # to be defined by subclass
- self._id = None # to be defined by subclass
-
- def SetCmd(self, cmd):
- hasAt = False
- for i in cmd:
- if i.startswith("at="):
- hasAt = True
- break
- if not hasAt:
- cmd.append(self._defaultAt)
- self._cmd = cmd
-
- def GetCmd(self):
- return self._cmd
-
- cmd = property(fset = SetCmd, fget = GetCmd)
-
- def SetCoords(self, coords):
- self._coords = list(coords)
-
- def GetCoords(self):
- return self._coords
-
- coords = property(fset = SetCoords, fget = GetCoords)
-
- def GetPdcType(self):
- return self._pdcType
-
- pdcType = property(fget = GetPdcType)
-
- def GetName(self):
- return self._name
-
- name = property(fget = GetName)
-
- def GetId(self):
- return self._id
-
- id = property(fget = GetId)
-
- def GetPropwin(self):
- return self._propwin
-
- def SetPropwin(self, win):
- self._propwin = win
-
- propwin = property(fget = GetPropwin, fset = SetPropwin)
-
- def GetLayer(self):
- return self._overlay
-
- layer = property(fget = GetLayer)
-
- def IsShown(self):
- if self._overlay and self._overlay.IsActive():
- return True
- return False
-
- def Show(self, show = True):
- """!Activate or deactivate overlay."""
- if show:
- if not self._overlay:
- self._add()
- self._overlay.SetActive(True)
- self._update()
- else:
- self.Hide()
-
- def Hide(self):
- if self._overlay:
- self._overlay.SetActive(False)
-
- def _add(self):
- self._overlay = self._renderer.AddOverlay(id = self._id, ltype = self._name,
- command = self.cmd, active = False,
- render = False, hidden = True)
- # check if successful
-
- def _update(self):
- self._renderer.ChangeOverlay(id = self._id, command = self._cmd,
- render = False)
-
-
-class BarscaleController(OverlayController):
- def __init__(self, renderer):
- OverlayController.__init__(self, renderer)
- self._id = 0
- self._name = 'barscale'
- self._defaultAt = 'at=0,95'
- self._cmd = ['d.barscale', self._defaultAt]
-
-
-class LegendController(OverlayController):
- def __init__(self, renderer):
- OverlayController.__init__(self, renderer)
- self._id = 1
- self._name = 'legend'
- # TODO: synchronize with d.legend?
- self._defaultAt = 'at=5,50,2,5'
- self._cmd = ['d.legend', self._defaultAt]
-
- def ResizeLegend(self, begin, end, screenSize):
- """!Resize legend according to given bbox coordinates."""
- w = abs(begin[0] - end[0])
- h = abs(begin[1] - end[1])
- if begin[0] < end[0]:
- x = begin[0]
- else:
- x = end[0]
- if begin[1] < end[1]:
- y = begin[1]
- else:
- y = end[1]
-
- at = [(screenSize[1] - (y + h)) / float(screenSize[1]) * 100,
- (screenSize[1] - y) / float(screenSize[1]) * 100,
- x / float(screenSize[0]) * 100,
- (x + w) / float(screenSize[0]) * 100]
- atStr = "at=%d,%d,%d,%d" % (at[0], at[1], at[2], at[3])
-
- for i, subcmd in enumerate(self._cmd):
- if subcmd.startswith('at='):
- self._cmd[i] = atStr
- break
-
- self._coords = [0, 0]
- self.Show()
Modified: grass/trunk/gui/wxpython/mapdisp/test_mapdisp.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/test_mapdisp.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapdisp/test_mapdisp.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -66,9 +66,8 @@
from core.settings import UserSettings
from core.globalvar import CheckWxVersion
from core.giface import StandaloneGrassInterface
-from gui_core.mapwindow import MapWindowProperties
-from mapdisp.mapwindow import BufferedWindow
-from mapdisp.frame import MapFrame
+from mapwin.base import MapWindowProperties
+from mapwin.buffered import BufferedMapWindow
from core.render import Map
@@ -174,14 +173,16 @@
mapWindowProperties.setValuesFromUserSettings()
width, height = self.frame.GetClientSize()
copyOfInitMap(map_, width, height)
- window = BufferedWindow(parent=panel, giface=giface, Map=map_,
- properties=mapWindowProperties)
+ window = BufferedMapWindow(parent=panel, giface=giface, Map=map_,
+ properties=mapWindowProperties)
sizer.Add(item=window, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
panel.SetSizer(sizer)
panel.Layout()
self.frame.Show()
def testMapDisplay(self, giface, map_):
+ from mapdisp.frame import MapFrame
+
# known issues (should be similar with d.mon):
# * opening map in digitizer ends with: vdigit/toolbars.py:723: 'selection' referenced before assignment
# * nviz start fails (closes window? segfaults?) after mapdisp/frame.py:306: 'NoneType' object has no attribute 'GetLayerNotebook'
@@ -204,8 +205,8 @@
width, height = self.frame.GetClientSize()
copyOfInitMap(map_, width, height)
- window = BufferedWindow(parent=panel, giface=giface, Map=map_,
- properties=mapWindowProperties)
+ window = BufferedMapWindow(parent=panel, giface=giface, Map=map_,
+ properties=mapWindowProperties)
giface.mapWindow = window
@@ -234,8 +235,8 @@
width, height = self.frame.GetClientSize()
copyOfInitMap(map_, width, height)
- window = BufferedWindow(parent=panel, giface=giface, Map=map_,
- properties=mapWindowProperties)
+ window = BufferedMapWindow(parent=panel, giface=giface, Map=map_,
+ properties=mapWindowProperties)
giface.mapWindow = window
@@ -249,7 +250,7 @@
self.frame.Show()
- from mapdisp.analysis import MeasureDistanceController
+ from mapwin.analysis import MeasureDistanceController
self.controller = MeasureDistanceController(giface, window)
self.controller.Start()
@@ -265,8 +266,8 @@
width, height = self.frame.GetClientSize()
copyOfInitMap(map_, width, height)
- window = BufferedWindow(parent=panel, giface=giface, Map=map_,
- properties=mapWindowProperties)
+ window = BufferedMapWindow(parent=panel, giface=giface, Map=map_,
+ properties=mapWindowProperties)
giface.mapWindow = window
@@ -280,7 +281,7 @@
self.frame.Show()
- from mapdisp.analysis import ProfileController
+ from mapwin.analysis import ProfileController
self.controller = ProfileController(giface, window)
self.controller.Start()
Modified: grass/trunk/gui/wxpython/mapswipe/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapswipe/frame.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapswipe/frame.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -23,7 +23,7 @@
from gui_core.mapdisp import DoubleMapFrame
from gui_core.dialogs import GetImageHandlers
-from gui_core.mapwindow import MapWindowProperties
+from mapwin.base import MapWindowProperties
from core.render import Map
from mapdisp import statusbar as sb
from core.debug import Debug
Modified: grass/trunk/gui/wxpython/mapswipe/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/mapswipe/mapwindow.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/mapswipe/mapwindow.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -21,7 +21,7 @@
from core.debug import Debug
from core.utils import _
-from mapdisp.mapwindow import BufferedWindow
+from mapwin.buffered import BufferedMapWindow
EVT_MY_MOUSE_EVENTS = wx.NewEventType()
@@ -30,15 +30,15 @@
EVT_MOTION = wx.PyEventBinder(EVT_MY_MOTION)
-class SwipeBufferedWindow(BufferedWindow):
+class SwipeBufferedWindow(BufferedMapWindow):
"""!A subclass of BufferedWindow class.
Enables to draw the image translated.
Special mouse events with changed coordinates are used.
"""
def __init__(self, parent, giface, Map, properties, **kwargs):
- BufferedWindow.__init__(self, parent=parent, giface=giface, Map=Map,
- properties=properties, **kwargs)
+ BufferedMapWindow.__init__(self, parent=parent, giface=giface, Map=Map,
+ properties=properties, **kwargs)
Debug.msg(2, "SwipeBufferedWindow.__init__()")
self.specialSize = super(SwipeBufferedWindow, self).GetClientSize()
Added: grass/trunk/gui/wxpython/mapwin/__init__.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/__init__.py (rev 0)
+++ grass/trunk/gui/wxpython/mapwin/__init__.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -0,0 +1,7 @@
+all = [
+ 'graphics',
+ 'buffered',
+ 'analysis',
+ 'decorations',
+ 'base',
+ ]
Property changes on: grass/trunk/gui/wxpython/mapwin/__init__.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/mapwin/analysis.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/analysis.py (rev 0)
+++ grass/trunk/gui/wxpython/mapwin/analysis.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -0,0 +1,284 @@
+# -*- coding: utf-8 -*-
+"""!
+ at package mapwin.analysis
+
+ at brief Map display controllers for analyses (profiling, measuring)
+
+Classes:
+ - analysis::AnalysisControllerBase
+ - analysis::ProfileController
+ - analysis::MeasureDistanceController
+
+(C) 2013 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.
+
+ at author Anna Petrasova <kratochanna gmail.com>
+"""
+
+import os
+import math
+import wx
+
+from core.utils import _
+import core.units as units
+
+from grass.pydispatch.signal import Signal
+
+
+class AnalysisControllerBase:
+ """!Base class for analysis which require drawing line in map display."""
+ def __init__(self, giface, mapWindow):
+ """!
+
+ @param giface grass interface
+ @param mapWindow instance of BufferedMapWindow
+ """
+ self._giface = giface
+ self._mapWindow = mapWindow
+
+ self._registeredGraphics = None
+
+ self._oldMouseUse = None
+ self._oldCursor = None
+
+ def IsActive(self):
+ """!Returns True if analysis mode is activated."""
+ return bool(self._registeredGraphics)
+
+ def _start(self, x, y):
+ """!Handles the actual start of drawing line
+ and adding each new point.
+
+ @param x,y east north coordinates
+ """
+ if not self._registeredGraphics.GetAllItems():
+ item = self._registeredGraphics.AddItem(coords=[[x, y]])
+ item.SetPropertyVal('penName', 'analysisPen')
+ else:
+ # needed to switch mouse begin and end to draw intermediate line properly
+ coords = self._registeredGraphics.GetItem(0).GetCoords()[-1]
+ self._mapWindow.mouse['begin'] = self._mapWindow.Cell2Pixel(coords)
+
+ def _addPoint(self, x, y):
+ """!New point added.
+
+ @param x,y east north coordinates
+ """
+ # add new point and calculate distance
+ item = self._registeredGraphics.GetItem(0)
+ coords = item.GetCoords() + [[x, y]]
+ item.SetCoords(coords)
+ # draw
+ self._mapWindow.ClearLines()
+ self._registeredGraphics.Draw(pdc=self._mapWindow.pdcTmp)
+ wx.Yield()
+
+ self._doAnalysis(coords)
+
+ def _doAnalysis(self, coords):
+ """!Perform the required analysis
+ (compute distnace, update profile)
+
+ @param coords EN coordinates
+ """
+ raise NotImplementedError()
+
+ def _disconnectAll(self):
+ """!Disconnect all mouse signals
+ to stop drawing."""
+ raise NotImplementedError()
+
+ def _connectAll(self):
+ """!Connect all mouse signals to draw."""
+ raise NotImplementedError()
+
+ def _getPen(self):
+ """!Returns wx.Pen instance."""
+ raise NotImplementedError()
+
+ def Stop(self, restore=True):
+ """!Analysis mode is stopped.
+
+ @param restore if restore previous cursor, mouse['use']
+ """
+ self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
+ self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
+ # disconnect mouse events
+ self._disconnectAll()
+ # unregister
+ self._mapWindow.UnregisterGraphicsToDraw(self._registeredGraphics)
+ self._registeredGraphics = None
+ self._mapWindow.Refresh()
+
+ if restore:
+ # restore mouse['use'] and cursor to the state before measuring starts
+ self._mapWindow.SetNamedCursor(self._oldCursor)
+ self._mapWindow.mouse['use'] = self._oldMouseUse
+
+ def Start(self):
+ """!Init analysis: register graphics to map window,
+ connect required mouse signals.
+ """
+ self._oldMouseUse = self._mapWindow.mouse['use']
+ self._oldCursor = self._mapWindow.GetNamedCursor()
+
+ self._registeredGraphics = self._mapWindow.RegisterGraphicsToDraw(graphicsType='line')
+
+ self._connectAll()
+
+ # change mouse['box'] and pen to draw line during dragging
+ # TODO: better solution for drawing this line
+ self._mapWindow.mouse['use'] = None
+ self._mapWindow.mouse['box'] = "line"
+ self._mapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
+
+ self._registeredGraphics.AddPen('analysisPen', self._getPen())
+
+ # change the cursor
+ self._mapWindow.SetNamedCursor('pencil')
+
+
+class ProfileController(AnalysisControllerBase):
+ """!Class controls profiling in map display.
+ It should be used inside ProfileFrame
+ """
+ def __init__(self, giface, mapWindow):
+ AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)
+
+ self.transectChanged = Signal('ProfileController.transectChanged')
+
+ def _doAnalysis(self, coords):
+ """!Informs profile dialog that profile changed.
+
+ @param coords EN coordinates
+ """
+ self.transectChanged.emit(coords=coords)
+
+ def _disconnectAll(self):
+ self._mapWindow.mouseLeftDown.disconnect(self._start)
+ self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
+
+ def _connectAll(self):
+ self._mapWindow.mouseLeftDown.connect(self._start)
+ self._mapWindow.mouseLeftUp.connect(self._addPoint)
+
+ def _getPen(self):
+ return wx.Pen(colour=wx.Colour(0, 100, 0), width=2, style=wx.SHORT_DASH)
+
+ def Stop(self, restore=True):
+ AnalysisControllerBase.Stop(self, restore=restore)
+
+ self.transectChanged.emit(coords=[])
+
+
+class MeasureDistanceController(AnalysisControllerBase):
+ """!Class controls measuring distance in map display."""
+ def __init__(self, giface, mapWindow):
+ AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)
+
+ self._projInfo = self._mapWindow.Map.projinfo
+ self._totaldist = 0.0 # total measured distance
+ self._useCtypes = False
+
+ def _doAnalysis(self, coords):
+ """!New point added.
+
+ @param x,y east north coordinates
+ """
+ self.MeasureDist(coords[-2], coords[-1])
+
+ def _disconnectAll(self):
+ self._mapWindow.mouseLeftDown.disconnect(self._start)
+ self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
+ self._mapWindow.mouseDClick.disconnect(self.Stop)
+
+ def _connectAll(self):
+ self._mapWindow.mouseLeftDown.connect(self._start)
+ self._mapWindow.mouseLeftUp.connect(self._addPoint)
+ self._mapWindow.mouseDClick.connect(self.Stop)
+
+ def _getPen(self):
+ return wx.Pen(colour='green', width=2, style=wx.SHORT_DASH)
+
+ def Stop(self, restore=True):
+ AnalysisControllerBase.Stop(self, restore=restore)
+
+ self._giface.WriteCmdLog(_('Measuring finished'))
+
+ def Start(self):
+ """!Init measurement routine that calculates map distance
+ along transect drawn on map display
+ """
+ AnalysisControllerBase.Start(self)
+ self._totaldist = 0.0 # total measured distance
+
+ # initiating output (and write a message)
+ # e.g., in Layer Manager switch to output console
+ # TODO: this should be something like: write important message or write tip
+ # TODO: mixed 'switching' and message? no, measuring handles 'swithing' on its own
+ self._giface.WriteWarning(_('Click and drag with left mouse button '
+ 'to measure.%s'
+ 'Double click with left button to clear.') % \
+ (os.linesep))
+ if self._projInfo['proj'] != 'xy':
+ mapunits = self._projInfo['units']
+ self._giface.WriteCmdLog(_('Measuring distance') + ' ('
+ + mapunits + '):')
+ else:
+ self._giface.WriteCmdLog(_('Measuring distance:'))
+
+ if self._projInfo['proj'] == 'll':
+ try:
+ import grass.lib.gis as gislib
+ gislib.G_begin_distance_calculations()
+ self._useCtypes = True
+ except ImportError, e:
+ self._giface.WriteWarning(_('Geodesic distance calculation '
+ 'is not available.\n'
+ 'Reason: %s' % e))
+
+ def MeasureDist(self, beginpt, endpt):
+ """!Calculate distance and print to output window.
+
+ @param beginpt,endpt EN coordinates
+ """
+ # move also Distance method?
+ dist, (north, east) = self._mapWindow.Distance(beginpt, endpt, screen=False)
+
+ dist = round(dist, 3)
+ mapunits = self._projInfo['units']
+ if mapunits == 'degrees' and self._useCtypes:
+ mapunits = 'meters'
+ d, dunits = units.formatDist(dist, mapunits)
+
+ self._totaldist += dist
+ td, tdunits = units.formatDist(self._totaldist,
+ mapunits)
+
+ strdist = str(d)
+ strtotdist = str(td)
+
+ if self._projInfo['proj'] == 'xy' or 'degree' not in self._projInfo['unit']:
+ angle = int(math.degrees(math.atan2(north, east)) + 0.5)
+ # uncomment below (or flip order of atan2(y,x) above) to use
+ # the mathematical theta convention (CCW from +x axis)
+ #angle = 90 - angle
+ if angle < 0:
+ angle = 360 + angle
+
+ mstring = '%s = %s %s\n%s = %s %s\n%s = %d %s\n%s' \
+ % (_('segment'), strdist, dunits,
+ _('total distance'), strtotdist, tdunits,
+ _('bearing'), angle, _('degrees (clockwise from grid-north)'),
+ '-' * 60)
+ else:
+ mstring = '%s = %s %s\n%s = %s %s\n%s' \
+ % (_('segment'), strdist, dunits,
+ _('total distance'), strtotdist, tdunits,
+ '-' * 60)
+
+ self._giface.WriteLog(mstring, priority=2)
+
+ return dist
Property changes on: grass/trunk/gui/wxpython/mapwin/analysis.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/mapwin/base.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/base.py (rev 0)
+++ grass/trunk/gui/wxpython/mapwin/base.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -0,0 +1,357 @@
+"""!
+ at package mapwin.mapwindow
+
+ at brief Map display canvas basic functionality - base class and properties.
+
+Classes:
+ - mapwindow::MapWindowProperties
+ - mapwindow::MapWindowBase
+
+(C) 2006-2012 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Vaclav Petras <wenzeslaus gmail.com> (handlers support)
+ at author Stepan Turek <stepan.turek seznam.cz> (handlers support)
+"""
+
+import wx
+
+from core.settings import UserSettings
+from core.gcmd import GError
+from core.utils import _
+
+from grass.script import core as grass
+from grass.pydispatch.signal import Signal
+
+
+class MapWindowProperties(object):
+ def __init__(self):
+ self._resolution = None
+ self.resolutionChanged = Signal('MapWindowProperties.resolutionChanged')
+ self._autoRender = None
+ self.autoRenderChanged = Signal('MapWindowProperties.autoRenderChanged')
+ self._showRegion = None
+ self.showRegionChanged = Signal('MapWindowProperties.showRegionChanged')
+ self._alignExtent = None
+ self.alignExtentChanged = Signal('MapWindowProperties.alignExtentChanged')
+
+ def setValuesFromUserSettings(self):
+ """Convenient function to get values from user settings into this object."""
+ self._resolution = UserSettings.Get(group='display',
+ key='compResolution',
+ subkey='enabled')
+ self._autoRender = UserSettings.Get(group='display',
+ key='autoRendering',
+ subkey='enabled')
+ self._showRegion = False # in statusbar.py was not from settings
+ self._alignExtent = UserSettings.Get(group='display',
+ key='alignExtent',
+ subkey='enabled')
+ @property
+ def resolution(self):
+ return self._resolution
+
+ @resolution.setter
+ def resolution(self, value):
+ if value != self._resolution:
+ self._resolution = value
+ self.resolutionChanged.emit(value=value)
+
+ @property
+ def autoRender(self):
+ return self._autoRender
+
+ @autoRender.setter
+ def autoRender(self, value):
+ if value != self._autoRender:
+ self._autoRender = value
+ self.autoRenderChanged.emit(value=value)
+
+ @property
+ def showRegion(self):
+ return self._showRegion
+
+ @showRegion.setter
+ def showRegion(self, value):
+ if value != self._showRegion:
+ self._showRegion = value
+ self.showRegionChanged.emit(value=value)
+
+ @property
+ def alignExtent(self):
+ return self._alignExtent
+
+ @alignExtent.setter
+ def alignExtent(self, value):
+ if value != self._alignExtent:
+ self._alignExtent = value
+ self.alignExtentChanged.emit(value=value)
+
+
+class MapWindowBase(object):
+ """!Abstract map display window class
+
+ Superclass for BufferedWindow class (2D display mode), and GLWindow
+ (3D display mode).
+
+ Subclasses have to define
+ - _bindMouseEvents method which binds MouseEvent handlers
+ - Pixel2Cell
+ - Cell2Pixel (if it is possible)
+ """
+ def __init__(self, parent, giface, Map):
+ self.parent = parent
+ self.Map = Map
+ self._giface = giface
+
+ # Emitted when someone registers as mouse event handler
+ self.mouseHandlerRegistered = Signal('MapWindow.mouseHandlerRegistered')
+ # Emitted when mouse event handler is unregistered
+ self.mouseHandlerUnregistered = Signal('MapWindow.mouseHandlerUnregistered')
+ # emitted after double click in pointer mode on legend, text, scalebar
+ self.overlayActivated = Signal('MapWindow.overlayActivated')
+
+ # mouse attributes -- position on the screen, begin and end of
+ # dragging, and type of drawing
+ self.mouse = {
+ 'begin': [0, 0], # screen coordinates
+ 'end' : [0, 0],
+ 'use' : "pointer",
+ 'box' : "point"
+ }
+ # last east, north coordinates, changes on mouse motion
+ self.lastEN = None
+
+ # stores overridden cursor
+ self._overriddenCursor = None
+
+ # dictionary where event types are stored as keys and lists of
+ # handlers for these types as values
+ self.handlersContainer = {
+ wx.EVT_LEFT_DOWN : [],
+ wx.EVT_LEFT_UP : [],
+ wx.EVT_LEFT_DCLICK : [],
+ wx.EVT_MIDDLE_DOWN : [],
+ wx.EVT_MIDDLE_UP : [],
+ wx.EVT_MIDDLE_DCLICK : [],
+ wx.EVT_RIGHT_DOWN : [],
+ wx.EVT_RIGHT_UP : [],
+ wx.EVT_RIGHT_DCLICK : [],
+ wx.EVT_MOTION : [],
+ wx.EVT_ENTER_WINDOW : [],
+ wx.EVT_LEAVE_WINDOW : [],
+ wx.EVT_MOUSEWHEEL : [],
+ wx.EVT_MOUSE_EVENTS : []
+ }
+
+ # available cursors
+ self._cursors = {
+ "default": wx.StockCursor(wx.CURSOR_ARROW),
+ "cross": wx.StockCursor(wx.CURSOR_CROSS),
+ "hand": wx.StockCursor(wx.CURSOR_HAND),
+ "pencil": wx.StockCursor(wx.CURSOR_PENCIL),
+ "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
+ }
+
+ # default cursor for window is arrow (at least we rely on it here)
+ # but we need to define attribute here
+ # cannot call SetNamedCursor since it expects the instance
+ # to be a wx window, so setting only the attribute
+ self._cursor = 'default'
+
+ wx.CallAfter(self.InitBinding)
+
+ def __del__(self):
+ self.UnregisterAllHandlers()
+
+ def InitBinding(self):
+ """!Binds helper functions, which calls all handlers
+ registered to events with the events
+ """
+ for ev, handlers in self.handlersContainer.iteritems():
+ self.Bind(ev, self.EventTypeHandler(handlers))
+
+ def EventTypeHandler(self, evHandlers):
+ return lambda event:self.HandlersCaller(event, evHandlers)
+
+ def HandlersCaller(self, event, handlers):
+ """!Hepler function which calls all handlers registered for
+ event
+ """
+ for handler in handlers:
+ try:
+ handler(event)
+ except:
+ handlers.remove(handler)
+ GError(parent = self,
+ message=_("Error occured during calling of handler: %s \n"
+ "Handler was unregistered.") % handler.__name__)
+
+ event.Skip()
+
+ def RegisterMouseEventHandler(self, event, handler, cursor = None):
+ """!Binds event handler
+
+ @depreciated This method is depreciated. Use Signals or drawing API instead.
+ Signals do not cover all events but new Signals can be added when needed
+ consider also adding generic signal. However, more interesing and useful
+ is higher level API to create objects, graphics etc.
+
+ Call event.Skip() in handler to allow default processing in MapWindow.
+
+ If any error occures inside of handler, the handler is removed.
+
+ Before handler is unregistered it is called with
+ string value "unregistered" of event parameter.
+
+ @code
+ # your class methods
+ def OnButton(self, event):
+ # current map display's map window
+ # expects LayerManager to be the parent
+ self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
+ if self.mapwin.RegisterEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
+ wx.StockCursor(wx.CURSOR_CROSS)):
+ self.parent.GetLayerTree().GetMapDisplay().Raise()
+ else:
+ # handle that you cannot get coordinates
+
+ def OnMouseAction(self, event):
+ # get real world coordinates of mouse click
+ coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
+ self.text.SetLabel('Coor: ' + str(coor))
+ self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction)
+ event.Skip()
+ @endcode
+
+ Emits mouseHandlerRegistered signal before handler is registered.
+
+ @param event one of mouse events
+ @param handler function to handle event
+ @param cursor cursor which temporary overrides current cursor
+
+ @return True if successful
+ @return False if event cannot be bind
+ """
+ self.mouseHandlerRegistered.emit()
+ # inserts handler into list
+ for containerEv, handlers in self.handlersContainer.iteritems():
+ if event == containerEv:
+ handlers.append(handler)
+
+ self.mouse['useBeforeGenericEvent'] = self.mouse['use']
+ self.mouse['use'] = 'genericEvent'
+
+ if cursor:
+ self._overriddenCursor = self.GetNamedCursor()
+ self.SetNamedCursor(cursor)
+
+ return True
+
+ def UnregisterAllHandlers(self):
+ """!Unregisters all registered handlers
+
+ @depreciated This method is depreciated. Use Signals or drawing API instead.
+
+ Before each handler is unregistered it is called with string
+ value "unregistered" of event parameter.
+ """
+ for containerEv, handlers in self.handlersContainer.iteritems():
+ for handler in handlers:
+ try:
+ handler("unregistered")
+ handlers.remove(handler)
+ except:
+ GError(parent = self,
+ message = _("Error occured during unregistration of handler: %s \n \
+ Handler was unregistered.") % handler.__name__)
+ handlers.remove(handler)
+
+ def UnregisterMouseEventHandler(self, event, handler):
+ """!Unbinds event handler for event
+
+ @depreciated This method is depreciated. Use Signals or drawing API instead.
+
+ Before handler is unregistered it is called with string value
+ "unregistered" of event parameter.
+
+ Emits mouseHandlerUnregistered signal after handler is unregistered.
+
+ @param handler handler to unbind
+ @param event event from which handler will be unbinded
+
+ @return True if successful
+ @return False if event cannot be unbind
+ """
+ # removes handler from list
+ for containerEv, handlers in self.handlersContainer.iteritems():
+ if event != containerEv:
+ continue
+ try:
+ handler("unregistered")
+ if handler in handlers:
+ handlers.remove(handler)
+ else:
+ grass.warning(_("Handler: %s was not registered") \
+ % handler.__name__)
+ except:
+ GError(parent = self,
+ message = _("Error occured during unregistration of handler: %s \n \
+ Handler was unregistered") % handler.__name__)
+ handlers.remove(handler)
+
+ # restore mouse use (previous state)
+ self.mouse['use'] = self.mouse['useBeforeGenericEvent']
+
+ # restore overridden cursor
+ if self._overriddenCursor:
+ self.SetNamedCursor(self._overriddenCursor)
+
+ self.mouseHandlerUnregistered.emit()
+ return True
+
+ def Pixel2Cell(self, xyCoords):
+ raise NotImplementedError()
+
+ def Cell2Pixel(self, enCoords):
+ raise NotImplementedError()
+
+ def OnMotion(self, event):
+ """!Tracks mouse motion and update statusbar
+
+ @todo remove this method when lastEN is not used
+
+ @see GetLastEN
+ """
+ try:
+ self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
+ except (ValueError):
+ self.lastEN = None
+
+ event.Skip()
+
+ def GetLastEN(self):
+ """!Returns last coordinates of mouse cursor.
+
+ @depreciated This method is depreciated. Use Signal with coordinates as parameters.
+
+ @see OnMotion
+ """
+ return self.lastEN
+
+ def SetNamedCursor(self, cursorName):
+ """!Sets cursor defined by name."""
+ cursor = self._cursors[cursorName]
+ self.SetCursor(cursor)
+ self._cursor = cursorName
+
+ def GetNamedCursor(self):
+ """!Returns current cursor name."""
+ return self._cursor
+
+ cursor = property(fget=GetNamedCursor, fset=SetNamedCursor)
Property changes on: grass/trunk/gui/wxpython/mapwin/base.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/mapwin/buffered.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/buffered.py (rev 0)
+++ grass/trunk/gui/wxpython/mapwin/buffered.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -0,0 +1,1791 @@
+"""!
+ at package mapwin.mapwindow
+
+ at brief Map display canvas - buffered window.
+
+Classes:
+ - mapwindow::BufferedWindow
+ - mapwindow::GraphicsSet
+ - mapwindow::GraphicsSetItem
+
+(C) 2006-2013 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Stepan Turek <stepan.turek seznam.cz> (handlers support, GraphicsSet)
+ at author Anna Petrasova <kratochanna gmail.com> (refactoring)
+ at author Vaclav Petras <wenzeslaus gmail.com> (refactoring)
+"""
+
+import os
+import time
+import math
+import sys
+from copy import copy
+
+import wx
+
+from grass.pydispatch.signal import Signal
+
+import grass.script as grass
+
+from gui_core.dialogs import SavedRegion
+from core.gcmd import RunCommand, GException, GError, GMessage
+from core.debug import Debug
+from core.settings import UserSettings
+from mapwin.base import MapWindowBase
+from core.utils import GetGEventAttribsForHandler, _
+import core.utils as utils
+from mapwin.graphics import GraphicsSet
+
+try:
+ import grass.lib.gis as gislib
+ haveCtypes = True
+except ImportError:
+ haveCtypes = False
+
+class BufferedMapWindow(MapWindowBase, wx.Window):
+ """!A Buffered window class (2D view mode)
+
+ Superclass for VDigitWindow (vector digitizer).
+
+ When the drawing needs to change, you app needs to call the
+ UpdateMap() method. Since the drawing is stored in a bitmap, you
+ can also save the drawing to file by calling the
+ SaveToFile() method.
+ """
+ def __init__(self, parent, giface, Map, properties,
+ id=wx.ID_ANY, overlays=None,
+ style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+ """!
+ @param parent parent window
+ @param giface grass interface instance
+ @param Map map instance
+ @param properties instance of MapWindowProperties
+ @param id wx window id
+ @param style wx window style
+ @param kwargs keyword arguments passed to MapWindow and wx.Window
+ """
+ MapWindowBase.__init__(self, parent=parent, giface=giface, Map=Map)
+ wx.Window.__init__(self, parent=parent, id=id, style=style, **kwargs)
+
+ self._properties = properties
+
+ # flags
+ self.resize = False # indicates whether or not a resize event has taken place
+ self.dragimg = None # initialize variable for map panning
+ self.alwaysRender = False # if it always sets render to True in self.UpdateMap()
+
+ # variables for drawing on DC
+ self.pen = None # pen for drawing zoom boxes, etc.
+ self.polypen = None # pen for drawing polylines (measurements, profiles, etc)
+ # List of wx.Point tuples defining a polyline (geographical coordinates)
+ self.polycoords = []
+ # ID of rubber band line
+ self.lineid = None
+ # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
+ self.plineid = None
+
+ # Emitted when zoom of a window is changed
+ self.zoomChanged = Signal('BufferedWindow.zoomChanged')
+
+ # Emitted when map was queried, parameters x, y are mouse coordinates
+ # TODO: change pixel coordinates to map coordinates (using Pixel2Cell)
+ self.mapQueried = Signal('BufferedWindow.mapQueried')
+
+ # Emitted when the zoom history stack is emptied
+ self.zoomHistoryUnavailable = Signal('BufferedWindow.zoomHistoryUnavailable')
+ # Emitted when the zoom history stack is not empty
+ self.zoomHistoryAvailable = Signal('BufferedWindow.zoomHistoryAvailable')
+
+ # Emitted when map enters the window
+ self.mouseEntered = Signal('BufferedWindow.mouseEntered')
+ # Emitted when left mouse button is released and mouse use is 'pointer'
+ # Parameters are x and y of the mouse click in map (cell) units
+ # new and experimental, if the concept would be used widely,
+ # it could replace register and unregister mechanism
+ # and partially maybe also internal mouse use dictionary
+ self.mouseLeftUpPointer = Signal('BufferedWindow.mouseLeftUpPointer')
+ # Emitted when left mouse button is released
+ self.mouseLeftUp = Signal('BufferedWindow.mouseLeftUp')
+ # Emitted when left mouse button was pressed
+ self.mouseLeftDown = Signal('BufferedWindow.mouseLeftDown')
+ # Emitted after double-click
+ self.mouseDClick = Signal('BufferedWindow.mouseDClick')
+ # Emitted when mouse us moving (mouse motion event)
+ # Parametres are x and y of the mouse position in map (cell) units
+ self.mouseMoving = Signal('BufferedWindow.mouseMoving')
+
+ # event bindings
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+ self.Bind(wx.EVT_IDLE, self.OnIdle)
+
+ self._bindMouseEvents()
+
+ self.processMouse = True
+
+ # render output objects
+ self.mapfile = None # image file to be rendered
+ self.img = None # wx.Image object (self.mapfile)
+ # decoration overlays
+ self.overlays = overlays
+ # images and their PseudoDC ID's for painting and dragging
+ self.imagedict = {}
+ self.select = {} # selecting/unselecting decorations for dragging
+ self.textdict = {} # text, font, and color indexed by id
+
+ # zoom objects
+ self.zoomhistory = [] # list of past zoom extents
+ self.currzoom = 0 # current set of extents in zoom history being used
+ self.zoomtype = 1 # 1 zoom in, 0 no zoom, -1 zoom out
+ self.hitradius = 10 # distance for selecting map decorations
+ self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
+
+ # OnSize called to make sure the buffer is initialized.
+ # This might result in OnSize getting called twice on some
+ # platforms at initialization, but little harm done.
+ ### self.OnSize(None)
+
+ self._definePseudoDC()
+ # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
+ self.redrawAll = True
+
+ # will store an off screen empty bitmap for saving to file
+ self._buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+
+ self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
+ # rerender when Map reports change
+ self.Map.layerChanged.connect(lambda: self.UpdateMap())
+
+ # vars for handling mouse clicks
+ self.dragid = -1
+ self.lastpos = (0, 0)
+
+ # list for registration of graphics to draw
+ self.graphicsSetList = []
+
+ def _definePseudoDC(self):
+ """!Define PseudoDC objects to use
+ """
+ # create PseudoDC used for background map, map decorations like scales and legends
+ self.pdc = wx.PseudoDC()
+ # used for digitization tool
+ self.pdcVector = None
+ # decorations (region box, etc.)
+ self.pdcDec = wx.PseudoDC()
+ # pseudoDC for temporal objects (select box, measurement tool, etc.)
+ self.pdcTmp = wx.PseudoDC()
+
+ def _bindMouseEvents(self):
+ self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
+ self.Bind(wx.EVT_MOTION, self.OnMotion)
+ self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
+
+ def OnContextMenu(self, event):
+ """!Show Map Display context menu"""
+ if hasattr(self, "digit"):
+ event.Skip()
+ return
+
+ if not hasattr(self, "popupCopyCoordinates"):
+ self.popupCopyCoordinates = wx.NewId()
+ self.Bind(wx.EVT_MENU, self.OnCopyCoordinates, id = self.popupCopyCoordinates)
+
+ # generate popup-menu
+ menu = wx.Menu()
+ menu.Append(self.popupCopyCoordinates, _("Copy coordinates to clipboard"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0, 0, 0, 0], pen = None):
+ """!Draws map and overlay decorations
+ """
+ if drawid == None:
+ if pdctype == 'image' and img:
+ drawid = self.imagedict[img]
+ elif pdctype == 'clear':
+ drawid = None
+ else:
+ drawid = wx.NewId()
+
+ # TODO: find better solution
+ if not pen:
+ if pdctype == 'polyline':
+ pen = self.polypen
+ else:
+ pen = self.pen
+
+ if img and pdctype == 'image':
+ # self.imagedict[img]['coords'] = coords
+ self.select[self.imagedict[img]['id']] = False # ?
+
+ pdc.BeginDrawing()
+
+ if drawid != 99:
+ bg = wx.TRANSPARENT_BRUSH
+ else:
+ bg = wx.Brush(self.GetBackgroundColour())
+
+ pdc.SetBackground(bg)
+
+ Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype = %s, coord=%s" % \
+ (drawid, pdctype, coords))
+
+ # set PseudoDC id
+ if drawid is not None:
+ pdc.SetId(drawid)
+
+ if pdctype == 'clear': # erase the display
+ bg = wx.WHITE_BRUSH
+ # bg = wx.Brush(self.GetBackgroundColour())
+ pdc.SetBackground(bg)
+ pdc.RemoveAll()
+ pdc.Clear()
+ pdc.EndDrawing()
+
+ self.Refresh()
+ return
+
+ if pdctype == 'image': # draw selected image
+ bitmap = wx.BitmapFromImage(img)
+ w,h = bitmap.GetSize()
+ pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
+ pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
+
+ elif pdctype == 'box': # draw a box on top of the map
+ if pen:
+ pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+ pdc.SetPen(pen)
+ x2 = max(coords[0],coords[2])
+ x1 = min(coords[0],coords[2])
+ y2 = max(coords[1],coords[3])
+ y1 = min(coords[1],coords[3])
+ rwidth = x2-x1
+ rheight = y2-y1
+ rect = wx.Rect(x1, y1, rwidth, rheight)
+ pdc.DrawRectangleRect(rect)
+ pdc.SetIdBounds(drawid, rect)
+
+ elif pdctype == 'line': # draw a line on top of the map
+ if pen:
+ pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+ pdc.SetPen(pen)
+ pdc.DrawLinePoint(wx.Point(coords[0], coords[1]),wx.Point(coords[2], coords[3]))
+ pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
+
+ elif pdctype == 'polyline': # draw a polyline on top of the map
+ if pen:
+ pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+ pdc.SetPen(pen)
+ if (len(coords) < 2):
+ return
+ i = 1
+ while i < len(coords):
+ pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
+ wx.Point(coords[i][0], coords[i][1]))
+ i += 1
+
+ # get bounding rectangle for polyline
+ xlist = []
+ ylist = []
+ if len(coords) > 0:
+ for point in coords:
+ x,y = point
+ xlist.append(x)
+ ylist.append(y)
+ x1 = min(xlist)
+ x2 = max(xlist)
+ y1 = min(ylist)
+ y2 = max(ylist)
+ pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
+ # self.ovlcoords[drawid] = [x1,y1,x2,y2]
+
+ elif pdctype == 'point': # draw point
+ if pen:
+ pdc.SetPen(pen)
+ pdc.DrawPoint(coords[0], coords[1])
+ coordsBound = (coords[0] - 5,
+ coords[1] - 5,
+ coords[0] + 5,
+ coords[1] + 5)
+ pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
+
+ elif pdctype == 'text': # draw text on top of map
+ if not img['active']:
+ return # only draw active text
+ if 'rotation' in img:
+ rotation = float(img['rotation'])
+ else:
+ rotation = 0.0
+ w, h = self.GetFullTextExtent(img['text'])[0:2]
+ pdc.SetFont(img['font'])
+ pdc.SetTextForeground(img['color'])
+ coords, bbox = self.TextBounds(img)
+ if rotation == 0:
+ pdc.DrawText(img['text'], coords[0], coords[1])
+ else:
+ pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
+ pdc.SetIdBounds(drawid, bbox)
+
+ pdc.EndDrawing()
+
+ self.Refresh()
+
+ return drawid
+
+ def TextBounds(self, textinfo, relcoords = False):
+ """!Return text boundary data
+
+ @param textinfo text metadata (text, font, color, rotation)
+ @param coords reference point
+
+ @return coords of nonrotated text bbox (TL corner)
+ @return bbox of rotated text bbox (wx.Rect)
+ @return relCoords are text coord inside bbox
+ """
+ if 'rotation' in textinfo:
+ rotation = float(textinfo['rotation'])
+ else:
+ rotation = 0.0
+
+ coords = textinfo['coords']
+ bbox = wx.Rect(coords[0], coords[1], 0, 0)
+ relCoords = (0, 0)
+ Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
+ (textinfo['text'], rotation))
+
+ self.Update()
+
+ self.SetFont(textinfo['font'])
+
+ w, h = self.GetTextExtent(textinfo['text'])
+
+ if rotation == 0:
+ bbox[2], bbox[3] = w, h
+ if relcoords:
+ return coords, bbox, relCoords
+ else:
+ return coords, bbox
+
+ boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
+ boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
+ if rotation > 0 and rotation < 90:
+ bbox[1] -= boxh
+ relCoords = (0, boxh)
+ elif rotation >= 90 and rotation < 180:
+ bbox[0] -= boxw
+ bbox[1] -= boxh
+ relCoords = (boxw, boxh)
+ elif rotation >= 180 and rotation < 270:
+ bbox[0] -= boxw
+ relCoords = (boxw, 0)
+ bbox[2] = boxw
+ bbox[3] = boxh
+ bbox.Inflate(h,h)
+ if relcoords:
+ return coords, bbox, relCoords
+ else:
+ return coords, bbox
+
+ def OnPaint(self, event):
+ """!Draw PseudoDC's to buffered paint DC
+
+ If self.redrawAll is False on self.pdcTmp content is re-drawn
+ """
+ Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
+ dc = wx.BufferedPaintDC(self, self._buffer)
+ dc.Clear()
+
+ # use PrepareDC to set position correctly
+ # probably does nothing, removed from wxPython 2.9
+ # self.PrepareDC(dc)
+
+ # create a clipping rect from our position and size
+ # and update region
+ rgn = self.GetUpdateRegion().GetBox()
+ dc.SetClippingRect(rgn)
+
+ switchDraw = False
+ if self.redrawAll is None:
+ self.redrawAll = True
+ switchDraw = True
+
+ if self.redrawAll: # redraw pdc and pdcVector
+ # draw to the dc using the calculated clipping rect
+ self.pdc.DrawToDCClipped(dc, rgn)
+
+ # draw vector map layer
+ if hasattr(self, "digit"):
+ # decorate with GDDC (transparency)
+ try:
+ gcdc = wx.GCDC(dc)
+ self.pdcVector.DrawToDCClipped(gcdc, rgn)
+ except NotImplementedError, e:
+ print >> sys.stderr, e
+ self.pdcVector.DrawToDCClipped(dc, rgn)
+
+ self.bufferLast = None
+ else: # do not redraw pdc and pdcVector
+ if self.bufferLast is None:
+ # draw to the dc
+ self.pdc.DrawToDC(dc)
+
+ if hasattr(self, "digit"):
+ # decorate with GDDC (transparency)
+ try:
+ gcdc = wx.GCDC(dc)
+ self.pdcVector.DrawToDC(gcdc)
+ except NotImplementedError, e:
+ print >> sys.stderr, e
+ self.pdcVector.DrawToDC(dc)
+
+ # store buffered image
+ # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
+ self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
+
+ self.pdc.DrawBitmap(self.bufferLast, 0, 0, False)
+ self.pdc.DrawToDC(dc)
+
+ # draw decorations (e.g. region box)
+ try:
+ gcdc = wx.GCDC(dc)
+ self.pdcDec.DrawToDC(gcdc)
+ except NotImplementedError, e:
+ print >> sys.stderr, e
+ self.pdcDec.DrawToDC(dc)
+
+ # draw temporary object on the foreground
+ ### self.pdcTmp.DrawToDCClipped(dc, rgn)
+ self.pdcTmp.DrawToDC(dc)
+
+ if switchDraw:
+ self.redrawAll = False
+
+ def OnSize(self, event):
+ """!Scale map image so that it is the same size as the Window
+ """
+ # re-render image on idle
+ self.resize = time.clock()
+
+ def OnIdle(self, event):
+ """!Only re-render a composite map image from GRASS during
+ idle time instead of multiple times during resizing.
+ """
+
+ # use OnInternalIdle() instead ?
+
+ if self.resize and self.resize + 0.2 < time.clock():
+ Debug.msg(3, "BufferedWindow.OnSize():")
+
+ # set size of the input image
+ self.Map.ChangeMapSize(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.Destroy()
+ self._buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+
+ # get the image to be rendered
+ self.img = self.GetImage()
+
+ # update map display
+ updatemap = True
+ if self.img and self.Map.width + self.Map.height > 0: # scale image after resize
+ self.img = self.img.Scale(self.Map.width, self.Map.height)
+ if len(self.Map.GetListOfLayers()) > 0:
+ self.UpdateMap()
+ updatemap = False
+
+ if updatemap:
+ self.UpdateMap(render = True)
+ self.resize = False
+ elif self.resize:
+ event.RequestMore()
+
+ event.Skip()
+
+ def SaveToFile(self, FileName, FileType, width, height):
+ """!This draws the pseudo DC to a buffer that can be saved to
+ a file.
+
+ @param FileName file name
+ @param FileType type of bitmap
+ @param width image width
+ @param height image height
+ """
+ busy = wx.BusyInfo(message = _("Please wait, exporting image..."),
+ parent = self)
+ wx.Yield()
+
+ self.Map.ChangeMapSize((width, height))
+ ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
+ self.Map.Render(force = True, windres = self._properties.resolution)
+ img = self.GetImage()
+ self.pdc.RemoveAll()
+ self.Draw(self.pdc, img, drawid = 99)
+
+ # compute size ratio to move overlay accordingly
+ cSize = self.GetClientSizeTuple()
+ ratio = float(width) / cSize[0], float(height) / cSize[1]
+
+ # redraw legend, scalebar
+ for img in self.GetOverlay():
+ # draw any active and defined overlays
+ if self.imagedict[img]['layer'].IsActive():
+ id = self.imagedict[img]['id']
+ coords = int(ratio[0] * self.overlays[id].coords[0]),\
+ int(ratio[1] * self.overlays[id].coords[1])
+ self.Draw(self.pdc, img = img, drawid = id,
+ pdctype = self.overlays[id].pdcType, coords = coords)
+
+ # redraw text labels
+ for id in self.textdict.keys():
+ textinfo = self.textdict[id]
+ oldCoords = textinfo['coords']
+ textinfo['coords'] = ratio[0] * textinfo['coords'][0],\
+ ratio[1] * textinfo['coords'][1]
+ self.Draw(self.pdc, img = self.textdict[id], drawid = id,
+ pdctype = 'text')
+ # set back old coordinates
+ textinfo['coords'] = oldCoords
+
+ dc = wx.BufferedDC(None, ibuffer)
+ dc.Clear()
+ # probably does nothing, removed from wxPython 2.9
+ # self.PrepareDC(dc)
+ self.pdc.DrawToDC(dc)
+ if self.pdcVector:
+ self.pdcVector.DrawToDC(dc)
+ ibuffer.SaveFile(FileName, FileType)
+
+ busy.Destroy()
+
+ self.UpdateMap(render = True)
+ self.Refresh()
+
+ def GetOverlay(self):
+ """!Converts rendered overlay files to wx.Image
+
+ Updates self.imagedict
+
+ @return list of images
+ """
+ imgs = []
+ for overlay in self.Map.GetListOfLayers(ltype = "overlay", active = True):
+ if overlay.mapfile is not None \
+ and os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
+ img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
+
+ for key in self.imagedict.keys():
+ if self.imagedict[key]['id'] == overlay.id:
+ del self.imagedict[key]
+
+ self.imagedict[img] = { 'id' : overlay.id,
+ 'layer' : overlay }
+ imgs.append(img)
+
+ return imgs
+
+ def GetImage(self):
+ """!Converts redered map files to wx.Image
+
+ Updates self.imagedict (id=99)
+
+ @return wx.Image instance (map composition)
+ """
+ imgId = 99
+ if self.mapfile and 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
+
+ for key in self.imagedict.keys():
+ if self.imagedict[key]['id'] == imgId:
+ del self.imagedict[key]
+
+ self.imagedict[img] = { 'id': imgId }
+
+ return img
+
+ def SetAlwaysRenderEnabled(self, alwaysRender = True):
+ self.alwaysRender = alwaysRender
+
+ def IsAlwaysRenderEnabled(self):
+ return self.alwaysRender
+
+ def UpdateMap(self, render = True, renderVector = True):
+ """!Updates the canvas anytime there is a change to the
+ underlaying images or to the geometry of the canvas.
+
+ This method should not be called directly.
+
+ @todo change direct calling of UpdateMap method to emittig grass
+ interface updateMap signal
+
+ @param render re-render map composition
+ @param renderVector re-render vector map layer enabled for editing (used for digitizer)
+ """
+ start = time.clock()
+ self.resize = False
+
+ # was if self.Map.cmdfile and ...
+ if self.IsAlwaysRenderEnabled() and self.img is None:
+ render = True
+
+ #
+ # render background image if needed
+ #
+
+ # here was the change of the layertree rerender variable
+ # but it is fully the problem of layertree
+ # and so it is handled there
+ # remove this comment when it is old enough
+
+ try:
+ if render:
+ # update display size
+ self.Map.ChangeMapSize(self.GetClientSize())
+ if self._properties.resolution:
+ # use computation region resolution for rendering
+ windres = True
+ else:
+ windres = False
+
+ self.mapfile = self.Map.Render(force = True,
+ windres = windres)
+ else:
+ self.mapfile = self.Map.Render(force = False)
+
+ except GException, e:
+ GError(message = e.value)
+ self.mapfile = None
+
+ self.img = self.GetImage() # id=99
+
+ #
+ # clear pseudoDcs
+ #
+ for pdc in (self.pdc,
+ self.pdcDec,
+ self.pdcTmp):
+ pdc.Clear()
+ pdc.RemoveAll()
+
+ #
+ # draw background map image to PseudoDC
+ #
+ if not self.img:
+ self.Draw(self.pdc, pdctype = 'clear')
+ else:
+ try:
+ id = self.imagedict[self.img]['id']
+ except:
+ return False
+
+ self.Draw(self.pdc, self.img, drawid = id)
+
+ #
+ # render vector map layer
+ #
+ if renderVector and hasattr(self, "digit"):
+ self._updateMap()
+ #
+ # render overlays
+ #
+ for img in self.GetOverlay():
+ # draw any active and defined overlays
+ if self.imagedict[img]['layer'].IsActive():
+ id = self.imagedict[img]['id']
+ self.Draw(self.pdc, img = img, drawid = id,
+ pdctype = self.overlays[id].pdcType, coords = self.overlays[id].coords)
+
+ for id in self.textdict.keys():
+ self.Draw(self.pdc, img = self.textdict[id], drawid = id,
+ pdctype = 'text', coords = [10, 10, 10, 10])
+
+ # optionally draw computational extent box
+ self.DrawCompRegionExtent()
+
+ #
+ # redraw pdcTmp if needed
+ #
+
+ # draw registered graphics
+ if len(self.graphicsSetList) > 0:
+ penOrig = self.pen
+ polypenOrig = self.polypen
+
+ for item in self.graphicsSetList:
+ try:
+ item.Draw(self.pdcTmp)
+ except:
+ GError(parent = self,
+ message = _('Unable to draw registered graphics. '
+ 'The graphics was unregistered.'))
+ self.UnregisterGraphicsToDraw(item)
+
+ self.pen = penOrig
+ self.polypen = polypenOrig
+
+ if len(self.polycoords) > 0:
+ self.DrawLines(self.pdcTmp)
+
+ stop = time.clock()
+
+ Debug.msg (1, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
+ (render, renderVector, (stop-start)))
+
+ return True
+
+ def DrawCompRegionExtent(self):
+ """!Draw computational region extent in the display
+
+ Display region is drawn as a blue box inside the computational region,
+ computational region inside a display region as a red box).
+ """
+ if self._properties.showRegion:
+ compReg = self.Map.GetRegion()
+ dispReg = self.Map.GetCurrentRegion()
+ reg = None
+ if utils.isInRegion(dispReg, compReg):
+ self.polypen = wx.Pen(colour = wx.Colour(0, 0, 255, 128), width = 3, style = wx.SOLID)
+ reg = dispReg
+ else:
+ self.polypen = wx.Pen(colour = wx.Colour(255, 0, 0, 128),
+ width = 3, style = wx.SOLID)
+ reg = compReg
+
+ regionCoords = []
+ regionCoords.append((reg['w'], reg['n']))
+ regionCoords.append((reg['e'], reg['n']))
+ regionCoords.append((reg['e'], reg['s']))
+ regionCoords.append((reg['w'], reg['s']))
+ regionCoords.append((reg['w'], reg['n']))
+ # draw region extent
+ self.DrawLines(pdc=self.pdcDec, polycoords=regionCoords)
+
+ def EraseMap(self):
+ """!Erase map canvas
+ """
+ self.Draw(self.pdc, pdctype = 'clear')
+
+ if hasattr(self, "digit"):
+ self.Draw(self.pdcVector, pdctype = 'clear')
+
+ self.Draw(self.pdcDec, pdctype = 'clear')
+ self.Draw(self.pdcTmp, pdctype = 'clear')
+
+ self.Map.AbortAllThreads()
+
+ def DragMap(self, moveto):
+ """!Drag the entire map image for panning.
+
+ @param moveto dx,dy
+ """
+ dc = wx.BufferedDC(wx.ClientDC(self))
+ dc.SetBackground(wx.Brush("White"))
+ dc.Clear()
+
+ self.dragimg = wx.DragImage(self._buffer)
+ self.dragimg.BeginDrag((0, 0), self)
+ self.dragimg.GetImageRect(moveto)
+ self.dragimg.Move(moveto)
+
+ self.dragimg.DoDrawImage(dc, moveto)
+ self.dragimg.EndDrag()
+
+ def DragItem(self, id, coords):
+ """!Drag an overlay decoration item
+ """
+ if id == 99 or id == '' or id == None: return
+ Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
+ x, y = self.lastpos
+ dx = coords[0] - x
+ dy = coords[1] - y
+ self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+ r = self.pdc.GetIdBounds(id)
+
+ if type(r) is list:
+ r = wx.Rect(r[0], r[1], r[2], r[3])
+ if id > 100: # text dragging
+ rtop = (r[0],r[1]-r[3],r[2],r[3])
+ r = r.Union(rtop)
+ rleft = (r[0]-r[2],r[1],r[2],r[3])
+ r = r.Union(rleft)
+ self.pdc.TranslateId(id, dx, dy)
+
+ r2 = self.pdc.GetIdBounds(id)
+ if type(r2) is list:
+ r2 = wx.Rect(r[0], r[1], r[2], r[3])
+ if id > 100: # text
+ self.textdict[id]['bbox'] = r2
+ self.textdict[id]['coords'][0] += dx
+ self.textdict[id]['coords'][1] += dy
+ r = r.Union(r2)
+ r.Inflate(4,4)
+ self.RefreshRect(r, False)
+ self.lastpos = (coords[0], coords[1])
+
+ def MouseDraw(self, pdc = None, begin = None, end = None):
+ """!Mouse box or line from 'begin' to 'end'
+
+ If not given from self.mouse['begin'] to self.mouse['end'].
+ """
+ if not pdc:
+ return
+
+ if begin is None:
+ begin = self.mouse['begin']
+ if end is None:
+ end = self.mouse['end']
+
+ Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
+ (self.mouse['use'], self.mouse['box'],
+ begin[0], begin[1], end[0], end[1]))
+
+ if self.mouse['box'] == "box":
+ boxid = wx.ID_NEW
+ mousecoords = [begin[0], begin[1],
+ end[0], end[1]]
+ r = pdc.GetIdBounds(boxid)
+ if type(r) is list:
+ r = wx.Rect(r[0], r[1], r[2], r[3])
+ r.Inflate(4, 4)
+ try:
+ pdc.ClearId(boxid)
+ except:
+ pass
+ self.RefreshRect(r, False)
+ pdc.SetId(boxid)
+ self.Draw(pdc, drawid = boxid, pdctype = 'box', coords = mousecoords)
+
+ elif self.mouse['box'] == "line":
+ self.lineid = wx.ID_NEW
+ mousecoords = [begin[0], begin[1], \
+ end[0], end[1]]
+ x1 = min(begin[0],end[0])
+ x2 = max(begin[0],end[0])
+ y1 = min(begin[1],end[1])
+ y2 = max(begin[1],end[1])
+ r = wx.Rect(x1,y1,x2-x1,y2-y1)
+ r.Inflate(4,4)
+ try:
+ pdc.ClearId(self.lineid)
+ except:
+ pass
+ self.RefreshRect(r, False)
+ pdc.SetId(self.lineid)
+ self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = mousecoords)
+
+ def DrawLines(self, pdc = None, polycoords = None):
+ """!Draw polyline in PseudoDC
+
+ Set self.pline to wx.NEW_ID + 1
+
+ polycoords - list of polyline vertices, geographical coordinates
+ (if not given, self.polycoords is used)
+ """
+ if not pdc:
+ pdc = self.pdcTmp
+
+ if not polycoords:
+ polycoords = self.polycoords
+
+ if len(polycoords) > 0:
+ self.plineid = wx.ID_NEW + 1
+ # convert from EN to XY
+ coords = []
+ for p in polycoords:
+ coords.append(self.Cell2Pixel(p))
+
+ self.Draw(pdc, drawid = self.plineid, pdctype = 'polyline', coords = coords)
+
+ Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
+ (coords, self.plineid))
+
+ return self.plineid
+
+ return -1
+
+ def DrawCross(self, pdc, coords, size, rotation = 0, pen = None,
+ text = None, textAlign = 'lr', textOffset = (5, 5)):
+ """!Draw cross in PseudoDC
+
+ @todo implement rotation
+
+ @param pdc PseudoDC
+ @param coords center coordinates
+ @param rotation rotate symbol
+ @param text draw also text (text, font, color, rotation)
+ @param textAlign alignment (default 'lower-right')
+ @param textOffset offset for text (from center point)
+ """
+ Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
+ (pdc, coords, size))
+ coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
+ (coords[0], coords[1] - size, coords[0], coords[1] + size))
+
+ self.lineid = wx.NewId()
+ for lineCoords in coordsCross:
+ self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = lineCoords, pen = pen)
+
+ if not text:
+ return self.lineid
+
+ if textAlign == 'ul':
+ coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
+ elif textAlign == 'ur':
+ coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
+ elif textAlign == 'lr':
+ coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
+ else:
+ coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
+
+ self.Draw(pdc, img = text,
+ pdctype = 'text', coords = coord, pen = pen)
+
+ return self.lineid
+
+ def _computeZoomToPointAndRecenter(self, position, zoomtype):
+ """!Computes zoom parameters for recenter mode.
+
+ Computes begin and end parameters for Zoom() method.
+ Used for zooming by single click (not box)
+ and mouse wheel zooming (zoom and recenter mode).
+ """
+ if zoomtype > 0:
+ begin = (position[0] - self.Map.width / 4,
+ position[1] - self.Map.height / 4)
+ end = (position[0] + self.Map.width / 4,
+ position[1] + self.Map.height / 4)
+ else:
+ begin = ((self.Map.width - position[0]) / 2,
+ (self.Map.height - position[1]) / 2)
+ end = (begin[0] + self.Map.width / 2,
+ begin[1] + self.Map.height / 2)
+ return begin, end
+
+ def MouseActions(self, event):
+ """!Mouse motion and button click notifier
+ """
+ if not self.processMouse:
+ return
+
+ # zoom with mouse wheel
+ if event.GetWheelRotation() != 0:
+ self.OnMouseWheel(event)
+
+ # left mouse button pressed
+ elif event.LeftDown():
+ self.OnLeftDown(event)
+
+ # left mouse button released
+ elif event.LeftUp():
+ self.OnLeftUp(event)
+
+ # dragging
+ elif event.Dragging():
+ self.OnDragging(event)
+
+ # double click
+ elif event.ButtonDClick():
+ self.OnButtonDClick(event)
+
+ # middle mouse button pressed
+ elif event.MiddleDown():
+ self.OnMiddleDown(event)
+
+ # middle mouse button relesed
+ elif event.MiddleUp():
+ self.OnMiddleUp(event)
+
+ # right mouse button pressed
+ elif event.RightDown():
+ self.OnRightDown(event)
+
+ # right mouse button released
+ elif event.RightUp():
+ self.OnRightUp(event)
+
+ elif event.Entering():
+ self.OnMouseEnter(event)
+
+ elif event.Moving():
+ pixelCoordinates = event.GetPositionTuple()[:]
+ coordinates = self.Pixel2Cell(pixelCoordinates)
+ self.mouseMoving.emit(x=coordinates[0], y=coordinates[1])
+ self.OnMouseMoving(event)
+
+ def OnMouseWheel(self, event):
+ """!Mouse wheel moved
+ """
+ zoomBehaviour = UserSettings.Get(group = 'display',
+ key = 'mouseWheelZoom',
+ subkey = 'selection')
+ if zoomBehaviour == 2:
+ event.Skip()
+ return
+
+ self.processMouse = False
+ current = event.GetPositionTuple()[:]
+ wheel = event.GetWheelRotation()
+ Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
+
+ if wheel > 0:
+ zoomtype = 1
+ else:
+ zoomtype = -1
+ if UserSettings.Get(group = 'display',
+ key = 'scrollDirection',
+ subkey = 'selection'):
+ zoomtype *= -1
+ # zoom 1/2 of the screen (TODO: settings)
+ if zoomBehaviour == 0: # zoom and recenter
+ begin, end = self._computeZoomToPointAndRecenter(position = current, zoomtype = zoomtype)
+
+ elif zoomBehaviour == 1: # zoom to current cursor position
+ begin = (current[0]/2, current[1]/2)
+ end = ((self.Map.width - current[0])/2 + current[0],
+ (self.Map.height - current[1])/2 + current[1])
+
+
+ # zoom
+ self.Zoom(begin, end, zoomtype)
+
+ # redraw map
+ self.UpdateMap()
+
+ self.Refresh()
+ self.processMouse = True
+
+ def OnDragging(self, event):
+ """!Mouse dragging
+ """
+ Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
+ current = event.GetPositionTuple()[:]
+ previous = self.mouse['begin']
+ move = (current[0] - previous[0],
+ current[1] - previous[1])
+
+ if hasattr(self, "digit"):
+ digitToolbar = self.toolbar
+ else:
+ digitToolbar = None
+
+ # dragging or drawing box with left button
+ if self.mouse['use'] == 'pan' or \
+ event.MiddleIsDown():
+ self.DragMap(move)
+
+ # dragging decoration overlay item
+ elif (self.mouse['use'] == 'pointer' and
+ not digitToolbar and
+ self.dragid != None):
+ coords = event.GetPositionTuple()
+ self.DragItem(self.dragid, coords)
+
+ # dragging anything else - rubber band box or line
+ else:
+ if (self.mouse['use'] == 'pointer' and
+ not digitToolbar):
+ return
+
+ self.mouse['end'] = event.GetPositionTuple()[:]
+ if (event.LeftIsDown() and
+ not (digitToolbar and
+ digitToolbar.GetAction() in ("moveLine",) and
+ self.digit.GetDisplay().GetSelected() > 0)):
+ self.MouseDraw(pdc = self.pdcTmp)
+
+ def OnLeftDown(self, event):
+ """!Left mouse button pressed
+ """
+ Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
+ self.mouse["use"])
+
+ self.mouse['begin'] = event.GetPositionTuple()[:]
+
+ # vector digizer
+ if self.mouse["use"] == "pointer" and \
+ hasattr(self, "digit"):
+ if event.ControlDown():
+ self.OnLeftDownUndo(event)
+ else:
+ self._onLeftDown(event)
+
+ elif self.mouse['use'] == 'pointer':
+ # get decoration or text id
+ idlist = []
+ self.dragid = ''
+ self.lastpos = self.mouse['begin']
+ idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
+ self.hitradius)
+ if 99 in idlist:
+ idlist.remove(99)
+ if idlist != []:
+ self.dragid = idlist[0] #drag whatever is on top
+ else:
+ pass
+ coords = self.Pixel2Cell(self.mouse['begin'])
+ self.mouseLeftDown.emit(x=coords[0], y=coords[1])
+
+ event.Skip()
+
+ def OnLeftUp(self, event):
+ """!Left mouse button released
+
+ Emits mapQueried signal when mouse use is 'query'.
+ """
+ Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
+ self.mouse["use"])
+
+ self.mouse['end'] = event.GetPositionTuple()[:]
+ coordinates = self.Pixel2Cell(self.mouse['end'])
+
+ if self.mouse['use'] in ["zoom", "pan"]:
+ # set region in zoom or pan
+ begin = self.mouse['begin']
+ end = self.mouse['end']
+
+ if self.mouse['use'] == 'zoom':
+ # set region for click (zero-width box)
+ if begin[0] - end[0] == 0 or \
+ begin[1] - end[1] == 0:
+ begin, end = self._computeZoomToPointAndRecenter(position = end, zoomtype = self.zoomtype)
+ self.Zoom(begin, end, self.zoomtype)
+
+ # redraw map
+ self.UpdateMap(render = True)
+
+ elif self.mouse["use"] == "query":
+ self.mapQueried.emit(x=self.mouse['end'][0], y=self.mouse['end'][1])
+
+ elif self.mouse["use"] == "pointer" and \
+ hasattr(self, "digit"):
+ self._onLeftUp(event)
+
+ elif (self.mouse['use'] == 'pointer' and
+ self.dragid >= 0):
+ # end drag of overlay decoration
+
+ if self.dragid < 99 and self.dragid in self.overlays:
+ self.overlays[self.dragid].coords = self.pdc.GetIdBounds(self.dragid)
+ elif self.dragid > 100 and self.dragid in self.textdict:
+ self.textdict[self.dragid]['bbox'] = self.pdc.GetIdBounds(self.dragid)
+ else:
+ pass
+ self.dragid = None
+
+ self.mouseLeftUpPointer.emit(x=coordinates[0], y=coordinates[1])
+
+ # TODO: decide which coordinates to send (e, n, mouse['begin'], mouse['end'])
+ self.mouseLeftUp.emit(x=coordinates[0], y=coordinates[1])
+
+ def OnButtonDClick(self, event):
+ """!Mouse button double click
+ """
+ Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
+ self.mouse["use"])
+
+ screenCoords = event.GetPosition()
+
+ if self.mouse['use'] == 'pointer':
+ # select overlay decoration options dialog
+ idlist = self.pdc.FindObjects(screenCoords[0], screenCoords[1], self.hitradius)
+ if idlist:
+ self.dragid = idlist[0]
+ self.overlayActivated.emit(overlayId=self.dragid)
+
+ coords = self.Pixel2Cell(screenCoords)
+ self.mouseDClick.emit(x=coords[0], y=coords[1])
+
+ def OnRightDown(self, event):
+ """!Right mouse button pressed
+ """
+ Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
+ self.mouse["use"])
+
+ if hasattr(self, "digit"):
+ self._onRightDown(event)
+
+ event.Skip()
+
+ def OnRightUp(self, event):
+ """!Right mouse button released
+ """
+ Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
+ self.mouse["use"])
+
+ if hasattr(self, "digit"):
+ self._onRightUp(event)
+
+ self.redrawAll = True
+ self.Refresh()
+
+ event.Skip()
+
+ def OnMiddleDown(self, event):
+ """!Middle mouse button pressed
+ """
+ if not event:
+ return
+
+ self.mouse['begin'] = event.GetPositionTuple()[:]
+
+ def OnMiddleUp(self, event):
+ """!Middle mouse button released
+ """
+ self.mouse['end'] = event.GetPositionTuple()[:]
+
+ # set region in zoom or pan
+ begin = self.mouse['begin']
+ end = self.mouse['end']
+
+ self.Zoom(begin, end, 0) # no zoom
+
+ # redraw map
+ self.UpdateMap(render = True)
+
+ def OnMouseEnter(self, event):
+ """!Mouse entered window and no mouse buttons were pressed
+
+ Emits the mouseEntered signal.
+ """
+ self.mouseEntered.emit()
+ event.Skip()
+
+ def OnMouseMoving(self, event):
+ """!Motion event and no mouse buttons were pressed
+ """
+ if self.mouse["use"] == "pointer" and \
+ hasattr(self, "digit"):
+ self._onMouseMoving(event)
+
+ event.Skip()
+
+ def OnCopyCoordinates(self, event):
+ """!Copy coordinates to cliboard"""
+ e, n = self.GetLastEN()
+ if wx.TheClipboard.Open():
+ do = wx.TextDataObject()
+ # TODO: put delimiter in settings and apply also for Go to in statusbar
+ delim = ';'
+ do.SetText(str(e) + delim + str(n))
+ wx.TheClipboard.SetData(do)
+ wx.TheClipboard.Close()
+
+ def ClearLines(self, pdc = None):
+ """!Clears temporary drawn lines from PseudoDC
+ """
+ if not pdc:
+ pdc = self.pdcTmp
+ try:
+ pdc.ClearId(self.lineid)
+ pdc.RemoveId(self.lineid)
+ except:
+ pass
+
+ try:
+ pdc.ClearId(self.plineid)
+ pdc.RemoveId(self.plineid)
+ except:
+ pass
+
+ Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
+ (self.lineid, self.plineid))
+
+ return True
+
+ def Pixel2Cell(self, xyCoords):
+ """!Convert image coordinates to real word coordinates
+
+ @param x, y image coordinates
+
+ @return easting, northing
+ @return None on error
+ """
+ try:
+ x = int(xyCoords[0])
+ y = int(xyCoords[1])
+ except:
+ return None
+
+ if self.Map.region["ewres"] > self.Map.region["nsres"]:
+ res = self.Map.region["ewres"]
+ else:
+ res = self.Map.region["nsres"]
+
+ w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+ n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+
+ east = w + x * res
+ north = n - y * res
+
+ return (east, north)
+
+ def Cell2Pixel(self, enCoords):
+ """!Convert real word coordinates to image coordinates
+ """
+ try:
+ east = float(enCoords[0])
+ north = float(enCoords[1])
+ except:
+ return None
+
+ if self.Map.region["ewres"] > self.Map.region["nsres"]:
+ res = self.Map.region["ewres"]
+ else:
+ res = self.Map.region["nsres"]
+
+ w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+ n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+
+ x = (east - w) / res
+ y = (n - north) / res
+
+ return (x, y)
+
+ def Zoom(self, begin, end, zoomtype):
+ """!Calculates new region while (un)zoom/pan-ing
+ """
+ x1, y1 = begin
+ x2, y2 = end
+ newreg = {}
+
+ # threshold - too small squares do not make sense
+ # can only zoom to windows of > 5x5 screen pixels
+ if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
+ if x1 > x2:
+ x1, x2 = x2, x1
+ if y1 > y2:
+ y1, y2 = y2, y1
+
+ # zoom in
+ if zoomtype > 0:
+ newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
+ newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
+
+ # zoom out
+ elif zoomtype < 0:
+ newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
+ newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + 2 * \
+ (self.Map.width - x2),
+ self.Map.height + 2 * \
+ (self.Map.height - y2)))
+ # pan
+ elif zoomtype == 0:
+ dx = x1 - x2
+ dy = y1 - y2
+ if dx == 0 and dy == 0:
+ dx = x1 - self.Map.width / 2
+ dy = y1 - self.Map.height / 2
+ newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
+ newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + dx,
+ self.Map.height + dy))
+
+ # if new region has been calculated, set the values
+ if newreg != {}:
+ # LL locations
+ if self.Map.projinfo['proj'] == 'll':
+ self.Map.region['n'] = min(self.Map.region['n'], 90.0)
+ self.Map.region['s'] = max(self.Map.region['s'], -90.0)
+
+ ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
+ cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
+
+ # calculate new center point and display resolution
+ self.Map.region['center_easting'] = ce
+ self.Map.region['center_northing'] = cn
+ self.Map.region['ewres'] = (newreg['e'] - newreg['w']) / self.Map.width
+ self.Map.region['nsres'] = (newreg['n'] - newreg['s']) / self.Map.height
+ if self._properties.alignExtent:
+ self.Map.AlignExtentFromDisplay()
+ else:
+ for k in ('n', 's', 'e', 'w'):
+ self.Map.region[k] = newreg[k]
+
+ if hasattr(self, "digit") and \
+ hasattr(self, "moveInfo"):
+ self._zoom(None)
+
+ self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+ self.Map.region['e'], self.Map.region['w'])
+
+ if self.redrawAll is False:
+ self.redrawAll = True
+
+ def ZoomBack(self):
+ """!Zoom to previous extents in zoomhistory list
+
+ Emits zoomChanged signal.
+ Emits zoomHistoryUnavailable signal when stack is empty.
+ """
+ Debug.msg(4, "BufferedWindow.ZoomBack(): hist)=%s" % self.zoomhistory)
+
+ zoom = list()
+
+ if len(self.zoomhistory) > 1:
+ self.zoomhistory.pop()
+ zoom = self.zoomhistory[-1]
+
+ if len(self.zoomhistory) < 2:
+ self.zoomHistoryUnavailable.emit()
+
+ # zoom to selected region
+ self.Map.GetRegion(n = zoom[0], s = zoom[1],
+ e = zoom[2], w = zoom[3],
+ update = True)
+ # update map
+ self.UpdateMap()
+
+ self.zoomChanged.emit()
+
+ def ZoomHistory(self, n, s, e, w):
+ """!Manages a list of last 10 zoom extents
+
+ Emits zoomChanged signal.
+ Emits zoomHistoryAvailable signal when stack is not empty.
+ Emits zoomHistoryUnavailable signal when stack is empty.
+
+ All methods which are changing zoom should call this method
+ to make a record in the history. The signal zoomChanged will be
+ then emitted automatically.
+
+ @param n,s,e,w north, south, east, west
+
+ @return removed history item if exists (or None)
+ """
+ removed = None
+ self.zoomhistory.append((n,s,e,w))
+
+ if len(self.zoomhistory) > 10:
+ removed = self.zoomhistory.pop(0)
+
+ if removed:
+ Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
+ (self.zoomhistory, removed))
+ else:
+ Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
+ (self.zoomhistory))
+
+ # update toolbar
+ if len(self.zoomhistory) > 1:
+ self.zoomHistoryAvailable.emit()
+ else:
+ self.zoomHistoryUnavailable.emit()
+
+ self.zoomChanged.emit()
+
+ return removed
+
+ def InitZoomHistory(self):
+ """Initializes zoom history.
+
+ @todo First item is handled in some special way. Improve the
+ documentation or fix the code.
+
+ It does not emits any signals.
+
+ This method can be possibly removed when the history will solve the
+ fist item in different way or when GCP manager (and possibly others)
+ will handle Map variable in the way that it will be prepared for
+ MapWindow/BufferedWindow and thus usable to initialize history.
+ """
+ self.zoomhistory.append((self.Map.region['n'],
+ self.Map.region['s'],
+ self.Map.region['e'],
+ self.Map.region['w']))
+ Debug.msg(4, "BufferedWindow.InitZoomHistory(): hist=%s" %
+ (self.zoomhistory))
+
+ def ResetZoomHistory(self):
+ """!Reset zoom history"""
+ self.zoomhistory = list()
+
+ def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
+ """!Set display extents to match selected raster
+ or vector map(s).
+
+ @param layers list of layers to be zoom to
+ @param ignoreNulls True to ignore null-values (valid only for rasters)
+ @param render True to re-render display
+ """
+ if not layers:
+ layers = self._giface.GetLayerList().GetSelectedLayers(checkedOnly=False)
+ layers = [layer.maplayer for layer in layers]
+
+ if not layers:
+ return
+
+ rast = []
+ vect = []
+ updated = False
+ for l in layers:
+ # only raster/vector layers are currently supported
+ if l.type == 'raster':
+ rast.append(l.GetName())
+ elif l.type == 'vector':
+ if hasattr(self, "digit") and \
+ self.toolbar.GetLayer() == l:
+ w, s, b, e, n, t = self.digit.GetDisplay().GetMapBoundingBox()
+ self.Map.GetRegion(n = n, s = s, w = w, e = e,
+ update = True)
+ updated = True
+ else:
+ vect.append(l.name)
+ elif l.type == 'rgb':
+ for rname in l.GetName().splitlines():
+ rast.append(rname)
+
+ if not updated:
+ self.Map.GetRegion(rast = rast,
+ vect = vect,
+ zoom = ignoreNulls,
+ update = True)
+
+ self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+ self.Map.region['e'], self.Map.region['w'])
+
+ if render:
+ self.UpdateMap()
+
+ def ZoomToWind(self):
+ """!Set display geometry to match computational region
+ settings (set with g.region)
+ """
+ self.Map.region = self.Map.GetRegion()
+
+ self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+ self.Map.region['e'], self.Map.region['w'])
+
+ self.UpdateMap()
+
+ def ZoomToDefault(self):
+ """!Set display geometry to match default region settings
+ """
+ self.Map.region = self.Map.GetRegion(default = True)
+ self.Map.AdjustRegion() # aling region extent to the display
+
+ self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+ self.Map.region['e'], self.Map.region['w'])
+
+ self.UpdateMap()
+
+ def GoTo(self, e, n):
+ region = self.Map.GetCurrentRegion()
+
+ region['center_easting'], region['center_northing'] = e, n
+
+ dn = (region['nsres'] * region['rows']) / 2.
+ region['n'] = region['center_northing'] + dn
+ region['s'] = region['center_northing'] - dn
+ de = (region['ewres'] * region['cols']) / 2.
+ region['e'] = region['center_easting'] + de
+ region['w'] = region['center_easting'] - de
+
+ self.Map.AdjustRegion()
+
+ # add to zoom history
+ self.ZoomHistory(region['n'], region['s'],
+ region['e'], region['w'])
+ self.UpdateMap()
+
+ def DisplayToWind(self):
+ """!Set computational region (WIND file) to match display
+ extents
+ """
+ tmpreg = os.getenv("GRASS_REGION")
+ if tmpreg:
+ del os.environ["GRASS_REGION"]
+
+ # We ONLY want to set extents here. Don't mess with resolution. Leave that
+ # for user to set explicitly with g.region
+ new = self.Map.AlignResolution()
+ RunCommand('g.region',
+ parent = self,
+ overwrite = True,
+ n = new['n'],
+ s = new['s'],
+ e = new['e'],
+ w = new['w'],
+ rows = int(new['rows']),
+ cols = int(new['cols']))
+
+ if tmpreg:
+ os.environ["GRASS_REGION"] = tmpreg
+
+ def ZoomToSaved(self):
+ """!Set display geometry to match extents in
+ saved region file
+ """
+ dlg = SavedRegion(parent = self,
+ title = _("Zoom to saved region extents"),
+ loadsave = 'load')
+
+ if dlg.ShowModal() == wx.ID_CANCEL or not dlg.GetName():
+ dlg.Destroy()
+ return
+
+ if not grass.find_file(name = dlg.GetName(), element = 'windows')['name']:
+ wx.MessageBox(parent = self,
+ message = _("Region <%s> not found. Operation canceled.") % dlg.GetName(),
+ caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
+ dlg.Destroy()
+ return
+
+ self.Map.GetRegion(regionName = dlg.GetName(),
+ update = True)
+
+ dlg.Destroy()
+
+ self.ZoomHistory(self.Map.region['n'],
+ self.Map.region['s'],
+ self.Map.region['e'],
+ self.Map.region['w'])
+
+ self.UpdateMap()
+
+ def SaveRegion(self, display = True):
+ """!Save display extents/compulational region to named region
+ file.
+
+ @param display True for display extends otherwise computational region
+ """
+ if display:
+ title = _("Save display extents to region file")
+ else:
+ title = _("Save computational region to region file")
+
+ dlg = SavedRegion(parent = self, title = title, loadsave = 'save')
+ if dlg.ShowModal() == wx.ID_CANCEL or not dlg.GetName():
+ dlg.Destroy()
+ return
+
+ # test to see if it already exists and ask permission to overwrite
+ if grass.find_file(name = dlg.GetName(), element = 'windows')['name']:
+ overwrite = wx.MessageBox(parent = self,
+ message = _("Region file <%s> already exists. "
+ "Do you want to overwrite it?") % (dlg.GetName()),
+ caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
+ if overwrite != wx.YES:
+ dlg.Destroy()
+ return
+
+ if display:
+ self._saveDisplayRegion(dlg.GetName())
+ else:
+ self._saveCompRegion(dlg.GetName())
+
+ dlg.Destroy()
+
+ def _saveCompRegion(self, name):
+ """!Save region settings to region file
+
+ @param name region name
+ """
+ RunCommand('g.region',
+ overwrite = True,
+ parent = self,
+ flags = 'u',
+ save = name)
+
+ def _saveDisplayRegion(self, name):
+ """!Save display extents to region file
+
+ @param name region name
+ """
+ new = self.Map.GetCurrentRegion()
+
+ tmpreg = os.getenv("GRASS_REGION")
+ if tmpreg:
+ del os.environ["GRASS_REGION"]
+
+ RunCommand('g.region',
+ overwrite = True,
+ parent = self,
+ flags = 'u',
+ n = new['n'],
+ s = new['s'],
+ e = new['e'],
+ w = new['w'],
+ rows = int(new['rows']),
+ cols = int(new['cols']),
+ save = name)
+
+ if tmpreg:
+ os.environ["GRASS_REGION"] = tmpreg
+
+ def Distance(self, beginpt, endpt, screen = True):
+ """!Calculates distance
+
+ Ctypes required for LL-locations
+
+ @param beginpt first point
+ @param endpt second point
+ @param screen True for screen coordinates otherwise EN
+ """
+ if screen:
+ e1, n1 = self.Pixel2Cell(beginpt)
+ e2, n2 = self.Pixel2Cell(endpt)
+ else:
+ e1, n1 = beginpt
+ e2, n2 = endpt
+
+ dEast = (e2 - e1)
+ dNorth = (n2 - n1)
+
+ if self.Map.projinfo['proj'] == 'll' and haveCtypes:
+ dist = gislib.G_distance(e1, n1, e2, n2)
+ else:
+ dist = math.sqrt(math.pow((dEast), 2) + math.pow((dNorth), 2))
+
+ return (dist, (dEast, dNorth))
+
+ def GetMap(self):
+ """!Get render.Map() instance"""
+ return self.Map
+
+ def RegisterGraphicsToDraw(self, graphicsType, setStatusFunc = None, drawFunc = None):
+ """! This method registers graphics to draw.
+
+ @param type (string) - graphics type: "point" or "line"
+ @param setStatusFunc (function reference) - function called before drawing each item
+ Status function should be in this form: setStatusFunc(item, itemOrderNum)
+ item - passes instance of GraphicsSetItem which will be drawn
+ itemOrderNum - number of item in drawing order (from O)
+ Hidden items are also counted in drawing order.
+ @param drawFunc (function reference) - defines own function for drawing
+ If function is not defined DrawCross method is used for type "point"
+ or DrawLines method for type "line".
+
+ @return reference to GraphicsSet, which was added.
+ """
+ item = GraphicsSet(parentMapWin = self,
+ graphicsType = graphicsType,
+ setStatusFunc = setStatusFunc,
+ drawFunc = drawFunc)
+ self.graphicsSetList.append(item)
+
+ return item
+
+ def UnregisterGraphicsToDraw(self, item):
+ """!Unregisteres GraphicsSet instance
+
+ @param item (GraphicsSetItem) - item to unregister
+
+ @return True - if item was unregistered
+ @return False - if item was not found
+ """
+ if item in self.graphicsSetList:
+ self.graphicsSetList.remove(item)
+ return True
+
+ return False
Property changes on: grass/trunk/gui/wxpython/mapwin/buffered.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:keywords
+ Author Date Id
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/mapwin/decorations.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/decorations.py (rev 0)
+++ grass/trunk/gui/wxpython/mapwin/decorations.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -0,0 +1,536 @@
+"""!
+ at package mapwin.decorations
+
+ at brief Map display decorations (overlays) - text, barscale and legend
+
+Classes:
+ - decorations::OverlayController
+ - decorations::BarscaleController
+ - decorations::LegendController
+ - decorations::DecorationDialog
+ - decorations::TextLayerDialog
+
+(C) 2006-2013 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.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import wx
+from core.utils import GetLayerNameFromCmd, _
+from gui_core.forms import GUI
+
+
+class OverlayController(object):
+
+ """!Base class for decorations (barscale, legend) controller."""
+
+ def __init__(self, renderer):
+ self._renderer = renderer
+ self._overlay = None
+ self._coords = [0, 0]
+ self._pdcType = 'image'
+ self._propwin = None
+ self._defaultAt = ''
+ self._cmd = None # to be set by user
+ self._name = None # to be defined by subclass
+ self._id = None # to be defined by subclass
+
+ def SetCmd(self, cmd):
+ hasAt = False
+ for i in cmd:
+ if i.startswith("at="):
+ hasAt = True
+ break
+ if not hasAt:
+ cmd.append(self._defaultAt)
+ self._cmd = cmd
+
+ def GetCmd(self):
+ return self._cmd
+
+ cmd = property(fset=SetCmd, fget=GetCmd)
+
+ def SetCoords(self, coords):
+ self._coords = list(coords)
+
+ def GetCoords(self):
+ return self._coords
+
+ coords = property(fset=SetCoords, fget=GetCoords)
+
+ def GetPdcType(self):
+ return self._pdcType
+
+ pdcType = property(fget=GetPdcType)
+
+ def GetName(self):
+ return self._name
+
+ name = property(fget=GetName)
+
+ def GetId(self):
+ return self._id
+
+ id = property(fget=GetId)
+
+ def GetPropwin(self):
+ return self._propwin
+
+ def SetPropwin(self, win):
+ self._propwin = win
+
+ propwin = property(fget=GetPropwin, fset=SetPropwin)
+
+ def GetLayer(self):
+ return self._overlay
+
+ layer = property(fget=GetLayer)
+
+ def IsShown(self):
+ if self._overlay and self._overlay.IsActive():
+ return True
+ return False
+
+ def Show(self, show=True):
+ """!Activate or deactivate overlay."""
+ if show:
+ if not self._overlay:
+ self._add()
+ self._overlay.SetActive(True)
+ self._update()
+ else:
+ self.Hide()
+
+ def Hide(self):
+ if self._overlay:
+ self._overlay.SetActive(False)
+
+ def _add(self):
+ self._overlay = self._renderer.AddOverlay(id=self._id, ltype=self._name,
+ command=self.cmd, active=False,
+ render=False, hidden=True)
+ # check if successful
+
+ def _update(self):
+ self._renderer.ChangeOverlay(id=self._id, command=self._cmd,
+ render=False)
+
+
+class BarscaleController(OverlayController):
+
+ def __init__(self, renderer):
+ OverlayController.__init__(self, renderer)
+ self._id = 0
+ self._name = 'barscale'
+ self._defaultAt = 'at=0,95'
+ self._cmd = ['d.barscale', self._defaultAt]
+
+
+class LegendController(OverlayController):
+
+ def __init__(self, renderer):
+ OverlayController.__init__(self, renderer)
+ self._id = 1
+ self._name = 'legend'
+ # TODO: synchronize with d.legend?
+ self._defaultAt = 'at=5,50,2,5'
+ self._cmd = ['d.legend', self._defaultAt]
+
+ def ResizeLegend(self, begin, end, screenSize):
+ """!Resize legend according to given bbox coordinates."""
+ w = abs(begin[0] - end[0])
+ h = abs(begin[1] - end[1])
+ if begin[0] < end[0]:
+ x = begin[0]
+ else:
+ x = end[0]
+ if begin[1] < end[1]:
+ y = begin[1]
+ else:
+ y = end[1]
+
+ at = [(screenSize[1] - (y + h)) / float(screenSize[1]) * 100,
+ (screenSize[1] - y) / float(screenSize[1]) * 100,
+ x / float(screenSize[0]) * 100,
+ (x + w) / float(screenSize[0]) * 100]
+ atStr = "at=%d,%d,%d,%d" % (at[0], at[1], at[2], at[3])
+
+ for i, subcmd in enumerate(self._cmd):
+ if subcmd.startswith('at='):
+ self._cmd[i] = atStr
+ break
+
+ self._coords = [0, 0]
+ self.Show()
+
+
+DECOR_DIALOG_LEGEND = 0
+DECOR_DIALOG_BARSCALE = 1
+
+
+class DecorationDialog(wx.Dialog):
+
+ """!Controls setting options and displaying/hiding map overlay
+ decorations
+ """
+
+ def __init__(self, parent, title, giface, overlayController,
+ ddstyle, **kwargs):
+
+ wx.Dialog.__init__(self, parent, wx.ID_ANY, title, **kwargs)
+
+ self.parent = parent # MapFrame
+ self._overlay = overlayController
+ self._ddstyle = ddstyle
+ self._giface = giface
+
+ self._oldMouseUse = None
+ self._oldCursor = None
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ self.chkbox = wx.CheckBox(parent=self, id=wx.ID_ANY)
+ self.chkbox.SetValue(True)
+
+ if self._ddstyle == DECOR_DIALOG_LEGEND:
+ self.chkbox.SetLabel("Show legend")
+ else:
+ self.chkbox.SetLabel("Show scale and North arrow")
+
+ box.Add(item=self.chkbox, proportion=0,
+ flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
+ sizer.Add(item=box, proportion=0,
+ flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ optnbtn = wx.Button(parent=self, id=wx.ID_ANY, label=_("Set options"))
+ box.Add(item=optnbtn, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
+ sizer.Add(item=box, proportion=0,
+ flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ if self._ddstyle == DECOR_DIALOG_LEGEND:
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ self.resizeBtn = wx.ToggleButton(
+ parent=self, id=wx.ID_ANY, label=_("Set size and position"))
+ self.resizeBtn.SetToolTipString(_("Click and drag on the map display to set legend "
+ "size and position and then press OK"))
+ self.resizeBtn.Disable()
+ self.resizeBtn.Bind(wx.EVT_TOGGLEBUTTON, self.OnResize)
+ box.Add(item=self.resizeBtn, proportion=0,
+ flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
+ sizer.Add(item=box, proportion=0,
+ flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ if self._ddstyle == DECOR_DIALOG_LEGEND:
+ labelText = _("Drag legend object with mouse in pointer mode to position.\n"
+ "Double-click to change options.\n"
+ "Define raster map name for legend in properties dialog.")
+ else:
+ labelText = _("Drag scale object with mouse in pointer mode to position.\n"
+ "Double-click to change options.")
+
+ label = wx.StaticText(parent=self, id=wx.ID_ANY,
+ label=labelText)
+
+ box.Add(item=label, proportion=0,
+ flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
+ sizer.Add(item=box, proportion=0,
+ flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+ line = wx.StaticLine(
+ parent=self, id=wx.ID_ANY, size=(20, -1), style = wx.LI_HORIZONTAL)
+ sizer.Add(item=line, proportion=0,
+ flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+ # buttons
+ btnsizer = wx.StdDialogButtonSizer()
+
+ self.btnOK = wx.Button(parent=self, id=wx.ID_OK)
+ self.btnOK.SetDefault()
+ self.btnOK.Enable(self._ddstyle != DECOR_DIALOG_LEGEND)
+ btnsizer.AddButton(self.btnOK)
+
+ btnCancel = wx.Button(parent=self, id=wx.ID_CANCEL)
+ btnsizer.AddButton(btnCancel)
+ btnsizer.Realize()
+
+ sizer.Add(item=btnsizer, proportion=0,
+ flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+ #
+ # bindings
+ #
+ optnbtn.Bind(wx.EVT_BUTTON, self.OnOptions)
+ btnCancel.Bind(wx.EVT_BUTTON, lambda evt: self.CloseDialog())
+ self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ mapName, found = GetLayerNameFromCmd(self._overlay.cmd)
+ if found:
+ # enable 'OK' and 'Resize' button
+ self.btnOK.Enable()
+
+ # set title
+ self.SetTitle(_('Legend of raster map <%s>') %
+ mapName)
+
+ def OnOptions(self, event):
+ """!Sets option for decoration map overlays
+ """
+ if self._overlay.propwin is None:
+ # build properties dialog
+ GUI(parent=self.parent).ParseCommand(cmd=self._overlay.cmd,
+ completed=(self.GetOptData, self._overlay.name, ''))
+
+ else:
+ if self._overlay.propwin.IsShown():
+ self._overlay.propwin.SetFocus()
+ else:
+ self._overlay.propwin.Show()
+
+ def OnResize(self, event):
+ window = self._giface.GetMapWindow()
+ if event.GetInt():
+ self._oldMouseUse = window.mouse['use']
+ self._oldCursor = window.GetNamedCursor()
+ window.SetNamedCursor('cross')
+ window.mouse['use'] = None
+ window.mouse['box'] = 'box'
+ window.pen = wx.Pen(colour='Black', width=2, style=wx.SHORT_DASH)
+ window.mouseLeftUp.connect(self._resizeLegend)
+ else:
+ self.Restore()
+ self.DisconnectResizing()
+
+ def Restore(self):
+ """!Restore conditions before resizing"""
+ window = self._giface.GetMapWindow()
+ if self._oldCursor:
+ window.SetNamedCursor(self._oldCursor)
+ if self._oldMouseUse:
+ window.mouse['use'] = self._oldMouseUse
+
+ def DisconnectResizing(self):
+ self._giface.GetMapWindow().mouseLeftUp.disconnect(self._resizeLegend)
+
+ def _resizeLegend(self, x, y):
+ """!Update legend after drawing new legend size (moved from BufferedWindow)"""
+ self.resizeBtn.SetValue(False)
+ window = self._giface.GetMapWindow()
+ self.DisconnectResizing()
+ self.Restore()
+ # resize legend
+ screenSize = window.GetClientSizeTuple()
+ self._overlay.ResizeLegend(window.mouse["begin"], window.mouse["end"], screenSize)
+ # redraw
+ self._giface.updateMap.emit()
+
+ def CloseDialog(self):
+ """!Hide dialog"""
+ if self._ddstyle == DECOR_DIALOG_LEGEND and self.resizeBtn.GetValue():
+ self.Restore()
+ self.resizeBtn.SetValue(False)
+ self.DisconnectResizing()
+
+ self.Hide()
+
+ def OnOK(self, event):
+ """!Button 'OK' pressed"""
+ # enable or disable overlay
+ self._overlay.Show(self.chkbox.IsChecked())
+
+ # update map
+ if self.parent.IsPaneShown('3d'):
+ self.parent.MapWindow.UpdateOverlays()
+
+ self._giface.updateMap.emit()
+
+ # hide dialog
+ self.CloseDialog()
+
+ def GetOptData(self, dcmd, layer, params, propwin):
+ """!Process decoration layer data"""
+ if dcmd:
+ self._overlay.cmd = dcmd
+ self._overlay.propwin = propwin
+ if params:
+ self.btnOK.Enable()
+ if self._ddstyle == DECOR_DIALOG_LEGEND and not self.parent.IsPaneShown('3d'):
+ self.resizeBtn.Enable()
+
+ def Show(self, show=True):
+ if show:
+ self.resizeBtn.Enable(not self.parent.IsPaneShown('3d'))
+ wx.Dialog.Show(self, show)
+
+
+class TextLayerDialog(wx.Dialog):
+
+ """
+ Controls setting options and displaying/hiding map overlay decorations
+ """
+
+ def __init__(self, parent, ovlId, title, name='text',
+ pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=wx.DEFAULT_DIALOG_STYLE):
+
+ wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+ from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+
+ self.ovlId = ovlId
+ self.parent = parent
+
+ if self.ovlId in self.parent.MapWindow.textdict.keys():
+ self.currText = self.parent.MapWindow.textdict[self.ovlId]['text']
+ self.currFont = self.parent.MapWindow.textdict[self.ovlId]['font']
+ self.currClr = self.parent.MapWindow.textdict[self.ovlId]['color']
+ self.currRot = self.parent.MapWindow.textdict[self.ovlId]['rotation']
+ self.currCoords = self.parent.MapWindow.textdict[self.ovlId]['coords']
+ self.currBB = self.parent.MapWindow.textdict[self.ovlId]['bbox']
+ else:
+ self.currClr = wx.BLACK
+ self.currText = ''
+ self.currFont = self.GetFont()
+ self.currRot = 0.0
+ self.currCoords = [10, 10]
+ self.currBB = wx.Rect()
+
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ box = wx.GridBagSizer(vgap=5, hgap=5)
+
+ # show/hide
+ self.chkbox = wx.CheckBox(parent=self, id=wx.ID_ANY,
+ label=_('Show text object'))
+ if self.parent.Map.GetOverlay(self.ovlId) is None:
+ self.chkbox.SetValue(True)
+ else:
+ self.chkbox.SetValue(self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive())
+ box.Add(item=self.chkbox, span=(1, 2),
+ flag=wx.ALIGN_LEFT | wx.ALL, border=5,
+ pos=(0, 0))
+
+ # text entry
+ label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Enter text:"))
+ box.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL,
+ pos=(1, 0))
+
+ self.textentry = ExpandoTextCtrl(
+ parent=self, id=wx.ID_ANY, value="", size=(300, -1))
+ self.textentry.SetFont(self.currFont)
+ self.textentry.SetForegroundColour(self.currClr)
+ self.textentry.SetValue(self.currText)
+ # get rid of unneeded scrollbar when text box first opened
+ self.textentry.SetClientSize((300, -1))
+
+ box.Add(item=self.textentry,
+ pos=(1, 1))
+
+ # rotation
+ label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Rotation:"))
+ box.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL,
+ pos=(2, 0))
+ self.rotation = wx.SpinCtrl(parent=self, id=wx.ID_ANY, value="", pos=(30, 50),
+ size = (75, -1), style = wx.SP_ARROW_KEYS)
+ self.rotation.SetRange(-360, 360)
+ self.rotation.SetValue(int(self.currRot))
+ box.Add(item=self.rotation,
+ flag=wx.ALIGN_RIGHT,
+ pos=(2, 1))
+
+ # font
+ fontbtn = wx.Button(parent=self, id=wx.ID_ANY, label=_("Set font"))
+ box.Add(item=fontbtn,
+ flag=wx.ALIGN_RIGHT,
+ pos=(3, 1))
+
+ self.sizer.Add(item=box, proportion=1,
+ flag=wx.ALL, border=10)
+
+ # note
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ label = wx.StaticText(parent=self, id=wx.ID_ANY,
+ label=_("Drag text with mouse in pointer mode "
+ "to position.\nDouble-click to change options"))
+ box.Add(item=label, proportion=0,
+ flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
+ self.sizer.Add(item=box, proportion=0,
+ flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER | wx.ALL, border=5)
+
+ line = wx.StaticLine(parent=self, id=wx.ID_ANY,
+ size=(20, -1), style = wx.LI_HORIZONTAL)
+ self.sizer.Add(item=line, proportion=0,
+ flag=wx.EXPAND | wx.ALIGN_CENTRE | wx.ALL, border=5)
+
+ btnsizer = wx.StdDialogButtonSizer()
+
+ btn = wx.Button(parent=self, id=wx.ID_OK)
+ btn.SetDefault()
+ btnsizer.AddButton(btn)
+
+ btn = wx.Button(parent=self, id=wx.ID_CANCEL)
+ btnsizer.AddButton(btn)
+ btnsizer.Realize()
+
+ self.sizer.Add(item=btnsizer, proportion=0,
+ flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+
+ self.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+
+ # bindings
+ self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnRefit, self.textentry)
+ self.Bind(wx.EVT_BUTTON, self.OnSelectFont, fontbtn)
+ self.Bind(wx.EVT_TEXT, self.OnText, self.textentry)
+ self.Bind(wx.EVT_SPINCTRL, self.OnRotation, self.rotation)
+
+ def OnRefit(self, event):
+ """!Resize text entry to match text"""
+ self.sizer.Fit(self)
+
+ def OnText(self, event):
+ """!Change text string"""
+ self.currText = event.GetString()
+
+ def OnRotation(self, event):
+ """!Change rotation"""
+ self.currRot = event.GetInt()
+
+ event.Skip()
+
+ def OnSelectFont(self, event):
+ """!Change font"""
+ data = wx.FontData()
+ data.EnableEffects(True)
+ data.SetColour(self.currClr) # set colour
+ data.SetInitialFont(self.currFont)
+
+ dlg = wx.FontDialog(self, data)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ data = dlg.GetFontData()
+ self.currFont = data.GetChosenFont()
+ self.currClr = data.GetColour()
+
+ self.textentry.SetFont(self.currFont)
+ self.textentry.SetForegroundColour(self.currClr)
+
+ self.Layout()
+
+ dlg.Destroy()
+
+ def GetValues(self):
+ """!Get text properties"""
+ return {'text': self.currText,
+ 'font': self.currFont,
+ 'color': self.currClr,
+ 'rotation': self.currRot,
+ 'coords': self.currCoords,
+ 'active': self.chkbox.IsChecked()}
Property changes on: grass/trunk/gui/wxpython/mapwin/decorations.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/mapwin/graphics.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/graphics.py (rev 0)
+++ grass/trunk/gui/wxpython/mapwin/graphics.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -0,0 +1,328 @@
+"""!
+ at package mapwin.graphics
+
+ at brief Map display canvas - buffered window.
+
+Classes:
+ - graphics::GraphicsSet
+ - graphics::GraphicsSetItem
+
+(C) 2006-2013 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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (handlers support, GraphicsSet)
+"""
+
+
+from copy import copy
+
+import wx
+
+from core.utils import _
+
+
+class GraphicsSet:
+
+ def __init__(self, parentMapWin, graphicsType,
+ setStatusFunc=None, drawFunc=None):
+ """!Class, which contains instances of GraphicsSetItem and
+ draws them For description of parameters look at method
+ RegisterGraphicsToDraw in BufferedWindow class.
+ """
+ self.pens = {
+ "default": wx.Pen(colour=wx.BLACK, width=2, style=wx.SOLID),
+ "selected": wx.Pen(colour=wx.GREEN, width=2, style=wx.SOLID),
+ "unused": wx.Pen(colour=wx.LIGHT_GREY, width=2, style=wx.SOLID),
+ "highest": wx.Pen(colour=wx.RED, width=2, style=wx.SOLID)
+ }
+
+ # list contains instances of GraphicsSetItem
+ self.itemsList = []
+
+ self.properties = {}
+ self.graphicsType = graphicsType
+ self.parentMapWin = parentMapWin
+ self.setStatusFunc = setStatusFunc
+
+ if drawFunc:
+ self.drawFunc = drawFunc
+
+ elif self.graphicsType == "point":
+ self.properties["size"] = 5
+
+ self.properties["text"] = {}
+ self.properties["text"]['font'] = wx.Font(pointSize=self.properties["size"],
+ family=wx.FONTFAMILY_DEFAULT,
+ style=wx.FONTSTYLE_NORMAL,
+ weight=wx.FONTWEIGHT_NORMAL)
+ self.properties["text"]['active'] = True
+
+ self.drawFunc = self.parentMapWin.DrawCross
+
+ elif self.graphicsType == "line":
+ self.drawFunc = self.parentMapWin.DrawLines
+
+ def Draw(self, pdc):
+ """!Draws all containing items.
+
+ @param pdc - device context, where items are drawn
+ """
+ itemOrderNum = 0
+ for item in self.itemsList:
+ if self.setStatusFunc is not None:
+ self.setStatusFunc(item, itemOrderNum)
+
+ if item.GetPropertyVal("hide") is True:
+ itemOrderNum += 1
+ continue
+
+ if self.graphicsType == "point":
+ if item.GetPropertyVal("penName"):
+ self.parentMapWin.pen = self.pens[item.GetPropertyVal("penName")]
+ else:
+ self.parentMapWin.pen = self.pens["default"]
+
+ coords = self.parentMapWin.Cell2Pixel(item.GetCoords())
+ size = self.properties["size"]
+
+ self.properties["text"]['coords'] = [coords[0] + size, coords[1] + size, size, size]
+ self.properties["text"]['color'] = self.parentMapWin.pen.GetColour()
+ self.properties["text"]['text'] = item.GetPropertyVal("label")
+
+ self.drawFunc(pdc=pdc,
+ coords=coords,
+ text=self.properties["text"],
+ size=self.properties["size"])
+
+ elif self.graphicsType == "line":
+ if item.GetPropertyVal("penName"):
+ self.parentMapWin.polypen = self.pens[item.GetPropertyVal("penName")]
+ else:
+ self.parentMapWin.polypen = self.pens["default"]
+ coords = item.GetCoords()
+
+ self.drawFunc(pdc=pdc,
+ polycoords=coords)
+ itemOrderNum += 1
+
+ def AddItem(self, coords, penName=None, label=None, hide=False):
+ """!Append item to the list.
+
+ Added item is put to the last place in drawing order.
+ Could be 'point' or 'line' according to graphicsType.
+
+ @param coords - list of east, north coordinates (double) of item
+ Example: point: [1023, 122]
+ line: [[10, 12],[20,40],[23, 2334]]
+ @param penName (string) the 'default' pen is used if is not defined
+ @param label (string) label, which will be drawn with point. It is
+ relavant just for 'point' type.
+ @param hide (bool) If it is True, the item is not drawn
+ when self.Draw is called. Hidden items are also counted in drawing
+ order.
+
+ @return (GraphicsSetItem) - added item reference
+ """
+ item = GraphicsSetItem(coords=coords, penName=penName, label=label, hide=hide)
+ self.itemsList.append(item)
+
+ return item
+
+ def DeleteItem(self, item):
+ """!Deletes item
+
+ @param item (GraphicsSetItem) - item to remove
+
+ @return True if item was removed
+ @return False if item was not found
+ """
+ try:
+ self.itemsList.remove(item)
+ except ValueError:
+ return False
+
+ return True
+
+ def GetAllItems(self):
+ """!Returns list of all containing instances of GraphicsSetItem, in order
+ as they are drawn. If you want to change order of drawing use: SetItemDrawOrder method.
+ """
+ # user can edit objects but not order in list, that is reason,
+ # why is returned shallow copy of data list it should be used
+ # SetItemDrawOrder for changing order
+ return copy(self.itemsList)
+
+ def GetItem(self, drawNum):
+ """!Get given item from the list.
+
+ @param drawNum (int) - drawing order (index) number of item
+
+ @return instance of GraphicsSetItem which is drawn in drawNum order
+ @return False if drawNum was out of range
+ """
+ if drawNum < len(self.itemsList) and drawNum >= 0:
+ return self.itemsList[drawNum]
+ else:
+ return False
+
+ def SetPropertyVal(self, propName, propVal):
+ """!Set property value
+
+ @param propName (string) - property name: "size", "text"
+ - both properties are relevant for "point" type
+ @param propVal - property value to be set
+
+ @return True - if value was set
+ @return False - if propName is not "size" or "text" or type is "line"
+ """
+ if propName in self.properties:
+ self.properties[propName] = propVal
+ return True
+
+ return False
+
+ def GetPropertyVal(self, propName):
+ """!Get property value
+
+ Raises KeyError if propName is not "size" or "text" or type is
+ "line"
+
+ @param propName (string) property name: "size", "text"
+ both properties are relevant for "point" type
+
+ @return value of property
+ """
+ if propName in self.properties:
+ return self.properties[propName]
+
+ raise KeyError(_("Property does not exist: %s") % (propName))
+
+ def AddPen(self, penName, pen):
+ """!Add pen
+
+ @param penName (string) - name of added pen
+ @param pen (wx.Pen) - added pen
+
+ @return True - if pen was added
+ @return False - if pen already exists
+ """
+ if penName in self.pens:
+ return False
+
+ self.pens[penName] = pen
+ return True
+
+ def GetPen(self, penName):
+ """!Get existing pen
+
+ @param penName (string) - name of pen
+
+ @return wx.Pen reference if is found
+ @return None if penName was not found
+ """
+ if penName in self.pens:
+ return self.pens[penName]
+
+ return None
+
+ def SetItemDrawOrder(self, item, drawNum):
+ """!Set draw order for item
+
+ @param item (GraphicsSetItem)
+ @param drawNum (int) - drawing order of item to be set
+
+ @return True - if order was changed
+ @return False - if drawNum is out of range or item was not found
+ """
+ if drawNum < len(self.itemsList) and drawNum >= 0 and \
+ item in self.itemsList:
+ self.itemsList.insert(drawNum, self.itemsList.pop(self.itemsList.index(item)))
+ return True
+
+ return False
+
+ def GetItemDrawOrder(self, item):
+ """!Get draw order for given item
+
+ @param item (GraphicsSetItem)
+
+ @return (int) - drawing order of item
+ @return None - if item was not found
+ """
+ try:
+ return self.itemsList.index(item)
+ except ValueError:
+ return None
+
+
+class GraphicsSetItem:
+
+ def __init__(self, coords, penName=None, label=None, hide=False):
+ """!Could be point or line according to graphicsType in
+ GraphicsSet class
+
+ @param coords - list of coordinates (double) of item
+ Example: point: [1023, 122]
+ line: [[10, 12],[20,40],[23, 2334]]
+ @param penName (string) if it is not defined 'default' pen is used
+ @param label (string) label, which will be drawn with point. It is
+ relevant just for 'point' type
+ @param hide (bool) if it is True, item is not drawn
+ Hidden items are also counted in drawing order in
+ GraphicsSet class.
+ """
+ self.coords = coords
+
+ self.properties = {"penName": penName,
+ "hide": hide,
+ "label": label}
+
+ def SetPropertyVal(self, propName, propVal):
+ """!Set property value
+
+ @param propName (string) - property name: "penName", "hide" or "label"
+ - property "label" is relevant just for 'point' type
+ @param propVal - property value to be set
+
+ @return True - if value was set
+ @return False - if propName is not "penName", "hide" or "label"
+ """
+ if propName in self.properties:
+ self.properties[propName] = propVal
+ return True
+
+ return False
+
+ def GetPropertyVal(self, propName):
+ """!Get property value
+
+ Raises KeyError if propName is not "penName", "hide" or
+ "label".
+
+ @param propName (string) - property name: "penName", "hide" or "label"
+ - property "label" is relevant just for 'point' type
+
+ @return value of property
+ """
+ if propName in self.properties:
+ return self.properties[propName]
+
+ raise KeyError(_("Property does not exist: %s") % (propName))
+
+ def SetCoords(self, coords):
+ """!Set coordinates of item
+
+ @param coords - list of east, north coordinates (double) of item
+ Example: point: [1023, 122]
+ line: [[10, 12],[20,40],[23, 2334]]
+ """
+ self.coords = coords
+
+ def GetCoords(self):
+ """!Get item coordinates
+
+ @returns coordinates
+ """
+ return self.coords
Property changes on: grass/trunk/gui/wxpython/mapwin/graphics.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Modified: grass/trunk/gui/wxpython/nviz/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/nviz/mapwindow.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/nviz/mapwindow.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -37,7 +37,7 @@
from core.gcmd import GMessage, GException, GError
from core.debug import Debug
-from gui_core.mapwindow import MapWindow
+from mapwin.base import MapWindowBase
from core.settings import UserSettings
from nviz.workspace import NvizSettings
from nviz.animation import Animation
@@ -69,7 +69,7 @@
"""!Get display instance"""
return self._display
-class GLWindow(MapWindow, glcanvas.GLCanvas):
+class GLWindow(MapWindowBase, glcanvas.GLCanvas):
"""!OpenGL canvas for Map Display Window"""
def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY):
"""All parameters except for id are mandatory. The todo is to remove
@@ -90,10 +90,10 @@
else:
glcanvas.GLCanvas.__init__(self, parent, id)
- MapWindow.__init__(self, parent=parent, giface=giface, Map=Map)
+ MapWindowBase.__init__(self, parent=parent, giface=giface, Map=Map)
self.Hide()
- # TODO: same signals as in BufferedWindow
+ # TODO: same signals as in BufferedMapWindow
# same interface is good, but how to ensure same names
# or avoid duplication, define in map window base class?
Modified: grass/trunk/gui/wxpython/vdigit/dialogs.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/dialogs.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/vdigit/dialogs.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -42,7 +42,7 @@
@param cats directory of lines (layer/categories) - used by vdigit
@param style dialog style
"""
- self.parent = parent # mapdisplay.BufferedWindow class instance
+ self.parent = parent # map window class instance
self.digit = parent.digit
# map name
@@ -584,7 +584,7 @@
"""
wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
- self.parent = parent # mapdisplay.BufferedWindow class instance
+ self.parent = parent # map window class instance
# panel = wx.Panel(parent=self, id=wx.ID_ANY)
@@ -648,7 +648,7 @@
wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style,
pos = pos)
- self.parent = parent # BufferedWindow
+ self.parent = parent # map window instance
self.data = data
self.winList = []
Modified: grass/trunk/gui/wxpython/vdigit/main.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/main.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/vdigit/main.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -29,7 +29,7 @@
class VDigit(IVDigit):
def __init__(self, mapwindow):
"""!Base class of vector digitizer
-
- @param mapwindow reference to mapwindow (mapdisp_window.BufferedWindow) instance
+
+ @param mapwindow reference to a map window instance
"""
IVDigit.__init__(self, mapwindow)
Modified: grass/trunk/gui/wxpython/vdigit/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/mapwindow.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/vdigit/mapwindow.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -22,22 +22,22 @@
from dbmgr.dialogs import DisplayAttributesDialog
from core.gcmd import RunCommand, GMessage, GError
from core.debug import Debug
-from mapdisp.mapwindow import BufferedWindow
+from mapwin.buffered import BufferedMapWindow
from core.settings import UserSettings
from core.utils import ListOfCatsToRange, _
from core.globalvar import QUERYLAYER
from vdigit.dialogs import VDigitCategoryDialog, VDigitZBulkDialog, VDigitDuplicatesDialog
from gui_core import gselect
-class VDigitWindow(BufferedWindow):
+class VDigitWindow(BufferedMapWindow):
"""!A Buffered window extended for vector digitizer.
"""
def __init__(self, parent, giface, Map, properties, tree=None,
id=wx.ID_ANY, lmgr=None,
style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
- BufferedWindow.__init__(self, parent=parent, giface=giface, Map=Map,
- properties=properties,
- style=style, **kwargs)
+ BufferedMapWindow.__init__(self, parent=parent, giface=giface, Map=Map,
+ properties=properties,
+ style=style, **kwargs)
self.lmgr = lmgr
self.tree = tree
self.pdcVector = wx.PseudoDC()
@@ -49,13 +49,13 @@
# Parameter text is a string with information
# currently used only for coordinates of mouse cursor + segmnt and
# total feature length
- self.digitizingInfo = Signal('BufferedWindow.digitizingInfo')
+ self.digitizingInfo = Signal('VDigitWindow.digitizingInfo')
# Emitted when some info about digitizing is or will be availbale
- self.digitizingInfoAvailable = Signal('BufferedWindow.digitizingInfo')
+ self.digitizingInfoAvailable = Signal('VDigitWindow.digitizingInfo')
# Emitted when some info about digitizing is or will be availbale
# digitizingInfo signal is emmited only between digitizingInfoAvailable
# and digitizingInfoUnavailable signals
- self.digitizingInfoUnavailable = Signal('BufferedWindow.digitizingInfo')
+ self.digitizingInfoUnavailable = Signal('VDigitWindow.digitizingInfo')
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.mouseMoving.connect(self._mouseMovingToDigitizingInfo)
@@ -469,8 +469,8 @@
# add line or boundary -> remove last point from the line
try:
removed = self.polycoords.pop()
- Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
- [removed,])
+ Debug.msg(4, "VDigitWindow.OnMiddleDown(): polycoords_poped=%s" %
+ [removed, ])
# self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
except:
pass
@@ -1050,10 +1050,10 @@
def _onMouseMoving(self, event):
self.mouse['end'] = event.GetPositionTuple()[:]
-
- Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
- (self.mouse['end'][0], self.mouse['end'][1]))
+ Debug.msg(5, "VDigitWindow.OnMouseMoving(): coords=%f,%f" %
+ (self.mouse['end'][0], self.mouse['end'][1]))
+
action = self.toolbar.GetAction()
if action == "addLine" and \
self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
Modified: grass/trunk/gui/wxpython/vdigit/wxdigit.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/wxdigit.py 2013-08-06 11:20:43 UTC (rev 57426)
+++ grass/trunk/gui/wxpython/vdigit/wxdigit.py 2013-08-06 16:10:51 UTC (rev 57427)
@@ -129,7 +129,7 @@
def __init__(self, mapwindow, driver = DisplayDriver):
"""!Base class for vector digitizer (ctypes interface)
- @param mapwindow reference for map window (BufferedWindow)
+ @param mapwindow reference to a map window
"""
self.poMapInfo = None # pointer to Map_info
self.mapWindow = mapwindow
More information about the grass-commit
mailing list