소스 검색

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 년 전
부모
커밋
6aa5b28afe
6개의 변경된 파일122개의 추가작업 그리고 91개의 파일을 삭제
  1. 6 1
      gui/wxpython/core/events.py
  2. 35 27
      gui/wxpython/gmodeler/dialogs.py
  3. 3 1
      gui/wxpython/gmodeler/frame.py
  4. 34 2
      gui/wxpython/gui_core/goutput.py
  5. 32 57
      gui/wxpython/gui_core/prompt.py
  6. 12 3
      gui/wxpython/lmgr/frame.py

+ 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
 generated in more than one class, put your event here. Otherwise,
 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
 
 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 core.gcmd            import GError, EncodeString
 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.gselect     import Select
 from gmodeler.model       import *
@@ -130,19 +130,21 @@ class ModelSearchDialog(wx.Dialog):
         self.SetName("ModelerDialog")
         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.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
                                    label=" %s " % _("Command"))
 
         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,
                                          modulesData = modulesData,
                                          showTip = True)
         self.search.Bind(EVT_MODULE_SELECTED,
                              lambda event:
-                                 self.cmd_prompt.SetTextAndFocus(event.name + ' '))                  
+                                 self.cmd_prompt.SetTextAndFocus(event.name + ' '))
         wx.CallAfter(self.cmd_prompt.SetFocus)
         
         self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
@@ -186,42 +188,48 @@ class ModelSearchDialog(wx.Dialog):
         """!Get dialog panel"""
         return self.panel
 
-    def GetCmd(self):
-        """!Get command"""
+    def _getCmd(self):
         line = self.cmd_prompt.GetCurLine()[0].strip()
         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
-    
-    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:
             GError(parent = self,
                    message = _("Command not defined.\n\n"
                                "Unable to add new action to the model."))
-            return
+            return False
         
         if cmd[0] not in globalvar.grassCmd:
             GError(parent = self,
                    message = _("'%s' is not a GRASS module.\n\n"
                                "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):
         """Cancel pressed, close window"""
         # 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.goutput     import GConsole, \
     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.gcmd            import GMessage, GException, GWarning, GError, RunCommand
 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_PREPARE, self.OnCmdPrepare)
         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.itemPanel, text=_('Items'), name = 'items')

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

@@ -30,6 +30,8 @@ import Queue
 import codecs
 import locale
 
+sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
 import wx
 from   wx import stc
 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.events     import gShowNotification, gMapCreated
 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.settings   import UserSettings
 from gui_core.widgets import SearchModuleWidget, EVT_MODULE_SELECTED
@@ -276,6 +278,9 @@ class GConsole(wx.SplitterWindow):
         # move to the if below
         # search depends on cmd prompt
         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:
             self.cmdPrompt.Hide()
 
@@ -578,7 +583,7 @@ class GConsole(wx.SplitterWindow):
             # except ignored commands (event is emitted)
 
             if self._ignoredCmdPattern and \
-              re.compile(self._ignoredCmdPattern).search(command[0]) and \
+              re.compile(self._ignoredCmdPattern).search(' '.join(command)) and \
               '--help' not in command:
                 event = gIgnoredCmdRun(cmd = command)
                 wx.PostEvent(self, event)
@@ -1204,3 +1209,30 @@ class GStc(stc.StyledTextCtrl):
                 self.SetStyling(p2 - p1, self.StyleUnknown)
         
         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.lib.mixins.listctrl as listmix
 
+from wx.lib.newevent import NewEvent
+
 from grass.script import core as grass
 from grass.script import task as gtask
 
 from core          import globalvar
 from core          import utils
 from core.gcmd     import EncodeString, DecodeString, GetRealCmd
+from core.events   import gShowNotification
 
 class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
     """!PopUp window used by GPromptPopUp"""
@@ -464,19 +467,17 @@ class TextCtrlAutoComplete(wx.ComboBox, listmix.ColumnSorterMixin):
         
         event.Skip()
 
+
+gPromptRunCmd, EVT_GPROMPT_RUN_CMD = NewEvent()
+
 class GPrompt(object):
     """!Abstract class for interactive wxGUI prompt
 
     See subclass GPromptPopUp and GPromptSTC.
     """
-    def __init__(self, parent, modulesData):
+    def __init__(self, parent, modulesData, updateCmdHistory):
         self.parent = parent                 # GConsole
         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
         self.modulesData = modulesData
@@ -490,6 +491,8 @@ class GPrompt(object):
         
         # command description (gtask.grassTask)
         self.cmdDesc   = None
+
+        self._updateCmdHistory = updateCmdHistory
         self.cmdbuffer = self._readHistory()
         self.cmdindex  = len(self.cmdbuffer)
         
@@ -530,16 +533,9 @@ class GPrompt(object):
         
         @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
-        
-        if cmdString[:2] == 'd.' and not self.parent.parent.GetMapDisplay():
-            self.parent.parent.NewDisplay(show = True)
-                
+
         self.commands.append(cmdString) # trace commands
 
         # parse command into list
@@ -548,28 +544,13 @@ class GPrompt(object):
         except UnicodeError:
             cmd = utils.split(EncodeString((cmdString)))
         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
         self.UpdateCmdHistory(cmd)
         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):
         """!Get main widget panel"""
@@ -643,9 +624,10 @@ class GPromptPopUp(GPrompt, TextCtrlAutoComplete):
         
 class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
     """!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
@@ -757,17 +739,10 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         cmd = text.strip().split(' ')[0]
         
         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):
         """!Hides autocomplete"""
@@ -788,6 +763,8 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         
         @param cmd command given as a list
         """
+        if not self._updateCmdHistory:
+            return
         # add command to history    
         self.cmdbuffer.append(' '.join(cmd))
         
@@ -1057,7 +1034,8 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             pos = self.GetCurrentPos()            
             self.InsertText(pos,txt)
             self.LineEnd()
-            self.parent.parent.statusbar.SetStatusText('')
+
+            self.ShowStatusText('')
             
         elif event.GetKeyCode() == wx.WXK_RETURN and \
                 self.AutoCompActive() == False:
@@ -1069,9 +1047,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             if len(items) == 1:
                 cmd = items[0].strip()
                 if cmd in globalvar.grassCmd and \
-                        cmd != 'r.mapcalc' and \
                         (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
-                    
                     try:
                         self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
                     except IOError:
@@ -1083,12 +1059,11 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
 
     def ShowStatusText(self, text):
         """!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):
         """!Returns all text left of the caret"""

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

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