frame.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. """!
  2. @package swipe.frame
  3. @brief Map Swipe Frame
  4. Classes:
  5. - dialogs::SwipeMapDialog
  6. (C) 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 Anna Kratochvilova <kratochanna gmail.com>
  10. """
  11. import sys
  12. import wx
  13. import time
  14. import grass.script as grass
  15. from gui_core.mapdisp import DoubleMapFrame
  16. from core.render import Map
  17. from mapdisp import statusbar as sb
  18. from core.debug import Debug
  19. from core.gcmd import RunCommand, GError
  20. from mapdisp.statusbar import EVT_AUTO_RENDER
  21. from swipe.toolbars import SwipeMapToolbar, SwipeMainToolbar, SwipeMiscToolbar
  22. from swipe.mapwindow import SwipeBufferedWindow
  23. from swipe.dialogs import SwipeMapDialog
  24. class SwipeMapFrame(DoubleMapFrame):
  25. def __init__(self, parent = None, title = _("GRASS GIS Map Swipe"), name = "swipe", **kwargs):
  26. DoubleMapFrame.__init__(self, parent = parent, title = title, name = name,
  27. firstMap = Map(), secondMap = Map(), **kwargs)
  28. Debug.msg (1, "SwipeMapFrame.__init__()")
  29. #
  30. # Add toolbars
  31. #
  32. toolbars = ['swipeMisc', 'swipeMap', 'swipeMain']
  33. if sys.platform == 'win32':
  34. self.AddToolbar(toolbars.pop(1))
  35. toolbars.reverse()
  36. else:
  37. self.AddToolbar(toolbars.pop(0))
  38. for toolb in toolbars:
  39. self.AddToolbar(toolb)
  40. #
  41. # create widgets
  42. #
  43. self.splitter = MapSplitter(parent = self, id = wx.ID_ANY)
  44. self.sliderH = wx.Slider(self, id = wx.ID_ANY, style = wx.SL_HORIZONTAL)
  45. self.sliderV = wx.Slider(self, id = wx.ID_ANY, style = wx.SL_VERTICAL)
  46. self.firstMapWindow = SwipeBufferedWindow(parent = self.splitter, Map = self.firstMap, frame = self)
  47. self.secondMapWindow = SwipeBufferedWindow(parent = self.splitter, Map = self.secondMap, frame = self)
  48. self.MapWindow = self.firstMapWindow # current by default
  49. self.firstMap.region = self.secondMap.region
  50. self.firstMapWindow.zoomhistory = self.secondMapWindow.zoomhistory
  51. self.splitter.SplitVertically(self.firstMapWindow, self.secondMapWindow, 0)
  52. self._addPanes()
  53. self._bindWindowsActivation()
  54. self._mgr.GetPane('sliderV').Hide()
  55. self._mgr.GetPane('sliderH').Show()
  56. self.slider = self.sliderH
  57. self.InitStatusbar()
  58. self.Bind(wx.EVT_SIZE, self.OnSize)
  59. self.Bind(EVT_AUTO_RENDER, self.OnAutoRenderChanged)
  60. self.Bind(wx.EVT_IDLE, self.OnIdle)
  61. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  62. self.SetSize((800, 600))
  63. self._mgr.Update()
  64. self.rasters = {'first': None, 'second': None}
  65. # default action in map toolbar
  66. self.OnPan(event = None)
  67. self.resize = False
  68. wx.CallAfter(self.CallAfterInit)
  69. def CallAfterInit(self):
  70. self.InitSliderBindings()
  71. if not (self.rasters['first'] and self.rasters['second']):
  72. self.OnSelectRasters(event = None)
  73. def InitStatusbar(self):
  74. """!Init statusbar (default items)."""
  75. # items for choice
  76. self.statusbarItems = [sb.SbCoordinates,
  77. sb.SbRegionExtent,
  78. sb.SbCompRegionExtent,
  79. sb.SbShowRegion,
  80. sb.SbAlignExtent,
  81. sb.SbResolution,
  82. sb.SbDisplayGeometry,
  83. sb.SbMapScale,
  84. sb.SbGoTo,
  85. sb.SbProjection]
  86. # create statusbar and its manager
  87. statusbar = self.CreateStatusBar(number = 4, style = 0)
  88. statusbar.SetStatusWidths([-5, -2, -1, -1])
  89. self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
  90. # fill statusbar manager
  91. self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
  92. self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
  93. self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
  94. self.statusbarManager.Update()
  95. def ResetSlider(self):
  96. if self.splitter.GetSplitMode() == wx.SPLIT_VERTICAL:
  97. size = self.splitter.GetSize()[0]
  98. else:
  99. size = self.splitter.GetSize()[1]
  100. self.slider.SetRange(0, size)
  101. self.slider.SetValue(self.splitter.GetSashPosition())
  102. def InitSliderBindings(self):
  103. self.sliderH.Bind(wx.EVT_SPIN, self.OnSliderPositionChanging)
  104. self.sliderH.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSliderPositionChanged)
  105. self.sliderV.Bind(wx.EVT_SPIN, self.OnSliderPositionChanging)
  106. self.sliderV.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSliderPositionChanged)
  107. self.splitter.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
  108. self.splitter.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
  109. def OnSliderPositionChanging(self, event):
  110. """!Slider changes its position, sash must be moved too."""
  111. Debug.msg (5, "SwipeMapFrame.OnSliderPositionChanging()")
  112. self.GetFirstWindow().movingSash = True
  113. self.GetSecondWindow().movingSash = True
  114. pos = event.GetPosition()
  115. if pos > 0:
  116. self.splitter.SetSashPosition(pos)
  117. self.splitter.OnSashChanging(None)
  118. def OnSliderPositionChanged(self, event):
  119. """!Slider position changed, sash must be moved too."""
  120. Debug.msg (5, "SwipeMapFrame.OnSliderPositionChanged()")
  121. self.splitter.SetSashPosition(event.GetPosition())
  122. self.splitter.OnSashChanged(None)
  123. def OnSashChanging(self, event):
  124. """!Sash position is changing, slider must be moved too."""
  125. Debug.msg (5, "SwipeMapFrame.OnSashChanging()")
  126. self.slider.SetValue(self.splitter.GetSashPosition())
  127. event.Skip()
  128. def OnSashChanged(self, event):
  129. """!Sash position changed, slider must be moved too."""
  130. Debug.msg (5, "SwipeMapFrame.OnSashChanged()")
  131. self.OnSashChanging(event)
  132. event.Skip()
  133. def OnSize(self, event):
  134. Debug.msg (4, "SwipeMapFrame.OnSize()")
  135. self.resize = time.clock()
  136. def OnIdle(self, event):
  137. if self.resize and time.clock() - self.resize > 0.2:
  138. w1 = self.GetFirstWindow()
  139. w2 = self.GetSecondWindow()
  140. sizeAll = self.splitter.GetSize()
  141. w1.SetClientSize(sizeAll)
  142. w2.SetClientSize(sizeAll)
  143. w1.OnSize(event)
  144. w2.OnSize(event)
  145. self.ResetSlider()
  146. self.resize = False
  147. def OnAutoRenderChanged(self, event):
  148. """!Auto rendering state changed."""
  149. style = self.splitter.GetWindowStyle()
  150. style ^= wx.SP_LIVE_UPDATE
  151. self.splitter.SetWindowStyle(style)
  152. def AddToolbar(self, name):
  153. """!Add defined toolbar to the window
  154. Currently known toolbars are:
  155. - 'swipeMap' - basic map toolbar
  156. - 'swipeMain' - swipe functionality
  157. """
  158. if name == "swipeMap":
  159. self.toolbars[name] = SwipeMapToolbar(self)
  160. self._mgr.AddPane(self.toolbars[name],
  161. wx.aui.AuiPaneInfo().
  162. Name(name).Caption(_("Map Toolbar")).
  163. ToolbarPane().Top().
  164. LeftDockable(False).RightDockable(False).
  165. BottomDockable(False).TopDockable(True).
  166. CloseButton(False).Layer(2).Row(1).
  167. BestSize((self.toolbars[name].GetBestSize())))
  168. if name == "swipeMain":
  169. self.toolbars[name] = SwipeMainToolbar(self)
  170. self._mgr.AddPane(self.toolbars[name],
  171. wx.aui.AuiPaneInfo().
  172. Name(name).Caption(_("Main Toolbar")).
  173. ToolbarPane().Top().
  174. LeftDockable(False).RightDockable(False).
  175. BottomDockable(False).TopDockable(True).
  176. CloseButton(False).Layer(2).Row(1).
  177. BestSize((self.toolbars[name].GetBestSize())))
  178. if name == "swipeMisc":
  179. self.toolbars[name] = SwipeMiscToolbar(self)
  180. self._mgr.AddPane(self.toolbars[name],
  181. wx.aui.AuiPaneInfo().
  182. Name(name).Caption(_("Misc Toolbar")).
  183. ToolbarPane().Top().
  184. LeftDockable(False).RightDockable(False).
  185. BottomDockable(False).TopDockable(True).
  186. CloseButton(False).Layer(2).Row(1).
  187. BestSize((self.toolbars[name].GetBestSize())))
  188. def _addPanes(self):
  189. """!Add splitter window and sliders to aui manager"""
  190. # splitter window
  191. self._mgr.AddPane(self.splitter, wx.aui.AuiPaneInfo().
  192. Name('splitter').CaptionVisible(False).PaneBorder(True).
  193. Dockable(False).Floatable(False).CloseButton(False).
  194. Center().Layer(1).BestSize((self.splitter.GetBestSize())))
  195. # sliders
  196. self._mgr.AddPane(self.sliderH, wx.aui.AuiPaneInfo().
  197. Name('sliderH').CaptionVisible(False).PaneBorder(False).
  198. CloseButton(False).Gripper(True).GripperTop(False).
  199. BottomDockable(True).TopDockable(True).
  200. LeftDockable(False).RightDockable(False).
  201. Bottom().Layer(1).BestSize((self.sliderH.GetBestSize())))
  202. self._mgr.AddPane(self.sliderV, wx.aui.AuiPaneInfo().
  203. Name('sliderV').CaptionVisible(False).PaneBorder(False).
  204. CloseButton(False).Gripper(True).GripperTop(True).
  205. BottomDockable(False).TopDockable(False).
  206. LeftDockable(True).RightDockable(True).
  207. Right().Layer(1).BestSize((self.sliderV.GetBestSize())))
  208. def UpdateRegion(self):
  209. """!
  210. Rerender the second window
  211. when the region of the first changed.
  212. """
  213. Debug.msg(3, "SwipeMapFrame.UpdateRegion()")
  214. if self.GetWindow() == self.GetSecondWindow():
  215. self.Render(self.GetFirstWindow())
  216. else:
  217. self.Render(self.GetSecondWindow())
  218. def OnZoomToMap(self, event):
  219. """!
  220. Set display extents to match selected raster (including NULLs)
  221. or vector map.
  222. """
  223. self.GetFirstWindow().ZoomToMap(layers = self.Map.GetListOfLayers())
  224. self.GetSecondWindow().ZoomToMap(layers = self.Map.GetListOfLayers())
  225. # needed again, don't know why
  226. self.firstMap.region = self.secondMap.region
  227. def OnZoomBack(self, event):
  228. self.GetFirstWindow().ZoomBack()
  229. self.secondMap.region = self.firstMap.region
  230. self.Render(self.GetSecondWindow())
  231. def OnSelectRasters(self, event):
  232. """!Choose raster maps and rerender."""
  233. dlg = SwipeMapDialog(self, first = self.rasters['first'], second = self.rasters['second'])
  234. if dlg.ShowModal() == wx.ID_OK:
  235. maps = dlg.GetValues()
  236. res1 = self.SetFirstRaster(name = maps[0])
  237. res2 = self.SetSecondRaster(name = maps[1])
  238. if not (res1 and res2):
  239. message = ''
  240. if not res1:
  241. message += _("Map <%s> not found. ") % maps[0]
  242. if not res2:
  243. message += _("Map <%s> not found.") % maps[1]
  244. GError(parent = self, message = message)
  245. dlg.Destroy()
  246. dlg.Destroy()
  247. self.OnRender(event = None)
  248. def SetFirstRaster(self, name):
  249. """!Set raster map to first Map"""
  250. raster = grass.find_file(name = name, element = 'cell')
  251. if raster['fullname']:
  252. self.rasters['first'] = raster['fullname']
  253. self.SetLayer(name = raster['fullname'], mapInstance = self.GetFirstMap())
  254. self.OnZoomToMap(event = None)
  255. return True
  256. return False
  257. def SetSecondRaster(self, name):
  258. """!Set raster map to second Map"""
  259. raster = grass.find_file(name = name, element = 'cell')
  260. if raster['fullname']:
  261. self.rasters['second'] = raster['fullname']
  262. self.SetLayer(name = raster['fullname'], mapInstance = self.GetSecondMap())
  263. self.OnZoomToMap(event = None)
  264. return True
  265. return False
  266. def SetLayer(self, name, mapInstance):
  267. """!Sets layer in Map.
  268. @param name layer (raster) name
  269. """
  270. Debug.msg (3, "SwipeMapFrame.SetLayer(): name=%s" % name)
  271. # this simple application enables to keep only one raster
  272. mapInstance.DeleteAllLayers()
  273. cmdlist = ['d.rast', 'map=%s' % name]
  274. # add layer to Map instance (core.render)
  275. newLayer = mapInstance.AddLayer(type = 'raster', command = cmdlist, l_active = True,
  276. name = name, l_hidden = False, l_opacity = 1.0,
  277. l_render = True)
  278. def OnSwitchWindows(self, event):
  279. """!Switch windows position."""
  280. Debug.msg(3, "SwipeMapFrame.OnSwitchWindows()")
  281. splitter = self.splitter
  282. w1, w2 = splitter.GetWindow1(), splitter.GetWindow2()
  283. splitter.ReplaceWindow(w1, w2)
  284. splitter.ReplaceWindow(w2, w1)
  285. # self.OnSize(None)
  286. splitter.OnSashChanged(None)
  287. def OnSwitchOrientation(self, event):
  288. """!Switch orientation of the sash."""
  289. Debug.msg(3, "SwipeMapFrame.OnSwitchOrientation()")
  290. splitter = self.splitter
  291. splitter.Unsplit()
  292. if splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  293. splitter.SplitVertically(self.firstMapWindow, self.secondMapWindow, 0)
  294. self.slider = self.sliderH
  295. self._mgr.GetPane('sliderH').Show()
  296. self._mgr.GetPane('sliderV').Hide()
  297. else:
  298. splitter.SplitHorizontally(self.firstMapWindow, self.secondMapWindow, 0)
  299. self.slider = self.sliderV
  300. self._mgr.GetPane('sliderV').Show()
  301. self._mgr.GetPane('sliderH').Hide()
  302. self._mgr.Update()
  303. splitter.OnSashChanged(None)
  304. self.OnSize(None)
  305. def GetMapToolbar(self):
  306. """!Returns toolbar with zooming tools"""
  307. return self.toolbars['swipeMap']
  308. def IsStandalone(self):
  309. if self.parent:
  310. return False
  311. return True
  312. def OnHelp(self, event):
  313. RunCommand('g.manual',
  314. quiet = True,
  315. entry = 'wxGUI.MapSwipe')
  316. def OnCloseWindow(self, event):
  317. self.GetFirstMap().Clean()
  318. self.GetSecondMap().Clean()
  319. self.Destroy()
  320. class MapSplitter(wx.SplitterWindow):
  321. """!Splitter window for displaying two maps"""
  322. def __init__(self, parent, id):
  323. wx.SplitterWindow.__init__(self, parent = parent, id = id,
  324. style = wx.SP_LIVE_UPDATE
  325. )
  326. Debug.msg(2, "MapSplitter.__init__()")
  327. self.sashWidthMin = 1
  328. self.sashWidthMax = 10
  329. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
  330. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
  331. wx.CallAfter(self.Init)
  332. def Init(self):
  333. self.OnSashChanged(evt = None)
  334. self.SetMinimumPaneSize(0)
  335. self.SetSashSize(self.sashWidthMin)
  336. # def OnMotion(self, event):
  337. # w = self.GetSashSize()
  338. # w1, w2 = self.GetWindow1(), self.GetWindow2()
  339. # if self.SashHitTest(event.GetX(), event.GetY(), tolerance = 20):
  340. # if w == self.sashWidthMin:
  341. # self.SetSashSize(self.sashWidthMax)
  342. # self.SetNeedUpdating(True)
  343. # w1.movingSash = True
  344. # w2.movingSash = True
  345. # else:
  346. # w1.movingSash = False
  347. # w1.movingSash = False
  348. # else:
  349. # if w == self.sashWidthMax:
  350. # self.SetSashSize(self.sashWidthMin)
  351. # self.SetNeedUpdating(True)
  352. # w1.movingSash = True
  353. # w2.movingSash = True
  354. # else:
  355. # w1.movingSash = False
  356. # w2.movingSash = False
  357. # event.Skip()
  358. def OnSashChanged(self, evt):
  359. Debug.msg(5, "MapSplitter.OnSashChanged()")
  360. w1, w2 = self.GetWindow1(), self.GetWindow2()
  361. w1.movingSash = False
  362. w2.movingSash = False
  363. wx.CallAfter(self.SashChanged)
  364. def SashChanged(self):
  365. Debug.msg(5, "MapSplitter.SashChanged()")
  366. w1, w2 = self.GetWindow1(), self.GetWindow2()
  367. w1.SetImageCoords((0, 0))
  368. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  369. w = w1.GetSize()[0]
  370. w2.SetImageCoords((-w, 0))
  371. else:
  372. h = w1.GetSize()[1]
  373. w2.SetImageCoords((0, -h))
  374. w1.UpdateMap(render = False, renderVector = False)
  375. w2.UpdateMap(render = False, renderVector = False)
  376. pos = self.GetSashPosition()
  377. self.last = pos
  378. def OnSashChanging(self, event):
  379. Debug.msg(5, "MapSplitter.OnSashChanging()")
  380. if not (self.GetWindowStyle() & wx.SP_LIVE_UPDATE):
  381. if event:
  382. event.Skip()
  383. return
  384. pos = self.GetSashPosition()
  385. dpos = pos - self.last
  386. self.last = pos
  387. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  388. dx = -dpos
  389. dy = 0
  390. else:
  391. dx = 0
  392. dy = -dpos
  393. self.GetWindow2().TranslateImage(dx, dy)
  394. self.GetWindow1().movingSash = True
  395. self.GetWindow2().movingSash = True