frame.py 24 KB

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