base.py 15 KB

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