Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wxGUI: New History pane for launching commands from history log #3234

Merged
merged 19 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gui/wxpython/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ include $(MODULE_TOPDIR)/include/Make/Python.make
DSTDIR = $(GUIDIR)/wxpython

SRCFILES := $(wildcard icons/*.py scripts/*.py xml/*) \
$(wildcard animation/*.py core/*.py datacatalog/*.py dbmgr/*.py gcp/*.py gmodeler/*.py \
$(wildcard animation/*.py core/*.py datacatalog/*.py history_catalog/*.py dbmgr/*.py gcp/*.py gmodeler/*.py \
gui_core/*.py iclass/*.py lmgr/*.py location_wizard/*.py main_window/*.py mapwin/*.py mapdisp/*.py \
mapswipe/*.py modules/*.py nviz/*.py psmap/*.py rdigit/*.py \
rlisetup/*.py startup/*.py timeline/*.py vdigit/*.py \
Expand All @@ -19,7 +19,7 @@ SRCFILES := $(wildcard icons/*.py scripts/*.py xml/*) \
DSTFILES := $(patsubst %,$(DSTDIR)/%,$(SRCFILES)) \
$(patsubst %.py,$(DSTDIR)/%.pyc,$(filter %.py,$(SRCFILES)))

PYDSTDIRS := $(patsubst %,$(DSTDIR)/%,animation core datacatalog dbmgr gcp gmodeler \
PYDSTDIRS := $(patsubst %,$(DSTDIR)/%,animation core datacatalog history_catalog dbmgr gcp gmodeler \
gui_core iclass lmgr location_wizard main_window mapwin mapdisp modules nviz psmap \
mapswipe vdigit wxplot web_services rdigit rlisetup startup \
vnet timeline iscatt tplot photo2image image2target)
Expand Down
32 changes: 2 additions & 30 deletions gui/wxpython/core/gconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ def RunCmd(
Debug.msg(2, "GPrompt:RunCmd(): empty command")
return

# update history file
self.UpdateHistoryFile(cmd_save_to_history)
# update history file, command prompt history and history model
self._giface.updateHistory.emit(cmd=cmd_save_to_history)

if command[0] in globalvar.grassCmd:
# send GRASS command without arguments to GUI command interface
Expand Down Expand Up @@ -810,31 +810,3 @@ def OnCmdDone(self, event):

def OnProcessPendingOutputWindowEvents(self, event):
wx.GetApp().ProcessPendingEvents()

def UpdateHistoryFile(self, command):
"""Update history file

:param command: the command given as a string
"""
env = grass.gisenv()
try:
filePath = os.path.join(
env["GISDBASE"], env["LOCATION_NAME"], env["MAPSET"], ".wxgui_history"
)
fileHistory = codecs.open(filePath, encoding="utf-8", mode="a")
except IOError as e:
GError(
_("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s")
% {"filePath": filePath, "error": e},
parent=self._guiparent,
)
return

try:
fileHistory.write(command + os.linesep)
finally:
fileHistory.close()

# update wxGUI prompt
if self._giface:
self._giface.UpdateCmdHistory(command)
16 changes: 3 additions & 13 deletions gui/wxpython/core/giface.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,6 @@ def GetProgress(self):
"""
raise NotImplementedError()

def UpdateCmdHistory(self, cmd):
"""Add the command to the current history list shown to the user

.. note::

Some implementations may not implement this method or do nothing.
"""
raise NotImplementedError()


class StandaloneGrassInterface(GrassInterface):
"""@implements GrassInterface"""
Expand Down Expand Up @@ -241,6 +232,9 @@ def __init__(self):
# Signal emitted when workspace is changed
self.workspaceChanged = Signal("StandaloneGrassInterface.workspaceChanged")

# Signal emitted when history should be updated
self.updateHistory = Signal("StandaloneGrassInterface.updateHistory")

# workaround, standalone grass interface should be moved to sep. file
from core.gconsole import GConsole, EVT_CMD_OUTPUT, EVT_CMD_PROGRESS

Expand Down Expand Up @@ -343,7 +337,3 @@ def GetProgress(self):
# TODO: implement some progress with same inface as gui one
# (probably using g.message or similarly to Write... functions)
raise NotImplementedError()

def UpdateCmdHistory(self, cmd):
"""There is no history displayed to the user, doing nothing"""
pass
5 changes: 1 addition & 4 deletions gui/wxpython/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,7 @@ def _defaultSettings(self):
# ask when quitting wxGUI or closing display
"askOnQuit": {"enabled": True},
# hide tabs
"hideTabs": {
"search": False,
"pyshell": False,
},
"hideTabs": {"search": False, "pyshell": False, "history": False},
"copySelectedTextToClipboard": {"enabled": False},
},
#
Expand Down
29 changes: 28 additions & 1 deletion gui/wxpython/gui_core/goutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
from wx import stc

from grass.pydispatch.signal import Signal
from grass.grassdb.history import (
read_history,
update_history,
copy_history,
get_current_mapset_history_path,
)

# needed just for testing
if __name__ == "__main__":
Expand Down Expand Up @@ -84,6 +90,7 @@ def __init__(
self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY)
# initialize variables
self.parent = parent # GMFrame | CmdPanel | ?
self.giface = giface
self._gconsole = gconsole
self._menuModel = menuModel

Expand Down Expand Up @@ -135,6 +142,18 @@ def __init__(
if not self._gcstyle & GC_PROMPT:
self.cmdPrompt.Hide()

# read history file
self._loadHistory()
if self.giface:
self.giface.currentMapsetChanged.connect(self._loadHistory)

if self._gcstyle == GC_PROMPT:
# connect update history signal only for main Console Window
self.giface.updateHistory.connect(
lambda cmd: self.cmdPrompt.UpdateCmdHistory(cmd)
)
self.giface.updateHistory.connect(lambda cmd: update_history(cmd))

# buttons
self.btnClear = ClearButton(parent=self.panelPrompt)
self.btnClear.SetToolTip(_("Clear prompt and output window"))
Expand Down Expand Up @@ -238,6 +257,13 @@ def _layout(self):
self.SetAutoLayout(True)
self.Layout()

def _loadHistory(self):
"""Load history from a history file to data structures"""
history_path = get_current_mapset_history_path()
if history_path:
self.cmdPrompt.cmdbuffer = read_history(history_path)
self.cmdPrompt.cmdindex = len(self.cmdPrompt.cmdbuffer)

def GetPanel(self, prompt=True):
"""Get panel

Expand Down Expand Up @@ -431,7 +457,8 @@ def OnCmdExportHistory(self, event):

if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
if self.cmdPrompt.CopyHistory(path):
history_path = get_current_mapset_history_path()
if copy_history(path, history_path):
self.showNotification.emit(
message=_("Command history saved to '{}'".format(path))
)
Expand Down
14 changes: 14 additions & 0 deletions gui/wxpython/gui_core/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@ def _createGeneralPage(self, notebook):

gridSizer.Add(hideSearch, pos=(row, 0), span=(1, 2))

row += 1
hideHistory = wx.CheckBox(
parent=panel,
id=wx.ID_ANY,
label=_("Hide '%s' tab (requires GUI restart)") % _("History"),
name="IsChecked",
)
hideHistory.SetValue(
self.settings.Get(group="manager", key="hideTabs", subkey="history")
)
self.winId["manager:hideTabs:history"] = hideHistory.GetId()

gridSizer.Add(hideHistory, pos=(row, 0), span=(1, 2))

row += 1
hidePyShell = wx.CheckBox(
parent=panel,
Expand Down
62 changes: 1 addition & 61 deletions gui/wxpython/gui_core/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@
@author Wolf Bergenheim <wolf bergenheim.net> (#962)
"""

import os
import difflib
import codecs
import sys
import shutil

import wx
import wx.stc
Expand All @@ -34,7 +31,7 @@

from core import globalvar
from core import utils
from core.gcmd import EncodeString, DecodeString, GError
from core.gcmd import EncodeString, DecodeString


class GPrompt(object):
Expand Down Expand Up @@ -65,10 +62,6 @@ def __init__(self, parent, giface, menuModel):
# command description (gtask.grassTask)
self.cmdDesc = None

self._loadHistory()
if giface:
giface.currentMapsetChanged.connect(self._loadHistory)

# list of traced commands
self.commands = list()

Expand All @@ -77,38 +70,6 @@ def __init__(self, parent, giface, menuModel):
giface.currentMapsetChanged.connect(self._reloadListOfMaps)
giface.grassdbChanged.connect(self._reloadListOfMaps)

def _readHistory(self):
"""Get list of commands from history file"""
hist = list()
env = grass.gisenv()
try:
fileHistory = codecs.open(
os.path.join(
env["GISDBASE"],
env["LOCATION_NAME"],
env["MAPSET"],
".wxgui_history",
),
encoding="utf-8",
mode="r",
errors="replace",
)
except IOError:
return hist

try:
for line in fileHistory.readlines():
hist.append(line.replace("\n", ""))
finally:
fileHistory.close()

return hist

def _loadHistory(self):
"""Load history from a history file to data structures"""
self.cmdbuffer = self._readHistory()
self.cmdindex = len(self.cmdbuffer)

def _getListOfMaps(self):
"""Get list of maps"""
result = dict()
Expand Down Expand Up @@ -140,27 +101,6 @@ def _runCmd(self, cmdString):
self.CmdErase()
self.ShowStatusText("")

def CopyHistory(self, targetFile):
"""Copy history file to the target location.
Returns True if file is successfully copied."""
env = grass.gisenv()
historyFile = os.path.join(
env["GISDBASE"],
env["LOCATION_NAME"],
env["MAPSET"],
".wxgui_history",
)
try:
shutil.copyfile(historyFile, targetFile)
except (IOError, OSError) as e:
GError(
_("Unable to copy file {} to {}'.\n\nDetails: {}").format(
historyFile, targetFile, e
)
)
return False
return True

def GetCommands(self):
"""Get list of launched commands"""
return self.commands
Expand Down
Loading
Loading