Browse Source

wxGUI/GPrompt: decoupling GPrompt and GConsole; standalone GConsole for testing available (co-author: wenzeslaus)

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@53944 15284696-431f-4ddb-bdfa-cd5b030d7da7
Anna Petrášová 12 years ago
parent
commit
6aa5b28afe

+ 6 - 1
gui/wxpython/core/events.py

@@ -6,7 +6,12 @@
 Put here only truly general events. Once you find that your event can be
 Put here only truly general events. Once you find that your event can be
 generated in more than one class, put your event here. Otherwise,
 generated in more than one class, put your event here. Otherwise,
 leave it in your class file.
 leave it in your class file.
-        
+
+General notice:
+Command events are propagated to parent windows. However they do not propagate 
+beyond dialogs. Events do not propagate at all.
+
+
 (C) 2012 by the GRASS Development Team
 (C) 2012 by the GRASS Development Team
 
 
 This program is free software under the GNU General Public License
 This program is free software under the GNU General Public License

+ 35 - 27
gui/wxpython/gmodeler/dialogs.py

@@ -35,7 +35,7 @@ from core.modulesdata     import ModulesData
 from gui_core.widgets     import SearchModuleWidget, EVT_MODULE_SELECTED
 from gui_core.widgets     import SearchModuleWidget, EVT_MODULE_SELECTED
 from core.gcmd            import GError, EncodeString
 from core.gcmd            import GError, EncodeString
 from gui_core.dialogs     import ElementDialog, MapLayersDialogForModeler
 from gui_core.dialogs     import ElementDialog, MapLayersDialogForModeler
-from gui_core.prompt      import GPromptSTC
+from gui_core.prompt      import GPromptSTC, EVT_GPROMPT_RUN_CMD
 from gui_core.forms       import CmdPanel
 from gui_core.forms       import CmdPanel
 from gui_core.gselect     import Select
 from gui_core.gselect     import Select
 from gmodeler.model       import *
 from gmodeler.model       import *
@@ -130,19 +130,21 @@ class ModelSearchDialog(wx.Dialog):
         self.SetName("ModelerDialog")
         self.SetName("ModelerDialog")
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
         
+        self._command = None
         self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
         self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
         
         
         self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
         self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
                                    label=" %s " % _("Command"))
                                    label=" %s " % _("Command"))
 
 
         modulesData = ModulesData()
         modulesData = ModulesData()
-        self.cmd_prompt = GPromptSTC(parent = self, modulesData = modulesData)
+        self.cmd_prompt = GPromptSTC(parent = self, modulesData = modulesData, updateCmdHistory = False)
+        self.cmd_prompt.Bind(EVT_GPROMPT_RUN_CMD, self.OnCommand)
         self.search = SearchModuleWidget(parent = self.panel,
         self.search = SearchModuleWidget(parent = self.panel,
                                          modulesData = modulesData,
                                          modulesData = modulesData,
                                          showTip = True)
                                          showTip = True)
         self.search.Bind(EVT_MODULE_SELECTED,
         self.search.Bind(EVT_MODULE_SELECTED,
                              lambda event:
                              lambda event:
-                                 self.cmd_prompt.SetTextAndFocus(event.name + ' '))                  
+                                 self.cmd_prompt.SetTextAndFocus(event.name + ' '))
         wx.CallAfter(self.cmd_prompt.SetFocus)
         wx.CallAfter(self.cmd_prompt.SetFocus)
         
         
         self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
         self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
@@ -186,42 +188,48 @@ class ModelSearchDialog(wx.Dialog):
         """!Get dialog panel"""
         """!Get dialog panel"""
         return self.panel
         return self.panel
 
 
-    def GetCmd(self):
-        """!Get command"""
+    def _getCmd(self):
         line = self.cmd_prompt.GetCurLine()[0].strip()
         line = self.cmd_prompt.GetCurLine()[0].strip()
         if len(line) == 0:
         if len(line) == 0:
-            list()
-        
-        try:
-            cmd = utils.split(str(line))
-        except UnicodeError:
-            cmd = utils.split(EncodeString((line)))
-            
+            cmd = list()
+        else:
+            try:
+                cmd = utils.split(str(line))
+            except UnicodeError:
+                cmd = utils.split(EncodeString((line)))
         return cmd
         return cmd
-    
-    def OnOk(self, event):
-        """!Button 'OK' pressed"""
-        # hide autocomplete
-        if self.cmd_prompt.AutoCompActive():
-            self.cmd_prompt.AutoCompCancel()
-        
-        self.btnOk.SetFocus()
-        cmd = self.GetCmd()
-        
+
+    def GetCmd(self):
+        """!Get command"""
+        return self._command
+
+    def ValidateCmd(self, cmd):
         if len(cmd) < 1:
         if len(cmd) < 1:
             GError(parent = self,
             GError(parent = self,
                    message = _("Command not defined.\n\n"
                    message = _("Command not defined.\n\n"
                                "Unable to add new action to the model."))
                                "Unable to add new action to the model."))
-            return
+            return False
         
         
         if cmd[0] not in globalvar.grassCmd:
         if cmd[0] not in globalvar.grassCmd:
             GError(parent = self,
             GError(parent = self,
                    message = _("'%s' is not a GRASS module.\n\n"
                    message = _("'%s' is not a GRASS module.\n\n"
                                "Unable to add new action to the model.") % cmd[0])
                                "Unable to add new action to the model.") % cmd[0])
-            return
-        
-        self.EndModal(wx.ID_OK)
-        
+            return False
+        return True
+
+    def OnCommand(self, event):
+        """!Command in prompt confirmed"""
+        if self.ValidateCmd(event.cmd):
+            self._command = event.cmd
+            self.EndModal(wx.ID_OK)
+
+    def OnOk(self, event):
+        """!Button 'OK' pressed"""
+        cmd = self._getCmd()
+        if self.ValidateCmd(cmd):
+            self._command = cmd
+            self.EndModal(wx.ID_OK)
+
     def OnCancel(self, event):
     def OnCancel(self, event):
         """Cancel pressed, close window"""
         """Cancel pressed, close window"""
         # hide autocomplete
         # hide autocomplete

+ 3 - 1
gui/wxpython/gmodeler/frame.py

@@ -36,7 +36,7 @@ from core                 import globalvar
 from gui_core.widgets     import GNotebook
 from gui_core.widgets     import GNotebook
 from gui_core.goutput     import GConsole, \
 from gui_core.goutput     import GConsole, \
     EVT_CMD_RUN, EVT_CMD_DONE, EVT_CMD_PREPARE, EVT_OUTPUT_TEXT
     EVT_CMD_RUN, EVT_CMD_DONE, EVT_CMD_PREPARE, EVT_OUTPUT_TEXT
-from core.events          import EVT_MAP_CREATED
+from core.events          import EVT_MAP_CREATED, EVT_SHOW_NOTIFICATION
 from core.debug           import Debug
 from core.debug           import Debug
 from core.gcmd            import GMessage, GException, GWarning, GError, RunCommand
 from core.gcmd            import GMessage, GException, GWarning, GError, RunCommand
 from gui_core.dialogs     import GetImageHandlers
 from gui_core.dialogs     import GetImageHandlers
@@ -112,6 +112,8 @@ class ModelFrame(wx.Frame):
         self.Bind(EVT_CMD_DONE, self.OnCmdDone)
         self.Bind(EVT_CMD_DONE, self.OnCmdDone)
         self.Bind(EVT_CMD_PREPARE, self.OnCmdPrepare)
         self.Bind(EVT_CMD_PREPARE, self.OnCmdPrepare)
         self.Bind(EVT_MAP_CREATED, self.OnMapCreated)
         self.Bind(EVT_MAP_CREATED, self.OnMapCreated)
+        self.Bind(EVT_SHOW_NOTIFICATION,
+                  lambda event: self.SetStatusText(event.message))
 
 
         self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model')
         self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model')
         self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items')
         self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items')

+ 34 - 2
gui/wxpython/gui_core/goutput.py

@@ -30,6 +30,8 @@ import Queue
 import codecs
 import codecs
 import locale
 import locale
 
 
+sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
 import wx
 import wx
 from   wx import stc
 from   wx import stc
 from wx.lib.newevent import NewEvent
 from wx.lib.newevent import NewEvent
@@ -42,7 +44,7 @@ from core            import utils
 from core.gcmd       import CommandThread, GMessage, GError, GException, EncodeString
 from core.gcmd       import CommandThread, GMessage, GError, GException, EncodeString
 from core.events     import gShowNotification, gMapCreated
 from core.events     import gShowNotification, gMapCreated
 from gui_core.forms  import GUI
 from gui_core.forms  import GUI
-from gui_core.prompt import GPromptSTC
+from gui_core.prompt import GPromptSTC, EVT_GPROMPT_RUN_CMD
 from core.debug      import Debug
 from core.debug      import Debug
 from core.settings   import UserSettings
 from core.settings   import UserSettings
 from gui_core.widgets import SearchModuleWidget, EVT_MODULE_SELECTED
 from gui_core.widgets import SearchModuleWidget, EVT_MODULE_SELECTED
@@ -276,6 +278,9 @@ class GConsole(wx.SplitterWindow):
         # move to the if below
         # move to the if below
         # search depends on cmd prompt
         # search depends on cmd prompt
         self.cmdPrompt = GPromptSTC(parent = self, modulesData = modulesData)
         self.cmdPrompt = GPromptSTC(parent = self, modulesData = modulesData)
+        self.cmdPrompt.Bind(EVT_GPROMPT_RUN_CMD, 
+                            lambda event: self.RunCmd(command = event.cmd))
+
         if not self._gcstyle & GC_PROMPT:
         if not self._gcstyle & GC_PROMPT:
             self.cmdPrompt.Hide()
             self.cmdPrompt.Hide()
 
 
@@ -578,7 +583,7 @@ class GConsole(wx.SplitterWindow):
             # except ignored commands (event is emitted)
             # except ignored commands (event is emitted)
 
 
             if self._ignoredCmdPattern and \
             if self._ignoredCmdPattern and \
-              re.compile(self._ignoredCmdPattern).search(command[0]) and \
+              re.compile(self._ignoredCmdPattern).search(' '.join(command)) and \
               '--help' not in command:
               '--help' not in command:
                 event = gIgnoredCmdRun(cmd = command)
                 event = gIgnoredCmdRun(cmd = command)
                 wx.PostEvent(self, event)
                 wx.PostEvent(self, event)
@@ -1204,3 +1209,30 @@ class GStc(stc.StyledTextCtrl):
                 self.SetStyling(p2 - p1, self.StyleUnknown)
                 self.SetStyling(p2 - p1, self.StyleUnknown)
         
         
         self.EnsureCaretVisible()
         self.EnsureCaretVisible()
+
+
+class GConsoleFrame(wx.Frame):
+    """!Standalone GConsole for testing only"""
+    def __init__(self, parent, id = wx.ID_ANY, title = "GConsole Test Frame",
+                 style = wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, **kwargs):
+        wx.Frame.__init__(self, parent = parent, id = id, title = title)
+
+        panel = wx.Panel(self, id = wx.ID_ANY)
+        self.goutput = GConsole(parent = panel, gcstyle = GC_SEARCH | GC_PROMPT)
+
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.goutput, proportion = 1, flag = wx.EXPAND, border = 0)
+
+        panel.SetSizer(mainSizer)
+        mainSizer.Fit(panel)
+        self.SetMinSize((550, 500))
+
+
+def testGConsole():
+    app = wx.PySimpleApp()
+    frame = GConsoleFrame(parent = None)
+    frame.Show()
+    app.MainLoop()
+
+if __name__ == '__main__':
+    testGConsole()

+ 32 - 57
gui/wxpython/gui_core/prompt.py

@@ -29,12 +29,15 @@ import wx
 import wx.stc
 import wx.stc
 import wx.lib.mixins.listctrl as listmix
 import wx.lib.mixins.listctrl as listmix
 
 
+from wx.lib.newevent import NewEvent
+
 from grass.script import core as grass
 from grass.script import core as grass
 from grass.script import task as gtask
 from grass.script import task as gtask
 
 
 from core          import globalvar
 from core          import globalvar
 from core          import utils
 from core          import utils
 from core.gcmd     import EncodeString, DecodeString, GetRealCmd
 from core.gcmd     import EncodeString, DecodeString, GetRealCmd
+from core.events   import gShowNotification
 
 
 class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
 class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
     """!PopUp window used by GPromptPopUp"""
     """!PopUp window used by GPromptPopUp"""
@@ -464,19 +467,17 @@ class TextCtrlAutoComplete(wx.ComboBox, listmix.ColumnSorterMixin):
         
         
         event.Skip()
         event.Skip()
 
 
+
+gPromptRunCmd, EVT_GPROMPT_RUN_CMD = NewEvent()
+
 class GPrompt(object):
 class GPrompt(object):
     """!Abstract class for interactive wxGUI prompt
     """!Abstract class for interactive wxGUI prompt
 
 
     See subclass GPromptPopUp and GPromptSTC.
     See subclass GPromptPopUp and GPromptSTC.
     """
     """
-    def __init__(self, parent, modulesData):
+    def __init__(self, parent, modulesData, updateCmdHistory):
         self.parent = parent                 # GConsole
         self.parent = parent                 # GConsole
         self.panel  = self.parent.GetPanel()
         self.panel  = self.parent.GetPanel()
-        
-        if self.parent.parent.GetName() not in ("LayerManager", "Modeler"):
-            self.standAlone = True
-        else:
-            self.standAlone = False
 
 
         # probably only subclasses need this
         # probably only subclasses need this
         self.modulesData = modulesData
         self.modulesData = modulesData
@@ -490,6 +491,8 @@ class GPrompt(object):
         
         
         # command description (gtask.grassTask)
         # command description (gtask.grassTask)
         self.cmdDesc   = None
         self.cmdDesc   = None
+
+        self._updateCmdHistory = updateCmdHistory
         self.cmdbuffer = self._readHistory()
         self.cmdbuffer = self._readHistory()
         self.cmdindex  = len(self.cmdbuffer)
         self.cmdindex  = len(self.cmdbuffer)
         
         
@@ -530,16 +533,9 @@ class GPrompt(object):
         
         
         @param cmdString command to run (given as a string)
         @param cmdString command to run (given as a string)
         """
         """
-        if self.parent.GetName() == "ModelerDialog":
-            self.parent.OnOk(None)
-            return
-        
-        if not cmdString or self.standAlone:
+        if not cmdString:
             return
             return
-        
-        if cmdString[:2] == 'd.' and not self.parent.parent.GetMapDisplay():
-            self.parent.parent.NewDisplay(show = True)
-                
+
         self.commands.append(cmdString) # trace commands
         self.commands.append(cmdString) # trace commands
 
 
         # parse command into list
         # parse command into list
@@ -548,28 +544,13 @@ class GPrompt(object):
         except UnicodeError:
         except UnicodeError:
             cmd = utils.split(EncodeString((cmdString)))
             cmd = utils.split(EncodeString((cmdString)))
         cmd = map(DecodeString, cmd)
         cmd = map(DecodeString, cmd)
-        
-        # send the command list to the processor 
-        if cmd[0] in ('r.mapcalc', 'r3.mapcalc') and len(cmd) == 1:
-            self.parent.parent.OnMapCalculator(event = None, cmd = cmd)
-        else:
-            self.parent.RunCmd(cmd)
-            
+
+        wx.PostEvent(self, gPromptRunCmd(cmd = cmd))
+
         # add command to history & clean prompt
         # add command to history & clean prompt
         self.UpdateCmdHistory(cmd)
         self.UpdateCmdHistory(cmd)
         self.OnCmdErase(None)
         self.OnCmdErase(None)
-        self.parent.parent.statusbar.SetStatusText('')
-        
-    def OnUpdateStatusBar(self, event):
-        """!Update Layer Manager status bar"""
-        if self.standAlone:
-            return
-        
-        if event is None:
-            self.parent.parent.statusbar.SetStatusText("")
-        else:
-            self.parent.parent.statusbar.SetStatusText(_("Type GRASS command and run by pressing ENTER"))
-            event.Skip()
+        self.ShowStatusText('')
         
         
     def GetPanel(self):
     def GetPanel(self):
         """!Get main widget panel"""
         """!Get main widget panel"""
@@ -643,9 +624,10 @@ class GPromptPopUp(GPrompt, TextCtrlAutoComplete):
         
         
 class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
 class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
     """!Styled wxGUI prompt with autocomplete and calltips"""    
     """!Styled wxGUI prompt with autocomplete and calltips"""    
-    def __init__(self, parent, modulesData, id = wx.ID_ANY, margin = False):
-        GPrompt.__init__(self, parent, modulesData)
-        wx.stc.StyledTextCtrl.__init__(self, self.panel, id)
+    def __init__(self, parent, modulesData, updateCmdHistory = True, margin = False):
+        GPrompt.__init__(self, parent = parent, 
+                         modulesData = modulesData, updateCmdHistory = updateCmdHistory)
+        wx.stc.StyledTextCtrl.__init__(self, self.panel, id = wx.ID_ANY)
         
         
         #
         #
         # styles
         # styles
@@ -757,17 +739,10 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         cmd = text.strip().split(' ')[0]
         cmd = text.strip().split(' ')[0]
         
         
         if not self.cmdDesc or cmd != self.cmdDesc.get_name():
         if not self.cmdDesc or cmd != self.cmdDesc.get_name():
-            if cmd in ('r.mapcalc', 'r3.mapcalc') and \
-                    self.parent.parent.GetName() == 'LayerManager':
-                self.parent.parent.OnMapCalculator(event = None, cmd = [cmd])
-                # add command to history & clean prompt
-                self.UpdateCmdHistory([cmd])
-                self.OnCmdErase(None)
-            else:
-                try:
-                    self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
-                except IOError:
-                    self.cmdDesc = None
+            try:
+                self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
+            except IOError:
+                self.cmdDesc = None
 
 
     def OnKillFocus(self, event):
     def OnKillFocus(self, event):
         """!Hides autocomplete"""
         """!Hides autocomplete"""
@@ -788,6 +763,8 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         
         
         @param cmd command given as a list
         @param cmd command given as a list
         """
         """
+        if not self._updateCmdHistory:
+            return
         # add command to history    
         # add command to history    
         self.cmdbuffer.append(' '.join(cmd))
         self.cmdbuffer.append(' '.join(cmd))
         
         
@@ -1057,7 +1034,8 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             pos = self.GetCurrentPos()            
             pos = self.GetCurrentPos()            
             self.InsertText(pos,txt)
             self.InsertText(pos,txt)
             self.LineEnd()
             self.LineEnd()
-            self.parent.parent.statusbar.SetStatusText('')
+
+            self.ShowStatusText('')
             
             
         elif event.GetKeyCode() == wx.WXK_RETURN and \
         elif event.GetKeyCode() == wx.WXK_RETURN and \
                 self.AutoCompActive() == False:
                 self.AutoCompActive() == False:
@@ -1069,9 +1047,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             if len(items) == 1:
             if len(items) == 1:
                 cmd = items[0].strip()
                 cmd = items[0].strip()
                 if cmd in globalvar.grassCmd and \
                 if cmd in globalvar.grassCmd and \
-                        cmd != 'r.mapcalc' and \
                         (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
                         (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
-                    
                     try:
                     try:
                         self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
                         self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
                     except IOError:
                     except IOError:
@@ -1083,12 +1059,11 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
 
 
     def ShowStatusText(self, text):
     def ShowStatusText(self, text):
         """!Sets statusbar text, if it's too long, it is cut off"""
         """!Sets statusbar text, if it's too long, it is cut off"""
-        maxLen = self.parent.parent.statusbar.GetFieldRect(0).GetWidth()/ 7 # any better way?
-        if len(text) < maxLen:
-            self.parent.parent.statusbar.SetStatusText(text)
-        else:
-            self.parent.parent.statusbar.SetStatusText(text[:maxLen]+'...')
-        
+        # event is not propagated beyond dialog
+        # thus when GPrompt in Modeler is inside a dialog, 
+        # it does not show text in modeler statusbar which is probably
+        # the right behaviour. The dialog itself should display the text.
+        wx.PostEvent(self, gShowNotification(self.GetId(), message = text))
         
         
     def GetTextLeft(self):
     def GetTextLeft(self):
         """!Returns all text left of the caret"""
         """!Returns all text left of the caret"""

+ 12 - 3
gui/wxpython/lmgr/frame.py

@@ -23,6 +23,7 @@ import os
 import tempfile
 import tempfile
 import stat
 import stat
 import platform
 import platform
+import re
 try:
 try:
     import xml.etree.ElementTree as etree
     import xml.etree.ElementTree as etree
 except ImportError:
 except ImportError:
@@ -261,11 +262,11 @@ class GMFrame(wx.Frame):
         # create 'command output' text area
         # create 'command output' text area
         self.goutput = GConsole(self,
         self.goutput = GConsole(self,
                                 gcstyle = GC_SEARCH | GC_PROMPT,
                                 gcstyle = GC_SEARCH | GC_PROMPT,
-                                ignoredCmdPattern = '^d\..*')
+                                ignoredCmdPattern = '^d\..*|^r[3]?\.mapcalc$')
         self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
         self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
         self.goutput.Bind(EVT_OUTPUT_TEXT, self.OnOutputText)
         self.goutput.Bind(EVT_OUTPUT_TEXT, self.OnOutputText)
         self.goutput.Bind(EVT_IGNORED_CMD_RUN,
         self.goutput.Bind(EVT_IGNORED_CMD_RUN,
-                          lambda event: self.DisplayCmdRun(event.cmd))
+                          lambda event: self.RunSpecialCmd(event.cmd))
         self._setCopyingOfSelectedText()
         self._setCopyingOfSelectedText()
         
         
         # create 'search module' notebook page
         # create 'search module' notebook page
@@ -536,11 +537,19 @@ class GMFrame(wx.Frame):
             self.SetFocus()
             self.SetFocus()
             self.Raise()
             self.Raise()
 
 
-    def DisplayCmdRun(self, command):
+    def RunSpecialCmd(self, command):
+        if re.compile('^d\..*').search(command[0]):
+            self.RunDisplayCmd(command)
+        elif re.compile('r[3]?\.mapcalc').search(command[0]):
+            self.OnMapCalculator(event = None, cmd = command)
+
+    def RunDisplayCmd(self, command):
         """!Handles display commands.
         """!Handles display commands.
 
 
         @param command command in a list
         @param command command in a list
         """
         """
+        if not self.currentPage:
+            self.NewDisplay(show = True)
         try:
         try:
             # display GRASS commands
             # display GRASS commands
             layertype = {'d.rast'         : 'raster',
             layertype = {'d.rast'         : 'raster',