Skip to content

GUI: Add "Copy in Python" button to module dialogs#7237

Open
polucifier wants to merge 4 commits intoOSGeo:mainfrom
polucifier:gui-copy-python
Open

GUI: Add "Copy in Python" button to module dialogs#7237
polucifier wants to merge 4 commits intoOSGeo:mainfrom
polucifier:gui-copy-python

Conversation

@polucifier
Copy link
Copy Markdown

@polucifier polucifier commented Mar 29, 2026

Description

This PR introduces a new button "Copy in Python" to all GRASS module dialogs in the wxGUI. While the existing "Copy" button provides the shell command, this new feature generates valid Python syntax using the grass.script library (standard gs alias).

image

The goal is to streamline the workflow for users transitioning from the GUI to Python scripting and to provide a quick way to prototype scripts directly from the interface.

Key Features & Improvements

  • Standard Syntax: Generates code using the gs.run_command() pattern, which is the most common entry point for GRASS scripting.
  • Robust String Escaping: Uses Python's built-in repr() function for parameter values. This ensures that Windows file paths (backslashes), quotes, and special characters are handled correctly without breaking the Python string literal.
  • Python Keyword Handling: Automatically detects reserved Python keywords (e.g., from, in, global). If a module parameter name conflicts with a keyword, the PR uses dictionary unpacking (e.g., **{'from': 'map'}) to ensure the generated code is syntactically valid.
  • Global Parameters: Correctly translates GUI-specific items like --overwrite, --verbose, and --quiet into their Pythonic True/False arguments.
  • Future-Proofing: Includes a fallback for any other double-dash flags (--) that might be introduced by third-party addons.

Example Output

For a module like v.distance -p -s --overwrite from=durham@user1 to=bridges@PERMANENT upload=dist format=plain, the button generates:

gs.run_command('v.distance', flags='ps', overwrite=True, to='bridges@PERMANENT', upload='dist', format='plain', **{'from': 'durham@user1'})

Testing

  • Manually tested with various modules
  • Verified that the generated strings are valid and executable when pasted into the GRASS Python console.
image

@echoix
Copy link
Copy Markdown
Member

echoix commented Mar 29, 2026

I like the concept, similar to what there is in QGIS, even if I never used these in qgis (only the copy as json and paste it again later on to rehave the same parameters set)

@github-actions github-actions bot added GUI wxGUI related Python Related code is in Python labels Mar 29, 2026
Comment thread gui/wxpython/gui_core/forms.py Outdated
Comment thread gui/wxpython/gui_core/forms.py Outdated
@polucifier
Copy link
Copy Markdown
Author

I like the concept, similar to what there is in QGIS, even if I never used these in qgis (only the copy as json and paste it again later on to rehave the same parameters set)

Thanks for the feedback, @echoix! I really like the QGIS workflow you mentioned. I'm going to convert this PR to a Draft for now while I refine the UI.

My plan is to replace the standalone 'Copy in Python' button with a MenuButton (dropdown) integrated into the main 'Copy' button. It will include:

  • Copy as Shell command (default)
  • Copy as Python script (using the logic from this PR)
  • Copy as JSON (as you suggested)

To complete the loop, I will also try to implement a 'Paste' button that can take a JSON string and automatically populate the dialog fields. I'll push these updates later this evening.

@polucifier polucifier marked this pull request as draft March 29, 2026 11:16
@ninsbl
Copy link
Copy Markdown
Member

ninsbl commented Mar 29, 2026

Nice. Did you consider returning tools API syntax?

@polucifier
Copy link
Copy Markdown
Author

Nice. Did you consider returning tools API syntax?

Hi @ninsbl, thank you very much for the suggestion! That's a great idea. Since I'm already reworking the 'Copy' button into a dropdown menu, I'll add two more options to it: 'Copy as Python (Tools API)' and 'Copy as Python (PyGRASS)'.

Combined with the Script API and JSON options, this should cover all the major Python workflows in GRASS. I'll push the updates later.

echoix and others added 2 commits March 29, 2026 09:14
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…dialogs

This commit introduces advanced clipboard functionality to the GRASS
module dialogs, drastically improving reproducibility and workflow sharing.

New Features:
- Replaced the standard "Copy" button with a backwards-compatible split
  button (using wx.BoxSizer to support older wxPython versions).
- Added a dropdown to copy module parameters in multiple formats: Shell
  command (default), Python (Script API), Python (Tools API), PyGRASS,
  and JSON.
- Added a "Paste" button that reads a JSON payload from the system
  clipboard and automatically populates the module dialog parameters
  and flags.

Paste Logic Improvements:
- Robustly handles wx widget type coercion (e.g., wx.SpinCtrl, wx.Choice,
  ColourSelect, and multi-selection wx.CheckBox arrays).
- Accurately mimics command-line behavior by resetting any omitted
  parameters to their standard defaults and actively unchecking omitted flags.
- Safely parses shorthand flags (e.g., "flags": "ps") without triggering
  false positives on full flag names.
- Explicitly ignores "ModelParam" widgets to prevent interference with
  the Graphical Modeler.
@polucifier
Copy link
Copy Markdown
Author

I have implemented a new Split Copy Button and a Paste functionality for the module dialogs. This update provides users with multiple export formats and a way to quickly restore or share tool parameters via JSON.

New UI layout with the Split Copy button and the Paste button:
image

The dropdown menu offers various formats, including the new Tools API and JSON:
image

The following examples demonstrate the generated output. Note that Python reserved keywords (e.g., from) are correctly handled using dictionary unpacking to ensure valid, executable code:
Shell command:

v.distance -p -s --overwrite from=durham@user1 to=bridges@PERMANENT upload=dist format=plain

Python (Script API):

gs.run_command('v.distance', flags='ps', overwrite=True, to='bridges@PERMANENT', upload='dist', format='plain', **{'from': 'durham@user1'})

Python (Tools API):

Tools().v_distance(flags='ps', overwrite=True, to='bridges@PERMANENT', upload='dist', format='plain', **{'from': 'durham@user1'})

Python (PyGRASS):

Module('v.distance', flags='ps', overwrite=True, to='bridges@PERMANENT', upload='dist', format='plain', **{'from': 'durham@user1'})

JSON format:

{
    "module": "v.distance",
    "params": {
        "flags": "ps",
        "overwrite": "True",
        "from": "durham@user1",
        "to": "bridges@PERMANENT",
        "upload": "dist",
        "format": "plain"
    }
}

Demonstration of the Paste functionality, which populates dialog fields and updates the command preview in real-time:
Paste.webm

@polucifier polucifier marked this pull request as ready for review March 30, 2026 00:48
@ninsbl
Copy link
Copy Markdown
Member

ninsbl commented Mar 30, 2026

Cool. Just for clarification: does Copy as JSON copy json equivalent to --json, or just the the parameters that were set with their values?

@ninsbl
Copy link
Copy Markdown
Member

ninsbl commented Mar 30, 2026

I now see the JSON structure in your comment above... Background for my question has been: #6697

Comment thread gui/wxpython/gui_core/forms.py Outdated
Comment thread gui/wxpython/gui_core/forms.py Outdated
Comment thread gui/wxpython/gui_core/forms.py Outdated
…d formatting

- forms.py: Catch specific exceptions (json.JSONDecodeError, ValueError)
  in OnPaste instead of a bare Exception to avoid swallowing unrelated bugs.
- forms.py: Fix a formatting bug where the generated Python command
  started with a leading comma when no flags were present.
- forms.py/task.py: Move _get_formatted_params and _extract_params_dict
  from GUI forms to grass.script.task (as cmd_to_python_args and
  cmd_to_dict) so they can be reused outside the GUI.
@polucifier polucifier requested a review from petrasovaa April 9, 2026 21:58
@landam landam added this to the 8.6.0 milestone Apr 15, 2026
@pesekon2
Copy link
Copy Markdown
Contributor

The functionality is great. I really like it. I would have some doubts about the usability of the Paste button but if @echoix says he can imagine users for that, why not having it.

I tested the functionality and it seems fine to me. However, I don't see the arrow on the dropout of the Copy button. It seems seems to me more like a wxPython issue than being connected to the PR. What is your version of wx? Mine is 4.2.3.

Screenshot from 2026-04-15 15-48-40

@landam landam added the enhancement New feature or request label Apr 15, 2026
win.SetValue(
val
if isinstance(val, bool)
else str(val).lower() in ("true", "1", "yes")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ruff] reported by reviewdog 🐶

Suggested change
else str(val).lower() in ("true", "1", "yes")
else str(val).lower() in {"true", "1", "yes"}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request GUI wxGUI related libraries Python Related code is in Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants