diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index bf520545742..5e7b1fffdce 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -69,7 +69,11 @@ from lmgr.toolbars import LMMiscToolbar, LMNvizToolbar, DisplayPanelToolbar from lmgr.workspace import WorkspaceManager from lmgr.pyshell import PyShellWindow -from lmgr.giface import LayerManagerGrassInterface +from lmgr.giface import ( + LayerManagerGrassInterface, + LayerManagerGrassInterfaceForMapDisplay, +) +from mapdisp.frame import MapPanel from datacatalog.catalog import DataCatalog from gui_core.forms import GUI from gui_core.wrap import Menu, TextEntryDialog @@ -151,8 +155,8 @@ def show_menu_errors(messages): # set pane sizes according to the full screen size of the primary monitor size = wx.Display().GetGeometry().GetSize() - self.PANE_BEST_SIZE = tuple(t / 5 for t in size) - self.PANE_MIN_SIZE = tuple(t / 10 for t in size) + self.PANE_BEST_SIZE = tuple(t / 3 for t in size) + self.PANE_MIN_SIZE = tuple(t / 7 for t in size) # create widgets and build panes self.CreateMenuBar() @@ -290,6 +294,22 @@ def IsPaneShown(self, name): return self._auimgr.GetPane(name).IsShown() return False + def _createMapNotebook(self): + """Create Map Display notebook""" + # create the notebook off-window to avoid flicker + client_size = self.GetClientSize() + notebook_style = ( + aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER + ) + self.mapnotebook = aui.AuiNotebook( + self, + -1, + wx.Point(client_size.x, client_size.y), + wx.Size(430, 200), + agwStyle=notebook_style, + ) + self.mapnotebook.SetArtProvider(aui.AuiDefaultTabArt()) + def _createDataCatalog(self, parent): """Initialize Data Catalog widget""" self.datacatalog = DataCatalog(parent=parent, giface=self._giface) @@ -360,21 +380,152 @@ def _createPythonShell(self, parent): else: self.pyshell = None - def _createMapDisplay(self, parent): - """Set up Map Display""" - # blank panel for testing - self.mapdisplay = wx.Panel(parent=parent) + def OnNewDisplay(self, event=None): + """Create new layer tree and map display window instance""" + self.NewDisplay() + + def NewDisplay(self, name=None, show=True): + """Create new layer tree structure and associated map display and + add it to display notebook tab + :param name: name of new map display window + :param show: show map display window if True + """ + Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.displayIndex) + if not name: + name = _("Map Display {number}").format(number=self.displayIndex + 1) + + # make a new page in the bookcontrol for the layer tree (on page 0 of + # the notebook) + self.pg_panel = wx.Panel( + self.notebookLayers, id=wx.ID_ANY, style=wx.BORDER_NONE + ) + self.notebookLayers.AddPage(page=self.pg_panel, text=name, select=True) + self.currentPage = self.notebookLayers.GetCurrentPage() + self.currentPageNum = self.notebookLayers.GetSelection() + + def CreateNewMapDisplay(layertree): + """Callback function which creates a new Map Display window + :param layertree: layer tree object + :return: reference to mapdisplay instance + """ + # create instance of Map Display interface + self._gifaceForDisplay = LayerManagerGrassInterfaceForMapDisplay( + self._giface, layertree + ) + + # create Map Display + mapdisplay = MapPanel( + parent=self.mapnotebook, + giface=self._gifaceForDisplay, + id=wx.ID_ANY, + tree=layertree, + lmgr=self, + Map=layertree.Map, + title=name, + size=globalvar.MAP_WINDOW_SIZE, + ) + # add map display panel to notebook and make it current + self.mapnotebook.AddPage(mapdisplay, name) + self.mapnotebook.SetSelection(self.currentPageNum) + + # set map display properties + self._setUpMapDisplay(mapdisplay) + + return mapdisplay + + # create layer tree (tree control for managing GIS layers) and put on + # new notebook page and new map display frame + self.currentPage.maptree = LayerTree( + parent=self.currentPage, + giface=self._giface, + createNewMapDisplay=CreateNewMapDisplay, + id=wx.ID_ANY, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.TR_HAS_BUTTONS + | wx.TR_LINES_AT_ROOT + | wx.TR_HIDE_ROOT + | wx.TR_DEFAULT_STYLE + | wx.NO_BORDER + | wx.FULL_REPAINT_ON_RESIZE, + lmgr=self, + notebook=self.notebookLayers, + title=name, + ) + + # layout for controls + cb_boxsizer = wx.BoxSizer(wx.VERTICAL) + cb_boxsizer.Add(self.GetLayerTree(), proportion=1, flag=wx.EXPAND, border=1) + self.currentPage.SetSizer(cb_boxsizer) + cb_boxsizer.Fit(self.GetLayerTree()) + self.currentPage.Layout() + self.GetLayerTree().Layout() + + self.displayIndex += 1 + + return self.GetMapDisplay() + + def _setUpMapDisplay(self, mapdisplay): + """Set up Map Display properties""" + page = self.currentPage + + def CanCloseDisplay(askIfSaveWorkspace): + """Callback to check if user wants to close display""" + pgnum = self.notebookLayers.GetPageIndex(page) + name = self.notebookLayers.GetPageText(pgnum) + caption = _("Close Map Display {}").format(name) + if not askIfSaveWorkspace or ( + askIfSaveWorkspace and self.workspace_manager.CanClosePage(caption) + ): + return pgnum + return None + + mapdisplay.canCloseDisplayCallback = CanCloseDisplay + + # bind various events + mapdisplay.Bind( + wx.EVT_ACTIVATE, + lambda event, page=self.currentPage: self._onMapDisplayFocus(page), + ) + + mapdisplay.starting3dMode.connect( + lambda firstTime, mapDisplayPage=self.currentPage: self._onStarting3dMode( + mapDisplayPage + ) + ) + mapdisplay.starting3dMode.connect(self.AddNvizTools) + mapdisplay.ending3dMode.connect(self.RemoveNvizTools) + mapdisplay.closingDisplay.connect(self._closePageNoEvent) + + # set default properties + mapdisplay.SetProperties( + render=UserSettings.Get( + group="display", key="autoRendering", subkey="enabled" + ), + mode=UserSettings.Get( + group="display", key="statusbarMode", subkey="selection" + ), + alignExtent=UserSettings.Get( + group="display", key="alignExtent", subkey="enabled" + ), + constrainRes=UserSettings.Get( + group="display", key="compResolution", subkey="enabled" + ), + showCompExtent=UserSettings.Get( + group="display", key="showCompExtent", subkey="enabled" + ), + ) def BuildPanes(self): """Build panes - toolbars as well as panels""" # initialize all main widgets + self._createMapNotebook() self._createDataCatalog(parent=self) self._createDisplay(parent=self) self._createSearchModule(parent=self) self._createConsole(parent=self) self._createPythonShell(parent=self) - self._createMapDisplay(parent=self) self.toolbars = { "workspace": LMWorkspaceToolbar(parent=self), "tools": LMToolsToolbar(parent=self), @@ -416,8 +567,8 @@ def BuildPanes(self): ) self._auimgr.AddPane( - self.mapdisplay, - aui.AuiPaneInfo().Name("map display").CenterPane().PaneBorder(True), + self.mapnotebook, + aui.AuiPaneInfo().Name("map display content").CenterPane().PaneBorder(True), ) self._auimgr.AddPane( @@ -552,17 +703,28 @@ def AddNvizTools(self, firstTime): ("toolbarWorkspace", "toolbarTools", "toolbarMisc", "toolbarNviz") ): self._auimgr.GetPane(toolbar).Row(1).Position(pos) - self._auimgr.Update() # create nviz tools tab self.nviz = NvizToolWindow( - parent=self.notebook, tree=self.GetLayerTree(), display=self.GetMapDisplay() + parent=self, tree=self.GetLayerTree(), display=self.GetMapDisplay() ) - idx = self.notebook.GetPageIndexByName("layers") - self.notebook.InsertNBPage( - index=idx + 1, page=self.nviz, text=_("3D view"), name="nviz" + self._auimgr.AddPane( + self.nviz, + aui.AuiPaneInfo() + .Name("nviz") + .Caption("3D view") + .Left() + .Layer(1) + .Position(3) + .BestSize(self.PANE_BEST_SIZE) + .MinSize(self.PANE_MIN_SIZE) + .CloseButton(False) + .MinimizeButton(True) + .MaximizeButton(True), ) - self.notebook.SetSelectionByName("nviz") + + self._auimgr.GetPane("nviz").Show() + self._auimgr.Update() # this is a bit strange here since a new window is created everytime if not firstTime: @@ -573,8 +735,6 @@ def RemoveNvizTools(self): """Remove nviz notebook page""" # if more mapwindow3D were possible, check here if nb page should be # removed - self.notebook.SetSelectionByName("layers") - self.notebook.DeleteNBPage("nviz") # hide toolbar self._auimgr.GetPane("toolbarNviz").Hide() @@ -582,6 +742,8 @@ def RemoveNvizTools(self): ("toolbarWorkspace", "toolbarTools", "toolbarMisc") ): self._auimgr.GetPane(toolbar).Row(1).Position(pos) + self._auimgr.DetachPane(self.nviz) + self.nviz.Destroy() self._auimgr.Update() def OnLocationWizard(self, event): @@ -723,8 +885,7 @@ def OnCBPageChanged(self, event): self.currentPage = self.notebookLayers.GetCurrentPage() self.currentPageNum = self.notebookLayers.GetSelection() try: - self.GetMapDisplay().SetFocus() - self.GetMapDisplay().Raise() + self.mapnotebook.SetSelection(self.GetMapDisplayIndex()) except Exception: pass @@ -744,6 +905,7 @@ def OnCBPageClosing(self, event): maptree = self.notebookLayers.GetPage(event.GetSelection()).maptree maptree.GetMapDisplay().CleanUp() + self.mapnotebook.DeletePage(self.GetMapDisplayIndex()) maptree.Close(True) self.currentPage = None @@ -756,6 +918,7 @@ def _closePageNoEvent(self, page_index): self.notebookLayers.Unbind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING) self.notebookLayers.DeletePage(page_index) self.notebookLayers.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.OnCBPageClosing) + self.mapnotebook.DeletePage(page_index) def RunSpecialCmd(self, command): """Run command from command line, check for GUI wrappers""" @@ -884,7 +1047,7 @@ def GetMapDisplay(self, onlyCurrent=True): :param bool onlyCurrent: True to return only active mapdisplay False for list of all mapdisplays - :return: MapFrame instance (or list) + :return: MapPanel instance (or list) :return: None no mapdisplay selected """ if onlyCurrent: @@ -903,6 +1066,11 @@ def GetAllMapDisplays(self): """Get all (open) map displays""" return self.GetMapDisplay(onlyCurrent=False) + def GetMapDisplayIndex(self): + """Get the index of the currently active map display tab. + Can be different than index of related layertree.""" + return self.mapnotebook.GetPageIndex(self.GetMapDisplay()) + def GetLogWindow(self): """Gets console for command output and messages""" return self._gconsole @@ -1404,8 +1572,7 @@ def OnRenameDisplay(self, event): if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() self.notebookLayers.SetPageText(page=self.currentPageNum, text=name) - mapdisplay = self.GetMapDisplay() - mapdisplay.SetTitle(name) + self.mapnotebook.SetPageText(page_idx=self.GetMapDisplayIndex(), text=name) dlg.Destroy() def OnRasterRules(self, event): @@ -1697,109 +1864,6 @@ def OnShowAttributeTable(self, event, selection=None): # show ATM window dbmanager.Show() - def OnNewDisplay(self, event=None): - """Create new layer tree and map display instance""" - self.NewDisplay() - - def NewDisplay(self, name=None, show=True): - """Create new layer tree, which will - create an associated map display frame - - :param name: name of new map display - :param show: show map display window if True - - :return: reference to mapdisplay intance - """ - Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.displayIndex) - - # make a new page in the bookcontrol for the layer tree (on page 0 of - # the notebook) - self.pg_panel = wx.Panel(self.notebookLayers, id=wx.ID_ANY, style=wx.EXPAND) - if name: - dispName = name - else: - dispName = _("Map Display {number}").format(number=self.displayIndex + 1) - self.notebookLayers.AddPage(page=self.pg_panel, text=dispName, select=True) - self.currentPage = self.notebookLayers.GetCurrentPage() - - # create layer tree (tree control for managing GIS layers) and put on - # new notebook page - self.currentPage.maptree = LayerTree( - self.currentPage, - giface=self._giface, - id=wx.ID_ANY, - pos=wx.DefaultPosition, - size=wx.DefaultSize, - style=wx.TR_HAS_BUTTONS - | wx.TR_LINES_AT_ROOT - | wx.TR_HIDE_ROOT - | wx.TR_DEFAULT_STYLE - | wx.NO_BORDER - | wx.FULL_REPAINT_ON_RESIZE, - idx=self.displayIndex, - lmgr=self, - notebook=self.notebookLayers, - showMapDisplay=show, - title=dispName, - ) - - # layout for controls - cb_boxsizer = wx.BoxSizer(wx.VERTICAL) - cb_boxsizer.Add(self.GetLayerTree(), proportion=1, flag=wx.EXPAND, border=1) - self.currentPage.SetSizer(cb_boxsizer) - cb_boxsizer.Fit(self.GetLayerTree()) - self.currentPage.Layout() - self.GetLayerTree().Layout() - - mapdisplay = self.currentPage.maptree.mapdisplay - mapdisplay.Bind( - wx.EVT_ACTIVATE, - lambda event, page=self.currentPage: self._onMapDisplayFocus(page), - ) - mapdisplay.starting3dMode.connect( - lambda firstTime, mapDisplayPage=self.currentPage: self._onStarting3dMode( - mapDisplayPage - ) - ) - mapdisplay.starting3dMode.connect(self.AddNvizTools) - mapdisplay.ending3dMode.connect(self.RemoveNvizTools) - mapdisplay.closingDisplay.connect(self._closePageNoEvent) - - # use default window layout - if UserSettings.Get(group="general", key="defWindowPos", subkey="enabled"): - dim = UserSettings.Get(group="general", key="defWindowPos", subkey="dim") - idx = 4 + self.displayIndex * 4 - try: - x, y = map(int, dim.split(",")[idx : idx + 2]) - w, h = map(int, dim.split(",")[idx + 2 : idx + 4]) - self.GetMapDisplay().SetPosition((x, y)) - self.GetMapDisplay().SetSize((w, h)) - except Exception: - pass - - # set default properties - mapdisplay.SetProperties( - render=UserSettings.Get( - group="display", key="autoRendering", subkey="enabled" - ), - mode=UserSettings.Get( - group="display", key="statusbarMode", subkey="selection" - ), - alignExtent=UserSettings.Get( - group="display", key="alignExtent", subkey="enabled" - ), - constrainRes=UserSettings.Get( - group="display", key="compResolution", subkey="enabled" - ), - showCompExtent=UserSettings.Get( - group="display", key="showCompExtent", subkey="enabled" - ), - ) - - self.displayIndex += 1 - - return self.GetMapDisplay() - def _onMapDisplayFocus(self, notebookLayerPage): """Changes bookcontrol page to page associated with display.""" # moved from mapdisp/frame.py @@ -1937,9 +2001,6 @@ def OnAddRasterMisc(self, event): ) ) - # show map display - self.GetMapDisplay().Show() - def OnAddVector(self, event): """Add vector map to the current layer tree""" # start new map display if no display is available @@ -1961,9 +2022,6 @@ def OnAddVectorMisc(self, event): ) ) - # show map display - self.GetMapDisplay().Show() - def OnAddVectorTheme(self, event): """Add thematic vector map to the current layer tree""" self.GetLayerTree().AddLayer("thememap") @@ -1989,9 +2047,6 @@ def OnAddOverlay(self, event): ) ) - # show map display - self.GetMapDisplay().Show() - def OnAddRaster3D(self, event): """Add 3D raster map to the current layer tree""" self.GetLayerTree().AddLayer("raster_3d") @@ -2049,9 +2104,6 @@ def OnAddCommand(self, event): self.GetLayerTree().AddLayer("command") - # show map display - self.GetMapDisplay().Show() - def OnAddGroup(self, event): """Add layer group""" # start new map display if no display is available @@ -2060,9 +2112,6 @@ def OnAddGroup(self, event): self.GetLayerTree().AddLayer("group") - # show map display - self.GetMapDisplay().Show() - def OnAddGrid(self, event): """Add grid map layer to the current layer tree""" self.GetLayerTree().AddLayer("grid") @@ -2083,9 +2132,6 @@ def OnAddLabels(self, event): self.GetLayerTree().AddLayer("labels") - # show map display - self.GetMapDisplay().Show() - def OnShowRegionExtent(self, event): """Add vector labels map layer to the current layer tree""" # start new map display if no display is available