Skip to content

Commit

Permalink
wxGUI/history: Create panel displaying info about command (#3365)
Browse files Browse the repository at this point in the history
The history log .wxgui_history is newly stored as a JSON file with command and command_info keys. When a new command is launched, it is stored as a new entry to a JSON file. The old plain text is preserved but not further used (we have two read methods but only one write method).

The command info contains the following:
runtime, timestamp, status (success, failed, aborted), if 2D/3D is set, and region settings.

The History browser is newly the object of the Splitter window. At the upper part, there is a tree catalog with history commands, and at the bottom part, there is a panel displaying the command info.


---------

Co-authored-by: lindakladivova <l.kladivova@seznam.cz>
Co-authored-by: Anna Petrasova <kratochanna@gmail.com>
  • Loading branch information
3 people committed Feb 18, 2024
1 parent 37c2bae commit 4d2dac4
Show file tree
Hide file tree
Showing 8 changed files with 743 additions and 182 deletions.
40 changes: 32 additions & 8 deletions gui/wxpython/core/gconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@

from grass.pydispatch.signal import Signal

from grass.grassdb.history import (
get_current_mapset_gui_history_path,
add_entry_to_history,
)
from grass.grassdb import history

from core import globalvar
from core.gcmd import CommandThread, GError, GException
Expand Down Expand Up @@ -500,16 +497,26 @@ def RunCmd(
Debug.msg(2, "GPrompt:RunCmd(): empty command")
return

# convert plain text history file to JSON format if needed
history_path = history.get_current_mapset_gui_history_path()
if history.get_history_file_extension(history_path) != ".json":
history.convert_plain_text_to_JSON(history_path)

# add entry to command history log
history_path = get_current_mapset_gui_history_path()
command_info = history.get_initial_command_info(env)
entry = {
"command": cmd_save_to_history,
"command_info": command_info,
}
try:
add_entry_to_history(cmd_save_to_history, history_path)
except OSError as e:
history_path = history.get_current_mapset_gui_history_path()
history.add_entry(history_path, entry)
except (OSError, ValueError) as e:
GError(str(e))

# update command prompt and history model
if self._giface:
self._giface.entryToHistoryAdded.emit(cmd=cmd_save_to_history)
self._giface.entryToHistoryAdded.emit(entry=entry)

if command[0] in globalvar.grassCmd:
# send GRASS command without arguments to GUI command interface
Expand Down Expand Up @@ -729,12 +736,29 @@ def OnCmdDone(self, event):
)
)
msg = _("Command aborted")
status = "aborted"
elif event.returncode != 0:
msg = _("Command ended with non-zero return code {returncode}").format(
returncode=event.returncode
)
status = "failed"
else:
msg = _("Command finished")
status = "success"

cmd_info = {"runtime": int(ctime), "status": status}

# update command history log by status and runtime duration
try:
history_path = history.get_current_mapset_gui_history_path()
history.update_entry(history_path, cmd_info)

# update history model
if self._giface:
entry = history.read(history_path)[-1]
self._giface.entryInHistoryUpdated.emit(entry=entry)
except (OSError, ValueError) as e:
GError(str(e))

self.WriteCmdLog(
"(%s) %s (%s)" % (str(time.ctime()), msg, stime),
Expand Down
5 changes: 5 additions & 0 deletions gui/wxpython/core/giface.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ def __init__(self):
"StandaloneGrassInterface.entryFromHistoryRemoved"
)

# Signal emitted when entry in history is updated
self.entryInHistoryUpdated = Signal(
"StandaloneGrassInterface.entryInHistoryUpdated"
)

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

Expand Down
31 changes: 0 additions & 31 deletions gui/wxpython/gui_core/goutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,10 @@

import textwrap

import os
import wx
from wx import stc

from grass.pydispatch.signal import Signal
from grass.grassdb.history import (
read_history,
create_history_file,
get_current_mapset_gui_history_path,
)

# needed just for testing
if __name__ == "__main__":
Expand Down Expand Up @@ -142,20 +136,6 @@ 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 signals only for main Console Window
self.giface.entryToHistoryAdded.connect(
lambda cmd: self.cmdPrompt.AddEntryToCmdHistoryBuffer(cmd)
)
self.giface.entryFromHistoryRemoved.connect(
lambda index: self.cmdPrompt.RemoveEntryFromCmdHistoryBuffer(index)
)

# buttons
self.btnClear = ClearButton(parent=self.panelPrompt)
self.btnClear.SetToolTip(_("Clear prompt and output window"))
Expand Down Expand Up @@ -244,17 +224,6 @@ 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_gui_history_path()
try:
if not os.path.exists(history_path):
create_history_file(history_path)
self.cmdPrompt.cmdbuffer = read_history(history_path)
self.cmdPrompt.cmdindex = len(self.cmdPrompt.cmdbuffer)
except OSError as e:
GError(str(e))

def GetPanel(self, prompt=True):
"""Get panel
Expand Down
45 changes: 36 additions & 9 deletions gui/wxpython/gui_core/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
from grass.script import core as grass
from grass.script import task as gtask

from grass.grassdb import history

from grass.pydispatch.signal import Signal

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


class GPrompt:
Expand Down Expand Up @@ -161,6 +163,16 @@ def __init__(self, parent, giface, menuModel, margin=False):
# show hint
self._showHint()

# read history file
self._loadHistory()
if giface:
giface.currentMapsetChanged.connect(self._loadHistory)
giface.entryToHistoryAdded.connect(
lambda entry: self._addEntryToCmdHistoryBuffer(entry)
)
giface.entryFromHistoryRemoved.connect(
lambda index: self._removeEntryFromCmdHistoryBuffer(index)
)
#
# bindings
#
Expand Down Expand Up @@ -304,23 +316,38 @@ def SetTextAndFocus(self, text):
self.SetCurrentPos(pos)
self.SetFocus()

def AddEntryToCmdHistoryBuffer(self, cmd):
"""Add entry to command history buffer
:param cmd: command given as a string
def _loadHistory(self):
"""Load history from a history file to data structures"""
try:
history_path = history.get_current_mapset_gui_history_path()
history.ensure_history_file(history_path)
self.cmdbuffer = [
entry["command"] for entry in history.read(history_path)
] or []
self.cmdindex = len(self.cmdbuffer)
except (OSError, ValueError) as e:
GError(str(e))

def _addEntryToCmdHistoryBuffer(self, entry):
"""Add entry to command history buffer.
:param entry dict: entry with 'command' and 'command_info' keys
command value is a string.
"""
# create command string
entry = entry["command"]
# add command to history
self.cmdbuffer.append(cmd)
self.cmdbuffer.append(entry)
# update also traced commands
self.commands.append(cmd)
self.commands.append(entry)

# keep command history to a manageable size
if len(self.cmdbuffer) > 200:
del self.cmdbuffer[0]
self.cmdindex = len(self.cmdbuffer)

def RemoveEntryFromCmdHistoryBuffer(self, index):
"""Remove entry from command history buffer
def _removeEntryFromCmdHistoryBuffer(self, index):
"""Remove entry from command history buffer.
:param index: index of deleted command
"""
# remove command at the given index from history buffer
Expand Down

0 comments on commit 4d2dac4

Please sign in to comment.