frame.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. """!
  2. @package mapswipe.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 os
  12. import sys
  13. import wx
  14. import time
  15. import grass.script as grass
  16. from gui_core.mapdisp import DoubleMapFrame
  17. from gui_core.dialogs import GetImageHandlers
  18. from gui_core.mapwindow import MapWindowProperties
  19. from core.render import Map
  20. from mapdisp import statusbar as sb
  21. from core.debug import Debug
  22. from core.gcmd import GError, GMessage
  23. from core.utils import _
  24. from mapswipe.toolbars import SwipeMapToolbar, SwipeMainToolbar, SwipeMiscToolbar
  25. from mapswipe.mapwindow import SwipeBufferedWindow
  26. from mapswipe.dialogs import SwipeMapDialog
  27. class SwipeMapFrame(DoubleMapFrame):
  28. def __init__(self, parent = None, giface = None,
  29. title = _("GRASS GIS Map Swipe"), name = "swipe", **kwargs):
  30. DoubleMapFrame.__init__(self, parent = parent, title = title, name = name,
  31. firstMap = Map(), secondMap = Map(), **kwargs)
  32. Debug.msg (1, "SwipeMapFrame.__init__()")
  33. #
  34. # Add toolbars
  35. #
  36. toolbars = ['swipeMisc', 'swipeMap', 'swipeMain']
  37. if sys.platform == 'win32':
  38. self.AddToolbar(toolbars.pop(1))
  39. toolbars.reverse()
  40. else:
  41. self.AddToolbar(toolbars.pop(0))
  42. for toolb in toolbars:
  43. self.AddToolbar(toolb)
  44. self._giface = giface
  45. #
  46. # create widgets
  47. #
  48. self.splitter = MapSplitter(parent = self, id = wx.ID_ANY)
  49. self.sliderH = wx.Slider(self, id = wx.ID_ANY, style = wx.SL_HORIZONTAL)
  50. self.sliderV = wx.Slider(self, id = wx.ID_ANY, style = wx.SL_VERTICAL)
  51. self.mapWindowProperties = MapWindowProperties()
  52. self.mapWindowProperties.setValuesFromUserSettings()
  53. self.mapWindowProperties.autoRenderChanged.connect(self.OnAutoRenderChanged)
  54. self.firstMapWindow = SwipeBufferedWindow(parent = self.splitter, giface = self._giface,
  55. properties=self.mapWindowProperties,
  56. Map = self.firstMap, frame = self)
  57. self.secondMapWindow = SwipeBufferedWindow(parent = self.splitter, giface = self._giface,
  58. properties=self.mapWindowProperties,
  59. Map = self.secondMap, frame = self)
  60. self.MapWindow = self.firstMapWindow # current by default
  61. self.firstMapWindow.zoomhistory = self.secondMapWindow.zoomhistory
  62. self.SetBindRegions(True)
  63. self._mode = 'swipe'
  64. self._addPanes()
  65. self._bindWindowsActivation()
  66. self._setUpMapWindow(self.firstMapWindow)
  67. self._setUpMapWindow(self.secondMapWindow)
  68. self._mgr.GetPane('sliderV').Hide()
  69. self._mgr.GetPane('sliderH').Show()
  70. self.slider = self.sliderH
  71. self.InitStatusbar()
  72. self.Bind(wx.EVT_SIZE, self.OnSize)
  73. self.Bind(wx.EVT_IDLE, self.OnIdle)
  74. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  75. self.SetSize((800, 600))
  76. self._mgr.Update()
  77. self.rasters = {'first': None, 'second': None}
  78. # default action in map toolbar
  79. self.OnPan(event = None)
  80. self.resize = False
  81. wx.CallAfter(self.CallAfterInit)
  82. def TrackCursor(self, event, showInFirst):
  83. """!Track cursor in one window and show cross in the other.
  84. Only for mirror mode.
  85. """
  86. if self._mode == 'swipe':
  87. event.Skip()
  88. return
  89. coords = event.GetPosition()
  90. if showInFirst:
  91. self.firstMapWindow.DrawMouseCross(coords = coords)
  92. else:
  93. self.secondMapWindow.DrawMouseCross(coords = coords)
  94. event.Skip()
  95. def ActivateFirstMap(self, event = None):
  96. """!Switch tracking direction"""
  97. super(SwipeMapFrame, self).ActivateFirstMap(event)
  98. self.firstMapWindow.Bind(wx.EVT_MOTION, lambda evt: self.TrackCursor(evt, showInFirst = False))
  99. self.secondMapWindow.Unbind(wx.EVT_MOTION)
  100. self.firstMapWindow.ClearLines()
  101. self.firstMapWindow.Refresh()
  102. def ActivateSecondMap(self, event = None):
  103. """!Switch tracking direction"""
  104. super(SwipeMapFrame, self).ActivateSecondMap(event)
  105. self.secondMapWindow.Bind(wx.EVT_MOTION, lambda evt: self.TrackCursor(evt, showInFirst = True))
  106. self.firstMapWindow.Unbind(wx.EVT_MOTION)
  107. self.secondMapWindow.ClearLines()
  108. self.secondMapWindow.Refresh()
  109. def CallAfterInit(self):
  110. self.InitSliderBindings()
  111. self.splitter.SplitVertically(self.firstMapWindow, self.secondMapWindow, 0)
  112. self.splitter.Init()
  113. if not (self.rasters['first'] and self.rasters['second']):
  114. self.OnSelectRasters(event = None)
  115. def InitStatusbar(self):
  116. """!Init statusbar (default items)."""
  117. # items for choice
  118. self.statusbarItems = [sb.SbCoordinates,
  119. sb.SbRegionExtent,
  120. sb.SbCompRegionExtent,
  121. sb.SbShowRegion,
  122. sb.SbAlignExtent,
  123. sb.SbResolution,
  124. sb.SbDisplayGeometry,
  125. sb.SbMapScale,
  126. sb.SbGoTo,
  127. sb.SbProjection]
  128. # create statusbar and its manager
  129. statusbar = self.CreateStatusBar(number = 4, style = 0)
  130. statusbar.SetStatusWidths([-5, -2, -1, -1])
  131. self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
  132. # fill statusbar manager
  133. self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
  134. self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
  135. sbRender = sb.SbRender(self, statusbar = statusbar, position = 3)
  136. self.statusbarManager.AddStatusbarItem(sbRender)
  137. self.statusbarManager.Update()
  138. def ResetSlider(self):
  139. if self.splitter.GetSplitMode() == wx.SPLIT_VERTICAL:
  140. size = self.splitter.GetSize()[0]
  141. else:
  142. size = self.splitter.GetSize()[1]
  143. self.slider.SetRange(0, size)
  144. self.slider.SetValue(self.splitter.GetSashPosition())
  145. def InitSliderBindings(self):
  146. self.sliderH.Bind(wx.EVT_SPIN, self.OnSliderPositionChanging)
  147. self.sliderH.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSliderPositionChanged)
  148. self.sliderV.Bind(wx.EVT_SPIN, self.OnSliderPositionChanging)
  149. self.sliderV.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSliderPositionChanged)
  150. self.splitter.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
  151. self.splitter.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
  152. def OnSliderPositionChanging(self, event):
  153. """!Slider changes its position, sash must be moved too."""
  154. Debug.msg (5, "SwipeMapFrame.OnSliderPositionChanging()")
  155. self.GetFirstWindow().movingSash = True
  156. self.GetSecondWindow().movingSash = True
  157. pos = event.GetPosition()
  158. if pos > 0:
  159. self.splitter.SetSashPosition(pos)
  160. self.splitter.OnSashChanging(None)
  161. def OnSliderPositionChanged(self, event):
  162. """!Slider position changed, sash must be moved too."""
  163. Debug.msg (5, "SwipeMapFrame.OnSliderPositionChanged()")
  164. self.splitter.SetSashPosition(event.GetPosition())
  165. self.splitter.OnSashChanged(None)
  166. def OnSashChanging(self, event):
  167. """!Sash position is changing, slider must be moved too."""
  168. Debug.msg (5, "SwipeMapFrame.OnSashChanging()")
  169. self.slider.SetValue(self.splitter.GetSashPosition())
  170. event.Skip()
  171. def OnSashChanged(self, event):
  172. """!Sash position changed, slider must be moved too."""
  173. Debug.msg (5, "SwipeMapFrame.OnSashChanged()")
  174. self.OnSashChanging(event)
  175. event.Skip()
  176. def OnSize(self, event):
  177. Debug.msg (4, "SwipeMapFrame.OnSize()")
  178. self.resize = time.clock()
  179. super(SwipeMapFrame, self).OnSize(event)
  180. def OnIdle(self, event):
  181. if self.resize and time.clock() - self.resize > 0.2:
  182. w1 = self.GetFirstWindow()
  183. w2 = self.GetSecondWindow()
  184. sizeAll = self.splitter.GetSize()
  185. w1.SetClientSize(sizeAll)
  186. w2.SetClientSize(sizeAll)
  187. w1.OnSize(event)
  188. w2.OnSize(event)
  189. self.ResetSlider()
  190. self.resize = False
  191. def OnAutoRenderChanged(self, value):
  192. """!Auto rendering state changed."""
  193. style = self.splitter.GetWindowStyle()
  194. style ^= wx.SP_LIVE_UPDATE
  195. self.splitter.SetWindowStyle(style)
  196. def AddToolbar(self, name):
  197. """!Add defined toolbar to the window
  198. Currently known toolbars are:
  199. - 'swipeMap' - basic map toolbar
  200. - 'swipeMain' - swipe functionality
  201. """
  202. if name == "swipeMap":
  203. self.toolbars[name] = SwipeMapToolbar(self)
  204. self._mgr.AddPane(self.toolbars[name],
  205. wx.aui.AuiPaneInfo().
  206. Name(name).Caption(_("Map Toolbar")).
  207. ToolbarPane().Top().
  208. LeftDockable(False).RightDockable(False).
  209. BottomDockable(False).TopDockable(True).
  210. CloseButton(False).Layer(2).Row(1).
  211. BestSize((self.toolbars[name].GetBestSize())))
  212. if name == "swipeMain":
  213. self.toolbars[name] = SwipeMainToolbar(self)
  214. self._mgr.AddPane(self.toolbars[name],
  215. wx.aui.AuiPaneInfo().
  216. Name(name).Caption(_("Main Toolbar")).
  217. ToolbarPane().Top().
  218. LeftDockable(False).RightDockable(False).
  219. BottomDockable(False).TopDockable(True).
  220. CloseButton(False).Layer(2).Row(1).
  221. BestSize((self.toolbars[name].GetBestSize())))
  222. if name == "swipeMisc":
  223. self.toolbars[name] = SwipeMiscToolbar(self)
  224. self._mgr.AddPane(self.toolbars[name],
  225. wx.aui.AuiPaneInfo().
  226. Name(name).Caption(_("Misc Toolbar")).
  227. ToolbarPane().Top().
  228. LeftDockable(False).RightDockable(False).
  229. BottomDockable(False).TopDockable(True).
  230. CloseButton(False).Layer(2).Row(1).
  231. BestSize((self.toolbars[name].GetBestSize())))
  232. def _addPanes(self):
  233. """!Add splitter window and sliders to aui manager"""
  234. # splitter window
  235. self._mgr.AddPane(self.splitter, wx.aui.AuiPaneInfo().
  236. Name('splitter').CaptionVisible(False).PaneBorder(True).
  237. Dockable(False).Floatable(False).CloseButton(False).
  238. Center().Layer(1).BestSize((self.splitter.GetBestSize())))
  239. # sliders
  240. self._mgr.AddPane(self.sliderH, wx.aui.AuiPaneInfo().
  241. Name('sliderH').CaptionVisible(False).PaneBorder(False).
  242. CloseButton(False).Gripper(True).GripperTop(False).
  243. BottomDockable(True).TopDockable(True).
  244. LeftDockable(False).RightDockable(False).
  245. Bottom().Layer(1).BestSize((self.sliderH.GetBestSize())))
  246. self._mgr.AddPane(self.sliderV, wx.aui.AuiPaneInfo().
  247. Name('sliderV').CaptionVisible(False).PaneBorder(False).
  248. CloseButton(False).Gripper(True).GripperTop(True).
  249. BottomDockable(False).TopDockable(False).
  250. LeftDockable(True).RightDockable(True).
  251. Right().Layer(1).BestSize((self.sliderV.GetBestSize())))
  252. def ZoomToMap(self):
  253. """!
  254. Set display extents to match selected raster (including NULLs)
  255. or vector map.
  256. """
  257. layers = []
  258. if self.rasters['first']:
  259. layers += self.firstMap.GetListOfLayers()
  260. if self.rasters['second']:
  261. layers += self.secondMap.GetListOfLayers()
  262. self.GetFirstWindow().ZoomToMap(layers = layers)
  263. self.GetSecondWindow().ZoomToMap(layers = layers)
  264. def OnZoomToMap(self, event):
  265. """!Zoom to map"""
  266. self.ZoomToMap()
  267. def OnZoomBack(self, event):
  268. self.GetFirstWindow().ZoomBack()
  269. self.secondMap.region = self.firstMap.region
  270. self.Render(self.GetSecondWindow())
  271. def OnSelectRasters(self, event):
  272. """!Choose raster maps and rerender."""
  273. dlg = SwipeMapDialog(self, first = self.rasters['first'], second = self.rasters['second'])
  274. dlg.CentreOnParent()
  275. if dlg.ShowModal() == wx.ID_OK:
  276. maps = dlg.GetValues()
  277. res1 = self.SetFirstRaster(name = maps[0])
  278. res2 = self.SetSecondRaster(name = maps[1])
  279. if not (res1 and res2):
  280. message = ''
  281. if not res1:
  282. message += _("Map <%s> not found. ") % maps[0]
  283. if not res2:
  284. message += _("Map <%s> not found.") % maps[1]
  285. GError(parent = self, message = message)
  286. dlg.Destroy()
  287. self.SetRasterNames()
  288. self.ZoomToMap()
  289. dlg.Destroy()
  290. self.OnRender(event = None)
  291. def SetFirstRaster(self, name):
  292. """!Set raster map to first Map"""
  293. raster = grass.find_file(name = name, element = 'cell')
  294. if raster['fullname']:
  295. self.rasters['first'] = raster['fullname']
  296. self.SetLayer(name = raster['fullname'], mapInstance = self.GetFirstMap())
  297. return True
  298. return False
  299. def SetSecondRaster(self, name):
  300. """!Set raster map to second Map"""
  301. raster = grass.find_file(name = name, element = 'cell')
  302. if raster['fullname']:
  303. self.rasters['second'] = raster['fullname']
  304. self.SetLayer(name = raster['fullname'], mapInstance = self.GetSecondMap())
  305. return True
  306. return False
  307. def SetLayer(self, name, mapInstance):
  308. """!Sets layer in Map.
  309. @param name layer (raster) name
  310. """
  311. Debug.msg (3, "SwipeMapFrame.SetLayer(): name=%s" % name)
  312. # this simple application enables to keep only one raster
  313. mapInstance.DeleteAllLayers()
  314. cmdlist = ['d.rast', 'map=%s' % name]
  315. # add layer to Map instance (core.render)
  316. newLayer = mapInstance.AddLayer(ltype = 'raster', command = cmdlist, active = True,
  317. name = name, hidden = False, opacity = 1.0,
  318. render = True)
  319. def OnSwitchWindows(self, event):
  320. """!Switch windows position."""
  321. Debug.msg(3, "SwipeMapFrame.OnSwitchWindows()")
  322. splitter = self.splitter
  323. w1, w2 = splitter.GetWindow1(), splitter.GetWindow2()
  324. splitter.ReplaceWindow(w1, w2)
  325. splitter.ReplaceWindow(w2, w1)
  326. # self.OnSize(None)
  327. splitter.OnSashChanged(None)
  328. def _saveToFile(self, fileName, fileType):
  329. """!Creates composite image by rendering both images and
  330. pasting them into the new one.
  331. @todo specify size of the new image (problem is inaccurate scaling)
  332. @todo make dividing line width and color optional
  333. """
  334. w1 = self.splitter.GetWindow1()
  335. w2 = self.splitter.GetWindow2()
  336. lineWidth = 1
  337. # render to temporary files
  338. filename1 = grass.tempfile(False) + '1'
  339. filename2 = grass.tempfile(False) + '2'
  340. width, height = self.splitter.GetClientSize()
  341. if self._mode == 'swipe':
  342. x, y = w2.GetImageCoords()
  343. w1.SaveToFile(filename1, fileType, width, height)
  344. w2.SaveToFile(filename2, fileType, width, height)
  345. else:
  346. fw, fh = w1.GetClientSize()
  347. w1.SaveToFile(filename1, fileType, fw, fh)
  348. sw, sh = w2.GetClientSize()
  349. w2.SaveToFile(filename2, fileType, sw, sh)
  350. # create empty white image - needed for line
  351. im = wx.EmptyImage(width, height)
  352. im.Replace(0, 0, 0, 255, 255, 255)
  353. # paste images
  354. if self._mode == 'swipe':
  355. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  356. im1 = wx.Image(filename1).GetSubImage((0, 0, width, -y))
  357. im.Paste(im1, 0, 0)
  358. im.Paste(wx.Image(filename2), -x, -y + lineWidth)
  359. else:
  360. im1 = wx.Image(filename1).GetSubImage((0, 0, -x, height))
  361. im.Paste(im1, 0, 0)
  362. im.Paste(wx.Image(filename2), -x + lineWidth, -y)
  363. else:
  364. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  365. im1 = wx.Image(filename1)
  366. im.Paste(im1, 0, 0)
  367. im.Paste(wx.Image(filename2), 0, fh + lineWidth)
  368. else:
  369. im1 = wx.Image(filename1)
  370. im.Paste(im1, 0, 0)
  371. im.Paste(wx.Image(filename2), fw + lineWidth, 0)
  372. im.SaveFile(fileName, fileType)
  373. # remove temporary files
  374. grass.try_remove(filename1)
  375. grass.try_remove(filename2)
  376. def SaveToFile(self, event):
  377. """!Save map to image
  378. """
  379. img = self.firstMapWindow.img or self.secondMapWindow.img
  380. if not img:
  381. GMessage(parent = self,
  382. message = _("Nothing to render (empty map). Operation canceled."))
  383. return
  384. filetype, ltype = GetImageHandlers(img)
  385. # get filename
  386. dlg = wx.FileDialog(parent = self,
  387. message = _("Choose a file name to save the image "
  388. "(no need to add extension)"),
  389. wildcard = filetype,
  390. style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
  391. if dlg.ShowModal() == wx.ID_OK:
  392. path = dlg.GetPath()
  393. if not path:
  394. dlg.Destroy()
  395. return
  396. base, ext = os.path.splitext(path)
  397. fileType = ltype[dlg.GetFilterIndex()]['type']
  398. extType = ltype[dlg.GetFilterIndex()]['ext']
  399. if ext != extType:
  400. path = base + '.' + extType
  401. self._saveToFile(path, fileType)
  402. dlg.Destroy()
  403. def OnSwitchOrientation(self, event):
  404. """!Switch orientation of the sash."""
  405. Debug.msg(3, "SwipeMapFrame.OnSwitchOrientation()")
  406. splitter = self.splitter
  407. splitter.Unsplit()
  408. if splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  409. splitter.SplitVertically(self.firstMapWindow, self.secondMapWindow, 0)
  410. self.slider = self.sliderH
  411. if self._mode == 'swipe':
  412. self._mgr.GetPane('sliderH').Show()
  413. self._mgr.GetPane('sliderV').Hide()
  414. else:
  415. splitter.SplitHorizontally(self.firstMapWindow, self.secondMapWindow, 0)
  416. self.slider = self.sliderV
  417. if self._mode == 'swipe':
  418. self._mgr.GetPane('sliderV').Show()
  419. self._mgr.GetPane('sliderH').Hide()
  420. self._mgr.Update()
  421. splitter.OnSashChanged(None)
  422. self.OnSize(None)
  423. self.SetRasterNames()
  424. def OnAddText(self, event):
  425. """!Double click on text overlay
  426. So far not implemented.
  427. """
  428. pass
  429. def SetViewMode(self, mode):
  430. """!Sets view mode.
  431. @param mode view mode ('swipe', 'mirror')
  432. """
  433. if self._mode == mode:
  434. return
  435. self._mode = mode
  436. self.toolbars['swipeMain'].SetMode(mode)
  437. # set window mode
  438. self.GetFirstWindow().SetMode(mode)
  439. self.GetSecondWindow().SetMode(mode)
  440. # hide/show slider
  441. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  442. self._mgr.GetPane('sliderV').Show(mode == 'swipe')
  443. size = self.splitter.GetSize()[1] / 2
  444. else:
  445. self._mgr.GetPane('sliderH').Show(mode == 'swipe')
  446. size = self.splitter.GetSize()[0] / 2
  447. # set sash in the middle
  448. self.splitter.SetSashPosition(size)
  449. self.slider.SetValue(size)
  450. self._mgr.Update()
  451. # enable / disable sash
  452. self.splitter.EnableSash(mode == 'swipe')
  453. # hack to make it work
  454. self.splitter.OnSashChanged(None)
  455. self.SendSizeEvent()
  456. def SetRasterNames(self):
  457. if self.rasters['first']:
  458. self.GetFirstWindow().SetRasterNameText(self.rasters['first'], 101)
  459. if self.rasters['second']:
  460. self.GetSecondWindow().SetRasterNameText(self.rasters['second'], 102)
  461. def GetMapToolbar(self):
  462. """!Returns toolbar with zooming tools"""
  463. return self.toolbars['swipeMap']
  464. def IsStandalone(self):
  465. """!Since we do not need layer manager, we are standalone"""
  466. return True
  467. def OnHelp(self, event):
  468. self._giface.Help(entry = 'wxGUI.mapswipe')
  469. def OnCloseWindow(self, event):
  470. self.GetFirstMap().Clean()
  471. self.GetSecondMap().Clean()
  472. self.Destroy()
  473. class MapSplitter(wx.SplitterWindow):
  474. """!Splitter window for displaying two maps"""
  475. def __init__(self, parent, id):
  476. wx.SplitterWindow.__init__(self, parent = parent, id = id,
  477. style = wx.SP_LIVE_UPDATE
  478. )
  479. Debug.msg(2, "MapSplitter.__init__()")
  480. self.sashWidthMin = 1
  481. self.sashWidthMax = 10
  482. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
  483. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
  484. self._moveSash = True
  485. def EnableSash(self, enable):
  486. self._moveSash = enable
  487. def Init(self):
  488. self.OnSashChanged(evt = None)
  489. self.SetMinimumPaneSize(0)
  490. self.SetSashSize(self.sashWidthMin)
  491. # def OnMotion(self, event):
  492. # w = self.GetSashSize()
  493. # w1, w2 = self.GetWindow1(), self.GetWindow2()
  494. # if self.SashHitTest(event.GetX(), event.GetY(), tolerance = 20):
  495. # if w == self.sashWidthMin:
  496. # self.SetSashSize(self.sashWidthMax)
  497. # self.SetNeedUpdating(True)
  498. # w1.movingSash = True
  499. # w2.movingSash = True
  500. # else:
  501. # w1.movingSash = False
  502. # w1.movingSash = False
  503. # else:
  504. # if w == self.sashWidthMax:
  505. # self.SetSashSize(self.sashWidthMin)
  506. # self.SetNeedUpdating(True)
  507. # w1.movingSash = True
  508. # w2.movingSash = True
  509. # else:
  510. # w1.movingSash = False
  511. # w2.movingSash = False
  512. # event.Skip()
  513. def OnSashChanged(self, evt):
  514. Debug.msg(5, "MapSplitter.OnSashChanged()")
  515. if not self._moveSash:
  516. return
  517. w1, w2 = self.GetWindow1(), self.GetWindow2()
  518. w1.movingSash = False
  519. w2.movingSash = False
  520. wx.CallAfter(self.SashChanged)
  521. def SashChanged(self):
  522. Debug.msg(5, "MapSplitter.SashChanged()")
  523. w1, w2 = self.GetWindow1(), self.GetWindow2()
  524. w1.SetImageCoords((0, 0))
  525. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  526. w = w1.GetSize()[0]
  527. w2.SetImageCoords((-w, 0))
  528. else:
  529. h = w1.GetSize()[1]
  530. w2.SetImageCoords((0, -h))
  531. w1.UpdateMap(render = False, renderVector = False)
  532. w2.UpdateMap(render = False, renderVector = False)
  533. pos = self.GetSashPosition()
  534. self.last = pos
  535. def OnSashChanging(self, event):
  536. Debug.msg(5, "MapSplitter.OnSashChanging()")
  537. if not self._moveSash:
  538. event.SetSashPosition(-1)
  539. return
  540. if not (self.GetWindowStyle() & wx.SP_LIVE_UPDATE):
  541. if event:
  542. event.Skip()
  543. return
  544. pos = self.GetSashPosition()
  545. dpos = pos - self.last
  546. self.last = pos
  547. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  548. dx = -dpos
  549. dy = 0
  550. else:
  551. dx = 0
  552. dy = -dpos
  553. self.GetWindow2().TranslateImage(dx, dy)
  554. self.GetWindow1().movingSash = True
  555. self.GetWindow2().movingSash = True