base.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. """
  2. @package mapwin.mapwindow
  3. @brief Map display canvas basic functionality - base class and properties.
  4. Classes:
  5. - mapwindow::MapWindowProperties
  6. - mapwindow::MapWindowBase
  7. (C) 2006-2012 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Martin Landa <landa.martin gmail.com>
  11. @author Michael Barton
  12. @author Jachym Cepicky
  13. @author Vaclav Petras <wenzeslaus gmail.com> (handlers support)
  14. @author Stepan Turek <stepan.turek seznam.cz> (handlers support)
  15. """
  16. import wx
  17. from core.settings import UserSettings
  18. from core.gcmd import GError
  19. from core.utils import _
  20. from grass.script import core as grass
  21. from grass.pydispatch.signal import Signal
  22. class MapWindowProperties(object):
  23. def __init__(self):
  24. self._resolution = None
  25. self.resolutionChanged = Signal(
  26. 'MapWindowProperties.resolutionChanged')
  27. self._autoRender = None
  28. self.autoRenderChanged = Signal(
  29. 'MapWindowProperties.autoRenderChanged')
  30. self._showRegion = None
  31. self.showRegionChanged = Signal(
  32. 'MapWindowProperties.showRegionChanged')
  33. self._alignExtent = None
  34. self.alignExtentChanged = Signal(
  35. 'MapWindowProperties.alignExtentChanged')
  36. def setValuesFromUserSettings(self):
  37. """Convenient function to get values from user settings into this object."""
  38. self._resolution = UserSettings.Get(group='display',
  39. key='compResolution',
  40. subkey='enabled')
  41. self._autoRender = UserSettings.Get(group='display',
  42. key='autoRendering',
  43. subkey='enabled')
  44. self._showRegion = False # in statusbar.py was not from settings
  45. self._alignExtent = UserSettings.Get(group='display',
  46. key='alignExtent',
  47. subkey='enabled')
  48. @property
  49. def resolution(self):
  50. return self._resolution
  51. @resolution.setter
  52. def resolution(self, value):
  53. if value != self._resolution:
  54. self._resolution = value
  55. self.resolutionChanged.emit(value=value)
  56. @property
  57. def autoRender(self):
  58. return self._autoRender
  59. @autoRender.setter
  60. def autoRender(self, value):
  61. if value != self._autoRender:
  62. self._autoRender = value
  63. self.autoRenderChanged.emit(value=value)
  64. @property
  65. def showRegion(self):
  66. return self._showRegion
  67. @showRegion.setter
  68. def showRegion(self, value):
  69. if value != self._showRegion:
  70. self._showRegion = value
  71. self.showRegionChanged.emit(value=value)
  72. @property
  73. def alignExtent(self):
  74. return self._alignExtent
  75. @alignExtent.setter
  76. def alignExtent(self, value):
  77. if value != self._alignExtent:
  78. self._alignExtent = value
  79. self.alignExtentChanged.emit(value=value)
  80. class MapWindowBase(object):
  81. """Abstract map display window class
  82. Superclass for BufferedWindow class (2D display mode), and GLWindow
  83. (3D display mode).
  84. Subclasses have to define
  85. - _bindMouseEvents method which binds MouseEvent handlers
  86. - Pixel2Cell
  87. - Cell2Pixel (if it is possible)
  88. """
  89. def __init__(self, parent, giface, Map):
  90. self.parent = parent
  91. self.Map = Map
  92. self._giface = giface
  93. # Emitted when someone registers as mouse event handler
  94. self.mouseHandlerRegistered = Signal(
  95. 'MapWindow.mouseHandlerRegistered')
  96. # Emitted when mouse event handler is unregistered
  97. self.mouseHandlerUnregistered = Signal(
  98. 'MapWindow.mouseHandlerUnregistered')
  99. # emitted after double click in pointer mode on legend, text, scalebar
  100. self.overlayActivated = Signal('MapWindow.overlayActivated')
  101. # emitted when overlay should be hidden
  102. self.overlayRemoved = Signal('MapWindow.overlayRemoved')
  103. # mouse attributes -- position on the screen, begin and end of
  104. # dragging, and type of drawing
  105. self.mouse = {
  106. 'begin': [0, 0], # screen coordinates
  107. 'end': [0, 0],
  108. 'use': "pointer",
  109. 'box': "point"
  110. }
  111. # last east, north coordinates, changes on mouse motion
  112. self.lastEN = None
  113. # stores overridden cursor
  114. self._overriddenCursor = None
  115. # dictionary where event types are stored as keys and lists of
  116. # handlers for these types as values
  117. self.handlersContainer = {
  118. wx.EVT_LEFT_DOWN: [],
  119. wx.EVT_LEFT_UP: [],
  120. wx.EVT_LEFT_DCLICK: [],
  121. wx.EVT_MIDDLE_DOWN: [],
  122. wx.EVT_MIDDLE_UP: [],
  123. wx.EVT_MIDDLE_DCLICK: [],
  124. wx.EVT_RIGHT_DOWN: [],
  125. wx.EVT_RIGHT_UP: [],
  126. wx.EVT_RIGHT_DCLICK: [],
  127. wx.EVT_MOTION: [],
  128. wx.EVT_ENTER_WINDOW: [],
  129. wx.EVT_LEAVE_WINDOW: [],
  130. wx.EVT_MOUSEWHEEL: [],
  131. wx.EVT_MOUSE_EVENTS: []
  132. }
  133. # available cursors
  134. self._cursors = {
  135. "default": wx.StockCursor(wx.CURSOR_ARROW),
  136. "cross": wx.StockCursor(wx.CURSOR_CROSS),
  137. "hand": wx.StockCursor(wx.CURSOR_HAND),
  138. "pencil": wx.StockCursor(wx.CURSOR_PENCIL),
  139. "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
  140. }
  141. # default cursor for window is arrow (at least we rely on it here)
  142. # but we need to define attribute here
  143. # cannot call SetNamedCursor since it expects the instance
  144. # to be a wx window, so setting only the attribute
  145. self._cursor = 'default'
  146. wx.CallAfter(self.InitBinding)
  147. def __del__(self):
  148. self.UnregisterAllHandlers()
  149. def InitBinding(self):
  150. """Binds helper functions, which calls all handlers
  151. registered to events with the events
  152. """
  153. for ev, handlers in self.handlersContainer.iteritems():
  154. self.Bind(ev, self.EventTypeHandler(handlers))
  155. def EventTypeHandler(self, evHandlers):
  156. return lambda event: self.HandlersCaller(event, evHandlers)
  157. def HandlersCaller(self, event, handlers):
  158. """Hepler function which calls all handlers registered for
  159. event
  160. """
  161. for handler in handlers:
  162. try:
  163. handler(event)
  164. except:
  165. handlers.remove(handler)
  166. GError(
  167. parent=self, message=_(
  168. "Error occurred during calling of handler: %s \n"
  169. "Handler was unregistered.") %
  170. handler.__name__)
  171. event.Skip()
  172. def RegisterMouseEventHandler(self, event, handler, cursor=None):
  173. """Binds event handler
  174. @depreciated This method is depreciated. Use Signals or drawing API
  175. instead. Signals do not cover all events but new Signals can be added
  176. when needed consider also adding generic signal. However, more
  177. interesing and useful is higher level API to create objects, graphics etc.
  178. Call event.Skip() in handler to allow default processing in MapWindow.
  179. If any error occurs inside of handler, the handler is removed.
  180. Before handler is unregistered it is called with
  181. string value "unregistered" of event parameter.
  182. ::
  183. # your class methods
  184. def OnButton(self, event):
  185. # current map display's map window
  186. # expects LayerManager to be the parent
  187. self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
  188. if self.mapwin.RegisterEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
  189. 'cross'):
  190. self.parent.GetLayerTree().GetMapDisplay().Raise()
  191. else:
  192. # handle that you cannot get coordinates
  193. def OnMouseAction(self, event):
  194. # get real world coordinates of mouse click
  195. coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
  196. self.text.SetLabel('Coor: ' + str(coor))
  197. self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction)
  198. event.Skip()
  199. Emits mouseHandlerRegistered signal before handler is registered.
  200. :param event: one of mouse events
  201. :param handler: function to handle event
  202. :param cursor: cursor which temporary overrides current cursor
  203. :return: True if successful
  204. :return: False if event cannot be bind
  205. """
  206. self.mouseHandlerRegistered.emit()
  207. # inserts handler into list
  208. for containerEv, handlers in self.handlersContainer.iteritems():
  209. if event == containerEv:
  210. handlers.append(handler)
  211. self.mouse['useBeforeGenericEvent'] = self.mouse['use']
  212. self.mouse['use'] = 'genericEvent'
  213. if cursor:
  214. self._overriddenCursor = self.GetNamedCursor()
  215. self.SetNamedCursor(cursor)
  216. return True
  217. def UnregisterAllHandlers(self):
  218. """Unregisters all registered handlers
  219. @depreciated This method is depreciated. Use Signals or drawing API instead.
  220. Before each handler is unregistered it is called with string
  221. value "unregistered" of event parameter.
  222. """
  223. for containerEv, handlers in self.handlersContainer.iteritems():
  224. for handler in handlers:
  225. try:
  226. handler("unregistered")
  227. handlers.remove(handler)
  228. except:
  229. GError(parent=self,
  230. message=_("Error occurred during unregistration of handler: %s \n \
  231. Handler was unregistered.") % handler.__name__)
  232. handlers.remove(handler)
  233. def UnregisterMouseEventHandler(self, event, handler):
  234. """Unbinds event handler for event
  235. @depreciated This method is depreciated. Use Signals or drawing API instead.
  236. Before handler is unregistered it is called with string value
  237. "unregistered" of event parameter.
  238. Emits mouseHandlerUnregistered signal after handler is unregistered.
  239. :param handler: handler to unbind
  240. :param event: event from which handler will be unbinded
  241. :return: True if successful
  242. :return: False if event cannot be unbind
  243. """
  244. # removes handler from list
  245. for containerEv, handlers in self.handlersContainer.iteritems():
  246. if event != containerEv:
  247. continue
  248. try:
  249. handler("unregistered")
  250. if handler in handlers:
  251. handlers.remove(handler)
  252. else:
  253. grass.warning(_("Handler: %s was not registered")
  254. % handler.__name__)
  255. except:
  256. GError(parent=self,
  257. message=_("Error occurred during unregistration of handler: %s \n \
  258. Handler was unregistered") % handler.__name__)
  259. handlers.remove(handler)
  260. # restore mouse use (previous state)
  261. self.mouse['use'] = self.mouse['useBeforeGenericEvent']
  262. # restore overridden cursor
  263. if self._overriddenCursor:
  264. self.SetNamedCursor(self._overriddenCursor)
  265. self.mouseHandlerUnregistered.emit()
  266. return True
  267. def Pixel2Cell(self, xyCoords):
  268. raise NotImplementedError()
  269. def Cell2Pixel(self, enCoords):
  270. raise NotImplementedError()
  271. def OnMotion(self, event):
  272. """Tracks mouse motion and update statusbar
  273. .. todo::
  274. remove this method when lastEN is not used
  275. :func:`GetLastEN`
  276. """
  277. try:
  278. self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
  279. except (ValueError):
  280. self.lastEN = None
  281. event.Skip()
  282. def GetLastEN(self):
  283. """Returns last coordinates of mouse cursor.
  284. @depreciated This method is depreciated. Use Signal with coordinates as parameters.
  285. :func:`OnMotion`
  286. """
  287. return self.lastEN
  288. def SetNamedCursor(self, cursorName):
  289. """Sets cursor defined by name."""
  290. cursor = self._cursors[cursorName]
  291. self.SetCursor(cursor)
  292. self._cursor = cursorName
  293. def GetNamedCursor(self):
  294. """Returns current cursor name."""
  295. return self._cursor
  296. cursor = property(fget=GetNamedCursor, fset=SetNamedCursor)
  297. def SetModePointer(self):
  298. """Sets mouse mode to pointer."""
  299. self.mouse['use'] = 'pointer'
  300. self.mouse['box'] = 'point'
  301. self.SetNamedCursor('default')
  302. def SetModePan(self):
  303. """Sets mouse mode to pan."""
  304. self.mouse['use'] = "pan"
  305. self.mouse['box'] = "box"
  306. self.zoomtype = 0
  307. self.SetNamedCursor('hand')
  308. def SetModeZoomIn(self):
  309. self._setModeZoom(zoomType=1)
  310. def SetModeZoomOut(self):
  311. self._setModeZoom(zoomType=-1)
  312. def _setModeZoom(self, zoomType):
  313. self.zoomtype = zoomType
  314. self.mouse['use'] = "zoom"
  315. self.mouse['box'] = "box"
  316. self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  317. self.SetNamedCursor('cross')
  318. def SetModeDrawRegion(self):
  319. self.mouse['use'] = 'drawRegion'
  320. self.mouse['box'] = "box"
  321. self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  322. self.SetNamedCursor('cross')
  323. def SetModeQuery(self):
  324. """Query mode on"""
  325. self.mouse['use'] = "query"
  326. self.mouse['box'] = "point"
  327. self.zoomtype = 0
  328. self.SetNamedCursor('cross')
  329. def DisactivateWin(self):
  330. """Use when the class instance is hidden in MapFrame."""
  331. raise NotImplementedError()
  332. def ActivateWin(self):
  333. """Used when the class instance is activated in MapFrame."""
  334. raise NotImplementedError()