base.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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('MapWindowProperties.resolutionChanged')
  26. self._autoRender = None
  27. self.autoRenderChanged = Signal('MapWindowProperties.autoRenderChanged')
  28. self._showRegion = None
  29. self.showRegionChanged = Signal('MapWindowProperties.showRegionChanged')
  30. self._alignExtent = None
  31. self.alignExtentChanged = Signal('MapWindowProperties.alignExtentChanged')
  32. def setValuesFromUserSettings(self):
  33. """Convenient function to get values from user settings into this object."""
  34. self._resolution = UserSettings.Get(group='display',
  35. key='compResolution',
  36. subkey='enabled')
  37. self._autoRender = UserSettings.Get(group='display',
  38. key='autoRendering',
  39. subkey='enabled')
  40. self._showRegion = False # in statusbar.py was not from settings
  41. self._alignExtent = UserSettings.Get(group='display',
  42. key='alignExtent',
  43. subkey='enabled')
  44. @property
  45. def resolution(self):
  46. return self._resolution
  47. @resolution.setter
  48. def resolution(self, value):
  49. if value != self._resolution:
  50. self._resolution = value
  51. self.resolutionChanged.emit(value=value)
  52. @property
  53. def autoRender(self):
  54. return self._autoRender
  55. @autoRender.setter
  56. def autoRender(self, value):
  57. if value != self._autoRender:
  58. self._autoRender = value
  59. self.autoRenderChanged.emit(value=value)
  60. @property
  61. def showRegion(self):
  62. return self._showRegion
  63. @showRegion.setter
  64. def showRegion(self, value):
  65. if value != self._showRegion:
  66. self._showRegion = value
  67. self.showRegionChanged.emit(value=value)
  68. @property
  69. def alignExtent(self):
  70. return self._alignExtent
  71. @alignExtent.setter
  72. def alignExtent(self, value):
  73. if value != self._alignExtent:
  74. self._alignExtent = value
  75. self.alignExtentChanged.emit(value=value)
  76. class MapWindowBase(object):
  77. """!Abstract map display window class
  78. Superclass for BufferedWindow class (2D display mode), and GLWindow
  79. (3D display mode).
  80. Subclasses have to define
  81. - _bindMouseEvents method which binds MouseEvent handlers
  82. - Pixel2Cell
  83. - Cell2Pixel (if it is possible)
  84. """
  85. def __init__(self, parent, giface, Map):
  86. self.parent = parent
  87. self.Map = Map
  88. self._giface = giface
  89. # Emitted when someone registers as mouse event handler
  90. self.mouseHandlerRegistered = Signal('MapWindow.mouseHandlerRegistered')
  91. # Emitted when mouse event handler is unregistered
  92. self.mouseHandlerUnregistered = Signal('MapWindow.mouseHandlerUnregistered')
  93. # emitted after double click in pointer mode on legend, text, scalebar
  94. self.overlayActivated = Signal('MapWindow.overlayActivated')
  95. # mouse attributes -- position on the screen, begin and end of
  96. # dragging, and type of drawing
  97. self.mouse = {
  98. 'begin': [0, 0], # screen coordinates
  99. 'end' : [0, 0],
  100. 'use' : "pointer",
  101. 'box' : "point"
  102. }
  103. # last east, north coordinates, changes on mouse motion
  104. self.lastEN = None
  105. # stores overridden cursor
  106. self._overriddenCursor = None
  107. # dictionary where event types are stored as keys and lists of
  108. # handlers for these types as values
  109. self.handlersContainer = {
  110. wx.EVT_LEFT_DOWN : [],
  111. wx.EVT_LEFT_UP : [],
  112. wx.EVT_LEFT_DCLICK : [],
  113. wx.EVT_MIDDLE_DOWN : [],
  114. wx.EVT_MIDDLE_UP : [],
  115. wx.EVT_MIDDLE_DCLICK : [],
  116. wx.EVT_RIGHT_DOWN : [],
  117. wx.EVT_RIGHT_UP : [],
  118. wx.EVT_RIGHT_DCLICK : [],
  119. wx.EVT_MOTION : [],
  120. wx.EVT_ENTER_WINDOW : [],
  121. wx.EVT_LEAVE_WINDOW : [],
  122. wx.EVT_MOUSEWHEEL : [],
  123. wx.EVT_MOUSE_EVENTS : []
  124. }
  125. # available cursors
  126. self._cursors = {
  127. "default": wx.StockCursor(wx.CURSOR_ARROW),
  128. "cross": wx.StockCursor(wx.CURSOR_CROSS),
  129. "hand": wx.StockCursor(wx.CURSOR_HAND),
  130. "pencil": wx.StockCursor(wx.CURSOR_PENCIL),
  131. "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
  132. }
  133. # default cursor for window is arrow (at least we rely on it here)
  134. # but we need to define attribute here
  135. # cannot call SetNamedCursor since it expects the instance
  136. # to be a wx window, so setting only the attribute
  137. self._cursor = 'default'
  138. wx.CallAfter(self.InitBinding)
  139. def __del__(self):
  140. self.UnregisterAllHandlers()
  141. def InitBinding(self):
  142. """!Binds helper functions, which calls all handlers
  143. registered to events with the events
  144. """
  145. for ev, handlers in self.handlersContainer.iteritems():
  146. self.Bind(ev, self.EventTypeHandler(handlers))
  147. def EventTypeHandler(self, evHandlers):
  148. return lambda event:self.HandlersCaller(event, evHandlers)
  149. def HandlersCaller(self, event, handlers):
  150. """!Hepler function which calls all handlers registered for
  151. event
  152. """
  153. for handler in handlers:
  154. try:
  155. handler(event)
  156. except:
  157. handlers.remove(handler)
  158. GError(parent = self,
  159. message=_("Error occured during calling of handler: %s \n"
  160. "Handler was unregistered.") % handler.__name__)
  161. event.Skip()
  162. def RegisterMouseEventHandler(self, event, handler, cursor = None):
  163. """!Binds event handler
  164. @depreciated This method is depreciated. Use Signals or drawing API instead.
  165. Signals do not cover all events but new Signals can be added when needed
  166. consider also adding generic signal. However, more interesing and useful
  167. is higher level API to create objects, graphics etc.
  168. Call event.Skip() in handler to allow default processing in MapWindow.
  169. If any error occures inside of handler, the handler is removed.
  170. Before handler is unregistered it is called with
  171. string value "unregistered" of event parameter.
  172. @code
  173. # your class methods
  174. def OnButton(self, event):
  175. # current map display's map window
  176. # expects LayerManager to be the parent
  177. self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
  178. if self.mapwin.RegisterEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
  179. 'cross'):
  180. self.parent.GetLayerTree().GetMapDisplay().Raise()
  181. else:
  182. # handle that you cannot get coordinates
  183. def OnMouseAction(self, event):
  184. # get real world coordinates of mouse click
  185. coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
  186. self.text.SetLabel('Coor: ' + str(coor))
  187. self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction)
  188. event.Skip()
  189. @endcode
  190. Emits mouseHandlerRegistered signal before handler is registered.
  191. @param event one of mouse events
  192. @param handler function to handle event
  193. @param cursor cursor which temporary overrides current cursor
  194. @return True if successful
  195. @return False if event cannot be bind
  196. """
  197. self.mouseHandlerRegistered.emit()
  198. # inserts handler into list
  199. for containerEv, handlers in self.handlersContainer.iteritems():
  200. if event == containerEv:
  201. handlers.append(handler)
  202. self.mouse['useBeforeGenericEvent'] = self.mouse['use']
  203. self.mouse['use'] = 'genericEvent'
  204. if cursor:
  205. self._overriddenCursor = self.GetNamedCursor()
  206. self.SetNamedCursor(cursor)
  207. return True
  208. def UnregisterAllHandlers(self):
  209. """!Unregisters all registered handlers
  210. @depreciated This method is depreciated. Use Signals or drawing API instead.
  211. Before each handler is unregistered it is called with string
  212. value "unregistered" of event parameter.
  213. """
  214. for containerEv, handlers in self.handlersContainer.iteritems():
  215. for handler in handlers:
  216. try:
  217. handler("unregistered")
  218. handlers.remove(handler)
  219. except:
  220. GError(parent = self,
  221. message = _("Error occured during unregistration of handler: %s \n \
  222. Handler was unregistered.") % handler.__name__)
  223. handlers.remove(handler)
  224. def UnregisterMouseEventHandler(self, event, handler):
  225. """!Unbinds event handler for event
  226. @depreciated This method is depreciated. Use Signals or drawing API instead.
  227. Before handler is unregistered it is called with string value
  228. "unregistered" of event parameter.
  229. Emits mouseHandlerUnregistered signal after handler is unregistered.
  230. @param handler handler to unbind
  231. @param event event from which handler will be unbinded
  232. @return True if successful
  233. @return False if event cannot be unbind
  234. """
  235. # removes handler from list
  236. for containerEv, handlers in self.handlersContainer.iteritems():
  237. if event != containerEv:
  238. continue
  239. try:
  240. handler("unregistered")
  241. if handler in handlers:
  242. handlers.remove(handler)
  243. else:
  244. grass.warning(_("Handler: %s was not registered") \
  245. % handler.__name__)
  246. except:
  247. GError(parent = self,
  248. message = _("Error occured during unregistration of handler: %s \n \
  249. Handler was unregistered") % handler.__name__)
  250. handlers.remove(handler)
  251. # restore mouse use (previous state)
  252. self.mouse['use'] = self.mouse['useBeforeGenericEvent']
  253. # restore overridden cursor
  254. if self._overriddenCursor:
  255. self.SetNamedCursor(self._overriddenCursor)
  256. self.mouseHandlerUnregistered.emit()
  257. return True
  258. def Pixel2Cell(self, xyCoords):
  259. raise NotImplementedError()
  260. def Cell2Pixel(self, enCoords):
  261. raise NotImplementedError()
  262. def OnMotion(self, event):
  263. """!Tracks mouse motion and update statusbar
  264. @todo remove this method when lastEN is not used
  265. @see GetLastEN
  266. """
  267. try:
  268. self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
  269. except (ValueError):
  270. self.lastEN = None
  271. event.Skip()
  272. def GetLastEN(self):
  273. """!Returns last coordinates of mouse cursor.
  274. @depreciated This method is depreciated. Use Signal with coordinates as parameters.
  275. @see OnMotion
  276. """
  277. return self.lastEN
  278. def SetNamedCursor(self, cursorName):
  279. """!Sets cursor defined by name."""
  280. cursor = self._cursors[cursorName]
  281. self.SetCursor(cursor)
  282. self._cursor = cursorName
  283. def GetNamedCursor(self):
  284. """!Returns current cursor name."""
  285. return self._cursor
  286. cursor = property(fget=GetNamedCursor, fset=SetNamedCursor)