frame.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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. super(SwipeMapFrame, self).OnSize(event)
  175. def OnIdle(self, event):
  176. if self.resize and time.clock() - self.resize > 0.2:
  177. w1 = self.GetFirstWindow()
  178. w2 = self.GetSecondWindow()
  179. sizeAll = self.splitter.GetSize()
  180. w1.SetClientSize(sizeAll)
  181. w2.SetClientSize(sizeAll)
  182. w1.OnSize(event)
  183. w2.OnSize(event)
  184. self.ResetSlider()
  185. self.resize = False
  186. def OnAutoRenderChanged(self, state):
  187. """!Auto rendering state changed."""
  188. style = self.splitter.GetWindowStyle()
  189. style ^= wx.SP_LIVE_UPDATE
  190. self.splitter.SetWindowStyle(style)
  191. def AddToolbar(self, name):
  192. """!Add defined toolbar to the window
  193. Currently known toolbars are:
  194. - 'swipeMap' - basic map toolbar
  195. - 'swipeMain' - swipe functionality
  196. """
  197. if name == "swipeMap":
  198. self.toolbars[name] = SwipeMapToolbar(self)
  199. self._mgr.AddPane(self.toolbars[name],
  200. wx.aui.AuiPaneInfo().
  201. Name(name).Caption(_("Map Toolbar")).
  202. ToolbarPane().Top().
  203. LeftDockable(False).RightDockable(False).
  204. BottomDockable(False).TopDockable(True).
  205. CloseButton(False).Layer(2).Row(1).
  206. BestSize((self.toolbars[name].GetBestSize())))
  207. if name == "swipeMain":
  208. self.toolbars[name] = SwipeMainToolbar(self)
  209. self._mgr.AddPane(self.toolbars[name],
  210. wx.aui.AuiPaneInfo().
  211. Name(name).Caption(_("Main Toolbar")).
  212. ToolbarPane().Top().
  213. LeftDockable(False).RightDockable(False).
  214. BottomDockable(False).TopDockable(True).
  215. CloseButton(False).Layer(2).Row(1).
  216. BestSize((self.toolbars[name].GetBestSize())))
  217. if name == "swipeMisc":
  218. self.toolbars[name] = SwipeMiscToolbar(self)
  219. self._mgr.AddPane(self.toolbars[name],
  220. wx.aui.AuiPaneInfo().
  221. Name(name).Caption(_("Misc Toolbar")).
  222. ToolbarPane().Top().
  223. LeftDockable(False).RightDockable(False).
  224. BottomDockable(False).TopDockable(True).
  225. CloseButton(False).Layer(2).Row(1).
  226. BestSize((self.toolbars[name].GetBestSize())))
  227. def _addPanes(self):
  228. """!Add splitter window and sliders to aui manager"""
  229. # splitter window
  230. self._mgr.AddPane(self.splitter, wx.aui.AuiPaneInfo().
  231. Name('splitter').CaptionVisible(False).PaneBorder(True).
  232. Dockable(False).Floatable(False).CloseButton(False).
  233. Center().Layer(1).BestSize((self.splitter.GetBestSize())))
  234. # sliders
  235. self._mgr.AddPane(self.sliderH, wx.aui.AuiPaneInfo().
  236. Name('sliderH').CaptionVisible(False).PaneBorder(False).
  237. CloseButton(False).Gripper(True).GripperTop(False).
  238. BottomDockable(True).TopDockable(True).
  239. LeftDockable(False).RightDockable(False).
  240. Bottom().Layer(1).BestSize((self.sliderH.GetBestSize())))
  241. self._mgr.AddPane(self.sliderV, wx.aui.AuiPaneInfo().
  242. Name('sliderV').CaptionVisible(False).PaneBorder(False).
  243. CloseButton(False).Gripper(True).GripperTop(True).
  244. BottomDockable(False).TopDockable(False).
  245. LeftDockable(True).RightDockable(True).
  246. Right().Layer(1).BestSize((self.sliderV.GetBestSize())))
  247. def ZoomToMap(self):
  248. """!
  249. Set display extents to match selected raster (including NULLs)
  250. or vector map.
  251. """
  252. if self.rasters['first']:
  253. self.GetFirstWindow().ZoomToMap(layers = self.firstMap.GetListOfLayers())
  254. if self.rasters['second']:
  255. self.GetSecondWindow().ZoomToMap(layers = self.secondMap.GetListOfLayers())
  256. def OnZoomToMap(self, event):
  257. """!Zoom to map"""
  258. self.ZoomToMap()
  259. def OnZoomBack(self, event):
  260. self.GetFirstWindow().ZoomBack()
  261. self.secondMap.region = self.firstMap.region
  262. self.Render(self.GetSecondWindow())
  263. def OnSelectRasters(self, event):
  264. """!Choose raster maps and rerender."""
  265. dlg = SwipeMapDialog(self, first = self.rasters['first'], second = self.rasters['second'])
  266. dlg.CentreOnParent()
  267. if dlg.ShowModal() == wx.ID_OK:
  268. maps = dlg.GetValues()
  269. res1 = self.SetFirstRaster(name = maps[0])
  270. res2 = self.SetSecondRaster(name = maps[1])
  271. if not (res1 and res2):
  272. message = ''
  273. if not res1:
  274. message += _("Map <%s> not found. ") % maps[0]
  275. if not res2:
  276. message += _("Map <%s> not found.") % maps[1]
  277. GError(parent = self, message = message)
  278. dlg.Destroy()
  279. self.SetRasterNames()
  280. self.ZoomToMap()
  281. dlg.Destroy()
  282. self.OnRender(event = None)
  283. def SetFirstRaster(self, name):
  284. """!Set raster map to first Map"""
  285. raster = grass.find_file(name = name, element = 'cell')
  286. if raster['fullname']:
  287. self.rasters['first'] = raster['fullname']
  288. self.SetLayer(name = raster['fullname'], mapInstance = self.GetFirstMap())
  289. return True
  290. return False
  291. def SetSecondRaster(self, name):
  292. """!Set raster map to second Map"""
  293. raster = grass.find_file(name = name, element = 'cell')
  294. if raster['fullname']:
  295. self.rasters['second'] = raster['fullname']
  296. self.SetLayer(name = raster['fullname'], mapInstance = self.GetSecondMap())
  297. return True
  298. return False
  299. def SetLayer(self, name, mapInstance):
  300. """!Sets layer in Map.
  301. @param name layer (raster) name
  302. """
  303. Debug.msg (3, "SwipeMapFrame.SetLayer(): name=%s" % name)
  304. # this simple application enables to keep only one raster
  305. mapInstance.DeleteAllLayers()
  306. cmdlist = ['d.rast', 'map=%s' % name]
  307. # add layer to Map instance (core.render)
  308. newLayer = mapInstance.AddLayer(ltype = 'raster', command = cmdlist, active = True,
  309. name = name, hidden = False, opacity = 1.0,
  310. render = True)
  311. def OnSwitchWindows(self, event):
  312. """!Switch windows position."""
  313. Debug.msg(3, "SwipeMapFrame.OnSwitchWindows()")
  314. splitter = self.splitter
  315. w1, w2 = splitter.GetWindow1(), splitter.GetWindow2()
  316. splitter.ReplaceWindow(w1, w2)
  317. splitter.ReplaceWindow(w2, w1)
  318. # self.OnSize(None)
  319. splitter.OnSashChanged(None)
  320. def _saveToFile(self, fileName, fileType):
  321. """!Creates composite image by rendering both images and
  322. pasting them into the new one.
  323. @todo specify size of the new image (problem is inaccurate scaling)
  324. @todo make dividing line width and color optional
  325. """
  326. w1 = self.splitter.GetWindow1()
  327. w2 = self.splitter.GetWindow2()
  328. lineWidth = 1
  329. # render to temporary files
  330. filename1 = grass.tempfile(False) + '1'
  331. filename2 = grass.tempfile(False) + '2'
  332. width, height = self.splitter.GetClientSize()
  333. if self._mode == 'swipe':
  334. x, y = w2.GetImageCoords()
  335. w1.SaveToFile(filename1, fileType, width, height)
  336. w2.SaveToFile(filename2, fileType, width, height)
  337. else:
  338. fw, fh = w1.GetClientSize()
  339. w1.SaveToFile(filename1, fileType, fw, fh)
  340. sw, sh = w2.GetClientSize()
  341. w2.SaveToFile(filename2, fileType, sw, sh)
  342. # create empty white image - needed for line
  343. im = wx.EmptyImage(width, height)
  344. im.Replace(0, 0, 0, 255, 255, 255)
  345. # paste images
  346. if self._mode == 'swipe':
  347. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  348. im1 = wx.Image(filename1).GetSubImage((0, 0, width, -y))
  349. im.Paste(im1, 0, 0)
  350. im.Paste(wx.Image(filename2), -x, -y + lineWidth)
  351. else:
  352. im1 = wx.Image(filename1).GetSubImage((0, 0, -x, height))
  353. im.Paste(im1, 0, 0)
  354. im.Paste(wx.Image(filename2), -x + lineWidth, -y)
  355. else:
  356. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  357. im1 = wx.Image(filename1)
  358. im.Paste(im1, 0, 0)
  359. im.Paste(wx.Image(filename2), 0, fh + lineWidth)
  360. else:
  361. im1 = wx.Image(filename1)
  362. im.Paste(im1, 0, 0)
  363. im.Paste(wx.Image(filename2), fw + lineWidth, 0)
  364. im.SaveFile(fileName, fileType)
  365. # remove temporary files
  366. grass.try_remove(filename1)
  367. grass.try_remove(filename2)
  368. def SaveToFile(self, event):
  369. """!Save map to image
  370. """
  371. img = self.firstMapWindow.img or self.secondMapWindow.img
  372. if not img:
  373. GMessage(parent = self,
  374. message = _("Nothing to render (empty map). Operation canceled."))
  375. return
  376. filetype, ltype = GetImageHandlers(img)
  377. # get filename
  378. dlg = wx.FileDialog(parent = self,
  379. message = _("Choose a file name to save the image "
  380. "(no need to add extension)"),
  381. wildcard = filetype,
  382. style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
  383. if dlg.ShowModal() == wx.ID_OK:
  384. path = dlg.GetPath()
  385. if not path:
  386. dlg.Destroy()
  387. return
  388. base, ext = os.path.splitext(path)
  389. fileType = ltype[dlg.GetFilterIndex()]['type']
  390. extType = ltype[dlg.GetFilterIndex()]['ext']
  391. if ext != extType:
  392. path = base + '.' + extType
  393. self._saveToFile(path, fileType)
  394. dlg.Destroy()
  395. def OnSwitchOrientation(self, event):
  396. """!Switch orientation of the sash."""
  397. Debug.msg(3, "SwipeMapFrame.OnSwitchOrientation()")
  398. splitter = self.splitter
  399. splitter.Unsplit()
  400. if splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  401. splitter.SplitVertically(self.firstMapWindow, self.secondMapWindow, 0)
  402. self.slider = self.sliderH
  403. if self._mode == 'swipe':
  404. self._mgr.GetPane('sliderH').Show()
  405. self._mgr.GetPane('sliderV').Hide()
  406. else:
  407. splitter.SplitHorizontally(self.firstMapWindow, self.secondMapWindow, 0)
  408. self.slider = self.sliderV
  409. if self._mode == 'swipe':
  410. self._mgr.GetPane('sliderV').Show()
  411. self._mgr.GetPane('sliderH').Hide()
  412. self._mgr.Update()
  413. splitter.OnSashChanged(None)
  414. self.OnSize(None)
  415. self.SetRasterNames()
  416. def OnAddText(self, event):
  417. """!Double click on text overlay
  418. So far not implemented.
  419. """
  420. pass
  421. def SetViewMode(self, mode):
  422. """!Sets view mode.
  423. @param mode view mode ('swipe', 'mirror')
  424. """
  425. if self._mode == mode:
  426. return
  427. self._mode = mode
  428. self.toolbars['swipeMain'].SetMode(mode)
  429. # set window mode
  430. self.GetFirstWindow().SetMode(mode)
  431. self.GetSecondWindow().SetMode(mode)
  432. # hide/show slider
  433. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  434. self._mgr.GetPane('sliderV').Show(mode == 'swipe')
  435. size = self.splitter.GetSize()[1] / 2
  436. else:
  437. self._mgr.GetPane('sliderH').Show(mode == 'swipe')
  438. size = self.splitter.GetSize()[0] / 2
  439. # set sash in the middle
  440. self.splitter.SetSashPosition(size)
  441. self.slider.SetValue(size)
  442. self._mgr.Update()
  443. # enable / disable sash
  444. self.splitter.EnableSash(mode == 'swipe')
  445. # hack to make it work
  446. self.splitter.OnSashChanged(None)
  447. self.SendSizeEvent()
  448. def SetRasterNames(self):
  449. if self.rasters['first']:
  450. self.GetFirstWindow().SetRasterNameText(self.rasters['first'], 101)
  451. if self.rasters['second']:
  452. self.GetSecondWindow().SetRasterNameText(self.rasters['second'], 102)
  453. def GetMapToolbar(self):
  454. """!Returns toolbar with zooming tools"""
  455. return self.toolbars['swipeMap']
  456. def IsStandalone(self):
  457. """!Since we do not need layer manager, we are standalone"""
  458. return True
  459. def OnHelp(self, event):
  460. self._giface.Help(entry = 'wxGUI.mapswipe')
  461. def OnCloseWindow(self, event):
  462. self.GetFirstMap().Clean()
  463. self.GetSecondMap().Clean()
  464. self.Destroy()
  465. class MapSplitter(wx.SplitterWindow):
  466. """!Splitter window for displaying two maps"""
  467. def __init__(self, parent, id):
  468. wx.SplitterWindow.__init__(self, parent = parent, id = id,
  469. style = wx.SP_LIVE_UPDATE
  470. )
  471. Debug.msg(2, "MapSplitter.__init__()")
  472. self.sashWidthMin = 1
  473. self.sashWidthMax = 10
  474. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
  475. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
  476. self._moveSash = True
  477. def EnableSash(self, enable):
  478. self._moveSash = enable
  479. def Init(self):
  480. self.OnSashChanged(evt = None)
  481. self.SetMinimumPaneSize(0)
  482. self.SetSashSize(self.sashWidthMin)
  483. # def OnMotion(self, event):
  484. # w = self.GetSashSize()
  485. # w1, w2 = self.GetWindow1(), self.GetWindow2()
  486. # if self.SashHitTest(event.GetX(), event.GetY(), tolerance = 20):
  487. # if w == self.sashWidthMin:
  488. # self.SetSashSize(self.sashWidthMax)
  489. # self.SetNeedUpdating(True)
  490. # w1.movingSash = True
  491. # w2.movingSash = True
  492. # else:
  493. # w1.movingSash = False
  494. # w1.movingSash = False
  495. # else:
  496. # if w == self.sashWidthMax:
  497. # self.SetSashSize(self.sashWidthMin)
  498. # self.SetNeedUpdating(True)
  499. # w1.movingSash = True
  500. # w2.movingSash = True
  501. # else:
  502. # w1.movingSash = False
  503. # w2.movingSash = False
  504. # event.Skip()
  505. def OnSashChanged(self, evt):
  506. Debug.msg(5, "MapSplitter.OnSashChanged()")
  507. if not self._moveSash:
  508. return
  509. w1, w2 = self.GetWindow1(), self.GetWindow2()
  510. w1.movingSash = False
  511. w2.movingSash = False
  512. wx.CallAfter(self.SashChanged)
  513. def SashChanged(self):
  514. Debug.msg(5, "MapSplitter.SashChanged()")
  515. w1, w2 = self.GetWindow1(), self.GetWindow2()
  516. w1.SetImageCoords((0, 0))
  517. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  518. w = w1.GetSize()[0]
  519. w2.SetImageCoords((-w, 0))
  520. else:
  521. h = w1.GetSize()[1]
  522. w2.SetImageCoords((0, -h))
  523. w1.UpdateMap(render = False, renderVector = False)
  524. w2.UpdateMap(render = False, renderVector = False)
  525. pos = self.GetSashPosition()
  526. self.last = pos
  527. def OnSashChanging(self, event):
  528. Debug.msg(5, "MapSplitter.OnSashChanging()")
  529. if not self._moveSash:
  530. event.SetSashPosition(-1)
  531. return
  532. if not (self.GetWindowStyle() & wx.SP_LIVE_UPDATE):
  533. if event:
  534. event.Skip()
  535. return
  536. pos = self.GetSashPosition()
  537. dpos = pos - self.last
  538. self.last = pos
  539. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  540. dx = -dpos
  541. dy = 0
  542. else:
  543. dx = 0
  544. dy = -dpos
  545. self.GetWindow2().TranslateImage(dx, dy)
  546. self.GetWindow1().movingSash = True
  547. self.GetWindow2().movingSash = True