frame.py 24 KB

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