mapwindow.py 12 KB

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