mapdisplay.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. """!
  2. @package gcp.mapdisplay
  3. @brief Display to manage ground control points with two toolbars, one
  4. for various display management functions, one for manipulating GCPs.
  5. Classes:
  6. - mapdisplay::MapFrame
  7. (C) 2006-2011 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Markus Metz
  11. """
  12. import os
  13. import math
  14. import platform
  15. from core import globalvar
  16. import wx
  17. import wx.aui
  18. from core.render import EVT_UPDATE_PRGBAR
  19. from mapdisp.toolbars import MapToolbar
  20. from gcp.toolbars import GCPDisplayToolbar, GCPManToolbar
  21. from mapdisp.gprint import PrintOptions
  22. from core.gcmd import GMessage
  23. from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
  24. from gui_core.mapdisp import MapFrameBase
  25. from core.settings import UserSettings
  26. from mapdisp.mapwindow import BufferedWindow
  27. import mapdisp.statusbar as sb
  28. # for standalone app
  29. cmdfilename = None
  30. class MapFrame(MapFrameBase):
  31. """!Main frame for map display window. Drawing takes place in
  32. child double buffered drawing window.
  33. """
  34. def __init__(self, parent=None, title=_("GRASS GIS Manage Ground Control Points"),
  35. toolbars=["gcpdisp"], tree=None, notebook=None, lmgr=None,
  36. page=None, Map=None, auimgr=None, name = 'GCPMapWindow', **kwargs):
  37. """!Main map display window with toolbars, statusbar and
  38. DrawWindow
  39. @param toolbars array of activated toolbars, e.g. ['map', 'digit']
  40. @param tree reference to layer tree
  41. @param notebook control book ID in Layer Manager
  42. @param lmgr Layer Manager
  43. @param page notebook page with layer tree
  44. @param Map instance of render.Map
  45. @param auimgs AUI manager
  46. @param kwargs wx.Frame attribures
  47. """
  48. MapFrameBase.__init__(self, parent = parent, title = title, toolbars = toolbars,
  49. Map = Map, auimgr = auimgr, name = name, **kwargs)
  50. self._layerManager = lmgr # Layer Manager object
  51. self.tree = tree # Layer Manager layer tree object
  52. self.page = page # Notebook page holding the layer tree
  53. self.layerbook = notebook # Layer Manager layer tree notebook
  54. #
  55. # Add toolbars
  56. #
  57. for toolb in toolbars:
  58. self.AddToolbar(toolb)
  59. self.activemap = self.toolbars['gcpdisp'].togglemap
  60. self.activemap.SetSelection(0)
  61. self.SrcMap = self.grwiz.SrcMap # instance of render.Map
  62. self.TgtMap = self.grwiz.TgtMap # instance of render.Map
  63. self._mgr.SetDockSizeConstraint(0.5, 0.5)
  64. #
  65. # Add statusbar
  66. #
  67. # items for choice
  68. self.statusbarItems = [sb.SbCoordinates,
  69. sb.SbRegionExtent,
  70. sb.SbCompRegionExtent,
  71. sb.SbShowRegion,
  72. sb.SbResolution,
  73. sb.SbDisplayGeometry,
  74. sb.SbMapScale,
  75. sb.SbProjection,
  76. sb.SbGoToGCP,
  77. sb.SbRMSError]
  78. # create statusbar and its manager
  79. statusbar = self.CreateStatusBar(number = 4, style = 0)
  80. statusbar.SetStatusWidths([-5, -2, -1, -1])
  81. self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
  82. # fill statusbar manager
  83. self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
  84. self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
  85. self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
  86. self.statusbarManager.SetMode(8) # goto GCP
  87. self.statusbarManager.Update()
  88. #
  89. # Init map display (buffered DC & set default cursor)
  90. #
  91. self.grwiz.SwitchEnv('source')
  92. self.SrcMapWindow = BufferedWindow(self, id=wx.ID_ANY,
  93. Map=self.SrcMap, tree=self.tree, lmgr=self._layerManager)
  94. self.grwiz.SwitchEnv('target')
  95. self.TgtMapWindow = BufferedWindow(self, id=wx.ID_ANY,
  96. Map=self.TgtMap, tree=self.tree, lmgr=self._layerManager)
  97. self.MapWindow = self.SrcMapWindow
  98. self.Map = self.SrcMap
  99. self.SrcMapWindow.SetCursor(self.cursors["cross"])
  100. self.TgtMapWindow.SetCursor(self.cursors["cross"])
  101. #
  102. # initialize region values
  103. #
  104. self._initMap(map = self.SrcMap)
  105. self._initMap(map = self.TgtMap)
  106. #
  107. # Bind various events
  108. #
  109. self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
  110. self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
  111. self.Bind(wx.EVT_SIZE, self.OnDispResize)
  112. self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
  113. #
  114. # Update fancy gui style
  115. #
  116. # AuiManager wants a CentrePane, workaround to get two equally sized windows
  117. self.list = self.CreateGCPList()
  118. #self.SrcMapWindow.SetSize((300, 300))
  119. #self.TgtMapWindow.SetSize((300, 300))
  120. self.list.SetSize((100, 150))
  121. self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
  122. Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
  123. RightDockable(False).PinButton().FloatingSize((600,200)).
  124. CloseButton(False).DestroyOnClose(True).
  125. Top().Layer(1).MinSize((200,100)))
  126. self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
  127. Name("source").Caption(_("Source Display")).Dockable(False).
  128. CloseButton(False).DestroyOnClose(True).Floatable(False).
  129. Centre())
  130. self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
  131. Name("target").Caption(_("Target Display")).Dockable(False).
  132. CloseButton(False).DestroyOnClose(True).Floatable(False).
  133. Right().Layer(0))
  134. srcwidth, srcheight = self.SrcMapWindow.GetSize()
  135. tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
  136. srcwidth = (srcwidth + tgtwidth) / 2
  137. self._mgr.GetPane("target").Hide()
  138. self._mgr.Update()
  139. self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
  140. self._mgr.GetPane("target").BestSize((srcwidth, srcheight))
  141. if self.show_target:
  142. self._mgr.GetPane("target").Show()
  143. else:
  144. self.activemap.Enable(False)
  145. # needed by Mac OS, does not harm on Linux, breaks display on Windows
  146. if platform.system() != 'Windows':
  147. self._mgr.Update()
  148. #
  149. # Init print module and classes
  150. #
  151. self.printopt = PrintOptions(self, self.MapWindow)
  152. #
  153. # Initialization of digitization tool
  154. #
  155. self.digit = None
  156. # set active map
  157. self.MapWindow = self.SrcMapWindow
  158. self.Map = self.SrcMap
  159. # do not init zoom history here, that happens when zooming to map(s)
  160. #
  161. # Re-use dialogs
  162. #
  163. self.dialogs = {}
  164. self.dialogs['attributes'] = None
  165. self.dialogs['category'] = None
  166. self.dialogs['barscale'] = None
  167. self.dialogs['legend'] = None
  168. self.decorationDialog = None # decoration/overlays
  169. def AddToolbar(self, name):
  170. """!Add defined toolbar to the window
  171. Currently known toolbars are:
  172. - 'map' - basic map toolbar
  173. - 'vdigit' - vector digitizer
  174. - 'gcpdisp' - GCP Manager, Display
  175. - 'gcpman' - GCP Manager, points management
  176. - 'nviz' - 3D view mode
  177. """
  178. # default toolbar
  179. if name == "map":
  180. self.toolbars['map'] = MapToolbar(self, self.Map)
  181. self._mgr.AddPane(self.toolbars['map'],
  182. wx.aui.AuiPaneInfo().
  183. Name("maptoolbar").Caption(_("Map Toolbar")).
  184. ToolbarPane().Top().
  185. LeftDockable(False).RightDockable(False).
  186. BottomDockable(False).TopDockable(True).
  187. CloseButton(False).Layer(2).
  188. BestSize((self.toolbars['map'].GetSize())))
  189. # GCP display
  190. elif name == "gcpdisp":
  191. self.toolbars['gcpdisp'] = GCPDisplayToolbar(self)
  192. self._mgr.AddPane(self.toolbars['gcpdisp'],
  193. wx.aui.AuiPaneInfo().
  194. Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
  195. ToolbarPane().Top().
  196. LeftDockable(False).RightDockable(False).
  197. BottomDockable(False).TopDockable(True).
  198. CloseButton(False).Layer(2))
  199. if self.show_target == False:
  200. self.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
  201. self.toolbars['gcpman'] = GCPManToolbar(self)
  202. self._mgr.AddPane(self.toolbars['gcpman'],
  203. wx.aui.AuiPaneInfo().
  204. Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
  205. ToolbarPane().Top().Row(1).
  206. LeftDockable(False).RightDockable(False).
  207. BottomDockable(False).TopDockable(True).
  208. CloseButton(False).Layer(2))
  209. self._mgr.Update()
  210. def OnUpdateProgress(self, event):
  211. """
  212. Update progress bar info
  213. """
  214. self.GetProgressBar().SetValue(event.value)
  215. event.Skip()
  216. def OnFocus(self, event):
  217. """
  218. Change choicebook page to match display.
  219. Or set display for georectifying
  220. """
  221. if self._layerManager and \
  222. self._layerManager.gcpmanagement:
  223. # in GCP Management, set focus to current MapWindow for mouse actions
  224. self.OnPointer(event)
  225. self.MapWindow.SetFocus()
  226. else:
  227. # change bookcontrol page to page associated with display
  228. # GCP Manager: use bookcontrol?
  229. if self.page:
  230. pgnum = self.layerbook.GetPageIndex(self.page)
  231. if pgnum > -1:
  232. self.layerbook.SetSelection(pgnum)
  233. event.Skip()
  234. def OnDraw(self, event):
  235. """!Re-display current map composition
  236. """
  237. self.MapWindow.UpdateMap(render = False)
  238. def OnRender(self, event):
  239. """!Re-render map composition (each map layer)
  240. """
  241. # delete tmp map layers (queries)
  242. qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
  243. for layer in qlayer:
  244. self.Map.DeleteLayer(layer)
  245. self.SrcMapWindow.UpdateMap(render=True)
  246. if self.show_target:
  247. self.TgtMapWindow.UpdateMap(render=True)
  248. # update statusbar
  249. self.StatusbarUpdate()
  250. def OnPointer(self, event):
  251. """!Pointer button clicked
  252. """
  253. # change the cursor
  254. self.SrcMapWindow.SetCursor(self.cursors["cross"])
  255. self.SrcMapWindow.mouse['use'] = "pointer"
  256. self.SrcMapWindow.mouse['box'] = "point"
  257. self.TgtMapWindow.SetCursor(self.cursors["cross"])
  258. self.TgtMapWindow.mouse['use'] = "pointer"
  259. self.TgtMapWindow.mouse['box'] = "point"
  260. def OnZoomIn(self, event):
  261. """
  262. Zoom in the map.
  263. Set mouse cursor, zoombox attributes, and zoom direction
  264. """
  265. if self.GetToolbar('map'):
  266. self.toolbars['map'].OnTool(event)
  267. self.toolbars['map'].action['desc'] = ''
  268. self.MapWindow.mouse['use'] = "zoom"
  269. self.MapWindow.mouse['box'] = "box"
  270. self.MapWindow.zoomtype = 1
  271. self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  272. # change the cursor
  273. self.MapWindow.SetCursor(self.cursors["cross"])
  274. if self.MapWindow == self.SrcMapWindow:
  275. win = self.TgtMapWindow
  276. elif self.MapWindow == self.TgtMapWindow:
  277. win = self.SrcMapWindow
  278. win.mouse['use'] = "zoom"
  279. win.mouse['box'] = "box"
  280. win.zoomtype = 1
  281. win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  282. # change the cursor
  283. win.SetCursor(self.cursors["cross"])
  284. def OnZoomOut(self, event):
  285. """
  286. Zoom out the map.
  287. Set mouse cursor, zoombox attributes, and zoom direction
  288. """
  289. if self.GetToolbar('map'):
  290. self.toolbars['map'].OnTool(event)
  291. self.toolbars['map'].action['desc'] = ''
  292. self.MapWindow.mouse['use'] = "zoom"
  293. self.MapWindow.mouse['box'] = "box"
  294. self.MapWindow.zoomtype = -1
  295. self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  296. # change the cursor
  297. self.MapWindow.SetCursor(self.cursors["cross"])
  298. if self.MapWindow == self.SrcMapWindow:
  299. win = self.TgtMapWindow
  300. elif self.MapWindow == self.TgtMapWindow:
  301. win = self.SrcMapWindow
  302. win.mouse['use'] = "zoom"
  303. win.mouse['box'] = "box"
  304. win.zoomtype = -1
  305. win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  306. # change the cursor
  307. win.SetCursor(self.cursors["cross"])
  308. def OnPan(self, event):
  309. """
  310. Panning, set mouse to drag
  311. """
  312. if self.GetToolbar('map'):
  313. self.toolbars['map'].OnTool(event)
  314. self.toolbars['map'].action['desc'] = ''
  315. self.MapWindow.mouse['use'] = "pan"
  316. self.MapWindow.mouse['box'] = "pan"
  317. self.MapWindow.zoomtype = 0
  318. # change the cursor
  319. self.MapWindow.SetCursor(self.cursors["hand"])
  320. if self.MapWindow == self.SrcMapWindow:
  321. win = self.TgtMapWindow
  322. elif self.MapWindow == self.TgtMapWindow:
  323. win = self.SrcMapWindow
  324. win.mouse['use'] = "pan"
  325. win.mouse['box'] = "pan"
  326. win.zoomtype = 0
  327. # change the cursor
  328. win.SetCursor(self.cursors["hand"])
  329. def OnErase(self, event):
  330. """
  331. Erase the canvas
  332. """
  333. self.MapWindow.EraseMap()
  334. if self.MapWindow == self.SrcMapWindow:
  335. win = self.TgtMapWindow
  336. elif self.MapWindow == self.TgtMapWindow:
  337. win = self.SrcMapWindow
  338. win.EraseMap()
  339. def OnZoomRegion(self, event):
  340. """
  341. Zoom to region
  342. """
  343. self.Map.getRegion()
  344. self.Map.getResolution()
  345. self.UpdateMap()
  346. # event.Skip()
  347. def OnAlignRegion(self, event):
  348. """
  349. Align region
  350. """
  351. if not self.Map.alignRegion:
  352. self.Map.alignRegion = True
  353. else:
  354. self.Map.alignRegion = False
  355. # event.Skip()
  356. def SaveToFile(self, event):
  357. """!Save map to image
  358. """
  359. img = self.MapWindow.img
  360. if not img:
  361. GMessage(parent = self,
  362. message = _("Nothing to render (empty map). Operation canceled."))
  363. return
  364. filetype, ltype = GetImageHandlers(img)
  365. # get size
  366. dlg = ImageSizeDialog(self)
  367. dlg.CentreOnParent()
  368. if dlg.ShowModal() != wx.ID_OK:
  369. dlg.Destroy()
  370. return
  371. width, height = dlg.GetValues()
  372. dlg.Destroy()
  373. # get filename
  374. dlg = wx.FileDialog(parent = self,
  375. message = _("Choose a file name to save the image "
  376. "(no need to add extension)"),
  377. wildcard = filetype,
  378. style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
  379. if dlg.ShowModal() == wx.ID_OK:
  380. path = dlg.GetPath()
  381. if not path:
  382. dlg.Destroy()
  383. return
  384. base, ext = os.path.splitext(path)
  385. fileType = ltype[dlg.GetFilterIndex()]['type']
  386. extType = ltype[dlg.GetFilterIndex()]['ext']
  387. if ext != extType:
  388. path = base + '.' + extType
  389. self.MapWindow.SaveToFile(path, fileType,
  390. width, height)
  391. dlg.Destroy()
  392. def PrintMenu(self, event):
  393. """
  394. Print options and output menu for map display
  395. """
  396. point = wx.GetMousePosition()
  397. printmenu = wx.Menu()
  398. # Add items to the menu
  399. setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
  400. printmenu.AppendItem(setup)
  401. self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
  402. preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
  403. printmenu.AppendItem(preview)
  404. self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
  405. doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
  406. printmenu.AppendItem(doprint)
  407. self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
  408. # Popup the menu. If an item is selected then its handler
  409. # will be called before PopupMenu returns.
  410. self.PopupMenu(printmenu)
  411. printmenu.Destroy()
  412. def FormatDist(self, dist):
  413. """!Format length numbers and units in a nice way,
  414. as a function of length. From code by Hamish Bowman
  415. Grass Development Team 2006"""
  416. mapunits = self.Map.projinfo['units']
  417. if mapunits == 'metres': mapunits = 'meters'
  418. outunits = mapunits
  419. dist = float(dist)
  420. divisor = 1.0
  421. # figure out which units to use
  422. if mapunits == 'meters':
  423. if dist > 2500.0:
  424. outunits = 'km'
  425. divisor = 1000.0
  426. else: outunits = 'm'
  427. elif mapunits == 'feet':
  428. # nano-bug: we match any "feet", but US Survey feet is really
  429. # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
  430. # miles the tick markers are rounded to the nearest 10th of a
  431. # mile (528'), the difference in foot flavours is ignored.
  432. if dist > 5280.0:
  433. outunits = 'miles'
  434. divisor = 5280.0
  435. else:
  436. outunits = 'ft'
  437. elif 'degree' in mapunits:
  438. if dist < 1:
  439. outunits = 'min'
  440. divisor = (1/60.0)
  441. else:
  442. outunits = 'deg'
  443. # format numbers in a nice way
  444. if (dist/divisor) >= 2500.0:
  445. outdist = round(dist/divisor)
  446. elif (dist/divisor) >= 1000.0:
  447. outdist = round(dist/divisor,1)
  448. elif (dist/divisor) > 0.0:
  449. outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
  450. else:
  451. outdist = float(dist/divisor)
  452. return (outdist, outunits)
  453. def OnZoomToRaster(self, event):
  454. """!
  455. Set display extents to match selected raster map (ignore NULLs)
  456. """
  457. self.MapWindow.ZoomToMap(ignoreNulls = True)
  458. def OnZoomToSaved(self, event):
  459. """!Set display geometry to match extents in
  460. saved region file
  461. """
  462. self.MapWindow.ZoomToSaved()
  463. def OnDisplayToWind(self, event):
  464. """!Set computational region (WIND file) to match display
  465. extents
  466. """
  467. self.MapWindow.DisplayToWind()
  468. def SaveDisplayRegion(self, event):
  469. """!Save display extents to named region file.
  470. """
  471. self.MapWindow.SaveDisplayRegion()
  472. def OnZoomMenu(self, event):
  473. """!Popup Zoom menu
  474. """
  475. point = wx.GetMousePosition()
  476. zoommenu = wx.Menu()
  477. # Add items to the menu
  478. zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
  479. zoommenu.AppendItem(zoomwind)
  480. self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
  481. zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
  482. zoommenu.AppendItem(zoomdefault)
  483. self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
  484. zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
  485. zoommenu.AppendItem(zoomsaved)
  486. self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
  487. savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
  488. zoommenu.AppendItem(savewind)
  489. self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
  490. savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
  491. zoommenu.AppendItem(savezoom)
  492. self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
  493. # Popup the menu. If an item is selected then its handler
  494. # will be called before PopupMenu returns.
  495. self.PopupMenu(zoommenu)
  496. zoommenu.Destroy()
  497. def IsStandalone(self):
  498. """!Check if Map display is standalone"""
  499. if self._layerManager:
  500. return False
  501. return True
  502. def GetLayerManager(self):
  503. """!Get reference to Layer Manager
  504. @return window reference
  505. @return None (if standalone)
  506. """
  507. return self._layerManager
  508. def GetSrcWindow(self):
  509. return self.SrcMapWindow
  510. def GetTgtWindow(self):
  511. return self.TgtMapWindow
  512. def GetShowTarget(self):
  513. return self.show_target
  514. def GetMapToolbar(self):
  515. """!Returns toolbar with zooming tools"""
  516. return self.toolbars['gcpdisp']