2
2
@package gui_core.mapdisp
4
4
@brief Base classes for Map display window
7
7
- mapdisp::MapFrameBase
8
- mapdisp::SingleMapFrame
9
- mapdisp::DoubleMapFrame
9
(C) 2009-2011 by the GRASS Development Team
11
(C) 2009-2014 by the GRASS Development Team
11
13
This program is free software under the GNU General Public License
12
14
(>=v2). Read the file COPYING that comes with GRASS for details.
14
16
@author Martin Landa <landa.martin gmail.com>
15
17
@author Michael Barton <michael.barton@asu.edu>
18
@author Vaclav Petras <wenzeslaus gmail.com>
19
@author Anna Kratochvilova <kratochanna gmail.com>
23
from core import globalvar
24
from core.debug import Debug
28
from core import globalvar
29
from core.debug import Debug
30
from core.utils import _
31
from gui_core.toolbars import ToolSwitcher
26
33
from grass.script import core as grass
28
36
class MapFrameBase(wx.Frame):
29
"""!Base class for map display window
31
Derived class must use statusbarManager or override
32
GetProperty, SetProperty and HasProperty methods.
37
"""Base class for map display window
39
Derived class must use (create and initialize) \c statusbarManager
41
GetProperty(), SetProperty() and HasProperty() methods.
43
Several methods has to be overriden or
44
\c NotImplementedError("MethodName") will be raised.
33
46
If derived class enables and disables auto-rendering,
34
47
it should override IsAutoRendered method.
49
It is expected that derived class will call _setUpMapWindow().
51
Derived class can has one or more map windows (and map renderes)
52
but implementation of MapFrameBase expects that one window and
53
one map will be current.
54
Current instances of map window and map renderer should be returned
55
by methods GetWindow() and GetMap() respectively.
57
AUI manager is stored in \c self._mgr.
36
59
def __init__(self, parent = None, id = wx.ID_ANY, title = None,
37
style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
38
Map = None, auimgr = None, name = None, **kwargs):
40
@param toolbars array of activated toolbars, e.g. ['map', 'digit']
41
@param Map instance of render.Map
42
@param auimgs AUI manager
43
@param name frame name
44
@param kwargs wx.Frame attributes
60
style = wx.DEFAULT_FRAME_STYLE,
61
auimgr = None, name = None, **kwargs):
48
self.Map = Map # instance of render.Map
65
Use \a auimgr parameter only if you know what you are doing.
67
:param parent: gui parent
69
:param title: window title
70
:param style: \c wx.Frame style
71
:param toolbars: array of activated toolbars, e.g. ['map', 'digit']
72
:param auimgr: AUI manager (if \c None, wx.aui.AuiManager is used)
73
:param name: frame name
74
:param kwargs: arguments passed to \c wx.Frame
49
77
self.parent = parent
51
79
wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
56
# "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
57
"default" : wx.StockCursor(wx.CURSOR_ARROW),
58
"cross" : wx.StockCursor(wx.CURSOR_CROSS),
59
"hand" : wx.StockCursor(wx.CURSOR_HAND),
60
"pencil" : wx.StockCursor(wx.CURSOR_PENCIL),
61
"sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
65
82
# set the size & system icon
78
self._mgr = wx.aui.AuiManager(self)
96
self._mgr = wx.aui.AuiManager(self)
80
def _initMap(self, map):
81
"""!Initialize map display, set dimensions and map region
100
# handles switching between tools in different toolbars
101
self._toolSwitcher = ToolSwitcher()
102
self._toolSwitcher.toggleToolChanged.connect(self._onToggleTool)
104
# set accelerator table for fullscreen
105
fullScreenId = wx.NewId()
106
self.Bind(wx.EVT_MENU, self.OnFullScreen, id=fullScreenId)
107
accelTable = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_F11, fullScreenId)])
108
self.SetAcceleratorTable(accelTable)
110
def _initMap(self, Map):
111
"""Initialize map display, set dimensions and map region
83
if not grass.find_program('g.region', ['--help']):
113
if not grass.find_program('g.region', '--help'):
84
114
sys.exit(_("GRASS module '%s' not found. Unable to start map "
85
115
"display window.") % 'g.region')
87
self.width, self.height = self.GetClientSize()
89
117
Debug.msg(2, "MapFrame._initMap():")
90
map.ChangeMapSize(self.GetClientSize())
91
map.region = map.GetRegion() # g.region -upgc
118
Map.ChangeMapSize(self.GetClientSize())
119
Map.region = Map.GetRegion() # g.region -upgc
92
120
# self.Map.SetRegion() # adjust region to match display window
122
def _onToggleTool(self):
123
self.GetWindow().UnregisterAllHandlers()
125
def OnSize(self, event):
126
"""Adjust statusbar on changing size"""
127
# reposition checkbox in statusbar
128
self.StatusbarReposition()
131
self.StatusbarUpdate()
133
def OnFullScreen(self, event):
134
"""!Switch fullscreen mode, hides also toolbars"""
135
for toolbar in self.toolbars.keys():
136
self._mgr.GetPane(self.toolbars[toolbar]).Show(self.IsFullScreen())
138
self.ShowFullScreen(not self.IsFullScreen())
141
def GetToolSwitcher(self):
142
return self._toolSwitcher
94
144
def SetProperty(self, name, value):
96
146
self.statusbarManager.SetProperty(name, value)
98
148
def GetProperty(self, name):
99
"""!Returns property"""
149
"""Returns property"""
100
150
return self.statusbarManager.GetProperty(name)
102
152
def HasProperty(self, name):
103
"""!Checks whether object has property"""
153
"""Checks whether object has property"""
104
154
return self.statusbarManager.HasProperty(name)
106
156
def GetPPM(self):
107
"""! Get pixel per meter
109
@todo now computed every time, is it necessary?
110
@todo enable user to specify ppm (and store it in UserSettings)
157
"""Get pixel per meter
160
now computed every time, is it necessary?
163
enable user to specify ppm (and store it in UserSettings)
112
165
# TODO: need to be fixed...
113
166
### screen X region problem
213
269
def StatusbarUpdate(self):
214
"""!Update statusbar content"""
270
"""Update statusbar content"""
271
Debug.msg(5, "MapFrameBase.StatusbarUpdate()")
215
272
self.statusbarManager.Update()
217
274
def IsAutoRendered(self):
218
"""!Check if auto-rendering is enabled"""
219
return self.GetProperty('render')
275
"""Check if auto-rendering is enabled"""
276
# TODO: this is now not the right place to access this attribute
277
# TODO: add mapWindowProperties to init parameters
278
# and pass the right object in the init of derived class?
279
# or do not use this method at all, let mapwindow decide
280
return self.mapWindowProperties.autoRender
221
282
def CoordinatesChanged(self):
222
"""!Shows current coordinates on statusbar.
224
Used in BufferedWindow to report change of map coordinates (under mouse cursor).
283
"""Shows current coordinates on statusbar.
226
self.statusbarManager.ShowItem('coordinates')
285
# assuming that the first mode is coordinates
286
# probably shold not be here but good solution is not available now
287
if self.statusbarManager.GetMode() == 0:
288
self.statusbarManager.ShowItem('coordinates')
228
290
def StatusbarReposition(self):
229
"""!Reposition items in statusbar"""
291
"""Reposition items in statusbar"""
230
292
self.statusbarManager.Reposition()
232
294
def StatusbarEnableLongHelp(self, enable = True):
233
"""!Enable/disable toolbars long help"""
295
"""Enable/disable toolbars long help"""
234
296
for toolbar in self.toolbars.itervalues():
235
297
toolbar.EnableLongHelp(enable)
237
299
def IsStandalone(self):
238
"""!Check if Map display is standalone"""
300
"""Check if map frame is standalone"""
239
301
raise NotImplementedError("IsStandalone")
241
303
def OnRender(self, event):
242
"""!Re-render map composition (each map layer)
304
"""Re-render map composition (each map layer)
244
306
raise NotImplementedError("OnRender")
246
308
def OnDraw(self, event):
247
"""!Re-display current map composition
309
"""Re-display current map composition
249
311
self.MapWindow.UpdateMap(render = False)
251
313
def OnErase(self, event):
254
316
self.MapWindow.EraseMap()
256
318
def OnZoomIn(self, event):
258
Set mouse cursor, zoombox attributes, and zoom direction
260
toolbar = self.GetMapToolbar()
261
self._switchTool(toolbar, event)
263
win = self.GetWindow()
264
self._prepareZoom(mapWindow = win, zoomType = 1)
319
"""Zoom in the map."""
320
self.MapWindow.SetModeZoomIn()
266
322
def OnZoomOut(self, event):
267
"""!Zoom out the map.
268
Set mouse cursor, zoombox attributes, and zoom direction
270
toolbar = self.GetMapToolbar()
271
self._switchTool(toolbar, event)
273
win = self.GetWindow()
274
self._prepareZoom(mapWindow = win, zoomType = -1)
276
def _prepareZoom(self, mapWindow, zoomType):
277
"""!Prepares MapWindow for zoom, toggles toolbar
279
@param mapWindow MapWindow to prepare
280
@param zoomType 1 for zoom in, -1 for zoom out
282
mapWindow.mouse['use'] = "zoom"
283
mapWindow.mouse['box'] = "box"
284
mapWindow.zoomtype = zoomType
285
mapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
288
mapWindow.SetCursor(self.cursors["cross"])
290
def _switchTool(self, toolbar, event):
291
"""!Helper function to switch tools"""
293
toolbar.OnTool(event)
294
toolbar.action['desc'] = ''
323
"""Zoom out the map."""
324
self.MapWindow.SetModeZoomOut()
326
def _setUpMapWindow(self, mapWindow):
327
"""Binds map windows' zoom history signals to map toolbar."""
328
# enable or disable zoom history tool
329
mapWindow.zoomHistoryAvailable.connect(
331
self.GetMapToolbar().Enable('zoomBack', enable=True))
332
mapWindow.zoomHistoryUnavailable.connect(
334
self.GetMapToolbar().Enable('zoomBack', enable=False))
335
mapWindow.mouseMoving.connect(self.CoordinatesChanged)
296
337
def OnPointer(self, event):
297
"""!Sets mouse mode to pointer."""
298
self.MapWindow.mouse['use'] = 'pointer'
338
"""Sets mouse mode to pointer."""
339
self.MapWindow.SetModePointer()
300
341
def OnPan(self, event):
301
"""!Panning, set mouse to drag
303
toolbar = self.GetMapToolbar()
304
self._switchTool(toolbar, event)
306
win = self.GetWindow()
307
self._preparePan(mapWindow = win)
309
def _preparePan(self, mapWindow):
310
"""!Prepares MapWindow for pan, toggles toolbar
312
@param mapWindow MapWindow to prepare
314
mapWindow.mouse['use'] = "pan"
315
mapWindow.mouse['box'] = "pan"
316
mapWindow.zoomtype = 0
319
mapWindow.SetCursor(self.cursors["hand"])
342
"""Panning, set mouse to drag
344
self.MapWindow.SetModePan()
321
346
def OnZoomBack(self, event):
322
"""!Zoom last (previously stored position)
347
"""Zoom last (previously stored position)
324
349
self.MapWindow.ZoomBack()
326
351
def OnZoomToMap(self, event):
328
353
Set display extents to match selected raster (including NULLs)
331
356
self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
333
358
def OnZoomToWind(self, event):
334
"""!Set display geometry to match computational region
359
"""Set display geometry to match computational region
335
360
settings (set with g.region)
337
362
self.MapWindow.ZoomToWind()
339
364
def OnZoomToDefault(self, event):
340
"""!Set display geometry to match default region settings
365
"""Set display geometry to match default region settings
342
367
self.MapWindow.ZoomToDefault()
370
class SingleMapFrame(MapFrameBase):
371
"""Frame with one map window.
373
It is base class for frames which needs only one map.
375
Derived class should have \c self.MapWindow or
376
it has to override GetWindow() methods.
378
@note To access maps use getters only
379
(when using class or when writing class itself).
381
def __init__(self, parent = None, giface = None, id = wx.ID_ANY, title = None,
382
style = wx.DEFAULT_FRAME_STYLE,
384
auimgr = None, name = None, **kwargs):
387
:param parent: gui parent
389
:param title: window title
390
:param style: \c wx.Frame style
391
:param map: instance of render.Map
392
:param name: frame name
393
:param kwargs: arguments passed to MapFrameBase
396
MapFrameBase.__init__(self, parent = parent, id = id, title = title,
398
auimgr = auimgr, name = name, **kwargs)
400
self.Map = Map # instance of render.Map
403
# initialize region values
405
self._initMap(Map = self.Map)
408
"""Returns map (renderer) instance"""
412
"""Returns map window"""
413
return self.MapWindow
415
def GetWindows(self):
416
"""Returns list of map windows"""
417
return [self.MapWindow]
419
def OnRender(self, event):
420
"""Re-render map composition (each map layer)
422
self.GetWindow().UpdateMap(render = True, renderVector = True)
425
self.StatusbarUpdate()
428
class DoubleMapFrame(MapFrameBase):
429
"""Frame with two map windows.
431
It is base class for frames which needs two maps.
432
There is no primary and secondary map. Both maps are equal.
433
However, one map is current.
435
It is expected that derived class will call _bindWindowsActivation()
436
when both map windows will be initialized.
438
Drived class should have method GetMapToolbar() returns toolbar
439
which has methods SetActiveMap() and Enable().
441
@note To access maps use getters only
442
(when using class or when writing class itself).
445
Use it in GCP manager (probably changes to both DoubleMapFrame
446
and GCP MapFrame will be neccessary).
448
def __init__(self, parent = None, id = wx.ID_ANY, title = None,
449
style = wx.DEFAULT_FRAME_STYLE,
450
firstMap = None, secondMap = None,
451
auimgr = None, name = None, **kwargs):
454
\a firstMap is set as active (by assign it to \c self.Map).
455
Derived class should assging to \c self.MapWindow to make one
456
map window current by dafault.
458
:param parent: gui parent
460
:param title: window title
461
:param style: \c wx.Frame style
462
:param name: frame name
463
:param kwargs: arguments passed to MapFrameBase
466
MapFrameBase.__init__(self, parent = parent, id = id, title = title,
468
auimgr = auimgr, name = name, **kwargs)
470
self.firstMap = firstMap
471
self.secondMap = secondMap
475
# initialize region values
477
self._initMap(Map = self.firstMap)
478
self._initMap(Map = self.secondMap)
479
self._bindRegions = False
481
def _bindWindowsActivation(self):
482
self.GetFirstWindow().Bind(wx.EVT_ENTER_WINDOW, self.ActivateFirstMap)
483
self.GetSecondWindow().Bind(wx.EVT_ENTER_WINDOW, self.ActivateSecondMap)
485
def _onToggleTool(self):
486
self.GetFirstWindow().UnregisterAllHandlers()
487
self.GetSecondWindow().UnregisterAllHandlers()
489
def GetFirstMap(self):
490
"""Returns first Map instance
494
def GetSecondMap(self):
495
"""Returns second Map instance
497
return self.secondMap
499
def GetFirstWindow(self):
500
"""Get first map window"""
501
return self.firstMapWindow
503
def GetSecondWindow(self):
504
"""Get second map window"""
505
return self.secondMapWindow
508
"""Returns current map (renderer) instance
510
@note Use this method to access current map renderer.
511
(It is not guarented that current map will be stored in
512
\c self.Map in future versions.)
517
"""Returns current map window
521
return self.MapWindow
523
def GetWindows(self):
524
"""Return list of all windows"""
525
return [self.firstMapWindow, self.secondMapWindow]
527
def ActivateFirstMap(self, event = None):
528
"""Make first Map and MapWindow active and (un)bind regions of the two Maps."""
529
if self.MapWindow == self.firstMapWindow:
532
self.Map = self.firstMap
533
self.MapWindow = self.firstMapWindow
534
self.GetMapToolbar().SetActiveMap(0)
536
# bind/unbind regions
537
if self._bindRegions:
538
self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap)
539
self.secondMapWindow.zoomChanged.disconnect(self.OnZoomChangedSecondMap)
541
def ActivateSecondMap(self, event = None):
542
"""Make second Map and MapWindow active and (un)bind regions of the two Maps."""
543
if self.MapWindow == self.secondMapWindow:
546
self.Map = self.secondMap
547
self.MapWindow = self.secondMapWindow
548
self.GetMapToolbar().SetActiveMap(1)
550
if self._bindRegions:
551
self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap)
552
self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap)
554
def SetBindRegions(self, on):
555
"""Set or unset binding display regions."""
556
self._bindRegions = on
559
if self.MapWindow == self.firstMapWindow:
560
self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap)
562
self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap)
564
if self.MapWindow == self.firstMapWindow:
565
self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap)
567
self.secondMapWindow.zoomChanged.disconnect(self.OnZoomChangedSecondMap)
569
def OnZoomChangedFirstMap(self):
570
"""Display region of the first window (Map) changed.
572
Synchronize the region of the second map and re-render it.
573
This is the default implementation which can be overridden.
575
region = self.GetFirstMap().GetCurrentRegion()
576
self.GetSecondMap().region.update(region)
577
self.Render(mapToRender = self.GetSecondWindow())
579
def OnZoomChangedSecondMap(self):
580
"""Display region of the second window (Map) changed.
582
Synchronize the region of the second map and re-render it.
583
This is the default implementation which can be overridden.
585
region = self.GetSecondMap().GetCurrentRegion()
586
self.GetFirstMap().region.update(region)
587
self.Render(mapToRender = self.GetFirstWindow())
589
def OnZoomIn(self, event):
590
"""Zoom in the map."""
591
self.GetFirstWindow().SetModeZoomIn()
592
self.GetSecondWindow().SetModeZoomIn()
594
def OnZoomOut(self, event):
595
"""Zoom out the map."""
596
self.GetFirstWindow().SetModeZoomOut()
597
self.GetSecondWindow().SetModeZoomOut()
599
def OnPan(self, event):
600
"""Panning, set mouse to pan"""
601
self.GetFirstWindow().SetModePan()
602
self.GetSecondWindow().SetModePan()
604
def OnPointer(self, event):
605
"""Set pointer mode (dragging overlays)"""
606
self.GetFirstWindow().SetModePointer()
607
self.GetSecondWindow().SetModePointer()
609
def OnQuery(self, event):
611
self.GetFirstWindow().SetModeQuery()
612
self.GetSecondWindow().SetModeQuery()
614
def OnRender(self, event):
615
"""Re-render map composition (each map layer)
617
self.Render(mapToRender = self.GetFirstWindow())
618
self.Render(mapToRender = self.GetSecondWindow())
620
def Render(self, mapToRender):
621
"""Re-render map composition"""
622
mapToRender.UpdateMap(render = True,
623
renderVector = mapToRender == self.GetFirstWindow())
626
self.StatusbarUpdate()
628
def OnErase(self, event):
631
self.Erase(mapToErase = self.GetFirstWindow())
632
self.Erase(mapToErase = self.GetSecondWindow())
634
def Erase(self, mapToErase):
637
mapToErase.EraseMap()
639
def OnDraw(self, event):
640
"""Re-display current map composition
642
self.Draw(mapToDraw = self.GetFirstWindow())
643
self.Draw(mapToDraw = self.GetSecondWindow())
645
def Draw(self, mapToDraw):
646
"""Re-display current map composition
648
mapToDraw.UpdateMap(render = False)