frame.py 24 KB

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