File: base.py

package info (click to toggle)
grass 8.4.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 277,040 kB
  • sloc: ansic: 460,798; python: 227,732; cpp: 42,026; sh: 11,262; makefile: 7,007; xml: 3,637; sql: 968; lex: 520; javascript: 484; yacc: 450; asm: 387; perl: 157; sed: 25; objc: 6; ruby: 4
file content (460 lines) | stat: -rw-r--r-- 15,320 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
"""
@package mapwin.mapwindow

@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.

@author Martin Landa <landa.martin gmail.com>
@author Michael Barton
@author Jachym Cepicky
@author Vaclav Petras <wenzeslaus gmail.com> (handlers support)
@author Stepan Turek <stepan.turek seznam.cz> (handlers support)
"""

import wx

from core.settings import UserSettings
from core.gcmd import GError
from gui_core.wrap import StockCursor

from grass.script import core as grass
from grass.pydispatch.signal import Signal


class MapWindowProperties:
    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")
        self._useDefinedProjection = False
        self.useDefinedProjectionChanged = Signal(
            "MapWindowProperties.useDefinedProjectionChanged"
        )
        self._sbItem = None
        self.sbItemChanged = Signal("MapWindowProperties.sbItemChanged")

    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)

    @property
    def useDefinedProjection(self):
        return self._useDefinedProjection

    @useDefinedProjection.setter
    def useDefinedProjection(self, value):
        if value != self._useDefinedProjection:
            self._useDefinedProjection = value
            self.useDefinedProjectionChanged.emit(value=value)

    @property
    def epsg(self):
        return UserSettings.Get(group="projection", key="statusbar", subkey="epsg")

    @property
    def sbItem(self):
        return self._sbItem

    @sbItem.setter
    def sbItem(self, mode):
        if mode != self._sbItem:
            self._sbItem = mode
            self.sbItemChanged.emit(mode=mode)


class MapWindowBase:
    """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")
        # emitted when overlay should be hidden
        self.overlayRemoved = Signal("MapWindow.overlayRemoved")

        # 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": StockCursor(cursorId=wx.CURSOR_ARROW),
            "cross": StockCursor(cursorId=wx.CURSOR_CROSS),
            "hand": StockCursor(cursorId=wx.CURSOR_HAND),
            "pencil": StockCursor(cursorId=wx.CURSOR_PENCIL),
            "sizenwse": StockCursor(cursorId=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.items():
            self.Bind(ev, self.EventTypeHandler(handlers))

    def EventTypeHandler(self, evHandlers):
        return lambda event: self.HandlersCaller(event, evHandlers)

    def HandlersCaller(self, event, handlers):
        """Helper function which calls all handlers registered for
        event
        """
        for handler in handlers:
            try:
                handler(event)
            except:
                handlers.remove(handler)
                GError(
                    parent=self,
                    message=_(
                        "Error occurred during calling of handler: %s \n"
                        "Handler was unregistered."
                    )
                    % handler.__name__,
                )

        event.Skip()

    def RegisterMouseEventHandler(self, event, handler, cursor=None):
        """Binds event handler

        @deprecated This method is deprecated. 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
        interesting 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 occurs inside of handler, the handler is removed.

        Before handler is unregistered it is called with
        string value "unregistered" of event parameter.

        ::

            # 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, "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()


        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.items():
            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

        @deprecated This method is deprecated. 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.items():
            for handler in handlers:
                try:
                    handler("unregistered")
                    handlers.remove(handler)
                except:
                    GError(
                        parent=self,
                        message=_(
                            "Error occurred during unregistration of handler: %s \n \
                                       Handler was unregistered."
                        )
                        % handler.__name__,
                    )
                    handlers.remove(handler)

    def UnregisterMouseEventHandler(self, event, handler):
        """Unbinds event handler for event

        @deprecated This method is deprecated. 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.items():
            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 occurred 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

        :func:`GetLastEN`
        """
        try:
            self.lastEN = self.Pixel2Cell(event.GetPosition())
        except ValueError:
            self.lastEN = None

        event.Skip()

    def GetLastEN(self):
        """Returns last coordinates of mouse cursor.

        @deprecated This method is deprecated. Use Signal with coordinates as
        parameters.

        :func:`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)

    def SetModePointer(self):
        """Sets mouse mode to pointer."""
        self.mouse["use"] = "pointer"
        self.mouse["box"] = "point"
        self.SetNamedCursor("default")

    def SetModePan(self):
        """Sets mouse mode to pan."""
        self.mouse["use"] = "pan"
        self.mouse["box"] = "box"
        self.zoomtype = 0
        self.SetNamedCursor("hand")

    def SetModeZoomIn(self):
        self._setModeZoom(zoomType=1)

    def SetModeZoomOut(self):
        self._setModeZoom(zoomType=-1)

    def _setModeZoom(self, zoomType):
        self.zoomtype = zoomType
        self.mouse["use"] = "zoom"
        self.mouse["box"] = "box"
        self.pen = wx.Pen(colour="Red", width=2, style=wx.SHORT_DASH)
        self.SetNamedCursor("cross")

    def SetModeDrawRegion(self):
        self.mouse["use"] = "drawRegion"
        self.mouse["box"] = "box"
        self.pen = wx.Pen(colour="Red", width=2, style=wx.SHORT_DASH)
        self.SetNamedCursor("cross")

    def SetModeQuery(self):
        """Query mode on"""
        self.mouse["use"] = "query"
        self.mouse["box"] = "point"
        self.zoomtype = 0
        self.SetNamedCursor("cross")

    def DisactivateWin(self):
        """Use when the class instance is hidden in MapFrame."""
        raise NotImplementedError()

    def ActivateWin(self):
        """Used when the class instance is activated in MapFrame."""
        raise NotImplementedError()