├── .github
└── workflows
│ └── mirror-to-codeberg.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── cli2gui
├── __init__.py
├── application
│ ├── __init__.py
│ ├── application.py
│ └── application2args.py
├── decorators.py
├── gui
│ ├── FiraCode-Regular.ttf
│ ├── __init__.py
│ ├── abstract_gui.py
│ ├── dearpygui_wrapper.py
│ ├── helpers.py
│ └── pysimplegui_wrapper.py
├── models.py
└── tojson
│ ├── __init__.py
│ ├── argparse2json.py
│ ├── click2json.py
│ ├── docopt2json.py
│ ├── getopt2json.py
│ └── optparse2json.py
├── comparison.md
├── documentation
├── reference
│ ├── README.md
│ └── cli2gui
│ │ ├── application
│ │ ├── application.md
│ │ ├── application2args.md
│ │ └── index.md
│ │ ├── decorators.md
│ │ ├── gui
│ │ ├── abstract_gui.md
│ │ ├── dearpygui_wrapper.md
│ │ ├── helpers.md
│ │ ├── index.md
│ │ └── pysimplegui_wrapper.md
│ │ ├── index.md
│ │ ├── models.md
│ │ └── tojson
│ │ ├── argparse2json.md
│ │ ├── click2json.md
│ │ ├── docopt2json.md
│ │ ├── getopt2json.md
│ │ ├── index.md
│ │ └── optparse2json.md
└── tutorials
│ └── README.md
├── pyproject.toml
├── readme-assets
├── icons
│ ├── name.png
│ └── proj-icon.png
└── screenshots
│ ├── dearpygui.png
│ └── freesimplegui.png
├── requirements.txt
└── tests
├── __init__.py
├── argparse
├── __init__.py
├── another_file.md
├── file.md
├── test_10.py
├── test_11.py
├── test_16.py
├── test_18.py
├── test_22.py
├── test_24.py
├── test_advanced.py
├── test_advanced_fsg.py
├── test_advanced_fsgqt.py
├── test_advanced_fsgweb.py
├── test_advanced_psg.py
└── test_simple.py
├── click
├── __init__.py
├── test_boolean.py
├── test_choice.py
├── test_feat_switches.py
└── test_simple.py
├── custom(argparse)
├── __init__.py
├── test_advanced.py
└── test_simple.py
├── dephell_argparse
├── __init__.py
├── test_advanced.py
└── test_simple.py
├── docopt
├── __init__.py
├── test_11.py
└── test_simple.py
├── getopt
├── __init__.py
└── test_simple.py
└── optparse
├── __init__.py
├── test_11.py
└── test_simple.py
/.github/workflows/mirror-to-codeberg.yaml:
--------------------------------------------------------------------------------
1 |
2 | # Sync repo to the Codeberg mirror
3 | name: Repo sync GitHub -> Codeberg
4 | on:
5 | push:
6 | branches:
7 | - '**'
8 |
9 | jobs:
10 | codeberg:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 | - uses: spyoungtech/mirror-action@v0.7.0
17 | with:
18 | REMOTE: "https://codeberg.org/FredHappyface/Cli2Gui.git"
19 | GIT_USERNAME: FredHappyface
20 | GIT_PASSWORD: ${{ secrets.CODEBERG_PASSWORD }}
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | uv.lock
3 |
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 | *$py.class
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | pip-wheel-metadata/
27 | share/python-wheels/
28 | *.egg-info/
29 | .installed.cfg
30 | *.egg
31 | MANIFEST
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Unit test / coverage reports
44 | htmlcov/
45 | .tox/
46 | .nox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | *.py,cover
54 | .hypothesis/
55 | .pytest_cache/
56 | cover/
57 |
58 | # Translations
59 | *.mo
60 | *.pot
61 |
62 | # Django stuff:
63 | *.log
64 | local_settings.py
65 | db.sqlite3
66 | db.sqlite3-journal
67 |
68 | # Flask stuff:
69 | instance/
70 | .webassets-cache
71 |
72 | # Scrapy stuff:
73 | .scrapy
74 |
75 | # Sphinx documentation
76 | docs/_build/
77 |
78 | # PyBuilder
79 | target/
80 |
81 | # Jupyter Notebook
82 | .ipynb_checkpoints
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | # For a library or package, you might want to ignore these files since the code is
90 | # intended to run in multiple environments; otherwise, check them in:
91 | # .python-version
92 |
93 | # pipenv
94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
97 | # install all needed dependencies.
98 | #Pipfile.lock
99 |
100 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
101 | __pypackages__/
102 |
103 | # Celery stuff
104 | celerybeat-schedule
105 | celerybeat.pid
106 |
107 | # SageMath parsed files
108 | *.sage.py
109 |
110 | # Environments
111 | .env
112 | .venv
113 | env/
114 | venv/
115 | ENV/
116 | env.bak/
117 | venv.bak/
118 |
119 | # Spyder project settings
120 | .spyderproject
121 | .spyproject
122 |
123 | # Rope project settings
124 | .ropeproject
125 |
126 | # mkdocs documentation
127 | /site
128 |
129 | # mypy
130 | .mypy_cache/
131 | .dmypy.json
132 | dmypy.json
133 |
134 | # Pyre type checker
135 | .pyre/
136 |
137 | # pytype static type analyzer
138 | .pytype/
139 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/astral-sh/ruff-pre-commit
3 | rev: v0.9.6
4 | hooks:
5 | - id: ruff
6 | args: [ --fix ]
7 | - id: ruff-format
8 |
9 | - repo: https://github.com/RobertCraigie/pyright-python
10 | rev: v1.1.394
11 | hooks:
12 | - id: pyright
13 |
14 | - repo: local
15 | hooks:
16 | - id: generate requirements
17 | name: generate requirements
18 | entry: uv export --no-hashes --no-dev -o requirements.txt
19 | language: system
20 | pass_filenames: false
21 | - id: safety
22 | name: safety
23 | entry: uv run safety
24 | language: system
25 | pass_filenames: false
26 | - id: make docs
27 | name: make docs
28 | entry: uv run handsdown --cleanup -o documentation/reference
29 | language: system
30 | pass_filenames: false
31 | - id: build package
32 | name: build package
33 | entry: uv build
34 | language: system
35 | pass_filenames: false
36 |
37 |
38 | - repo: https://github.com/pre-commit/pre-commit-hooks
39 | rev: v5.0.0
40 | hooks:
41 | - id: trailing-whitespace
42 | - id: end-of-file-fixer
43 | - id: check-case-conflict
44 | - id: check-executables-have-shebangs
45 | - id: check-json
46 | - id: check-merge-conflict
47 | - id: check-shebang-scripts-are-executable
48 | - id: check-symlinks
49 | - id: check-toml
50 | - id: check-vcs-permalinks
51 | - id: check-yaml
52 | - id: detect-private-key
53 | - id: mixed-line-ending
54 |
55 | - repo: https://github.com/boidolr/pre-commit-images
56 | rev: v1.8.4
57 | hooks:
58 | - id: optimize-jpg
59 | - id: optimize-png
60 | - id: optimize-svg
61 | - id: optimize-webp
62 |
63 | exclude: "tests/data|documentation/reference"
64 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All major and minor version changes will be documented in this file. Details of
4 | patch-level version changes can be found in [commit messages](../../commits/master).
5 |
6 | ## 2025 - 2025/02/18
7 |
8 | - fpg write to file picker #24
9 | - dpg add file picker #16
10 |
11 | ## 2024.3 - 2024/10/05
12 |
13 | - Code improvements
14 |
15 | ## 2024.2 - 2024/10/04
16 |
17 | - Add `freesimplegui`
18 | - Update docs
19 |
20 | ## 2024.1 - 2024/03/19
21 |
22 | - Add `dearpygui` support as default, (download the `psg` extra for `pysimplegui` support)
23 | - fix types for `argparse..add_mutually_exclusive_group()`
24 |
25 | ## 2024 - 2024/01/20
26 |
27 | - Update deps
28 | - Code improvements
29 |
30 | ## 2023 - 2023/08/31
31 |
32 | - Update deps
33 |
34 | ## 2022.3 - 2022/12/31
35 |
36 | - Feature, support defaults https://github.com/FHPythonUtils/Cli2Gui/issues/11
37 | - Use full module namespace in-place of relative imports
38 | - Use `Enum` for widget types. eg. `types.ItemType.Bool`
39 | - Update internal types
40 | - Add more supported types for other parsers. e.g `click`
41 | - Argparse supports: Bool, Int, Choice, File, Text
42 | - Click supports: Bool, Int, Choice, Text
43 | - DocOpt supports: Bool, Text
44 | - GetOpt supports: Bool, Text
45 | - Optparse supports: Bool, Int, Choice, Text
46 |
47 | ## 2022.2.1 - 2022/12/30
48 |
49 | - Fix https://github.com/FHPythonUtils/Cli2Gui/issues/13
50 |
51 | ## 2022.2 - 2022/09/02
52 |
53 | - Fix https://github.com/FHPythonUtils/Cli2Gui/issues/10, basic support for subparsers. `parser.add_subparsers()`
54 |
55 | ```py
56 | parser = argparse.ArgumentParser(description="this is an example parser")
57 | subparsers = parser.add_subparsers(help='types of A')
58 | parser.add_argument("-v",)
59 |
60 | a_parser = subparsers.add_parser("A")
61 | b_parser = subparsers.add_parser("B")
62 |
63 | a_parser.add_argument("something", choices=['a1', 'a2'])
64 |
65 | args = parser.parse_args()
66 | ```
67 |
68 | ## 2022.1 - 2022/04/07
69 |
70 | - Fix https://github.com/FHPythonUtils/Cli2Gui/issues/7
71 | - `catpandoc` is now optional https://github.com/FHPythonUtils/Cli2Gui/issues/5
72 |
73 | ## 2022 - 2022/01/24
74 |
75 | - Bump pillow version (CVE-2022-22815, CVE-2022-22816, CVE-2022-22817)
76 | - Update deps
77 |
78 | ## 2021.2.1 - 2021/10/14
79 |
80 | - Use pre-commit to enforce reasonable standards + consistency
81 | - Update readme with improved docs on installing and running python (fairly generic)
82 | - Remove classifiers for license + python versions and rely on poetry to generate these
83 | - Update tooling config (pyproject.toml)
84 |
85 | ## 2021.2 - 2021/07/24
86 |
87 | - Use enum for parser + gui
88 | - Use datatypes + typeddict...
89 | - Add option for end user to select parser at runtime https://github.com/FHPythonUtils/Cli2Gui/issues/4
90 | - Replace 'if' case/switch with function mappings
91 |
92 | ## 2021.1 - 2021/06/06
93 |
94 | - reformat
95 | - improve documentation
96 | - typing improvements
97 | - use relative imports
98 | - update pyproject.toml
99 |
100 | ## 2021 - 2021/01/18
101 |
102 | - Modelled the radio groups
103 |
104 | ## 2020.9.1 - 2020/10/14
105 |
106 | - New Pillow release
107 | - Typing fixes as recommended here https://github.com/microsoft/pylance-release/issues/485
108 |
109 | ## 2020.9 - 2020/10/13
110 |
111 | - Added typing (drop py < 3.7)
112 | - Update docstrings
113 | - Update internal representation (tidy up)
114 | - Use flavours for additional pysimplegui modules install `cli2gui[web]` and
115 | `cli2gui[qt]` for the respective versions
116 | - Modernize parts of the codebase (eg. decorators.py)
117 | - Use camelCase for variables
118 |
119 | ## 2020.8.1 - 2020/05/06
120 |
121 | - Updated classifiers
122 |
123 | ## 2020.8 - 2020/04/27
124 |
125 | - Added dephell_argparse support
126 |
127 | ## 2020.7.1 - 2020/04/24
128 |
129 | - Added catch for ResourceWarning when running in `python -Wd`
130 |
131 | ## 2020.7 - 2020/04/16
132 |
133 | - using poetry and dephell build systems
134 |
135 | ## 2020.6 - 2020/03/24
136 |
137 | - added rudimentary click support
138 |
139 | ## 2020.5 - 2020/03/22
140 |
141 | - added menu
142 | - included part of catpandoc to achieve this (excluding catimage as this leads
143 | to a circular import 😱)
144 | - updated documentation to reflect this
145 | - updated requirements.txt
146 |
147 | ## 2020.4
148 |
149 | - bump
150 |
151 | ## 2020.3 - 2020/03/17
152 |
153 | - can use pysimplegui, pysimpleguiqt, pysimpleguiweb
154 | - updated readme and added data structures documentation
155 | - bugfixes
156 | - lint fixes
157 |
158 | ## 2020.3 - 2020/03/12
159 |
160 | - added docopt parser
161 | - base24 scheme can be used as theme
162 | - Updated run_function. If not specified, program continues as normal
163 | (can only run once)
164 |
165 | ## 2020.2 - 2020/03/12
166 |
167 | - Fix
168 |
169 | ## 2020.1 - 2020/03/12
170 |
171 | - Updated readme
172 | - added images
173 | - added getopt parser
174 | - added optparse parser
175 | - Program icon is to left of title
176 | - Streamlined argparse2json
177 | - refactor
178 |
179 | ## 2020 - 2020/03/06
180 |
181 | - First release
182 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 FredHappyface
4 | Copyright (c) 2013-2017 Chris
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | this software and associated documentation files (the "Software"), to deal in
8 | the Software without restriction, including without limitation the rights to
9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](../../)
2 | [](../../issues)
3 | [](/LICENSE.md)
4 | [](../../commits/master)
5 | [](../../commits/master)
6 | [](https://pypistats.org/packages/cli2gui)
7 | [](https://pepy.tech/project/cli2gui)
8 | [](https://pypi.org/project/cli2gui)
9 |
10 |
11 | # Cli2Gui
12 |
13 |
14 |
15 | **Project Description:**
16 |
17 | `Cli2Gui` enables you to convert command-line interface (CLI) applications into graphical user
18 | interfaces (GUIs) with minimal effort. Designed to accommodate a wide variety of use cases,
19 | this library allows developers to maintain a CLI while seamlessly enabling a GUI option for
20 | popular Python parsers like `argparse`, `docopt` with GUI frameworks such as `freesimplegui`,
21 | `dearpygui`, and more. By using `Cli2Gui`, developers can extend the accessibility of their
22 | applications, catering to both command-line users and those who prefer interactive graphical tools.
23 |
24 | With a decorator-based approach, `Cli2Gui` allows you to keep your CLI logic intact while adding
25 | GUI support as needed. Customization options such as theming, custom icons, and program descriptions
26 | make it easy to enhance the user experience. Whether you are working on small scripts or complex
27 | tools, `Cli2Gui` provides a flexible and user-friendly way to bridge the gap between command-line
28 | and graphical interfaces, offering convenience for both developers and end-users.
29 |
30 | - [Screenshots](#screenshots)
31 | - [Documentation](#documentation)
32 | - [Install With PIP](#install-with-pip)
33 | - [Language information](#language-information)
34 | - [Built for](#built-for)
35 | - [Building](#building)
36 | - [Download Project](#download-project)
37 | - [Clone](#clone)
38 | - [Using The Command Line](#using-the-command-line)
39 | - [Using GitHub Desktop](#using-github-desktop)
40 | - [Download Zip File](#download-zip-file)
41 | - [Community Files](#community-files)
42 | - [Licence](#licence)
43 | - [Changelog](#changelog)
44 | - [Code of Conduct](#code-of-conduct)
45 | - [Contributing](#contributing)
46 | - [Security](#security)
47 | - [Support](#support)
48 |
49 | ## Screenshots
50 |
51 |
52 |

53 |

54 |
55 |
56 |
57 | ## Documentation
58 |
59 | A high-level overview of how the documentation is organized organized will help you know
60 | where to look for certain things:
61 |
62 | - [Tutorials](/documentation/tutorials) take you by the hand through a series of steps to get
63 | started using the software. Start here if you’re new.
64 | - The [Technical Reference](/documentation/reference) documents APIs and other aspects of the
65 | machinery. This documentation describes how to use the classes and functions at a lower level
66 | and assume that you have a good high-level understanding of the software.
67 |
71 |
72 | ## Install With PIP
73 |
74 | ```python
75 | pip install cli2gui
76 | ```
77 |
78 | Head to https://pypi.org/project/cli2gui/ for more info
79 |
80 | ## Language information
81 |
82 | ### Built for
83 |
84 | This program has been written for Python versions 3.8 - 3.11 and has been tested with both 3.8 and
85 | 3.11
86 |
87 | ## Building
88 |
89 | This project uses https://github.com/FHPythonUtils/FHMake to automate most of the building. This
90 | command generates the documentation, updates the requirements.txt and builds the library artefacts
91 |
92 | Note the functionality provided by fhmake can be approximated by the following
93 |
94 | ```sh
95 | handsdown --cleanup -o documentation/reference
96 | poetry export -f requirements.txt --output requirements.txt
97 | poetry export -f requirements.txt --with dev --output requirements_optional.txt
98 | poetry build
99 | ```
100 |
101 | `fhmake audit` can be run to perform additional checks
102 |
103 | ## Download Project
104 |
105 | ### Clone
106 |
107 | #### Using The Command Line
108 |
109 | 1. Press the Clone or download button in the top right
110 | 2. Copy the URL (link)
111 | 3. Open the command line and change directory to where you wish to
112 | clone to
113 | 4. Type 'git clone' followed by URL in step 2
114 |
115 | ```bash
116 | git clone https://github.com/FHPythonUtils/Cli2Gui
117 | ```
118 |
119 | More information can be found at
120 | https://help.github.com/en/articles/cloning-a-repository
121 |
122 | #### Using GitHub Desktop
123 |
124 | 1. Press the Clone or download button in the top right
125 | 2. Click open in desktop
126 | 3. Choose the path for where you want and click Clone
127 |
128 | More information can be found at
129 | https://help.github.com/en/desktop/contributing-to-projects/cloning-a-repository-from-github-to-github-desktop
130 |
131 | ### Download Zip File
132 |
133 | 1. Download this GitHub repository
134 | 2. Extract the zip archive
135 | 3. Copy/ move to the desired location
136 |
137 | ## Community Files
138 |
139 | ### Licence
140 |
141 | MIT License
142 | Copyright (c) FredHappyface
143 | (See the [LICENSE](/LICENSE.md) for more information.)
144 |
145 | ### Changelog
146 |
147 | See the [Changelog](/CHANGELOG.md) for more information.
148 |
149 | ### Code of Conduct
150 |
151 | Online communities include people from many backgrounds. The *Project*
152 | contributors are committed to providing a friendly, safe and welcoming
153 | environment for all. Please see the
154 | [Code of Conduct](https://github.com/FHPythonUtils/.github/blob/master/CODE_OF_CONDUCT.md)
155 | for more information.
156 |
157 | ### Contributing
158 |
159 | Contributions are welcome, please see the
160 | [Contributing Guidelines](https://github.com/FHPythonUtils/.github/blob/master/CONTRIBUTING.md)
161 | for more information.
162 |
163 | ### Security
164 |
165 | Thank you for improving the security of the project, please see the
166 | [Security Policy](https://github.com/FHPythonUtils/.github/blob/master/SECURITY.md)
167 | for more information.
168 |
169 | ### Support
170 |
171 | Thank you for using this project, I hope it is of use to you. Please be aware that
172 | those involved with the project often do so for fun along with other commitments
173 | (such as work, family, etc). Please see the
174 | [Support Policy](https://github.com/FHPythonUtils/.github/blob/master/SUPPORT.md)
175 | for more information.
176 |
--------------------------------------------------------------------------------
/cli2gui/__init__.py:
--------------------------------------------------------------------------------
1 | """Entry point for the program."""
2 |
3 | from __future__ import annotations
4 |
5 | from cli2gui.decorators import Cli2Gui, Click2Gui
6 |
7 | _ = (Cli2Gui, Click2Gui)
8 |
--------------------------------------------------------------------------------
/cli2gui/application/__init__.py:
--------------------------------------------------------------------------------
1 | """The GUI application and related functionality."""
2 |
--------------------------------------------------------------------------------
/cli2gui/application/application.py:
--------------------------------------------------------------------------------
1 | """Application here uses PySimpleGUI."""
2 |
3 | from __future__ import annotations
4 |
5 | import logging
6 | import sys
7 | from typing import Any
8 |
9 | from cli2gui import models
10 | from cli2gui.application.application2args import argFormat
11 | from cli2gui.gui import helpers
12 | from cli2gui.gui.dearpygui_wrapper import DearPyGuiWrapper
13 | from cli2gui.gui.pysimplegui_wrapper import PySimpleGUIWrapper
14 |
15 |
16 | def run(buildSpec: models.FullBuildSpec) -> Any:
17 | """Establish the main entry point.
18 |
19 | Args:
20 | ----
21 | buildSpec (types.FullBuildSpec): args that customise the application such as the theme
22 | or the function to run
23 |
24 | """
25 |
26 | # Set the theme
27 | theme = helpers.get_base24_theme(buildSpec.theme, buildSpec.darkTheme)
28 |
29 | buildSpec.gui = buildSpec.gui.replace("pysimplegui", "psg").replace("freesimplegui", "fsg")
30 |
31 | if buildSpec.gui in [
32 | "psg",
33 | "psgqt",
34 | "psgweb",
35 | "fsg",
36 | # "fsgqt", # cannot test on windows
37 | # "fsgweb", # bug in remi prevents this from working
38 | ]:
39 | gui_wrapper = PySimpleGUIWrapper
40 | else:
41 | gui_wrapper = DearPyGuiWrapper
42 |
43 | if gui_wrapper is PySimpleGUIWrapper:
44 | gui = gui_wrapper(theme, buildSpec.gui)
45 | elif gui_wrapper is DearPyGuiWrapper:
46 | gui = gui_wrapper(theme)
47 |
48 | def quit_callback() -> None:
49 | sys.exit(0)
50 |
51 | def run_callback(values: dict[str, Any]) -> None:
52 | args = argFormat(values, buildSpec.parser)
53 | if not buildSpec.run_function:
54 | return args
55 | buildSpec.run_function(args)
56 | return None
57 |
58 | try:
59 | gui.main(buildSpec=buildSpec, quit_callback=quit_callback, run_callback=run_callback)
60 |
61 | except KeyboardInterrupt:
62 | logging.error("Application Exited Early!") # noqa: TRY400
63 |
--------------------------------------------------------------------------------
/cli2gui/application/application2args.py:
--------------------------------------------------------------------------------
1 | """Functions to create args from key/value pairs."""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import optparse
7 | from pathlib import Path
8 | from typing import Any
9 |
10 | from cli2gui.models import SEP, ParserType
11 |
12 |
13 | def processValue(key: str, value: str) -> tuple[str, Any]:
14 | if SEP not in key:
15 | return key, value or None
16 | key, _type = key.split(SEP, maxsplit=1)
17 | if len(str(value)) == 0 or value is None:
18 | return key, None
19 | if _type == "ItemType.Bool":
20 | return key, bool(value)
21 | if "ItemType.File" in _type:
22 | _, mode, encoding = _type.split(";", maxsplit=2)
23 | if "b" in mode:
24 | return key, open(value, mode=mode)
25 | return key, open(value, mode=mode, encoding=encoding)
26 | if _type == "ItemType.Path":
27 | return key, Path(value)
28 | if _type == "ItemType.Int":
29 | return key, int(value)
30 | if _type == "ItemType.Text":
31 | return key, value
32 | if _type == "ItemType.Float":
33 | return key, float(value)
34 | if _type == "ItemType.List":
35 | return key, value
36 | if _type == "ItemType.Tuple":
37 | return key, value
38 | if _type == "ItemType.DateTime":
39 | return key, value
40 |
41 | return key, value
42 |
43 |
44 | def argparseFormat(values: dict[str, Any]) -> argparse.Namespace:
45 | """Format args for argparse."""
46 | args = {}
47 | for key, _value in values.items():
48 | cleankey, value = processValue(key, _value)
49 | args[cleankey] = value
50 | return argparse.Namespace(**args)
51 |
52 |
53 | def optparseFormat(values: dict[str, Any]) -> tuple[optparse.Values, list[str]]:
54 | """Format args for optparse."""
55 | args = {}
56 | for key, _value in values.items():
57 | cleankey, value = processValue(key, _value)
58 | args[cleankey] = value
59 | return (optparse.Values(args), [])
60 |
61 |
62 | def getoptFormat(values: dict[str, Any]) -> tuple[list[Any], list[Any]]:
63 | """Format args for getopt."""
64 | return ([processValue(key, _value) for key, _value in values.items() if _value], [])
65 |
66 |
67 | def docoptFormat(values: dict[str, Any]) -> dict[str, Any]:
68 | """Format args for docopt."""
69 | import docopt
70 |
71 | args = {}
72 | for key, _value in values.items():
73 | cleankey, value = processValue(key, _value)
74 | args[cleankey] = value
75 | return docopt.Dict(args)
76 |
77 |
78 | def clickFormat(values: dict[str, Any]) -> list[Any]:
79 | """Format args for click."""
80 | args = []
81 | for key, _value in values.items():
82 | val = str(_value)
83 | if not callable(key) and len(val) > 0:
84 | cleankey, value = processValue(key, _value)
85 | args.extend([cleankey, value])
86 | return args
87 |
88 |
89 | def argFormat(values: dict[str, Any], argumentParser: str | ParserType) -> Any:
90 | """Format the args for the desired parser.
91 |
92 | Args:
93 | ----
94 | values (dict[str, Any]): values from simple gui
95 | argumentParser (str): argument parser to use
96 |
97 | Returns:
98 | -------
99 | Any: args
100 |
101 | """
102 | convertMap = {
103 | ParserType.OPTPARSE: optparseFormat,
104 | ParserType.ARGPARSE: argparseFormat,
105 | ParserType.DEPHELL_ARGPARSE: argparseFormat,
106 | ParserType.DOCOPT: docoptFormat,
107 | ParserType.GETOPT: getoptFormat,
108 | ParserType.CLICK: clickFormat,
109 | }
110 | if argumentParser in convertMap:
111 | return convertMap[argumentParser](values)
112 | return None
113 |
--------------------------------------------------------------------------------
/cli2gui/decorators.py:
--------------------------------------------------------------------------------
1 | """Decorator and entry point for the program."""
2 |
3 | from __future__ import annotations
4 |
5 | import getopt
6 | import sys
7 | import warnings
8 | from argparse import ArgumentParser
9 | from collections.abc import Callable
10 | from optparse import OptionParser
11 | from pathlib import Path
12 | from shlex import quote
13 | from typing import Any, Iterable
14 |
15 | from cli2gui.application import application
16 | from cli2gui.models import BuildSpec, FullBuildSpec, GUIType, ParserType
17 | from cli2gui.tojson import (
18 | argparse2json,
19 | click2json,
20 | docopt2json,
21 | getopt2json,
22 | optparse2json,
23 | )
24 |
25 | DO_COMMAND = "--cli2gui"
26 | DO_NOT_COMMAND = "--disable-cli2gui"
27 |
28 |
29 | def createFromParser(
30 | selfParser: Any,
31 | argsParser: tuple[Any, ...],
32 | kwargsParser: dict[Any, Any],
33 | sourcePath: str,
34 | buildSpec: BuildSpec,
35 | **kwargs: dict[Any, Any],
36 | ) -> FullBuildSpec:
37 | """Generate a buildSpec from a parser.
38 |
39 | Args:
40 | ----
41 | selfParser (Any): A parser that acts on self. eg. ArgumentParser.parse_args
42 | argsParser (tuple[Any, ...]): A parser that acts on function
43 | arguments. eg. getopt.getopt
44 | kwargsParser (dict[Any, Any]): A parser that acts on named params
45 | sourcePath (str): Program source path
46 | buildSpec (BuildSpec): Build spec
47 | **kwargs (dict[Any, Any]): kwargs
48 |
49 | Returns:
50 | -------
51 | types.FullBuildSpec: buildSpec to be used by the application
52 |
53 | Raises:
54 | ------
55 | RuntimeError: Throw error if incorrect parser selected
56 |
57 | """
58 | _ = kwargsParser
59 | runCmd = kwargs.get("target")
60 | if runCmd is None:
61 | if hasattr(sys, "frozen"):
62 | runCmd = quote(sourcePath)
63 | else:
64 | runCmd = f"{quote(sys.executable)} -u {quote(sourcePath)}"
65 | buildSpec.program_name = buildSpec.program_name or Path(sys.argv[0]).name.replace(".py", "")
66 |
67 | # CUSTOM: this seems like a pretty poor pattern to use...
68 | if buildSpec.parser == ParserType.CUSTOM:
69 | buildSpec.parser = input(
70 | f"!Custom parser selected! Choose one of: {[x.value for x in ParserType]}"
71 | )
72 | if buildSpec.parser not in ParserType._value2member_map_:
73 | msg = f"!Custom parser must be one of: {[x.value for x in ParserType]}"
74 | raise RuntimeError(msg)
75 |
76 | parser = buildSpec.parser
77 | # Select parser
78 | convertMap = {
79 | "self": {
80 | ParserType.OPTPARSE: optparse2json.convert,
81 | ParserType.ARGPARSE: argparse2json.convert,
82 | ParserType.DEPHELL_ARGPARSE: argparse2json.convert,
83 | ParserType.DOCOPT: docopt2json.convert,
84 | },
85 | "args": {
86 | ParserType.GETOPT: getopt2json.convert,
87 | },
88 | }
89 | if parser in convertMap["self"]:
90 | return FullBuildSpec(
91 | **convertMap["self"][parser](selfParser).__dict__, **buildSpec.__dict__
92 | )
93 | if parser in convertMap["args"]:
94 | return FullBuildSpec(
95 | **convertMap["args"][parser](argsParser).__dict__, **buildSpec.__dict__
96 | )
97 |
98 | # click is unique in behaviour so we cant use the mapping -_-
99 | if parser == ParserType.CLICK:
100 | return FullBuildSpec(
101 | **click2json.convert(buildSpec.run_function).__dict__, **buildSpec.__dict__
102 | )
103 |
104 | msg = f"!Parser must be one of: {[x.value for x in ParserType]}"
105 | raise RuntimeError(msg)
106 |
107 |
108 | def Click2Gui(
109 | run_function: Callable[..., Any],
110 | gui: str | GUIType = "dearpygui",
111 | theme: str | list[str] = "",
112 | darkTheme: str | list[str] = "",
113 | image: str = "",
114 | program_name: str = "",
115 | program_description: str = "",
116 | max_args_shown: int = 5,
117 | menu: str | dict[str, Any] = "",
118 | **kwargs: dict[str, Any],
119 | ) -> None:
120 | """Use this decorator in the function containing the argument parser.
121 | Serializes data to JSON and launches the Cli2Gui application.
122 |
123 | Args:
124 | ----
125 | run_function (Callable[..., Any]): The name of the function to call eg.
126 | gui (str, optional): Override the gui to use. Current options are:
127 | "dearpygui", "pysimplegui", "pysimpleguiqt","pysimpleguiweb","freesimplegui",
128 | Defaults to "dearpygui".
129 | theme (Union[str, list[str]], optional): Set a base24 theme. Can
130 | also pass a base24 scheme file. eg. one-light.yaml. Defaults to "".
131 | darkTheme (Union[str, list[str]], optional): Set a base24 dark
132 | theme variant. Can also pass a base24 scheme file. eg. one-dark.yaml.
133 | Defaults to "".
134 | image (str, optional): Set the program icon. File
135 | extensions can be any that PIL supports. Defaults to "".
136 | program_name (str, optional): Override the program name.
137 | Defaults to "".
138 | program_description (str, optional): Override the program
139 | description. Defaults to "".
140 | max_args_shown (int, optional): Maximum number of args shown before
141 | using a scrollbar. Defaults to 5.
142 | menu (Union[dict[str, Any]], optional): Add a menu to the program.
143 | Defaults to "". eg. THIS_DIR = str(Path(__file__).resolve().parent)
144 | menu={"File": THIS_DIR + "/file.md"}
145 | **kwargs (dict[Any, Any]): kwargs
146 |
147 | Returns:
148 | -------
149 | Any: Runs the application
150 |
151 | """
152 | bSpec = BuildSpec(
153 | run_function=run_function,
154 | parser=ParserType.CLICK,
155 | gui=gui,
156 | theme=theme,
157 | darkTheme=darkTheme,
158 | image=image,
159 | program_name=program_name,
160 | program_description=program_description,
161 | max_args_shown=max_args_shown,
162 | menu=menu,
163 | )
164 |
165 | buildSpec = createFromParser(
166 | None, (), kwargs, sys.argv[0], bSpec, **{**locals(), **locals()["kwargs"]}
167 | )
168 | return application.run(buildSpec)
169 |
170 |
171 | def Cli2Gui(
172 | run_function: Callable[..., Any],
173 | auto_enable: bool = False,
174 | parser: str | ParserType = "argparse",
175 | gui: str | ParserType = "dearpygui",
176 | theme: str | list[str] = "",
177 | darkTheme: str | list[str] = "",
178 | image: str = "",
179 | program_name: str = "",
180 | program_description: str = "",
181 | max_args_shown: int = 5,
182 | menu: str | dict[str, Any] = "",
183 | ) -> Any:
184 | """Use this decorator in the function containing the argument parser.
185 | Serialises data to JSON and launches the Cli2Gui application.
186 |
187 | Args:
188 | ----
189 | run_function (Callable[..., Any]): The name of the function to call eg.
190 | auto_enable (bool, optional): Enable the GUI by default. If enabled by
191 | default requires `--disable-cli2gui`, otherwise requires `--cli2gui`.
192 | Defaults to False.
193 | parser (str, optional): Override the parser to use. Current
194 | options are: "argparse", "getopt", "optparse", "docopt",
195 | "dephell_argparse". Defaults to "argparse".
196 | gui (str, optional): Override the gui to use. Current options are:
197 | "dearpygui", "pysimplegui", "pysimpleguiqt","pysimpleguiweb","freesimplegui",
198 | Defaults to "dearpygui".
199 | theme (Union[str, list[str]], optional): Set a base24 theme. Can
200 | also pass a base24 scheme file. eg. one-light.yaml. Defaults to "".
201 | darkTheme (Union[str, list[str]], optional): Set a base24 dark
202 | theme variant. Can also pass a base24 scheme file. eg. one-dark.yaml.
203 | Defaults to "".
204 | image (str, optional): Set the program icon. File
205 | extensions can be any that PIL supports. Defaults to "".
206 | program_name (str, optional): Override the program name.
207 | Defaults to "".
208 | program_description (str, optional): Override the program
209 | description. Defaults to "".
210 | max_args_shown (int, optional): Maximum number of args shown before
211 | using a scrollbar. Defaults to 5.
212 | menu (Union[dict[str, Any]], optional): Add a menu to the program.
213 | Defaults to "". eg. THIS_DIR = str(Path(__file__).resolve().parent)
214 | menu={"File": THIS_DIR + "/file.md"}
215 |
216 | Returns:
217 | -------
218 | Any: Runs the application
219 |
220 | """
221 | bSpec = BuildSpec(
222 | run_function=run_function,
223 | parser=parser,
224 | gui=gui,
225 | theme=theme,
226 | darkTheme=darkTheme,
227 | image=image,
228 | program_name=program_name,
229 | program_description=program_description,
230 | max_args_shown=max_args_shown,
231 | menu=menu,
232 | )
233 |
234 | def build(callingFunction: Callable[..., Any]) -> Callable[..., Any]:
235 | """Generate the buildspec and run the GUI.
236 |
237 | Args:
238 | ----
239 | callingFunction (Callable[..., Any]): The calling function eg.
240 | ArgumentParser.parse_args
241 |
242 | Returns:
243 | -------
244 | Callable[..., Any]: some calling function
245 |
246 | """
247 |
248 | def runCli2Gui(self: Any, *args: Iterable[Any], **kwargs: dict[str, Any]) -> None:
249 | """Run the gui/ application.
250 |
251 | :return None: the gui/ application
252 | """
253 | buildSpec = createFromParser(
254 | self,
255 | args,
256 | kwargs,
257 | callingFunction.__name__,
258 | bSpec,
259 | **{**locals(), **locals()["kwargs"]},
260 | )
261 | return application.run(buildSpec)
262 |
263 | def inner(*args: tuple[Any, Any], **kwargs: dict[Any, Any]) -> Any:
264 | """Replace the inner functions with run_cli2gui. eg. When.
265 | ArgumentParser.parse_args is called, do run_cli2gui.
266 |
267 | Returns
268 | -------
269 | Any: Do the calling_function
270 |
271 | """
272 | getopt.getopt = runCli2Gui
273 | getopt.gnu_getopt = runCli2Gui
274 | OptionParser.parse_args = runCli2Gui
275 | ArgumentParser.parse_args = runCli2Gui
276 | try:
277 | import docopt
278 |
279 | docopt.docopt = runCli2Gui
280 | except ImportError:
281 | pass
282 | try:
283 | import dephell_argparse
284 |
285 | dephell_argparse.Parser.parse_args = runCli2Gui
286 | except ImportError:
287 | pass
288 | # Using type=argparse.FileType('r') leads to a resource warning
289 | with warnings.catch_warnings():
290 | warnings.filterwarnings("ignore", category=ResourceWarning)
291 | return callingFunction(*args, **kwargs)
292 |
293 | inner.__name__ = callingFunction.__name__
294 | return inner
295 |
296 | def runWithoutCli2Gui(callingFunction: Callable[..., Any]) -> Callable[..., Any]:
297 | def inner(*args: Iterable[Any], **kwargs: dict[Any, Any]) -> Any:
298 | # Using type=argparse.FileType('r') leads to a resource warning
299 | with warnings.catch_warnings():
300 | warnings.filterwarnings("ignore", category=ResourceWarning)
301 | return callingFunction(*args, **kwargs)
302 |
303 | inner.__name__ = callingFunction.__name__
304 | return inner
305 |
306 | """If enabled by default requires do_not_command, otherwise requires do_command."""
307 | if (not auto_enable and DO_COMMAND not in sys.argv) or (
308 | auto_enable and DO_NOT_COMMAND in sys.argv
309 | ):
310 | if DO_NOT_COMMAND in sys.argv:
311 | sys.argv.remove(DO_NOT_COMMAND)
312 | return runWithoutCli2Gui
313 | return build
314 |
315 |
316 | if __name__ == "__main__":
317 | pass
318 |
--------------------------------------------------------------------------------
/cli2gui/gui/FiraCode-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/cli2gui/gui/FiraCode-Regular.ttf
--------------------------------------------------------------------------------
/cli2gui/gui/__init__.py:
--------------------------------------------------------------------------------
1 | """Package containing GUI classes and functionality, responsible for driving the various
2 | GUI backends supported by cli2gui."""
3 |
--------------------------------------------------------------------------------
/cli2gui/gui/abstract_gui.py:
--------------------------------------------------------------------------------
1 | """Abstract base class for GUI wrappers."""
2 |
3 | from __future__ import annotations
4 |
5 | from abc import ABC, abstractmethod
6 | from typing import Any, Callable
7 |
8 | from cli2gui import models
9 |
10 |
11 | class AbstractGUI(ABC):
12 | """Abstract base class for GUI wrappers."""
13 |
14 | @abstractmethod
15 | def __init__(self) -> None:
16 | """Abstract base class for GUI wrappers."""
17 |
18 | @abstractmethod
19 | def main(
20 | self,
21 | buildSpec: models.FullBuildSpec,
22 | quit_callback: Callable[[], None],
23 | run_callback: Callable[[dict[str, Any]], None],
24 | ) -> None:
25 | """Abstract method for the main function."""
26 | raise NotImplementedError
27 |
--------------------------------------------------------------------------------
/cli2gui/gui/dearpygui_wrapper.py:
--------------------------------------------------------------------------------
1 | """Wrapper class for Dear PyGui."""
2 |
3 | from __future__ import annotations
4 |
5 | from pathlib import Path
6 | from typing import Any, Callable
7 |
8 | import dearpygui.dearpygui as dpg
9 |
10 | from cli2gui.gui import helpers
11 | from cli2gui.gui.abstract_gui import AbstractGUI
12 | from cli2gui.models import SEP, FullBuildSpec, Group, Item, ItemType
13 |
14 | THISDIR = Path(__file__).resolve().parent
15 |
16 |
17 | def hex_to_rgb(hex_code: str) -> tuple[int, int, int, int]:
18 | """Convert a color hex code to a tuple of integers (r, g, b)."""
19 | hex_code = hex_code.lstrip("#")
20 | r = int(hex_code[0:2], 16)
21 | g = int(hex_code[2:4], 16)
22 | b = int(hex_code[4:6], 16)
23 | return (r, g, b, 255)
24 |
25 |
26 | class DearPyGuiWrapper(AbstractGUI):
27 | """Wrapper class for Dear PyGui."""
28 |
29 | def __init__(self, base24Theme: list[str]) -> None:
30 | """Dearpygui wrapper class.
31 |
32 | :param list[str] base24Theme: list representing a base24 theme. Containing 24 elements
33 | (of hex strings like "#e7e7e9")
34 | """
35 | self.base24Theme = base24Theme
36 | super().__init__()
37 |
38 | def _helpText(self, item: Item) -> None:
39 | dpg.add_text(
40 | helpers.stringSentencecase(f"\n- {item.dest}: {item.commands}"),
41 | color=hex_to_rgb(self.base24Theme[13]),
42 | )
43 | dpg.add_text(helpers.stringSentencecase(item.help))
44 |
45 | def _helpFlagWidget(self, item: Item) -> None:
46 | with dpg.group(horizontal=False):
47 | self._helpText(item)
48 | dpg.add_checkbox(tag=item.dest, default_value=(item.default or False))
49 |
50 | def _helpTextWidget(self, item: Item) -> None:
51 | with dpg.group(horizontal=False):
52 | self._helpText(item)
53 | dpg.add_input_text(tag=item.dest, default_value=(item.default or ""))
54 |
55 | def _helpFloatCounterWidget(self, item: Item) -> None:
56 | with dpg.group(horizontal=False):
57 | self._helpText(item)
58 | dpg.add_input_float(
59 | format="%.9f",
60 | tag=item.dest,
61 | default_value=float(item.default or 0),
62 | min_value=-(2**16),
63 | max_value=2**16,
64 | step=1,
65 | step_fast=10,
66 | )
67 |
68 | def _helpIntCounterWidget(self, item: Item) -> None:
69 | with dpg.group(horizontal=False):
70 | self._helpText(item)
71 | dpg.add_input_int(
72 | tag=item.dest,
73 | default_value=int(item.default or 0),
74 | min_value=-(2**16),
75 | max_value=2**16,
76 | step=1,
77 | step_fast=10,
78 | )
79 |
80 | def _helpFileWidget(self, item: Item) -> None:
81 | """Create a UI element with an input text field and a file picker."""
82 |
83 | def file_picker_callback(_sender: str, app_data: dict[str, Any]) -> None:
84 | """Update the input text field with the selected file path."""
85 |
86 | file_path = ""
87 | # User may have selected and edited, or just written a name
88 | user_input_path = Path(app_data.get("file_path_name", ""))
89 |
90 | # Get the selection if possible
91 | selected_path = Path(next(iter(app_data.get("selections", {}).values()), ""))
92 |
93 | if user_input_path.stem == selected_path.stem:
94 | file_path = selected_path
95 | # User may have selected and edited, or just written a name
96 | else:
97 | file_path = user_input_path.stem + Path(item.default or "").suffix
98 | dpg.set_value(item.dest, str(file_path))
99 |
100 | with dpg.group(horizontal=False):
101 | self._helpText(item)
102 |
103 | dpg.add_input_text(tag=item.dest, default_value=(item.default or ""))
104 |
105 | dpg.add_button(
106 | label="Browse", callback=lambda: dpg.show_item(f"{item.dest}_file_dialog")
107 | )
108 |
109 | with dpg.file_dialog(
110 | directory_selector=False,
111 | show=False,
112 | callback=file_picker_callback,
113 | id=f"{item.dest}_file_dialog",
114 | width=650,
115 | height=400,
116 | file_count=1,
117 | ):
118 | dpg.add_file_extension(".*", color=hex_to_rgb(self.base24Theme[13]))
119 |
120 | def _helpDropdownWidget(self, item: Item) -> None:
121 | with dpg.group(horizontal=False):
122 | self._helpText(item)
123 | dpg.add_combo(tag=item.dest, items=item.additional_properties["choices"])
124 |
125 | def addWidgetFromItem(self, item: Item) -> None:
126 | """Select a widget based on the item type.
127 |
128 | :param Item item: the item
129 | """
130 | functionMap = {
131 | ItemType.Bool: self._helpFlagWidget,
132 | ItemType.File: self._helpFileWidget,
133 | ItemType.FileWrite: self._helpFileWidget,
134 | ItemType.Path: self._helpFileWidget,
135 | ItemType.Choice: self._helpDropdownWidget,
136 | ItemType.Int: self._helpIntCounterWidget,
137 | ItemType.Text: self._helpTextWidget,
138 | ItemType.Float: self._helpFloatCounterWidget,
139 | ItemType.List: self._helpTextWidget,
140 | ItemType.Tuple: self._helpTextWidget,
141 | ItemType.DateTime: self._helpTextWidget,
142 | }
143 | if item.type in functionMap:
144 | return functionMap[item.type](item)
145 | return None
146 |
147 | def addItemsAndGroups(
148 | self,
149 | section: Group,
150 | ) -> list[Item]:
151 | """Items and groups and return a list of these so we can get values from the dpg widgets.
152 |
153 | :param Group section: section with a name to display and items
154 | :return list[Item]: flattened list of items
155 | """
156 | dpg.add_text(
157 | f"=== {helpers.stringTitlecase(section.name, ' ')} ===",
158 | color=hex_to_rgb(self.base24Theme[14]),
159 | )
160 |
161 | items = []
162 |
163 | for item in section.arg_items:
164 | if item.type == ItemType.RadioGroup:
165 | rGroup = item.additional_properties["radio"]
166 | for rElement in rGroup:
167 | self.addWidgetFromItem(rElement)
168 | items.append(rElement)
169 | else:
170 | self.addWidgetFromItem(item)
171 | items.append(item)
172 | for group in section.groups:
173 | items.extend(self.addItemsAndGroups(group))
174 |
175 | return items
176 |
177 | def open_menu_item(self, sender: str, _app_data: None) -> None:
178 | """Open a menu item.
179 |
180 | :param _type_ sender: file to open
181 | :param _type_ _app_data: [unused]
182 | """
183 | f = Path(sender)
184 | if f.is_file():
185 | with dpg.window(label=f.name, width=825, height=400, horizontal_scrollbar=True):
186 | dpg.add_text(helpers.read_file(sender))
187 | else:
188 | with dpg.window(label="Error"):
189 | dpg.add_text(f"File {sender} Not Found :(")
190 |
191 | def main(
192 | self,
193 | buildSpec: FullBuildSpec,
194 | quit_callback: Callable[[], None],
195 | run_callback: Callable[[dict[str, Any]], None],
196 | ) -> None:
197 | """Run the gui (dpg) with a given buildSpec, quit_callback, and run_callback.
198 |
199 | - Theming + Configure dpg
200 | - Menu Prep
201 | - Create Window, set up Menu and Widgets
202 | - Then, start dpg
203 |
204 | :param FullBuildSpec buildSpec: Full cli parse/ build spec
205 | :param Callable[[], None] quit_callback: generic callable used to quit
206 | :param Callable[[dict[str, Any]], None] run_callback: generic callable used to run
207 | """
208 |
209 | ################
210 | # Theming + Configure dpg
211 | ################
212 |
213 | dpg.create_context()
214 | with dpg.font_registry():
215 | default_font = dpg.add_font(f"{THISDIR}/FiraCode-Regular.ttf", 17)
216 |
217 | with dpg.theme() as global_theme, dpg.theme_component(dpg.mvAll):
218 | dpg.add_theme_color(
219 | dpg.mvThemeCol_WindowBg,
220 | hex_to_rgb(self.base24Theme[16]),
221 | category=dpg.mvThemeCat_Core,
222 | )
223 | dpg.add_theme_color(
224 | dpg.mvThemeCol_FrameBg,
225 | hex_to_rgb(self.base24Theme[17]),
226 | category=dpg.mvThemeCat_Core,
227 | )
228 | dpg.add_theme_color(
229 | dpg.mvThemeCol_PopupBg,
230 | hex_to_rgb(self.base24Theme[17]),
231 | category=dpg.mvThemeCat_Core,
232 | )
233 | dpg.add_theme_color(
234 | dpg.mvThemeCol_FrameBgActive,
235 | hex_to_rgb(self.base24Theme[17]),
236 | category=dpg.mvThemeCat_Core,
237 | )
238 | dpg.add_theme_color(
239 | dpg.mvThemeCol_FrameBgHovered,
240 | hex_to_rgb(self.base24Theme[17]),
241 | category=dpg.mvThemeCat_Core,
242 | )
243 | dpg.add_theme_color(
244 | dpg.mvThemeCol_Text,
245 | hex_to_rgb(self.base24Theme[6]),
246 | category=dpg.mvThemeCat_Core,
247 | )
248 | dpg.add_theme_color(
249 | dpg.mvThemeCol_MenuBarBg,
250 | hex_to_rgb(self.base24Theme[0]),
251 | category=dpg.mvThemeCat_Core,
252 | )
253 | dpg.add_theme_color(
254 | dpg.mvThemeCol_ScrollbarBg,
255 | hex_to_rgb(self.base24Theme[2]),
256 | category=dpg.mvThemeCat_Core,
257 | )
258 | dpg.add_theme_color(
259 | dpg.mvThemeCol_ScrollbarGrab,
260 | hex_to_rgb(self.base24Theme[17]),
261 | category=dpg.mvThemeCat_Core,
262 | )
263 | dpg.add_theme_color(
264 | dpg.mvThemeCol_Header,
265 | hex_to_rgb(self.base24Theme[0]),
266 | category=dpg.mvThemeCat_Core,
267 | )
268 | dpg.add_theme_color(
269 | dpg.mvThemeCol_HeaderHovered,
270 | hex_to_rgb(self.base24Theme[1]),
271 | category=dpg.mvThemeCat_Core,
272 | )
273 | dpg.add_theme_color(
274 | dpg.mvThemeCol_HeaderActive,
275 | hex_to_rgb(self.base24Theme[1]),
276 | category=dpg.mvThemeCat_Core,
277 | )
278 | dpg.add_theme_color(
279 | dpg.mvThemeCol_ScrollbarGrabActive,
280 | hex_to_rgb(self.base24Theme[1]),
281 | category=dpg.mvThemeCat_Core,
282 | )
283 | dpg.add_theme_color(
284 | dpg.mvThemeCol_ScrollbarGrabHovered,
285 | hex_to_rgb(self.base24Theme[1]),
286 | category=dpg.mvThemeCat_Core,
287 | )
288 | dpg.add_theme_color(
289 | dpg.mvThemeCol_Button,
290 | hex_to_rgb(self.base24Theme[1]),
291 | category=dpg.mvThemeCat_Core,
292 | )
293 | dpg.add_theme_color(
294 | dpg.mvThemeCol_ButtonHovered,
295 | hex_to_rgb(self.base24Theme[2]),
296 | category=dpg.mvThemeCat_Core,
297 | )
298 | dpg.add_theme_color(
299 | dpg.mvThemeCol_ButtonActive,
300 | hex_to_rgb(self.base24Theme[2]),
301 | category=dpg.mvThemeCat_Core,
302 | )
303 | dpg.add_theme_color(
304 | dpg.mvThemeCol_Border,
305 | hex_to_rgb(self.base24Theme[2]),
306 | category=dpg.mvThemeCat_Core,
307 | )
308 | dpg.add_theme_color(
309 | dpg.mvThemeCol_BorderShadow,
310 | hex_to_rgb(self.base24Theme[2]),
311 | category=dpg.mvThemeCat_Core,
312 | )
313 | dpg.add_theme_color(
314 | dpg.mvThemeCol_CheckMark,
315 | hex_to_rgb(self.base24Theme[14]),
316 | category=dpg.mvThemeCat_Core,
317 | )
318 | dpg.add_theme_color(
319 | dpg.mvThemeCol_TitleBg,
320 | hex_to_rgb(self.base24Theme[1]),
321 | category=dpg.mvThemeCat_Core,
322 | )
323 | dpg.add_theme_color(
324 | dpg.mvThemeCol_TitleBgActive,
325 | hex_to_rgb(self.base24Theme[14]),
326 | category=dpg.mvThemeCat_Core,
327 | )
328 | dpg.add_theme_style(dpg.mvStyleVar_FrameRounding, 5, category=dpg.mvThemeCat_Core)
329 | dpg.add_theme_style(dpg.mvStyleVar_FrameBorderSize, 1, category=dpg.mvThemeCat_Core)
330 |
331 | dpg.bind_theme(global_theme)
332 | dpg.bind_font(default_font)
333 |
334 | ################
335 | # Menu Prep
336 | ################
337 |
338 | if len(buildSpec.menu) == 0:
339 | buildSpec.menu = {}
340 |
341 | elif isinstance(buildSpec.menu, str):
342 | buildSpec.menu = {"File": buildSpec.menu}
343 |
344 | ################
345 | # Create Window, set up Menu and Widgets
346 | ################
347 |
348 | # Define "Run" and "Exit" buttons
349 | def _run_callback() -> None:
350 | _items: list[Item] = [item for item in items if item.dest]
351 | myd = {}
352 | for item in _items:
353 | value = dpg.get_value(item.dest)
354 | key = f"{item.dest}{SEP}{item.type}"
355 | if item.type in [ItemType.File, ItemType.FileWrite]:
356 | prop = item.additional_properties
357 | key += f";{prop.get('file_mode')};{prop.get('file_encoding')}"
358 |
359 | myd[key] = value
360 |
361 | run_callback(myd)
362 |
363 | def close_dpg() -> None:
364 | dpg.destroy_context()
365 | quit_callback()
366 |
367 | dpg.create_viewport(
368 | title=buildSpec.program_name,
369 | width=875,
370 | height=min(max(400, 120 * buildSpec.max_args_shown), 1080),
371 | )
372 |
373 | dpg.set_exit_callback(close_dpg)
374 |
375 | with dpg.window(label="", tag="primary", on_close=close_dpg):
376 | if len(buildSpec.menu) > 0:
377 | with dpg.menu_bar(), dpg.menu(label="Open"):
378 | for menu_item in buildSpec.menu:
379 | dpg.add_menu_item(
380 | label=menu_item,
381 | tag=buildSpec.menu[menu_item],
382 | callback=self.open_menu_item,
383 | )
384 | # Program Description
385 | dpg.add_text(
386 | helpers.stringSentencecase(
387 | buildSpec.program_description or buildSpec.parser_description
388 | )
389 | )
390 |
391 | # Add widgets
392 | items = []
393 | for widget in buildSpec.widgets:
394 | items.extend(self.addItemsAndGroups(widget))
395 |
396 | dpg.add_button(label="Run", callback=_run_callback)
397 | dpg.add_button(label="Exit", callback=close_dpg)
398 |
399 | ################
400 | # Start dpg
401 | ################
402 |
403 | dpg.setup_dearpygui()
404 | dpg.show_viewport()
405 | dpg.set_primary_window(window="primary", value=True)
406 | dpg.start_dearpygui()
407 | dpg.destroy_context()
408 |
--------------------------------------------------------------------------------
/cli2gui/gui/helpers.py:
--------------------------------------------------------------------------------
1 | """Basic helper package containing commonly used functions, such as isDarkMode,
2 | get_base24_theme etc."""
3 |
4 | from __future__ import annotations
5 |
6 | from pathlib import Path
7 |
8 | try:
9 | from getostheme import isDarkMode
10 | except ImportError:
11 |
12 | def isDarkMode() -> bool:
13 | """Monkeypatch for getostheme.isDarkMode."""
14 | return True
15 |
16 |
17 | import yaml
18 |
19 |
20 | def _themeFromFile(themeFile: str) -> list[str]:
21 | """Set the base24 theme from a base24 scheme.yaml to the application.
22 |
23 | Args:
24 | ----
25 | themeFile (str): path to file
26 |
27 | Returns:
28 | -------
29 | list[str]: theme to set
30 |
31 | """
32 | schemeDictTheme = yaml.safe_load(Path(themeFile).read_text(encoding="utf-8"))
33 | return ["#" + schemeDictTheme["palette"][f"base{x:02X}"] for x in range(24)]
34 |
35 |
36 | def get_base24_theme(
37 | theme: str | list[str],
38 | darkTheme: str | list[str],
39 | ) -> list[str]:
40 | """Set the base24 theme to the application.
41 |
42 | Args:
43 | ----
44 | theme (Union[str, list[str]]): the light theme
45 | darkTheme (Union[str, list[str]]): the dark theme
46 |
47 | """
48 | # Light theme
49 | theme = theme or [
50 | "#e7e7e9",
51 | "#dfdfe1",
52 | "#cacace",
53 | "#a0a1a7",
54 | "#696c77",
55 | "#383a42",
56 | "#202227",
57 | "#090a0b",
58 | "#ca1243",
59 | "#c18401",
60 | "#febb2a",
61 | "#50a14f",
62 | "#0184bc",
63 | "#4078f2",
64 | "#a626a4",
65 | "#986801",
66 | "#f0f0f1",
67 | "#fafafa",
68 | "#ec2258",
69 | "#f4a701",
70 | "#6db76c",
71 | "#01a7ef",
72 | "#709af5",
73 | "#d02fcd",
74 | ]
75 | if isinstance(theme, str):
76 | theme = _themeFromFile(theme)
77 |
78 | # Dark theme
79 | darkTheme = darkTheme or [
80 | "#282c34",
81 | "#3f4451",
82 | "#4f5666",
83 | "#545862",
84 | "#9196a1",
85 | "#abb2bf",
86 | "#e6e6e6",
87 | "#ffffff",
88 | "#e06c75",
89 | "#d19a66",
90 | "#e5c07b",
91 | "#98c379",
92 | "#56b6c2",
93 | "#61afef",
94 | "#c678dd",
95 | "#be5046",
96 | "#21252b",
97 | "#181a1f",
98 | "#ff7b86",
99 | "#efb074",
100 | "#b1e18b",
101 | "#63d4e0",
102 | "#67cdff",
103 | "#e48bff",
104 | ]
105 | if isinstance(darkTheme, str):
106 | darkTheme = _themeFromFile(darkTheme)
107 |
108 | return darkTheme if isDarkMode() else theme
109 |
110 |
111 | def stringTitlecase(string: str, splitStr: str = "ALL") -> str:
112 | """Convert a string to title case."""
113 |
114 | titleCase = ""
115 | if len(string) > 0:
116 | if splitStr == "ALL":
117 | titleCase = " ".join(
118 | (part[0].upper() + part[1:]) for part in string.replace("-", "_").split("_")
119 | )
120 | else:
121 | titleCase = " ".join((part[0].upper() + part[1:]) for part in string.split(splitStr))
122 | return titleCase
123 |
124 |
125 | def stringSentencecase(string: str) -> str:
126 | """Convert a string to sentence case."""
127 |
128 | if string:
129 | return string[0].upper() + string[1:]
130 | return ""
131 |
132 |
133 | def read_file(file_path: str, maxLines: int = 200) -> str:
134 | """Get the contents of a file path, attempt to parse with catpandoc..pandoc2plain.
135 |
136 | :param str file_path: path to the file (absolute recommended)
137 | :return str: file contents
138 | """
139 | try:
140 | from catpandoc.application import pandoc2plain
141 |
142 | lines = pandoc2plain(file_path, 80).split("\n")
143 | except ImportError:
144 | lines = Path(file_path).read_text(encoding="utf-8").split("\n")
145 |
146 | if len(lines) > maxLines:
147 | popupText = "\n".join(lines[:maxLines]) + "\n\nMORE TEXT IN SRC FILE"
148 | else:
149 | popupText = "\n".join(lines)
150 |
151 | return popupText
152 |
--------------------------------------------------------------------------------
/cli2gui/gui/pysimplegui_wrapper.py:
--------------------------------------------------------------------------------
1 | """Wrapper class for PySimpleGUI."""
2 |
3 | from __future__ import annotations
4 |
5 | import io
6 | import logging
7 | from typing import Any, Callable
8 |
9 | from PIL import Image, ImageTk
10 |
11 | from cli2gui.gui import helpers
12 | from cli2gui.gui.abstract_gui import AbstractGUI
13 | from cli2gui.models import SEP, FullBuildSpec, Group, Item, ItemType
14 |
15 |
16 | class PySimpleGUIWrapper(AbstractGUI):
17 | """Wrapper class for PySimpleGUI."""
18 |
19 | def __init__(self, base24Theme: list[str], psg_lib: str) -> None:
20 | """PySimpleGUI wrapper class.
21 |
22 | :param list[str] base24Theme: list representing a base24 theme. Containing 24 elements
23 | (of hex strings like "#e7e7e9")
24 | :param str psg_lib: string representing the pysimplegui lib to use
25 | """
26 | super().__init__()
27 |
28 | if psg_lib == "psg":
29 | import PySimpleGUI as gui_lib
30 | elif psg_lib == "psgqt":
31 | import PySimpleGUIQt as gui_lib
32 | elif psg_lib == "psgweb":
33 | import PySimpleGUIWeb as gui_lib
34 | elif psg_lib == "fsgqt":
35 | import FreeSimpleGUIQt as gui_lib
36 | elif psg_lib == "fsgweb":
37 | import FreeSimpleGUIWeb as gui_lib
38 | else:
39 | import FreeSimpleGUI as gui_lib
40 |
41 | self.sg = gui_lib
42 | self.psg_lib = psg_lib
43 | self.sizes = {
44 | "title_size": 18,
45 | "label_size": (30, None),
46 | "input_size": (30, 1),
47 | "button": (10, 1),
48 | "padding": (5, 10),
49 | "help_text_size": 14,
50 | "text_size": 11,
51 | }
52 |
53 | if psg_lib not in ["psg", "fsg"]:
54 | self.sizes = {
55 | "title_size": 18,
56 | "label_size": (600, None),
57 | "input_size": (30, 1),
58 | "button": (10, 1),
59 | "padding": (5, 10),
60 | "help_text_size": 14,
61 | "text_size": 11,
62 | }
63 | accent = {"red": 8, "blue": 13, "green": 11, "purple": 14}
64 | self.sg.LOOK_AND_FEEL_TABLE["theme"] = {
65 | "BACKGROUND": base24Theme[16],
66 | "TEXT": base24Theme[6],
67 | "INPUT": base24Theme[17],
68 | "TEXT_INPUT": base24Theme[6],
69 | "SCROLL": base24Theme[17],
70 | "BUTTON": (base24Theme[6], base24Theme[0]),
71 | "PROGRESS": (base24Theme[accent["purple"]], base24Theme[0]),
72 | "BORDER": 0,
73 | "SLIDER_DEPTH": 0,
74 | "PROGRESS_DEPTH": 0,
75 | }
76 | self.sg.theme("theme")
77 |
78 | def _inputText(self, key: str, default: str | None = None) -> Any:
79 | """Return an input text field."""
80 | return self.sg.InputText(
81 | default or "",
82 | size=self.sizes["input_size"],
83 | pad=self.sizes["padding"],
84 | key=key,
85 | font=("sans", self.sizes["text_size"]),
86 | )
87 |
88 | def _spin(self, key: str, default: str | None = None) -> Any:
89 | """Return an input text field."""
90 | return self.sg.Spin(
91 | list(range(-50, 51)),
92 | initial_value=default or 0,
93 | size=self.sizes["input_size"],
94 | pad=self.sizes["padding"],
95 | key=key,
96 | font=("sans", self.sizes["text_size"]),
97 | )
98 |
99 | def _check(self, key: str, default: str | None = None) -> Any:
100 | """Return a checkbox."""
101 | return self.sg.Check(
102 | "",
103 | size=self.sizes["input_size"],
104 | pad=self.sizes["padding"],
105 | key=key,
106 | default=bool(default or ""),
107 | )
108 |
109 | def _button(self, text: str) -> Any:
110 | """Return a button."""
111 | return self.sg.Button(
112 | text,
113 | size=self.sizes["button"],
114 | pad=self.sizes["padding"],
115 | font=("sans", self.sizes["text_size"]),
116 | )
117 |
118 | def _label(self, text: str, font: int = 11) -> Any:
119 | """Return a label."""
120 | return self.sg.Text(
121 | text,
122 | size=(
123 | int(self.sizes["label_size"][0] * 11 / font),
124 | self.sizes["label_size"][1],
125 | ),
126 | pad=self.sizes["padding"],
127 | font=("sans", font),
128 | )
129 |
130 | def _dropdown(self, key: str, argItems: list[str]) -> Any:
131 | """Return a dropdown."""
132 | return self.sg.Drop(
133 | tuple(argItems),
134 | size=self.sizes["input_size"],
135 | pad=self.sizes["padding"],
136 | key=key,
137 | )
138 |
139 | def _fileBrowser(
140 | self,
141 | key: str,
142 | default: str | None = None,
143 | _type: ItemType = ItemType.File,
144 | additional_properties: dict | None = None,
145 | ) -> list[Any]:
146 | """Return a fileBrowser button and field."""
147 | prop = additional_properties or {}
148 |
149 | height = self.sizes["input_size"][1]
150 | width = self.sizes["input_size"][0]
151 |
152 | key = f"{key}{SEP}{_type}"
153 | if _type in [ItemType.FileWrite, ItemType.File]:
154 | key += f";{prop.get('file_mode')};{prop.get('file_encoding')}"
155 |
156 | browser = self.sg.FileBrowse(
157 | key="@@" + key,
158 | size=(int(width / 3), height),
159 | pad=(0, self.sizes["padding"][1]),
160 | )
161 |
162 | if _type in [ItemType.FileWrite, ItemType.Path]:
163 | browser = self.sg.SaveAs(
164 | button_text="Select/Create",
165 | key="@@" + key,
166 | size=(int(width / 3), height),
167 | pad=(0, self.sizes["padding"][1]),
168 | )
169 | fb: list[Any] = [
170 | self.sg.InputText(
171 | default or "",
172 | size=(width - int(width / 3), height),
173 | pad=(0, self.sizes["padding"][1]),
174 | key=key,
175 | font=("sans", self.sizes["text_size"]),
176 | ),
177 | browser,
178 | ]
179 | return fb
180 |
181 | """Different sized labels
182 | """
183 |
184 | def _helpArgName(self, displayName: str, commands: list[str]) -> Any:
185 | """Return a label for the arg name."""
186 | return self._label("- " + helpers.stringTitlecase(displayName) + ": " + str(commands), 14)
187 |
188 | def _helpArgHelp(self, helpText: str) -> Any:
189 | """Return a label for the arg help text."""
190 | return self._label(helpers.stringSentencecase(helpText))
191 |
192 | def _helpArgNameAndHelp(self, commands: list[str], helpText: str, displayName: str) -> Any:
193 | """Return a column containing the argument name and help text."""
194 | return self.sg.Column(
195 | [[self._helpArgName(displayName, commands)], [self._helpArgHelp(helpText)]],
196 | pad=(0, 0),
197 | )
198 |
199 | def _title(self, text: str, image: str = "") -> list[Any]:
200 | """Return a set of self that make up the application header."""
201 | programTitle: list[Any] = [
202 | self.sg.Text(text, pad=self.sizes["padding"], font=("sans", self.sizes["title_size"]))
203 | ]
204 | if image:
205 | programTitle = [
206 | self.sg.Image(data=self.getImgData(image, first=True)),
207 | self.sg.Text(
208 | text,
209 | pad=self.sizes["padding"],
210 | font=("sans", self.sizes["title_size"]),
211 | ),
212 | ]
213 | return programTitle
214 |
215 | """Generate help widget group
216 | """
217 |
218 | def _helpFlagWidget(
219 | self,
220 | item: Item,
221 | ) -> list[Any]:
222 | """Return a set of self that make up an arg with true/ false."""
223 | return [
224 | self._helpArgNameAndHelp(item.commands, item.help, item.display_name),
225 | self.sg.Column(
226 | [[self._check(f"{item.dest}{SEP}{item.type}", default=item.default)]], pad=(0, 0)
227 | ),
228 | ]
229 |
230 | def _helpTextWidget(
231 | self,
232 | item: Item,
233 | ) -> list[Any]:
234 | """Return a set of self that make up an arg with text."""
235 | return [
236 | self._helpArgNameAndHelp(item.commands, item.help, item.display_name),
237 | self.sg.Column(
238 | [[self._inputText(f"{item.dest}{SEP}{item.type}", default=item.default)]],
239 | pad=(0, 0),
240 | ),
241 | ]
242 |
243 | def _helpCounterWidget(
244 | self,
245 | item: Item,
246 | ) -> list[Any]:
247 | """Return a set of self that make up an arg with text."""
248 | return [
249 | self._helpArgNameAndHelp(item.commands, item.help, item.display_name),
250 | self.sg.Column(
251 | [[self._spin(f"{item.dest}{SEP}{item.type}", default=item.default)]], pad=(0, 0)
252 | ),
253 | ]
254 |
255 | def _helpFileWidget(
256 | self,
257 | item: Item,
258 | ) -> list[Any]:
259 | """Return a set of self that make up an arg with a file."""
260 | return [
261 | self._helpArgNameAndHelp(item.commands, item.help, item.display_name),
262 | self.sg.Column(
263 | [self._fileBrowser(item.dest, item.default, item.type, item.additional_properties)],
264 | pad=(0, 0),
265 | ),
266 | ]
267 |
268 | def _helpDropdownWidget(
269 | self,
270 | item: Item,
271 | ) -> list[Any]:
272 | """Return a set of self that make up an arg with a choice."""
273 | return [
274 | self._helpArgNameAndHelp(item.commands, item.help, item.display_name),
275 | self.sg.Column(
276 | [
277 | [
278 | self._dropdown(
279 | f"{item.dest}{SEP}{item.type}", item.additional_properties["choices"]
280 | )
281 | ]
282 | ],
283 | pad=(0, 0),
284 | ),
285 | ]
286 |
287 | def addWidgetFromItem(self, item: Item) -> list[Any]:
288 | """Select a widget based on the item type.
289 |
290 | :param Item item: the item
291 | """
292 | functionMap = {
293 | ItemType.Bool: self._helpFlagWidget,
294 | ItemType.File: self._helpFileWidget,
295 | ItemType.FileWrite: self._helpFileWidget,
296 | ItemType.Path: self._helpFileWidget,
297 | ItemType.Choice: self._helpDropdownWidget,
298 | ItemType.Int: self._helpCounterWidget,
299 | ItemType.Text: self._helpTextWidget,
300 | ItemType.Float: self._helpTextWidget,
301 | ItemType.List: self._helpTextWidget,
302 | ItemType.Tuple: self._helpTextWidget,
303 | ItemType.DateTime: self._helpTextWidget,
304 | }
305 | if item.type in functionMap:
306 | return functionMap[item.type](
307 | item,
308 | )
309 | return []
310 |
311 | def generatePopup(
312 | self,
313 | buildSpec: FullBuildSpec,
314 | values: dict[Any, Any] | list[Any],
315 | ) -> Any:
316 | """Create the popup window.
317 |
318 | Args:
319 | ----
320 | buildSpec (FullBuildSpec): [description]
321 | values (Union[dict[Any, Any]): Returned when a button is clicked. Such
322 | as the menu
323 |
324 | Returns:
325 | -------
326 | Window: A PySimpleGui Window
327 |
328 | """
329 | maxLines = 30 if self.psg_lib == "psgqt" else 200
330 | popupText = helpers.read_file(buildSpec.menu[values[0]], maxLines)
331 |
332 | if self.psg_lib in ["psg", "fsg"]:
333 | popupLayout = [
334 | self._title(values[0]),
335 | [
336 | self.sg.Column(
337 | [
338 | [
339 | self.sg.Text(
340 | text=popupText,
341 | size=(850, maxLines + 10),
342 | font=("Courier", self.sizes["text_size"]),
343 | )
344 | ]
345 | ],
346 | size=(850, 400),
347 | pad=(0, 0),
348 | scrollable=True,
349 | vertical_scroll_only=True,
350 | )
351 | ],
352 | ]
353 | else:
354 | popupLayout = [
355 | self._title(values[0]),
356 | [
357 | self.sg.Text(
358 | text=popupText,
359 | size=(850, (self.sizes["text_size"]) * (2 * maxLines + 10)),
360 | font=("Courier", self.sizes["text_size"]),
361 | )
362 | ],
363 | ]
364 | return self.sg.Window(
365 | values[0],
366 | popupLayout,
367 | alpha_channel=0.95,
368 | icon=self.getImgData(buildSpec.image, first=True) if buildSpec.image else None,
369 | )
370 |
371 | def addItemsAndGroups(
372 | self,
373 | section: Group,
374 | ) -> list[list[Any]]:
375 | """Items and groups and return a list of psg Elements.
376 |
377 | :param Group section: section with a name to display and items
378 | :return list[list[Element]]: updated argConstruct
379 |
380 | """
381 |
382 | argConstruct: list[list[Any]] = []
383 |
384 | argConstruct.append([self._label(helpers.stringTitlecase(section.name, " "), 14)])
385 | for item in section.arg_items:
386 | if item.type == ItemType.RadioGroup:
387 | rGroup = item.additional_properties["radio"]
388 | for rElement in rGroup:
389 | argConstruct.append(self.addWidgetFromItem(rElement))
390 | else:
391 | argConstruct.append(self.addWidgetFromItem(item))
392 | for group in section.groups:
393 | argConstruct.extend(self.addItemsAndGroups(group))
394 | return argConstruct
395 |
396 | def createLayout(
397 | self,
398 | buildSpec: FullBuildSpec,
399 | menu: str | list[str],
400 | ) -> list[list[Any]]:
401 | """Create the pysimplegui layout from the build spec.
402 |
403 | :param FullBuildSpec buildSpec: build spec containing widget
404 | :param str | list[str] menu: menu definition. containing menu keys
405 |
406 | :return list[list[Any]]: list of self (layout list)
407 |
408 | """
409 | argConstruct = []
410 | for widget in buildSpec.widgets:
411 | argConstruct.extend(self.addItemsAndGroups(widget))
412 |
413 | # Set the layout
414 | layout: list[list[Any]] = [[]]
415 | if isinstance(menu, list):
416 | layout: list[list[Any]] = [[self.sg.Menu([["Open", menu]], tearoff=True)]]
417 |
418 | layout.extend(
419 | [
420 | self._title(str(buildSpec.program_name), buildSpec.image),
421 | [
422 | self._label(
423 | helpers.stringSentencecase(
424 | buildSpec.program_description
425 | if buildSpec.program_description
426 | else buildSpec.parser_description
427 | )
428 | )
429 | ],
430 | ]
431 | )
432 | if len(argConstruct) > buildSpec.max_args_shown and self.psg_lib in (
433 | "psg",
434 | "fsg",
435 | ):
436 | layout.append(
437 | [
438 | self.sg.Column(
439 | argConstruct,
440 | size=(
441 | 850,
442 | min(
443 | max(
444 | 280,
445 | buildSpec.max_args_shown
446 | * 3.5
447 | * (self.sizes["help_text_size"] + self.sizes["text_size"]),
448 | ),
449 | 700,
450 | ),
451 | ),
452 | pad=(0, 0),
453 | scrollable=True,
454 | vertical_scroll_only=True,
455 | )
456 | ]
457 | )
458 | else:
459 | layout.extend(argConstruct)
460 | layout.append([self._button("Run"), self._button("Exit")])
461 | return layout
462 |
463 | def main(
464 | self,
465 | buildSpec: FullBuildSpec,
466 | quit_callback: Callable[[], None],
467 | run_callback: Callable[[dict[str, Any]], None],
468 | ) -> None:
469 | """Run the gui (psg) with a given buildSpec, quit_callback, and run_callback.
470 |
471 | :param FullBuildSpec buildSpec: Full cli parse/ build spec
472 | :param Callable[[], None] quit_callback: generic callable used to quit
473 | :param Callable[[dict[str, Any]], None] run_callback: generic callable used to run
474 | """
475 | menu = list(buildSpec.menu) if buildSpec.menu else ""
476 |
477 | layout = self.createLayout(buildSpec=buildSpec, menu=menu)
478 |
479 | # Build window from args
480 | window = self.sg.Window(
481 | buildSpec.program_name,
482 | layout,
483 | alpha_channel=0.95,
484 | icon=self.getImgData(buildSpec.image, first=True) if buildSpec.image else None,
485 | )
486 |
487 | # While the application is running
488 | while True:
489 | eventAndValues: tuple[Any, dict[Any, Any] | list[Any]] = window.read()
490 | event, values = eventAndValues
491 | if event in (None, "Exit"):
492 | quit_callback()
493 | try:
494 | # Create and open the popup window for the menu item
495 | if values is not None:
496 | if 0 in values and values[0] is not None:
497 | popup = self.generatePopup(buildSpec, values)
498 | popup.read()
499 | args = {}
500 | for key in values:
501 | if key != 0:
502 | args[key] = values[key]
503 | run_callback(args)
504 |
505 | except Exception:
506 | logging.exception("Something went wrong: ")
507 |
508 | def getImgData(self, imagePath: str, *, first: bool = False) -> bytes:
509 | """Generate image data using PIL."""
510 | img = Image.open(imagePath)
511 | img.thumbnail((self.sizes["title_size"] * 3, self.sizes["title_size"] * 3))
512 | if first: # tkinter is inactive the first time
513 | bio = io.BytesIO()
514 | img.save(bio, format="PNG")
515 | del img
516 | return bio.getvalue()
517 | return ImageTk.PhotoImage(img) # type:ignore[type-error]
518 |
--------------------------------------------------------------------------------
/cli2gui/models.py:
--------------------------------------------------------------------------------
1 | """Types for cli2gui."""
2 |
3 | from __future__ import annotations
4 |
5 | from collections.abc import Callable
6 | from dataclasses import dataclass
7 | from enum import Enum
8 | from typing import Any
9 |
10 | SEP = "#%#"
11 |
12 |
13 | @dataclass
14 | class BuildSpec:
15 | """Representation for the BuildSpec."""
16 |
17 | run_function: Callable[..., Any]
18 | parser: str | ParserType
19 | gui: str | GUIType
20 | theme: str | list[str]
21 | darkTheme: str | list[str]
22 | image: str
23 | program_name: str
24 | program_description: str
25 | max_args_shown: int
26 | menu: str | dict[str, Any]
27 |
28 |
29 | @dataclass
30 | class Item:
31 | """Representation for an arg_item."""
32 |
33 | type: ItemType
34 | display_name: str
35 | commands: list[str]
36 | help: str
37 | dest: str
38 | default: Any
39 | required: bool = False
40 | choices: list[Any] = None
41 | nargs: str = None
42 | additional_properties: dict[str, Any] = None
43 |
44 |
45 | class ItemType(Enum):
46 | """Enum of ItemTypes."""
47 |
48 | RadioGroup = "RadioGroup"
49 | Bool = "Bool"
50 | File = "File"
51 | FileWrite = "FileWrite"
52 | Path = "Path"
53 | Choice = "Choice"
54 | Int = "Int"
55 | Text = "Text"
56 | Float = "Float"
57 | List = "List"
58 | Tuple = "Tuple"
59 | DateTime = "DateTime"
60 |
61 |
62 | @dataclass
63 | class Group:
64 | """Representation for an argument group."""
65 |
66 | name: str
67 | arg_items: list[Item]
68 | groups: list[Group] | list[Any]
69 |
70 |
71 | @dataclass
72 | class ParserRep:
73 | """Representation for a parser."""
74 |
75 | parser_description: str
76 | widgets: list[Group]
77 |
78 |
79 | @dataclass
80 | class FullBuildSpec:
81 | """Representation for the FullBuildSpec (BuildSpec + ParserRep)."""
82 |
83 | run_function: Callable[..., Any]
84 | parser: str
85 | gui: str
86 | theme: str | list[str]
87 | darkTheme: str | list[str]
88 | image: str
89 | program_name: str
90 | program_description: str
91 | max_args_shown: int
92 | menu: str | dict[str, Any]
93 | parser_description: str
94 | widgets: list[Group]
95 |
96 |
97 | # Supported parser types
98 | class ParserType(str, Enum):
99 | """Supported parser types.
100 |
101 | """
102 |
103 | OPTPARSE = "optparse"
104 | ARGPARSE = "argparse"
105 | DEPHELL_ARGPARSE = "dephell_argparse"
106 | DOCOPT = "docopt"
107 | GETOPT = "getopt"
108 | CLICK = "click"
109 | CUSTOM = "input()" # this seems like a pretty poor pattern to use
110 |
111 |
112 | # Supported gui types
113 | class GUIType(str, Enum):
114 | """Supported gui types.
115 |
116 | """
117 |
118 | PSG = "pysimplegui"
119 | WEB = "pysimpleguiweb"
120 | QT = "pysimpleguiqt"
121 | FSG = "freesimplegui"
122 | FSGWEB = "freesimpleguiweb"
123 | FSGQT = "freesimpleguiqt"
124 | DPG = "dearpygui"
125 |
--------------------------------------------------------------------------------
/cli2gui/tojson/__init__.py:
--------------------------------------------------------------------------------
1 | """Package containing transforms for each supported parser/cli library, responsible for
2 | taking each parser representation (and BuildSpec) and generating a FullBuildSpec."""
3 |
--------------------------------------------------------------------------------
/cli2gui/tojson/argparse2json.py:
--------------------------------------------------------------------------------
1 | """Generate a dict describing argparse arguments.
2 | pylint and pylance both want me to not access protected methods - I know better ;).
3 | """
4 | # ruff: noqa: SLF001
5 |
6 | from __future__ import annotations
7 |
8 | import argparse
9 | from argparse import (
10 | Action,
11 | _CountAction,
12 | _HelpAction,
13 | _MutuallyExclusiveGroup,
14 | _StoreFalseAction,
15 | _StoreTrueAction,
16 | _SubParsersAction,
17 | )
18 | from os import path
19 | from pathlib import Path
20 | from sys import argv
21 | from typing import Any, Generator, TypedDict
22 |
23 | from cli2gui.models import Group, Item, ItemType, ParserRep
24 |
25 |
26 | class ArgparseGroup(TypedDict):
27 | """Class to represent an ArgparseGroup."""
28 |
29 | name: str
30 | arg_items: list[argparse.Action]
31 | groups: list[ArgparseGroup] | list[Any]
32 |
33 |
34 | def iterParsers(
35 | parser: argparse.ArgumentParser,
36 | ) -> list[tuple[str, argparse.ArgumentParser]]:
37 | """Iterate over name, parser pairs."""
38 | defaultParser = [
39 | ("::cli2gui/default", parser),
40 | ]
41 | candidateSubparsers = [
42 | action for action in parser._actions if isinstance(action, _SubParsersAction)
43 | ]
44 | if len(candidateSubparsers) == 0:
45 | return defaultParser
46 |
47 | return defaultParser + list(candidateSubparsers[0].choices.items())
48 |
49 |
50 | def isDefaultProgname(name: str, subparser: argparse.ArgumentParser) -> bool:
51 | """Identify if the passed name is the default program name."""
52 | return subparser.prog == f"{path.split(argv[0])[-1]} {name}"
53 |
54 |
55 | def chooseName(name: str, subparser: argparse.ArgumentParser) -> str:
56 | """Get the program name."""
57 | return name if isDefaultProgname(name, subparser) else subparser.prog
58 |
59 |
60 | def containsActions(
61 | actionA: list[argparse.Action], actionB: list[argparse.Action]
62 | ) -> set[argparse.Action]:
63 | """Check if any actions(a) are present in actions(b)."""
64 | return set(actionA).intersection(set(actionB))
65 |
66 |
67 | def reapplyMutexGroups(
68 | mutexGroups: list[argparse._MutuallyExclusiveGroup],
69 | actionGroups: list[Any],
70 | ) -> list[Any]:
71 | """_argparse stores mutually exclusive groups independently.
72 | of all other groups. So, they must be manually re-combined
73 | with the groups/subgroups to which they were originally declared
74 | in order to have them appear in the correct location in the UI.
75 |
76 | Order is attempted to be preserved by inserting the MutexGroup
77 | into the _actions list at the first occurrence of any item
78 | where the two groups intersect.
79 | """
80 |
81 | def swapActions(actions: list[Action]) -> list[Action]:
82 | for mutexgroup in mutexGroups:
83 | mutexActions = mutexgroup._group_actions
84 | if containsActions(mutexActions, actions):
85 | # make a best guess as to where we should store the group
86 | targetindex = actions.index(mutexgroup._group_actions[0])
87 | # insert the _ArgumentGroup container
88 | actions[targetindex] = mutexgroup
89 | # remove the duplicated individual actions
90 | actions = [action for action in actions if action not in mutexActions]
91 | return actions
92 |
93 | return [
94 | group.update({"arg_items": swapActions(group["arg_items"])}) or group
95 | for group in actionGroups
96 | ]
97 |
98 |
99 | def extractRawGroups(actionGroup: argparse._ArgumentGroup) -> ArgparseGroup:
100 | """Recursively extract argument groups and associated actions from ParserGroup objects."""
101 | return {
102 | "name": str(actionGroup.title),
103 | # List of arg_items that are not help messages
104 | "arg_items": [
105 | action for action in actionGroup._group_actions if not isinstance(action, _HelpAction)
106 | ],
107 | "groups": [extractRawGroups(group) for group in actionGroup._action_groups],
108 | }
109 |
110 |
111 | def fileActionToJson(action: argparse.Action, widget: ItemType) -> Item:
112 | """Convert an action of type Path or argparse.FileType to an Item."""
113 | item = actionToJson(action=action, widget=widget)
114 | if isinstance(action.type, argparse.FileType):
115 | item.additional_properties = {
116 | **item.additional_properties,
117 | "file_mode": action.type._mode,
118 | "file_encoding": action.type._encoding,
119 | }
120 | return item
121 |
122 |
123 | def actionToJson(action: argparse.Action, widget: ItemType) -> Item:
124 | """Generate json for an action and set the widget - used by the application."""
125 | choices = [str(choice) for choice in action.choices] if action.choices else []
126 | return Item(
127 | type=widget,
128 | display_name=str(action.metavar or action.dest),
129 | help=str(action.help),
130 | commands=list(action.option_strings),
131 | dest=action.dest,
132 | default=action.default,
133 | additional_properties={"choices": choices, "nargs": action.nargs},
134 | )
135 |
136 |
137 | def buildRadioGroup(mutexGroup: _MutuallyExclusiveGroup) -> Item:
138 | """Create a radio group for a mutex group of arguments."""
139 | commands = [action.option_strings for action in mutexGroup._group_actions]
140 | return Item(
141 | display_name="",
142 | help="",
143 | dest="",
144 | default="",
145 | type=ItemType.RadioGroup,
146 | commands=commands,
147 | additional_properties={"radio": list(categorizeItems(mutexGroup._group_actions))},
148 | )
149 |
150 |
151 | def categorizeItems(
152 | actions: list[argparse.Action],
153 | ) -> Generator[Item, None, None]:
154 | """Catergorise each action and generate json."""
155 | for action in actions:
156 | if isinstance(action, _MutuallyExclusiveGroup):
157 | yield buildRadioGroup(action)
158 | elif isinstance(action, (_StoreTrueAction, _StoreFalseAction)):
159 | yield actionToJson(action, ItemType.Bool)
160 | elif isinstance(action, _CountAction):
161 | yield actionToJson(action, ItemType.Int)
162 | elif action.choices:
163 | yield actionToJson(action, ItemType.Choice)
164 |
165 | elif isinstance(action.type, argparse.FileType) and "w" in action.type._mode:
166 | yield fileActionToJson(action, ItemType.FileWrite)
167 |
168 | elif isinstance(action.type, argparse.FileType):
169 | yield fileActionToJson(action, ItemType.File)
170 | elif action.type is Path:
171 | yield actionToJson(action, ItemType.Path)
172 |
173 | elif action.type is int:
174 | yield actionToJson(action, ItemType.Int)
175 | elif action.type is float:
176 | yield actionToJson(action, ItemType.Float)
177 | else:
178 | yield actionToJson(action, ItemType.Text)
179 |
180 |
181 | def categorizeGroups(groups: list[ArgparseGroup]) -> list[Group]:
182 | """Categorize the parser groups and arg_items."""
183 | return [
184 | Group(
185 | name=group["name"],
186 | arg_items=list(categorizeItems(group["arg_items"])),
187 | groups=categorizeGroups(group["groups"]),
188 | )
189 | for group in groups
190 | ]
191 |
192 |
193 | def stripEmpty(groups: list[ArgparseGroup]) -> list[ArgparseGroup]:
194 | """Remove groups where group['arg_items'] is false."""
195 | return [group for group in groups if group["arg_items"]]
196 |
197 |
198 | def process(parser: argparse.ArgumentParser) -> list[Group]:
199 | """Reapply the mutex groups and then categorize them and the arg_items under the parser."""
200 | mutexGroups = parser._mutually_exclusive_groups
201 | rawActionGroups = [
202 | extractRawGroups(group) for group in parser._action_groups if group._group_actions
203 | ]
204 | correctedActionGroups = reapplyMutexGroups(mutexGroups, rawActionGroups)
205 | return categorizeGroups(stripEmpty(correctedActionGroups))
206 |
207 |
208 | def convert(parser: argparse.ArgumentParser) -> ParserRep:
209 | """Convert argparse to a dict.
210 |
211 | Args:
212 | ----
213 | parser (argparse.ArgumentParser): argparse parser
214 |
215 | Returns:
216 | -------
217 | ParserRep: dictionary representing parser object
218 |
219 | """
220 | widgets = []
221 | for _, subparser in iterParsers(parser):
222 | widgets.extend(process(subparser))
223 |
224 | return ParserRep(
225 | parser_description=f"{parser.prog}: {parser.description or ''}", widgets=widgets
226 | )
227 |
--------------------------------------------------------------------------------
/cli2gui/tojson/click2json.py:
--------------------------------------------------------------------------------
1 | """Generate a dict describing optparse arguments."""
2 |
3 | from __future__ import annotations
4 |
5 | import contextlib
6 | from typing import Any, Generator
7 |
8 | from cli2gui.models import Group, Item, ItemType, ParserRep
9 |
10 |
11 | def extract(parser: Any) -> list[Group]:
12 | """Get the actions as json for the parser."""
13 | try:
14 | argumentList = [
15 | Group(
16 | name="Positional Arguments",
17 | arg_items=list(categorize([parser.commands[key] for key in parser.commands])),
18 | groups=[],
19 | )
20 | ]
21 | except AttributeError:
22 | argumentList: list[Group] = []
23 | argumentList.append(
24 | Group(name="Optional Arguments", arg_items=list(categorize(parser.params)), groups=[])
25 | )
26 | return argumentList
27 |
28 |
29 | def actionToJson(action: Any, widget: ItemType, other: dict | None = None) -> Item:
30 | """Generate json for an action and set the widget - used by the application."""
31 | nargs = ""
32 | with contextlib.suppress(AttributeError):
33 | nargs = action.params[0].nargs if len(action.params) > 0 else "" or ""
34 |
35 | commands = action.opts + action.secondary_opts
36 | return Item(
37 | type=widget,
38 | display_name=action.name,
39 | help=action.help,
40 | commands=commands,
41 | dest=action.callback or commands[0],
42 | default=action.default,
43 | additional_properties={"nargs": nargs, **(other or {})},
44 | )
45 |
46 |
47 | def categorize(actions: list[Any]) -> Generator[Item, None, None]:
48 | """Catergorise each action and generate json."""
49 | import click
50 |
51 | for action in actions:
52 | if isinstance(action.type, click.Choice):
53 | yield actionToJson(action, ItemType.Choice, {"choices": action.type.choices})
54 | elif isinstance(action.type, click.types.IntParamType):
55 | yield actionToJson(action, ItemType.Int)
56 | elif isinstance(action.type, click.types.FloatParamType):
57 | yield actionToJson(action, ItemType.Float)
58 | elif isinstance(action.type, click.types.BoolParamType):
59 | yield actionToJson(action, ItemType.Bool)
60 | elif isinstance(action.type, click.types.Path):
61 | yield actionToJson(action, ItemType.Path)
62 | else:
63 | yield actionToJson(action, ItemType.Text)
64 |
65 |
66 | def convert(parser: Any) -> ParserRep:
67 | """Convert click to a dict.
68 |
69 | Args:
70 | ----
71 | parser (click.core.Command): click parser
72 |
73 | Returns:
74 | -------
75 | ParserRep: dictionary representing parser object
76 |
77 | """
78 | return ParserRep(parser_description="", widgets=extract(parser))
79 |
--------------------------------------------------------------------------------
/cli2gui/tojson/docopt2json.py:
--------------------------------------------------------------------------------
1 | """Generate a dict for docopt."""
2 |
3 | from __future__ import annotations
4 |
5 | import re
6 | from typing import Any, Iterator
7 |
8 | from cli2gui.models import Group, Item, ItemType, ParserRep
9 |
10 |
11 | def actionToJson(action: tuple[str, str, int, Any, str], widget: ItemType, *, isPos: bool) -> Item:
12 | """Generate json for an action and set the widget - used by the application."""
13 |
14 | if isPos or len(action) < 5:
15 | return Item(
16 | type=widget,
17 | display_name=action[0],
18 | help=action[1],
19 | commands=[action[0]],
20 | dest=action[0],
21 | default=None,
22 | additional_properties={"nargs": ""},
23 | )
24 |
25 | default = action[3] if action[3] != "" else None
26 | return Item(
27 | type=widget,
28 | display_name=(action[1] or action[0]).replace("-", " ").strip(),
29 | help=action[4],
30 | commands=[x for x in action[0:2] if x != ""],
31 | dest=action[1] or action[0],
32 | default=default,
33 | additional_properties={"nargs": action[2]},
34 | )
35 |
36 |
37 | def categorize(
38 | actions: list[tuple[str, str, int, Any, str]], *, isPos: bool = False
39 | ) -> Iterator[Item]:
40 | """Catergorise each action and generate json.
41 |
42 | Each action is in the form (short, long, argcount, value, help_message)
43 |
44 | """
45 | for action in actions:
46 | # ('-h', '--help', 0, False, 'show this help message and exit')
47 | if action[0] == "-h" and action[1] == "--help":
48 | pass
49 | elif not isPos and action[2] == 0:
50 | yield actionToJson(action, ItemType.Bool, isPos=isPos)
51 | else:
52 | yield actionToJson(action, ItemType.Text, isPos=isPos)
53 |
54 |
55 | def extract(parser: Any) -> list[Group]:
56 | """Get the actions as json for the parser."""
57 | return [
58 | Group(
59 | name="Positional Arguments",
60 | arg_items=list(categorize(parsePos(parser), isPos=True)),
61 | groups=[],
62 | ),
63 | Group(name="Optional Arguments", arg_items=list(categorize(parseOpt(parser))), groups=[]),
64 | ]
65 |
66 |
67 | def parseSection(name: str, source: str) -> list[str]:
68 | """Taken from docopt."""
69 | pattern = re.compile(
70 | "^([^\n]*" + name + "[^\n]*\n?(?:[ \t].*?(?:\n|$))*)",
71 | re.IGNORECASE | re.MULTILINE,
72 | )
73 | return [s.strip() for s in pattern.findall(source)]
74 |
75 |
76 | def parse(optionDescription: str) -> tuple[str, str, int, Any, str]:
77 | """Parse an option help text, adapted from docopt."""
78 | short, long, argcount, value = "", "", 0, False
79 | options, _, description = optionDescription.strip().partition(" ")
80 | options = options.replace(",", " ").replace("=", " ")
81 | for section in options.split():
82 | if section.startswith("--"):
83 | long = section
84 | elif section.startswith("-"):
85 | short = section
86 | else:
87 | argcount = 1
88 | if argcount > 0:
89 | matched = re.findall(r"\[default: (.*)\]", description, flags=re.IGNORECASE)
90 | value = matched[0] if matched else ""
91 | return (short, long, argcount, value, description.strip())
92 |
93 |
94 | def parseOpt(doc: Any) -> list[tuple[str, str, int, Any, str]]:
95 | """Parse an option help text, adapted from docopt."""
96 | defaults = []
97 | for _section in parseSection("options:", doc):
98 | _, _, section = _section.partition(":")
99 | split = re.split(r"\n[ \t]*(-\S+?)", "\n" + section)[1:]
100 | split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
101 | options = [parse(s) for s in split if s.startswith("-")]
102 | defaults += options
103 | return defaults
104 |
105 |
106 | def parsePos(doc: str) -> list[tuple[str, str]]:
107 | """Parse positional arguments from docstring."""
108 | defaults = []
109 | for _section in parseSection("arguments:", doc):
110 | _, _, section = _section.partition(":")
111 | defaults.append(
112 | tuple(col.strip() for col in section.strip().partition(" ") if len(col.strip()) > 0)
113 | )
114 | return defaults
115 |
116 |
117 | def convert(parser: Any) -> ParserRep:
118 | """Convert getopt to a dict.
119 |
120 | Args:
121 | ----
122 | parser (Any): docopt parser
123 |
124 | Returns:
125 | -------
126 | ParserRep: dictionary representing parser object
127 |
128 | """
129 | return ParserRep(parser_description="", widgets=extract(parser))
130 |
--------------------------------------------------------------------------------
/cli2gui/tojson/getopt2json.py:
--------------------------------------------------------------------------------
1 | """Generate a dict for getopt."""
2 |
3 | from __future__ import annotations
4 |
5 | from collections.abc import Callable
6 | from typing import Generator
7 |
8 | from cli2gui.models import Group, Item, ItemType, ParserRep
9 |
10 | # ruff: noqa: SLF001
11 |
12 |
13 | def actionToJson(action: str, widget: ItemType, *, short: bool = True) -> Item:
14 | """Convert an arg to json, behave in the same way as argparse hence the large
15 | amount of duplication.
16 | """
17 | return Item(
18 | type=widget,
19 | display_name=action,
20 | help="",
21 | commands=[("-" if short else "--") + action],
22 | dest=("-" if short else "--") + action,
23 | default=None,
24 | additional_properties={},
25 | )
26 |
27 |
28 | def catLong(actions: list[str]) -> Generator[Item, None, None]:
29 | """Categorize long args."""
30 | for action in actions:
31 | # True/ false
32 | if "=" in action:
33 | yield actionToJson(action[:-1], ItemType.Text, short=False)
34 | else:
35 | yield actionToJson(action, ItemType.Bool, short=False)
36 |
37 |
38 | def catShort(actions: list[str]) -> Generator[Item, None, None]:
39 | """Categorize short args."""
40 | index = 0
41 | while index < len(actions):
42 | try:
43 | # True/ false
44 | if ":" in actions[index + 1]:
45 | yield actionToJson(actions[index], ItemType.Text)
46 | index += 2
47 | else:
48 | yield actionToJson(actions[index], ItemType.Bool)
49 | index += 1
50 | except IndexError:
51 | yield actionToJson(actions[index], ItemType.Bool)
52 | break
53 |
54 |
55 | def process(
56 | group: list[str],
57 | groupName: str,
58 | categorize: Callable[[list[str]], Generator[Item, None, None]],
59 | ) -> list[Group]:
60 | """Generate a group (or section)."""
61 | return [Group(name=groupName, arg_items=list(categorize(group)), groups=[])]
62 |
63 |
64 | def convert(parser: tuple[list[str], list[str]]) -> ParserRep:
65 | """Convert getopt to a dict.
66 |
67 | Args:
68 | ----
69 | parser (tuple[list[str], list[str]]): getopt parser
70 |
71 | Returns:
72 | -------
73 | ParserRep: dictionary representing parser object
74 |
75 | """
76 | return ParserRep(
77 | parser_description="",
78 | widgets=process(parser[0], "Short Args", catShort)
79 | + process(parser[1], "Long Args", catLong),
80 | )
81 |
--------------------------------------------------------------------------------
/cli2gui/tojson/optparse2json.py:
--------------------------------------------------------------------------------
1 | """Generate a dict describing optparse arguments.
2 |
3 | pylint and pylance both want me to not access protected methods - I know better ;)
4 | """
5 |
6 | # ruff: noqa: SLF001
7 | from __future__ import annotations
8 |
9 | import optparse
10 | from typing import Generator
11 |
12 | from cli2gui.models import Group, Item, ItemType, ParserRep
13 |
14 |
15 | def extractOptions(optionGroup: optparse.OptionGroup) -> Group:
16 | """Get the actions as json for each item under a group."""
17 | return Group(
18 | name=optionGroup.title,
19 | arg_items=list(
20 | categorize(
21 | [action for action in optionGroup.option_list if action.action not in "help"]
22 | )
23 | ),
24 | groups=[],
25 | )
26 |
27 |
28 | def extractGroups(parser: optparse.OptionParser) -> Group:
29 | """Get the actions as json for each item and group under the parser."""
30 | argItems = list(
31 | categorize([action for action in parser.option_list if action.action not in "help"])
32 | )
33 | return Group(
34 | name="Arguments",
35 | arg_items=argItems,
36 | groups=[extractOptions(group) for group in parser.option_groups],
37 | )
38 |
39 |
40 | def actionToJson(action: optparse.Option, widget: ItemType) -> Item:
41 | """Generate json for an action and set the widget - used by the application."""
42 | choices = action.choices or [] # type: ignore[general-type-issues] # choices is confirmed to exist\
43 | default = action.default if action.default != ("NO", "DEFAULT") else None
44 | return Item(
45 | type=widget,
46 | display_name=str(action.metavar or action.dest),
47 | help=str(action.help),
48 | commands=action._long_opts + action._short_opts,
49 | dest=action.dest or "",
50 | default=default,
51 | additional_properties={
52 | "nargs": str(action.nargs or ""),
53 | "choices": choices,
54 | },
55 | )
56 |
57 |
58 | def categorize(actions: list[optparse.Option]) -> Generator[Item, None, None]:
59 | """Catergorise each action and generate json."""
60 | for action in actions:
61 | # _actions which are either, store_bool, etc..
62 | if action.action in ("store_true", "store_false"):
63 | yield actionToJson(action, ItemType.Bool)
64 | # _actions which are of type _CountAction
65 | elif action.choices: # type: ignore[general-type-issues] # choices is confirmed to exist
66 | yield actionToJson(action, ItemType.Choice)
67 | elif action.action in ("count",):
68 | yield actionToJson(action, ItemType.Int)
69 | else:
70 | yield actionToJson(action, ItemType.Text)
71 |
72 |
73 | def convert(parser: optparse.OptionParser) -> ParserRep:
74 | """Convert argparse to a dict.
75 |
76 | Args:
77 | ----
78 | parser (optparse.OptionParser): optparse parser
79 |
80 | Returns:
81 | -------
82 | ParserRep: dictionary representing parser object
83 |
84 | """
85 | return ParserRep(parser_description="", widgets=[extractGroups(parser)])
86 |
--------------------------------------------------------------------------------
/comparison.md:
--------------------------------------------------------------------------------
1 | # Comparison to similar projects
2 |
3 | Do let me know if any of these are incorrect. Some of the comparisons are
4 | based off documentation/ the readme
5 |
6 | ## Parser Support
7 |
8 | | Parser | Cli2Gui | [Gooey](https://github.com/chriskiehl/Gooey) | Quick |
9 | | --------------------------------------------------------------- | ------------------- | -------------------------------------------- | ----- |
10 | | [Argparse](https://docs.python.org/3/library/argparse.html) | ✔ | ✔ | ❌ |
11 | | [Optparse](https://docs.python.org/3/library/optparse.html) | ✔ | ❌ | ❌ |
12 | | [DocOpt](https://github.com/docopt/docopt) | ✔ | ❌ | ❌ |
13 | | [Click](https://github.com/pallets/click) | ✔ * | ❌ | ✔ |
14 | | [GetOpt](https://docs.python.org/3/library/getopt.html) | ✔ | ❌ | ❌ |
15 | | [Dephell Argparse](https://github.com/dephell/dephell_argparse) | ✔ | ❌ | ❌ |
16 |
17 | ```none
18 | * Partial support (use [Click2Gui](#click2gui))
19 |
20 | This works for simpler programs but sadly falls flat for more complex programs
21 | ```
22 |
23 | ## GUI Toolkit Support
24 |
25 | | GUI Toolkits | Cli2Gui | Gooey | Quick |
26 | | ------------ | ------- | ----- | ----- |
27 | | Tkinter | ✔ | ❌ | ❌ |
28 | | WxWidgets | ❌ | ✔ | ❌ |
29 | | Qt | ✔ | ❌ | ✔ |
30 | | Gtk | ❌ | ❌ | ❌ |
31 | | Web | ✔ | ❌ | ❌ |
32 |
33 | ## GUI Feature Support
34 |
35 | | Basic GUI | Cli2Gui | Gooey | Quick |
36 | | -------------------------- | ------- | ---------------- | ---------------- |
37 | | Override name/ description | ✔ | ✔ | ❌ |
38 | | Theming | ✔ | ⚠ Limited | ⚠ Limited |
39 | | DarkMode | ✔ | ❌ | ✔ |
40 | | Window Size | ✔ | ✔ | ❌ |
41 | | Element Size | ✔ | ❌ | ❌ |
42 | | Custom Images | ✔ | ✔ | ❌ |
43 |
44 | Cli2Gui is pretty lacking in these features and will probably remain that way
45 | to ease maintainability - the primary aim is to support multiple argparse
46 | libraries over fancy widgets
47 |
48 | | Advanced GUI | Cli2Gui | Gooey | Quick |
49 | | ---------------------- | ------- | ----- | ----- |
50 | | Dropdown | ✔ | ✔ | ✔ |
51 | | Slider | ❌ | ✔ | ✔ |
52 | | Tabs | ❌ | ✔ | ✔ |
53 | | Menus | ✔ | ✔ | ❌ |
54 | | Max Args before Scroll | ✔ | ❌ | ❌ |
55 |
--------------------------------------------------------------------------------
/documentation/reference/README.md:
--------------------------------------------------------------------------------
1 | # Cli2gui Index
2 |
3 | > Auto-generated documentation index.
4 |
5 | A full list of `Cli2gui` project modules.
6 |
7 | - [Cli2gui](cli2gui/index.md#cli2gui)
8 | - [Application](cli2gui/application/index.md#application)
9 | - [Application](cli2gui/application/application.md#application)
10 | - [Application2args](cli2gui/application/application2args.md#application2args)
11 | - [Decorators](cli2gui/decorators.md#decorators)
12 | - [Gui](cli2gui/gui/index.md#gui)
13 | - [AbstractGUI](cli2gui/gui/abstract_gui.md#abstractgui)
14 | - [DearPyGuiWrapper](cli2gui/gui/dearpygui_wrapper.md#dearpyguiwrapper)
15 | - [Helpers](cli2gui/gui/helpers.md#helpers)
16 | - [PySimpleGUIWrapper](cli2gui/gui/pysimplegui_wrapper.md#pysimpleguiwrapper)
17 | - [Models](cli2gui/models.md#models)
18 | - [Tojson](cli2gui/tojson/index.md#tojson)
19 | - [Argparse2json](cli2gui/tojson/argparse2json.md#argparse2json)
20 | - [Click2json](cli2gui/tojson/click2json.md#click2json)
21 | - [Docopt2json](cli2gui/tojson/docopt2json.md#docopt2json)
22 | - [Getopt2json](cli2gui/tojson/getopt2json.md#getopt2json)
23 | - [Optparse2json](cli2gui/tojson/optparse2json.md#optparse2json)
24 |
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/application/application.md:
--------------------------------------------------------------------------------
1 | # Application
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Application](./index.md#application) / Application
4 |
5 | > Auto-generated documentation for [cli2gui.application.application](../../../../cli2gui/application/application.py) module.
6 |
7 | - [Application](#application)
8 | - [run](#run)
9 |
10 | ## run
11 |
12 | [Show source in application.py:16](../../../../cli2gui/application/application.py#L16)
13 |
14 | Establish the main entry point.
15 |
16 | #### Arguments
17 |
18 | ----
19 | - `buildSpec` *types.FullBuildSpec* - args that customise the application such as the theme
20 | or the function to run
21 |
22 | #### Signature
23 |
24 | ```python
25 | def run(buildSpec: models.FullBuildSpec) -> Any: ...
26 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/application/application2args.md:
--------------------------------------------------------------------------------
1 | # Application2args
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Application](./index.md#application) / Application2args
4 |
5 | > Auto-generated documentation for [cli2gui.application.application2args](../../../../cli2gui/application/application2args.py) module.
6 |
7 | - [Application2args](#application2args)
8 | - [argFormat](#argformat)
9 | - [argparseFormat](#argparseformat)
10 | - [clickFormat](#clickformat)
11 | - [docoptFormat](#docoptformat)
12 | - [getoptFormat](#getoptformat)
13 | - [optparseFormat](#optparseformat)
14 | - [processValue](#processvalue)
15 |
16 | ## argFormat
17 |
18 | [Show source in application2args.py:89](../../../../cli2gui/application/application2args.py#L89)
19 |
20 | Format the args for the desired parser.
21 |
22 | #### Arguments
23 |
24 | ----
25 | values (dict[str, Any]): values from simple gui
26 | - `argumentParser` *str* - argument parser to use
27 |
28 | #### Returns
29 |
30 | -------
31 | - `Any` - args
32 |
33 | #### Signature
34 |
35 | ```python
36 | def argFormat(values: dict[str, Any], argumentParser: str | ParserType) -> Any: ...
37 | ```
38 |
39 |
40 |
41 | ## argparseFormat
42 |
43 | [Show source in application2args.py:44](../../../../cli2gui/application/application2args.py#L44)
44 |
45 | Format args for argparse.
46 |
47 | #### Signature
48 |
49 | ```python
50 | def argparseFormat(values: dict[str, Any]) -> argparse.Namespace: ...
51 | ```
52 |
53 |
54 |
55 | ## clickFormat
56 |
57 | [Show source in application2args.py:78](../../../../cli2gui/application/application2args.py#L78)
58 |
59 | Format args for click.
60 |
61 | #### Signature
62 |
63 | ```python
64 | def clickFormat(values: dict[str, Any]) -> list[Any]: ...
65 | ```
66 |
67 |
68 |
69 | ## docoptFormat
70 |
71 | [Show source in application2args.py:67](../../../../cli2gui/application/application2args.py#L67)
72 |
73 | Format args for docopt.
74 |
75 | #### Signature
76 |
77 | ```python
78 | def docoptFormat(values: dict[str, Any]) -> dict[str, Any]: ...
79 | ```
80 |
81 |
82 |
83 | ## getoptFormat
84 |
85 | [Show source in application2args.py:62](../../../../cli2gui/application/application2args.py#L62)
86 |
87 | Format args for getopt.
88 |
89 | #### Signature
90 |
91 | ```python
92 | def getoptFormat(values: dict[str, Any]) -> tuple[list[Any], list[Any]]: ...
93 | ```
94 |
95 |
96 |
97 | ## optparseFormat
98 |
99 | [Show source in application2args.py:53](../../../../cli2gui/application/application2args.py#L53)
100 |
101 | Format args for optparse.
102 |
103 | #### Signature
104 |
105 | ```python
106 | def optparseFormat(values: dict[str, Any]) -> tuple[optparse.Values, list[str]]: ...
107 | ```
108 |
109 |
110 |
111 | ## processValue
112 |
113 | [Show source in application2args.py:13](../../../../cli2gui/application/application2args.py#L13)
114 |
115 | #### Signature
116 |
117 | ```python
118 | def processValue(key: str, value: str) -> tuple[str, Any]: ...
119 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/application/index.md:
--------------------------------------------------------------------------------
1 | # Application
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / Application
4 |
5 | > Auto-generated documentation for [cli2gui.application](../../../../cli2gui/application/__init__.py) module.
6 |
7 | - [Application](#application)
8 | - [Modules](#modules)
9 |
10 | ## Modules
11 |
12 | - [Application](./application.md)
13 | - [Application2args](./application2args.md)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/decorators.md:
--------------------------------------------------------------------------------
1 | # Decorators
2 |
3 | [Cli2gui Index](../README.md#cli2gui-index) / [Cli2gui](./index.md#cli2gui) / Decorators
4 |
5 | > Auto-generated documentation for [cli2gui.decorators](../../../cli2gui/decorators.py) module.
6 |
7 | - [Decorators](#decorators)
8 | - [Cli2Gui](#cli2gui)
9 | - [Click2Gui](#click2gui)
10 | - [createFromParser](#createfromparser)
11 |
12 | ## Cli2Gui
13 |
14 | [Show source in decorators.py:171](../../../cli2gui/decorators.py#L171)
15 |
16 | Use this decorator in the function containing the argument parser.
17 | Serialises data to JSON and launches the Cli2Gui application.
18 |
19 | #### Arguments
20 |
21 | ----
22 | run_function (Callable[..., Any]): The name of the function to call eg.
23 | - `auto_enable` *bool, optional* - Enable the GUI by default. If enabled by
24 | default requires `--disable-cli2gui`, otherwise requires `--cli2gui`.
25 | Defaults to False.
26 | - `parser` *str, optional* - Override the parser to use. Current
27 | - `options` *are* - "argparse", "getopt", "optparse", "docopt",
28 | "dephell_argparse". Defaults to "argparse".
29 | - `gui` *str, optional* - Override the gui to use. Current options are:
30 | "dearpygui", "pysimplegui", "pysimpleguiqt","pysimpleguiweb","freesimplegui",
31 | Defaults to "dearpygui".
32 | theme (Union[str, list[str]], optional): Set a base24 theme. Can
33 | also pass a base24 scheme file. eg. one-light.yaml. Defaults to "".
34 | darkTheme (Union[str, list[str]], optional): Set a base24 dark
35 | theme variant. Can also pass a base24 scheme file. eg. one-dark.yaml.
36 | Defaults to "".
37 | - `image` *str, optional* - Set the program icon. File
38 | extensions can be any that PIL supports. Defaults to "".
39 | - `program_name` *str, optional* - Override the program name.
40 | Defaults to "".
41 | - `program_description` *str, optional* - Override the program
42 | description. Defaults to "".
43 | - `max_args_shown` *int, optional* - Maximum number of args shown before
44 | using a scrollbar. Defaults to 5.
45 | menu (Union[dict[str, Any]], optional): Add a menu to the program.
46 | Defaults to "". eg. THIS_DIR = str(Path(__file__).resolve().parent)
47 | - `menu={"File"` - THIS_DIR + "/file.md"}
48 |
49 | #### Returns
50 |
51 | -------
52 | - `Any` - Runs the application
53 |
54 | #### Signature
55 |
56 | ```python
57 | def Cli2Gui(
58 | run_function: Callable[..., Any],
59 | auto_enable: bool = False,
60 | parser: str | ParserType = "argparse",
61 | gui: str | ParserType = "dearpygui",
62 | theme: str | list[str] = "",
63 | darkTheme: str | list[str] = "",
64 | image: str = "",
65 | program_name: str = "",
66 | program_description: str = "",
67 | max_args_shown: int = 5,
68 | menu: str | dict[str, Any] = "",
69 | ) -> Any: ...
70 | ```
71 |
72 |
73 |
74 | ## Click2Gui
75 |
76 | [Show source in decorators.py:108](../../../cli2gui/decorators.py#L108)
77 |
78 | Use this decorator in the function containing the argument parser.
79 | Serializes data to JSON and launches the Cli2Gui application.
80 |
81 | #### Arguments
82 |
83 | ----
84 | run_function (Callable[..., Any]): The name of the function to call eg.
85 | - `gui` *str, optional* - Override the gui to use. Current options are:
86 | "dearpygui", "pysimplegui", "pysimpleguiqt","pysimpleguiweb","freesimplegui",
87 | Defaults to "dearpygui".
88 | theme (Union[str, list[str]], optional): Set a base24 theme. Can
89 | also pass a base24 scheme file. eg. one-light.yaml. Defaults to "".
90 | darkTheme (Union[str, list[str]], optional): Set a base24 dark
91 | theme variant. Can also pass a base24 scheme file. eg. one-dark.yaml.
92 | Defaults to "".
93 | - `image` *str, optional* - Set the program icon. File
94 | extensions can be any that PIL supports. Defaults to "".
95 | - `program_name` *str, optional* - Override the program name.
96 | Defaults to "".
97 | - `program_description` *str, optional* - Override the program
98 | description. Defaults to "".
99 | - `max_args_shown` *int, optional* - Maximum number of args shown before
100 | using a scrollbar. Defaults to 5.
101 | menu (Union[dict[str, Any]], optional): Add a menu to the program.
102 | Defaults to "". eg. THIS_DIR = str(Path(__file__).resolve().parent)
103 | - `menu={"File"` - THIS_DIR + "/file.md"}
104 | **kwargs (dict[Any, Any]): kwargs
105 |
106 | #### Returns
107 |
108 | -------
109 | - `Any` - Runs the application
110 |
111 | #### Signature
112 |
113 | ```python
114 | def Click2Gui(
115 | run_function: Callable[..., Any],
116 | gui: str | GUIType = "dearpygui",
117 | theme: str | list[str] = "",
118 | darkTheme: str | list[str] = "",
119 | image: str = "",
120 | program_name: str = "",
121 | program_description: str = "",
122 | max_args_shown: int = 5,
123 | menu: str | dict[str, Any] = "",
124 | **kwargs: dict[str, Any]
125 | ) -> None: ...
126 | ```
127 |
128 |
129 |
130 | ## createFromParser
131 |
132 | [Show source in decorators.py:29](../../../cli2gui/decorators.py#L29)
133 |
134 | Generate a buildSpec from a parser.
135 |
136 | #### Arguments
137 |
138 | ----
139 | - `selfParser` *Any* - A parser that acts on self. eg. ArgumentParser.parse_args
140 | argsParser (tuple[Any, ...]): A parser that acts on function
141 | arguments. eg. getopt.getopt
142 | kwargsParser (dict[Any, Any]): A parser that acts on named params
143 | - `sourcePath` *str* - Program source path
144 | - `buildSpec` *BuildSpec* - Build spec
145 | **kwargs (dict[Any, Any]): kwargs
146 |
147 | #### Returns
148 |
149 | -------
150 | - `types.FullBuildSpec` - buildSpec to be used by the application
151 |
152 | #### Raises
153 |
154 | ------
155 | - `RuntimeError` - Throw error if incorrect parser selected
156 |
157 | #### Signature
158 |
159 | ```python
160 | def createFromParser(
161 | selfParser: Any,
162 | argsParser: tuple[Any, ...],
163 | kwargsParser: dict[Any, Any],
164 | sourcePath: str,
165 | buildSpec: BuildSpec,
166 | **kwargs: dict[Any, Any]
167 | ) -> FullBuildSpec: ...
168 | ```
169 |
170 | #### See also
171 |
172 | - [BuildSpec](./models.md#buildspec)
173 | - [FullBuildSpec](./models.md#fullbuildspec)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/gui/abstract_gui.md:
--------------------------------------------------------------------------------
1 | # AbstractGUI
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Gui](./index.md#gui) / AbstractGUI
4 |
5 | > Auto-generated documentation for [cli2gui.gui.abstract_gui](../../../../cli2gui/gui/abstract_gui.py) module.
6 |
7 | - [AbstractGUI](#abstractgui)
8 | - [AbstractGUI](#abstractgui-1)
9 | - [AbstractGUI().main](#abstractgui()main)
10 |
11 | ## AbstractGUI
12 |
13 | [Show source in abstract_gui.py:11](../../../../cli2gui/gui/abstract_gui.py#L11)
14 |
15 | Abstract base class for GUI wrappers.
16 |
17 | #### Signature
18 |
19 | ```python
20 | class AbstractGUI(ABC):
21 | @abstractmethod
22 | def __init__(self) -> None: ...
23 | ```
24 |
25 | ### AbstractGUI().main
26 |
27 | [Show source in abstract_gui.py:18](../../../../cli2gui/gui/abstract_gui.py#L18)
28 |
29 | Abstract method for the main function.
30 |
31 | #### Signature
32 |
33 | ```python
34 | @abstractmethod
35 | def main(
36 | self,
37 | buildSpec: models.FullBuildSpec,
38 | quit_callback: Callable[[], None],
39 | run_callback: Callable[[dict[str, Any]], None],
40 | ) -> None: ...
41 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/gui/dearpygui_wrapper.md:
--------------------------------------------------------------------------------
1 | # DearPyGuiWrapper
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Gui](./index.md#gui) / DearPyGuiWrapper
4 |
5 | > Auto-generated documentation for [cli2gui.gui.dearpygui_wrapper](../../../../cli2gui/gui/dearpygui_wrapper.py) module.
6 |
7 | - [DearPyGuiWrapper](#dearpyguiwrapper)
8 | - [DearPyGuiWrapper](#dearpyguiwrapper-1)
9 | - [DearPyGuiWrapper()._helpFileWidget](#dearpyguiwrapper()_helpfilewidget)
10 | - [DearPyGuiWrapper().addItemsAndGroups](#dearpyguiwrapper()additemsandgroups)
11 | - [DearPyGuiWrapper().addWidgetFromItem](#dearpyguiwrapper()addwidgetfromitem)
12 | - [DearPyGuiWrapper().main](#dearpyguiwrapper()main)
13 | - [DearPyGuiWrapper().open_menu_item](#dearpyguiwrapper()open_menu_item)
14 | - [hex_to_rgb](#hex_to_rgb)
15 |
16 | ## DearPyGuiWrapper
17 |
18 | [Show source in dearpygui_wrapper.py:26](../../../../cli2gui/gui/dearpygui_wrapper.py#L26)
19 |
20 | Wrapper class for Dear PyGui.
21 |
22 | #### Signature
23 |
24 | ```python
25 | class DearPyGuiWrapper(AbstractGUI):
26 | def __init__(self, base24Theme: list[str]) -> None: ...
27 | ```
28 |
29 | #### See also
30 |
31 | - [AbstractGUI](./abstract_gui.md#abstractgui)
32 |
33 | ### DearPyGuiWrapper()._helpFileWidget
34 |
35 | [Show source in dearpygui_wrapper.py:80](../../../../cli2gui/gui/dearpygui_wrapper.py#L80)
36 |
37 | Create a UI element with an input text field and a file picker.
38 |
39 | #### Signature
40 |
41 | ```python
42 | def _helpFileWidget(self, item: Item) -> None: ...
43 | ```
44 |
45 | #### See also
46 |
47 | - [Item](../models.md#item)
48 |
49 | ### DearPyGuiWrapper().addItemsAndGroups
50 |
51 | [Show source in dearpygui_wrapper.py:147](../../../../cli2gui/gui/dearpygui_wrapper.py#L147)
52 |
53 | Items and groups and return a list of these so we can get values from the dpg widgets.
54 |
55 | #### Arguments
56 |
57 | - `section` *Group* - section with a name to display and items
58 |
59 | #### Returns
60 |
61 | Type: *list[Item]*
62 | flattened list of items
63 |
64 | #### Signature
65 |
66 | ```python
67 | def addItemsAndGroups(self, section: Group) -> list[Item]: ...
68 | ```
69 |
70 | #### See also
71 |
72 | - [Group](../models.md#group)
73 | - [Item](../models.md#item)
74 |
75 | ### DearPyGuiWrapper().addWidgetFromItem
76 |
77 | [Show source in dearpygui_wrapper.py:125](../../../../cli2gui/gui/dearpygui_wrapper.py#L125)
78 |
79 | Select a widget based on the item type.
80 |
81 | #### Arguments
82 |
83 | - `item` *Item* - the item
84 |
85 | #### Signature
86 |
87 | ```python
88 | def addWidgetFromItem(self, item: Item) -> None: ...
89 | ```
90 |
91 | #### See also
92 |
93 | - [Item](../models.md#item)
94 |
95 | ### DearPyGuiWrapper().main
96 |
97 | [Show source in dearpygui_wrapper.py:191](../../../../cli2gui/gui/dearpygui_wrapper.py#L191)
98 |
99 | Run the gui (dpg) with a given buildSpec, quit_callback, and run_callback.
100 |
101 | - Theming + Configure dpg
102 | - Menu Prep
103 | - Create Window, set up Menu and Widgets
104 | - Then, start dpg
105 |
106 | #### Arguments
107 |
108 | - `buildSpec` *FullBuildSpec* - Full cli parse/ build spec
109 | :param Callable[[], None] quit_callback: generic callable used to quit
110 | :param Callable[[dict[str, Any]], None] run_callback: generic callable used to run
111 |
112 | #### Signature
113 |
114 | ```python
115 | def main(
116 | self,
117 | buildSpec: FullBuildSpec,
118 | quit_callback: Callable[[], None],
119 | run_callback: Callable[[dict[str, Any]], None],
120 | ) -> None: ...
121 | ```
122 |
123 | #### See also
124 |
125 | - [FullBuildSpec](../models.md#fullbuildspec)
126 |
127 | ### DearPyGuiWrapper().open_menu_item
128 |
129 | [Show source in dearpygui_wrapper.py:177](../../../../cli2gui/gui/dearpygui_wrapper.py#L177)
130 |
131 | Open a menu item.
132 |
133 | #### Arguments
134 |
135 | - `sender` *_type_* - file to open
136 | - `_app_data` *_type_* - [unused]
137 |
138 | #### Signature
139 |
140 | ```python
141 | def open_menu_item(self, sender: str, _app_data: None) -> None: ...
142 | ```
143 |
144 |
145 |
146 | ## hex_to_rgb
147 |
148 | [Show source in dearpygui_wrapper.py:17](../../../../cli2gui/gui/dearpygui_wrapper.py#L17)
149 |
150 | Convert a color hex code to a tuple of integers (r, g, b).
151 |
152 | #### Signature
153 |
154 | ```python
155 | def hex_to_rgb(hex_code: str) -> tuple[int, int, int, int]: ...
156 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/gui/helpers.md:
--------------------------------------------------------------------------------
1 | # Helpers
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Gui](./index.md#gui) / Helpers
4 |
5 | > Auto-generated documentation for [cli2gui.gui.helpers](../../../../cli2gui/gui/helpers.py) module.
6 |
7 | - [Helpers](#helpers)
8 | - [_themeFromFile](#_themefromfile)
9 | - [get_base24_theme](#get_base24_theme)
10 | - [isDarkMode](#isdarkmode)
11 | - [read_file](#read_file)
12 | - [stringSentencecase](#stringsentencecase)
13 | - [stringTitlecase](#stringtitlecase)
14 |
15 | ## _themeFromFile
16 |
17 | [Show source in helpers.py:20](../../../../cli2gui/gui/helpers.py#L20)
18 |
19 | Set the base24 theme from a base24 scheme.yaml to the application.
20 |
21 | #### Arguments
22 |
23 | ----
24 | - `themeFile` *str* - path to file
25 |
26 | #### Returns
27 |
28 | -------
29 | - `list[str]` - theme to set
30 |
31 | #### Signature
32 |
33 | ```python
34 | def _themeFromFile(themeFile: str) -> list[str]: ...
35 | ```
36 |
37 |
38 |
39 | ## get_base24_theme
40 |
41 | [Show source in helpers.py:36](../../../../cli2gui/gui/helpers.py#L36)
42 |
43 | Set the base24 theme to the application.
44 |
45 | #### Arguments
46 |
47 | ----
48 | theme (Union[str, list[str]]): the light theme
49 | darkTheme (Union[str, list[str]]): the dark theme
50 |
51 | #### Signature
52 |
53 | ```python
54 | def get_base24_theme(
55 | theme: str | list[str], darkTheme: str | list[str]
56 | ) -> list[str]: ...
57 | ```
58 |
59 |
60 |
61 | ## isDarkMode
62 |
63 | [Show source in helpers.py:12](../../../../cli2gui/gui/helpers.py#L12)
64 |
65 | Monkeypatch for getostheme.isDarkMode.
66 |
67 | #### Signature
68 |
69 | ```python
70 | def isDarkMode() -> bool: ...
71 | ```
72 |
73 |
74 |
75 | ## read_file
76 |
77 | [Show source in helpers.py:133](../../../../cli2gui/gui/helpers.py#L133)
78 |
79 | Get the contents of a file path, attempt to parse with catpandoc..pandoc2plain.
80 |
81 | #### Arguments
82 |
83 | - `file_path` *str* - path to the file (absolute recommended)
84 |
85 | #### Returns
86 |
87 | Type: *str*
88 | file contents
89 |
90 | #### Signature
91 |
92 | ```python
93 | def read_file(file_path: str, maxLines: int = 200) -> str: ...
94 | ```
95 |
96 |
97 |
98 | ## stringSentencecase
99 |
100 | [Show source in helpers.py:125](../../../../cli2gui/gui/helpers.py#L125)
101 |
102 | Convert a string to sentence case.
103 |
104 | #### Signature
105 |
106 | ```python
107 | def stringSentencecase(string: str) -> str: ...
108 | ```
109 |
110 |
111 |
112 | ## stringTitlecase
113 |
114 | [Show source in helpers.py:111](../../../../cli2gui/gui/helpers.py#L111)
115 |
116 | Convert a string to title case.
117 |
118 | #### Signature
119 |
120 | ```python
121 | def stringTitlecase(string: str, splitStr: str = "ALL") -> str: ...
122 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/gui/index.md:
--------------------------------------------------------------------------------
1 | # Gui
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / Gui
4 |
5 | > Auto-generated documentation for [cli2gui.gui](../../../../cli2gui/gui/__init__.py) module.
6 |
7 | - [Gui](#gui)
8 | - [Modules](#modules)
9 |
10 | ## Modules
11 |
12 | - [AbstractGUI](./abstract_gui.md)
13 | - [DearPyGuiWrapper](./dearpygui_wrapper.md)
14 | - [Helpers](./helpers.md)
15 | - [PySimpleGUIWrapper](./pysimplegui_wrapper.md)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/gui/pysimplegui_wrapper.md:
--------------------------------------------------------------------------------
1 | # PySimpleGUIWrapper
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Gui](./index.md#gui) / PySimpleGUIWrapper
4 |
5 | > Auto-generated documentation for [cli2gui.gui.pysimplegui_wrapper](../../../../cli2gui/gui/pysimplegui_wrapper.py) module.
6 |
7 | - [PySimpleGUIWrapper](#pysimpleguiwrapper)
8 | - [PySimpleGUIWrapper](#pysimpleguiwrapper-1)
9 | - [PySimpleGUIWrapper()._button](#pysimpleguiwrapper()_button)
10 | - [PySimpleGUIWrapper()._check](#pysimpleguiwrapper()_check)
11 | - [PySimpleGUIWrapper()._dropdown](#pysimpleguiwrapper()_dropdown)
12 | - [PySimpleGUIWrapper()._fileBrowser](#pysimpleguiwrapper()_filebrowser)
13 | - [PySimpleGUIWrapper()._helpArgHelp](#pysimpleguiwrapper()_helparghelp)
14 | - [PySimpleGUIWrapper()._helpArgName](#pysimpleguiwrapper()_helpargname)
15 | - [PySimpleGUIWrapper()._helpArgNameAndHelp](#pysimpleguiwrapper()_helpargnameandhelp)
16 | - [PySimpleGUIWrapper()._helpCounterWidget](#pysimpleguiwrapper()_helpcounterwidget)
17 | - [PySimpleGUIWrapper()._helpDropdownWidget](#pysimpleguiwrapper()_helpdropdownwidget)
18 | - [PySimpleGUIWrapper()._helpFileWidget](#pysimpleguiwrapper()_helpfilewidget)
19 | - [PySimpleGUIWrapper()._helpFlagWidget](#pysimpleguiwrapper()_helpflagwidget)
20 | - [PySimpleGUIWrapper()._helpTextWidget](#pysimpleguiwrapper()_helptextwidget)
21 | - [PySimpleGUIWrapper()._inputText](#pysimpleguiwrapper()_inputtext)
22 | - [PySimpleGUIWrapper()._label](#pysimpleguiwrapper()_label)
23 | - [PySimpleGUIWrapper()._spin](#pysimpleguiwrapper()_spin)
24 | - [PySimpleGUIWrapper()._title](#pysimpleguiwrapper()_title)
25 | - [PySimpleGUIWrapper().addItemsAndGroups](#pysimpleguiwrapper()additemsandgroups)
26 | - [PySimpleGUIWrapper().addWidgetFromItem](#pysimpleguiwrapper()addwidgetfromitem)
27 | - [PySimpleGUIWrapper().createLayout](#pysimpleguiwrapper()createlayout)
28 | - [PySimpleGUIWrapper().generatePopup](#pysimpleguiwrapper()generatepopup)
29 | - [PySimpleGUIWrapper().getImgData](#pysimpleguiwrapper()getimgdata)
30 | - [PySimpleGUIWrapper().main](#pysimpleguiwrapper()main)
31 |
32 | ## PySimpleGUIWrapper
33 |
34 | [Show source in pysimplegui_wrapper.py:16](../../../../cli2gui/gui/pysimplegui_wrapper.py#L16)
35 |
36 | Wrapper class for PySimpleGUI.
37 |
38 | #### Signature
39 |
40 | ```python
41 | class PySimpleGUIWrapper(AbstractGUI):
42 | def __init__(self, base24Theme: list[str], psg_lib: str) -> None: ...
43 | ```
44 |
45 | #### See also
46 |
47 | - [AbstractGUI](./abstract_gui.md#abstractgui)
48 |
49 | ### PySimpleGUIWrapper()._button
50 |
51 | [Show source in pysimplegui_wrapper.py:109](../../../../cli2gui/gui/pysimplegui_wrapper.py#L109)
52 |
53 | Return a button.
54 |
55 | #### Signature
56 |
57 | ```python
58 | def _button(self, text: str) -> Any: ...
59 | ```
60 |
61 | ### PySimpleGUIWrapper()._check
62 |
63 | [Show source in pysimplegui_wrapper.py:99](../../../../cli2gui/gui/pysimplegui_wrapper.py#L99)
64 |
65 | Return a checkbox.
66 |
67 | #### Signature
68 |
69 | ```python
70 | def _check(self, key: str, default: str | None = None) -> Any: ...
71 | ```
72 |
73 | ### PySimpleGUIWrapper()._dropdown
74 |
75 | [Show source in pysimplegui_wrapper.py:130](../../../../cli2gui/gui/pysimplegui_wrapper.py#L130)
76 |
77 | Return a dropdown.
78 |
79 | #### Signature
80 |
81 | ```python
82 | def _dropdown(self, key: str, argItems: list[str]) -> Any: ...
83 | ```
84 |
85 | ### PySimpleGUIWrapper()._fileBrowser
86 |
87 | [Show source in pysimplegui_wrapper.py:139](../../../../cli2gui/gui/pysimplegui_wrapper.py#L139)
88 |
89 | Return a fileBrowser button and field.
90 |
91 | #### Signature
92 |
93 | ```python
94 | def _fileBrowser(
95 | self,
96 | key: str,
97 | default: str | None = None,
98 | _type: ItemType = ItemType.File,
99 | additional_properties: dict | None = None,
100 | ) -> list[Any]: ...
101 | ```
102 |
103 | #### See also
104 |
105 | - [ItemType](../models.md#itemtype)
106 |
107 | ### PySimpleGUIWrapper()._helpArgHelp
108 |
109 | [Show source in pysimplegui_wrapper.py:188](../../../../cli2gui/gui/pysimplegui_wrapper.py#L188)
110 |
111 | Return a label for the arg help text.
112 |
113 | #### Signature
114 |
115 | ```python
116 | def _helpArgHelp(self, helpText: str) -> Any: ...
117 | ```
118 |
119 | ### PySimpleGUIWrapper()._helpArgName
120 |
121 | [Show source in pysimplegui_wrapper.py:184](../../../../cli2gui/gui/pysimplegui_wrapper.py#L184)
122 |
123 | Return a label for the arg name.
124 |
125 | #### Signature
126 |
127 | ```python
128 | def _helpArgName(self, displayName: str, commands: list[str]) -> Any: ...
129 | ```
130 |
131 | ### PySimpleGUIWrapper()._helpArgNameAndHelp
132 |
133 | [Show source in pysimplegui_wrapper.py:192](../../../../cli2gui/gui/pysimplegui_wrapper.py#L192)
134 |
135 | Return a column containing the argument name and help text.
136 |
137 | #### Signature
138 |
139 | ```python
140 | def _helpArgNameAndHelp(
141 | self, commands: list[str], helpText: str, displayName: str
142 | ) -> Any: ...
143 | ```
144 |
145 | ### PySimpleGUIWrapper()._helpCounterWidget
146 |
147 | [Show source in pysimplegui_wrapper.py:243](../../../../cli2gui/gui/pysimplegui_wrapper.py#L243)
148 |
149 | Return a set of self that make up an arg with text.
150 |
151 | #### Signature
152 |
153 | ```python
154 | def _helpCounterWidget(self, item: Item) -> list[Any]: ...
155 | ```
156 |
157 | #### See also
158 |
159 | - [Item](../models.md#item)
160 |
161 | ### PySimpleGUIWrapper()._helpDropdownWidget
162 |
163 | [Show source in pysimplegui_wrapper.py:268](../../../../cli2gui/gui/pysimplegui_wrapper.py#L268)
164 |
165 | Return a set of self that make up an arg with a choice.
166 |
167 | #### Signature
168 |
169 | ```python
170 | def _helpDropdownWidget(self, item: Item) -> list[Any]: ...
171 | ```
172 |
173 | #### See also
174 |
175 | - [Item](../models.md#item)
176 |
177 | ### PySimpleGUIWrapper()._helpFileWidget
178 |
179 | [Show source in pysimplegui_wrapper.py:255](../../../../cli2gui/gui/pysimplegui_wrapper.py#L255)
180 |
181 | Return a set of self that make up an arg with a file.
182 |
183 | #### Signature
184 |
185 | ```python
186 | def _helpFileWidget(self, item: Item) -> list[Any]: ...
187 | ```
188 |
189 | #### See also
190 |
191 | - [Item](../models.md#item)
192 |
193 | ### PySimpleGUIWrapper()._helpFlagWidget
194 |
195 | [Show source in pysimplegui_wrapper.py:218](../../../../cli2gui/gui/pysimplegui_wrapper.py#L218)
196 |
197 | Return a set of self that make up an arg with true/ false.
198 |
199 | #### Signature
200 |
201 | ```python
202 | def _helpFlagWidget(self, item: Item) -> list[Any]: ...
203 | ```
204 |
205 | #### See also
206 |
207 | - [Item](../models.md#item)
208 |
209 | ### PySimpleGUIWrapper()._helpTextWidget
210 |
211 | [Show source in pysimplegui_wrapper.py:230](../../../../cli2gui/gui/pysimplegui_wrapper.py#L230)
212 |
213 | Return a set of self that make up an arg with text.
214 |
215 | #### Signature
216 |
217 | ```python
218 | def _helpTextWidget(self, item: Item) -> list[Any]: ...
219 | ```
220 |
221 | #### See also
222 |
223 | - [Item](../models.md#item)
224 |
225 | ### PySimpleGUIWrapper()._inputText
226 |
227 | [Show source in pysimplegui_wrapper.py:78](../../../../cli2gui/gui/pysimplegui_wrapper.py#L78)
228 |
229 | Return an input text field.
230 |
231 | #### Signature
232 |
233 | ```python
234 | def _inputText(self, key: str, default: str | None = None) -> Any: ...
235 | ```
236 |
237 | ### PySimpleGUIWrapper()._label
238 |
239 | [Show source in pysimplegui_wrapper.py:118](../../../../cli2gui/gui/pysimplegui_wrapper.py#L118)
240 |
241 | Return a label.
242 |
243 | #### Signature
244 |
245 | ```python
246 | def _label(self, text: str, font: int = 11) -> Any: ...
247 | ```
248 |
249 | ### PySimpleGUIWrapper()._spin
250 |
251 | [Show source in pysimplegui_wrapper.py:88](../../../../cli2gui/gui/pysimplegui_wrapper.py#L88)
252 |
253 | Return an input text field.
254 |
255 | #### Signature
256 |
257 | ```python
258 | def _spin(self, key: str, default: str | None = None) -> Any: ...
259 | ```
260 |
261 | ### PySimpleGUIWrapper()._title
262 |
263 | [Show source in pysimplegui_wrapper.py:199](../../../../cli2gui/gui/pysimplegui_wrapper.py#L199)
264 |
265 | Return a set of self that make up the application header.
266 |
267 | #### Signature
268 |
269 | ```python
270 | def _title(self, text: str, image: str = "") -> list[Any]: ...
271 | ```
272 |
273 | ### PySimpleGUIWrapper().addItemsAndGroups
274 |
275 | [Show source in pysimplegui_wrapper.py:371](../../../../cli2gui/gui/pysimplegui_wrapper.py#L371)
276 |
277 | Items and groups and return a list of psg Elements.
278 |
279 | #### Arguments
280 |
281 | - `section` *Group* - section with a name to display and items
282 |
283 | #### Returns
284 |
285 | Type: *list[list[Element]]*
286 | updated argConstruct
287 |
288 | #### Signature
289 |
290 | ```python
291 | def addItemsAndGroups(self, section: Group) -> list[list[Any]]: ...
292 | ```
293 |
294 | #### See also
295 |
296 | - [Group](../models.md#group)
297 |
298 | ### PySimpleGUIWrapper().addWidgetFromItem
299 |
300 | [Show source in pysimplegui_wrapper.py:287](../../../../cli2gui/gui/pysimplegui_wrapper.py#L287)
301 |
302 | Select a widget based on the item type.
303 |
304 | #### Arguments
305 |
306 | - `item` *Item* - the item
307 |
308 | #### Signature
309 |
310 | ```python
311 | def addWidgetFromItem(self, item: Item) -> list[Any]: ...
312 | ```
313 |
314 | #### See also
315 |
316 | - [Item](../models.md#item)
317 |
318 | ### PySimpleGUIWrapper().createLayout
319 |
320 | [Show source in pysimplegui_wrapper.py:396](../../../../cli2gui/gui/pysimplegui_wrapper.py#L396)
321 |
322 | Create the pysimplegui layout from the build spec.
323 |
324 | #### Arguments
325 |
326 | - `buildSpec` *FullBuildSpec* - build spec containing widget
327 | :param str | list[str] menu: menu definition. containing menu keys
328 |
329 | #### Returns
330 |
331 | Type: *list[list[Any]]*
332 | list of self (layout list)
333 |
334 | #### Signature
335 |
336 | ```python
337 | def createLayout(
338 | self, buildSpec: FullBuildSpec, menu: str | list[str]
339 | ) -> list[list[Any]]: ...
340 | ```
341 |
342 | #### See also
343 |
344 | - [FullBuildSpec](../models.md#fullbuildspec)
345 |
346 | ### PySimpleGUIWrapper().generatePopup
347 |
348 | [Show source in pysimplegui_wrapper.py:311](../../../../cli2gui/gui/pysimplegui_wrapper.py#L311)
349 |
350 | Create the popup window.
351 |
352 | #### Arguments
353 |
354 | ----
355 | - `buildSpec` *FullBuildSpec* - [description]
356 | values (Union[dict[Any, Any]): Returned when a button is clicked. Such
357 | as the menu
358 |
359 | #### Returns
360 |
361 | -------
362 | - `Window` - A PySimpleGui Window
363 |
364 | #### Signature
365 |
366 | ```python
367 | def generatePopup(
368 | self, buildSpec: FullBuildSpec, values: dict[Any, Any] | list[Any]
369 | ) -> Any: ...
370 | ```
371 |
372 | #### See also
373 |
374 | - [FullBuildSpec](../models.md#fullbuildspec)
375 |
376 | ### PySimpleGUIWrapper().getImgData
377 |
378 | [Show source in pysimplegui_wrapper.py:508](../../../../cli2gui/gui/pysimplegui_wrapper.py#L508)
379 |
380 | Generate image data using PIL.
381 |
382 | #### Signature
383 |
384 | ```python
385 | def getImgData(self, imagePath: str, first: bool = False) -> bytes: ...
386 | ```
387 |
388 | ### PySimpleGUIWrapper().main
389 |
390 | [Show source in pysimplegui_wrapper.py:463](../../../../cli2gui/gui/pysimplegui_wrapper.py#L463)
391 |
392 | Run the gui (psg) with a given buildSpec, quit_callback, and run_callback.
393 |
394 | #### Arguments
395 |
396 | - `buildSpec` *FullBuildSpec* - Full cli parse/ build spec
397 | :param Callable[[], None] quit_callback: generic callable used to quit
398 | :param Callable[[dict[str, Any]], None] run_callback: generic callable used to run
399 |
400 | #### Signature
401 |
402 | ```python
403 | def main(
404 | self,
405 | buildSpec: FullBuildSpec,
406 | quit_callback: Callable[[], None],
407 | run_callback: Callable[[dict[str, Any]], None],
408 | ) -> None: ...
409 | ```
410 |
411 | #### See also
412 |
413 | - [FullBuildSpec](../models.md#fullbuildspec)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/index.md:
--------------------------------------------------------------------------------
1 | # Cli2gui
2 |
3 | [Cli2gui Index](../README.md#cli2gui-index) / Cli2gui
4 |
5 | > Auto-generated documentation for [cli2gui](../../../cli2gui/__init__.py) module.
6 |
7 | - [Cli2gui](#cli2gui)
8 | - [Modules](#modules)
9 |
10 | ## Modules
11 |
12 | - [Application](application/index.md)
13 | - [Decorators](./decorators.md)
14 | - [Gui](gui/index.md)
15 | - [Models](./models.md)
16 | - [Tojson](tojson/index.md)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/models.md:
--------------------------------------------------------------------------------
1 | # Models
2 |
3 | [Cli2gui Index](../README.md#cli2gui-index) / [Cli2gui](./index.md#cli2gui) / Models
4 |
5 | > Auto-generated documentation for [cli2gui.models](../../../cli2gui/models.py) module.
6 |
7 | - [Models](#models)
8 | - [BuildSpec](#buildspec)
9 | - [FullBuildSpec](#fullbuildspec)
10 | - [GUIType](#guitype)
11 | - [Group](#group)
12 | - [Item](#item)
13 | - [ItemType](#itemtype)
14 | - [ParserRep](#parserrep)
15 | - [ParserType](#parsertype)
16 |
17 | ## BuildSpec
18 |
19 | [Show source in models.py:14](../../../cli2gui/models.py#L14)
20 |
21 | Representation for the BuildSpec.
22 |
23 | #### Signature
24 |
25 | ```python
26 | class BuildSpec: ...
27 | ```
28 |
29 |
30 |
31 | ## FullBuildSpec
32 |
33 | [Show source in models.py:80](../../../cli2gui/models.py#L80)
34 |
35 | Representation for the FullBuildSpec (BuildSpec + ParserRep).
36 |
37 | #### Signature
38 |
39 | ```python
40 | class FullBuildSpec: ...
41 | ```
42 |
43 |
44 |
45 | ## GUIType
46 |
47 | [Show source in models.py:113](../../../cli2gui/models.py#L113)
48 |
49 | Supported gui types.
50 |
51 | #### Signature
52 |
53 | ```python
54 | class GUIType(str, Enum): ...
55 | ```
56 |
57 |
58 |
59 | ## Group
60 |
61 | [Show source in models.py:63](../../../cli2gui/models.py#L63)
62 |
63 | Representation for an argument group.
64 |
65 | #### Signature
66 |
67 | ```python
68 | class Group: ...
69 | ```
70 |
71 |
72 |
73 | ## Item
74 |
75 | [Show source in models.py:30](../../../cli2gui/models.py#L30)
76 |
77 | Representation for an arg_item.
78 |
79 | #### Signature
80 |
81 | ```python
82 | class Item: ...
83 | ```
84 |
85 |
86 |
87 | ## ItemType
88 |
89 | [Show source in models.py:45](../../../cli2gui/models.py#L45)
90 |
91 | Enum of ItemTypes.
92 |
93 | #### Signature
94 |
95 | ```python
96 | class ItemType(Enum): ...
97 | ```
98 |
99 |
100 |
101 | ## ParserRep
102 |
103 | [Show source in models.py:72](../../../cli2gui/models.py#L72)
104 |
105 | Representation for a parser.
106 |
107 | #### Signature
108 |
109 | ```python
110 | class ParserRep: ...
111 | ```
112 |
113 |
114 |
115 | ## ParserType
116 |
117 | [Show source in models.py:98](../../../cli2gui/models.py#L98)
118 |
119 | Supported parser types.
120 |
121 | #### Signature
122 |
123 | ```python
124 | class ParserType(str, Enum): ...
125 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/tojson/argparse2json.md:
--------------------------------------------------------------------------------
1 | # Argparse2json
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Tojson](./index.md#tojson) / Argparse2json
4 |
5 | > Auto-generated documentation for [cli2gui.tojson.argparse2json](../../../../cli2gui/tojson/argparse2json.py) module.
6 |
7 | - [Argparse2json](#argparse2json)
8 | - [ArgparseGroup](#argparsegroup)
9 | - [actionToJson](#actiontojson)
10 | - [buildRadioGroup](#buildradiogroup)
11 | - [categorizeGroups](#categorizegroups)
12 | - [categorizeItems](#categorizeitems)
13 | - [chooseName](#choosename)
14 | - [containsActions](#containsactions)
15 | - [convert](#convert)
16 | - [extractRawGroups](#extractrawgroups)
17 | - [fileActionToJson](#fileactiontojson)
18 | - [isDefaultProgname](#isdefaultprogname)
19 | - [iterParsers](#iterparsers)
20 | - [process](#process)
21 | - [reapplyMutexGroups](#reapplymutexgroups)
22 | - [stripEmpty](#stripempty)
23 |
24 | ## ArgparseGroup
25 |
26 | [Show source in argparse2json.py:26](../../../../cli2gui/tojson/argparse2json.py#L26)
27 |
28 | Class to represent an ArgparseGroup.
29 |
30 | #### Signature
31 |
32 | ```python
33 | class ArgparseGroup(TypedDict): ...
34 | ```
35 |
36 |
37 |
38 | ## actionToJson
39 |
40 | [Show source in argparse2json.py:123](../../../../cli2gui/tojson/argparse2json.py#L123)
41 |
42 | Generate json for an action and set the widget - used by the application.
43 |
44 | #### Signature
45 |
46 | ```python
47 | def actionToJson(action: argparse.Action, widget: ItemType) -> Item: ...
48 | ```
49 |
50 | #### See also
51 |
52 | - [ItemType](../models.md#itemtype)
53 | - [Item](../models.md#item)
54 |
55 |
56 |
57 | ## buildRadioGroup
58 |
59 | [Show source in argparse2json.py:137](../../../../cli2gui/tojson/argparse2json.py#L137)
60 |
61 | Create a radio group for a mutex group of arguments.
62 |
63 | #### Signature
64 |
65 | ```python
66 | def buildRadioGroup(mutexGroup: _MutuallyExclusiveGroup) -> Item: ...
67 | ```
68 |
69 | #### See also
70 |
71 | - [Item](../models.md#item)
72 |
73 |
74 |
75 | ## categorizeGroups
76 |
77 | [Show source in argparse2json.py:181](../../../../cli2gui/tojson/argparse2json.py#L181)
78 |
79 | Categorize the parser groups and arg_items.
80 |
81 | #### Signature
82 |
83 | ```python
84 | def categorizeGroups(groups: list[ArgparseGroup]) -> list[Group]: ...
85 | ```
86 |
87 | #### See also
88 |
89 | - [ArgparseGroup](#argparsegroup)
90 | - [Group](../models.md#group)
91 |
92 |
93 |
94 | ## categorizeItems
95 |
96 | [Show source in argparse2json.py:151](../../../../cli2gui/tojson/argparse2json.py#L151)
97 |
98 | Catergorise each action and generate json.
99 |
100 | #### Signature
101 |
102 | ```python
103 | def categorizeItems(actions: list[argparse.Action]) -> Generator[Item, None, None]: ...
104 | ```
105 |
106 | #### See also
107 |
108 | - [Item](../models.md#item)
109 |
110 |
111 |
112 | ## chooseName
113 |
114 | [Show source in argparse2json.py:55](../../../../cli2gui/tojson/argparse2json.py#L55)
115 |
116 | Get the program name.
117 |
118 | #### Signature
119 |
120 | ```python
121 | def chooseName(name: str, subparser: argparse.ArgumentParser) -> str: ...
122 | ```
123 |
124 |
125 |
126 | ## containsActions
127 |
128 | [Show source in argparse2json.py:60](../../../../cli2gui/tojson/argparse2json.py#L60)
129 |
130 | Check if any actions(a) are present in actions(b).
131 |
132 | #### Signature
133 |
134 | ```python
135 | def containsActions(
136 | actionA: list[argparse.Action], actionB: list[argparse.Action]
137 | ) -> set[argparse.Action]: ...
138 | ```
139 |
140 |
141 |
142 | ## convert
143 |
144 | [Show source in argparse2json.py:208](../../../../cli2gui/tojson/argparse2json.py#L208)
145 |
146 | Convert argparse to a dict.
147 |
148 | #### Arguments
149 |
150 | ----
151 | - `parser` *argparse.ArgumentParser* - argparse parser
152 |
153 | #### Returns
154 |
155 | -------
156 | - `ParserRep` - dictionary representing parser object
157 |
158 | #### Signature
159 |
160 | ```python
161 | def convert(parser: argparse.ArgumentParser) -> ParserRep: ...
162 | ```
163 |
164 | #### See also
165 |
166 | - [ParserRep](../models.md#parserrep)
167 |
168 |
169 |
170 | ## extractRawGroups
171 |
172 | [Show source in argparse2json.py:99](../../../../cli2gui/tojson/argparse2json.py#L99)
173 |
174 | Recursively extract argument groups and associated actions from ParserGroup objects.
175 |
176 | #### Signature
177 |
178 | ```python
179 | def extractRawGroups(actionGroup: argparse._ArgumentGroup) -> ArgparseGroup: ...
180 | ```
181 |
182 | #### See also
183 |
184 | - [ArgparseGroup](#argparsegroup)
185 |
186 |
187 |
188 | ## fileActionToJson
189 |
190 | [Show source in argparse2json.py:111](../../../../cli2gui/tojson/argparse2json.py#L111)
191 |
192 | Convert an action of type Path or argparse.FileType to an Item.
193 |
194 | #### Signature
195 |
196 | ```python
197 | def fileActionToJson(action: argparse.Action, widget: ItemType) -> Item: ...
198 | ```
199 |
200 | #### See also
201 |
202 | - [ItemType](../models.md#itemtype)
203 | - [Item](../models.md#item)
204 |
205 |
206 |
207 | ## isDefaultProgname
208 |
209 | [Show source in argparse2json.py:50](../../../../cli2gui/tojson/argparse2json.py#L50)
210 |
211 | Identify if the passed name is the default program name.
212 |
213 | #### Signature
214 |
215 | ```python
216 | def isDefaultProgname(name: str, subparser: argparse.ArgumentParser) -> bool: ...
217 | ```
218 |
219 |
220 |
221 | ## iterParsers
222 |
223 | [Show source in argparse2json.py:34](../../../../cli2gui/tojson/argparse2json.py#L34)
224 |
225 | Iterate over name, parser pairs.
226 |
227 | #### Signature
228 |
229 | ```python
230 | def iterParsers(
231 | parser: argparse.ArgumentParser,
232 | ) -> list[tuple[str, argparse.ArgumentParser]]: ...
233 | ```
234 |
235 |
236 |
237 | ## process
238 |
239 | [Show source in argparse2json.py:198](../../../../cli2gui/tojson/argparse2json.py#L198)
240 |
241 | Reapply the mutex groups and then categorize them and the arg_items under the parser.
242 |
243 | #### Signature
244 |
245 | ```python
246 | def process(parser: argparse.ArgumentParser) -> list[Group]: ...
247 | ```
248 |
249 | #### See also
250 |
251 | - [Group](../models.md#group)
252 |
253 |
254 |
255 | ## reapplyMutexGroups
256 |
257 | [Show source in argparse2json.py:67](../../../../cli2gui/tojson/argparse2json.py#L67)
258 |
259 | _argparse stores mutually exclusive groups independently.
260 | of all other groups. So, they must be manually re-combined
261 | with the groups/subgroups to which they were originally declared
262 | in order to have them appear in the correct location in the UI.
263 |
264 | Order is attempted to be preserved by inserting the MutexGroup
265 | into the _actions list at the first occurrence of any item
266 | where the two groups intersect.
267 |
268 | #### Signature
269 |
270 | ```python
271 | def reapplyMutexGroups(
272 | mutexGroups: list[argparse._MutuallyExclusiveGroup], actionGroups: list[Any]
273 | ) -> list[Any]: ...
274 | ```
275 |
276 |
277 |
278 | ## stripEmpty
279 |
280 | [Show source in argparse2json.py:193](../../../../cli2gui/tojson/argparse2json.py#L193)
281 |
282 | Remove groups where group['arg_items'] is false.
283 |
284 | #### Signature
285 |
286 | ```python
287 | def stripEmpty(groups: list[ArgparseGroup]) -> list[ArgparseGroup]: ...
288 | ```
289 |
290 | #### See also
291 |
292 | - [ArgparseGroup](#argparsegroup)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/tojson/click2json.md:
--------------------------------------------------------------------------------
1 | # Click2json
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Tojson](./index.md#tojson) / Click2json
4 |
5 | > Auto-generated documentation for [cli2gui.tojson.click2json](../../../../cli2gui/tojson/click2json.py) module.
6 |
7 | - [Click2json](#click2json)
8 | - [actionToJson](#actiontojson)
9 | - [categorize](#categorize)
10 | - [convert](#convert)
11 | - [extract](#extract)
12 |
13 | ## actionToJson
14 |
15 | [Show source in click2json.py:29](../../../../cli2gui/tojson/click2json.py#L29)
16 |
17 | Generate json for an action and set the widget - used by the application.
18 |
19 | #### Signature
20 |
21 | ```python
22 | def actionToJson(action: Any, widget: ItemType, other: dict | None = None) -> Item: ...
23 | ```
24 |
25 | #### See also
26 |
27 | - [ItemType](../models.md#itemtype)
28 | - [Item](../models.md#item)
29 |
30 |
31 |
32 | ## categorize
33 |
34 | [Show source in click2json.py:47](../../../../cli2gui/tojson/click2json.py#L47)
35 |
36 | Catergorise each action and generate json.
37 |
38 | #### Signature
39 |
40 | ```python
41 | def categorize(actions: list[Any]) -> Generator[Item, None, None]: ...
42 | ```
43 |
44 | #### See also
45 |
46 | - [Item](../models.md#item)
47 |
48 |
49 |
50 | ## convert
51 |
52 | [Show source in click2json.py:66](../../../../cli2gui/tojson/click2json.py#L66)
53 |
54 | Convert click to a dict.
55 |
56 | #### Arguments
57 |
58 | ----
59 | - `parser` *click.core.Command* - click parser
60 |
61 | #### Returns
62 |
63 | -------
64 | - `ParserRep` - dictionary representing parser object
65 |
66 | #### Signature
67 |
68 | ```python
69 | def convert(parser: Any) -> ParserRep: ...
70 | ```
71 |
72 | #### See also
73 |
74 | - [ParserRep](../models.md#parserrep)
75 |
76 |
77 |
78 | ## extract
79 |
80 | [Show source in click2json.py:11](../../../../cli2gui/tojson/click2json.py#L11)
81 |
82 | Get the actions as json for the parser.
83 |
84 | #### Signature
85 |
86 | ```python
87 | def extract(parser: Any) -> list[Group]: ...
88 | ```
89 |
90 | #### See also
91 |
92 | - [Group](../models.md#group)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/tojson/docopt2json.md:
--------------------------------------------------------------------------------
1 | # Docopt2json
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Tojson](./index.md#tojson) / Docopt2json
4 |
5 | > Auto-generated documentation for [cli2gui.tojson.docopt2json](../../../../cli2gui/tojson/docopt2json.py) module.
6 |
7 | - [Docopt2json](#docopt2json)
8 | - [actionToJson](#actiontojson)
9 | - [categorize](#categorize)
10 | - [convert](#convert)
11 | - [extract](#extract)
12 | - [parse](#parse)
13 | - [parseOpt](#parseopt)
14 | - [parsePos](#parsepos)
15 | - [parseSection](#parsesection)
16 |
17 | ## actionToJson
18 |
19 | [Show source in docopt2json.py:11](../../../../cli2gui/tojson/docopt2json.py#L11)
20 |
21 | Generate json for an action and set the widget - used by the application.
22 |
23 | #### Signature
24 |
25 | ```python
26 | def actionToJson(
27 | action: tuple[str, str, int, Any, str], widget: ItemType, isPos: bool
28 | ) -> Item: ...
29 | ```
30 |
31 | #### See also
32 |
33 | - [ItemType](../models.md#itemtype)
34 | - [Item](../models.md#item)
35 |
36 |
37 |
38 | ## categorize
39 |
40 | [Show source in docopt2json.py:37](../../../../cli2gui/tojson/docopt2json.py#L37)
41 |
42 | Catergorise each action and generate json.
43 |
44 | Each action is in the form (short, long, argcount, value, help_message)
45 |
46 | #### Signature
47 |
48 | ```python
49 | def categorize(
50 | actions: list[tuple[str, str, int, Any, str]], isPos: bool = False
51 | ) -> Iterator[Item]: ...
52 | ```
53 |
54 | #### See also
55 |
56 | - [Item](../models.md#item)
57 |
58 |
59 |
60 | ## convert
61 |
62 | [Show source in docopt2json.py:117](../../../../cli2gui/tojson/docopt2json.py#L117)
63 |
64 | Convert getopt to a dict.
65 |
66 | #### Arguments
67 |
68 | ----
69 | - `parser` *Any* - docopt parser
70 |
71 | #### Returns
72 |
73 | -------
74 | - `ParserRep` - dictionary representing parser object
75 |
76 | #### Signature
77 |
78 | ```python
79 | def convert(parser: Any) -> ParserRep: ...
80 | ```
81 |
82 | #### See also
83 |
84 | - [ParserRep](../models.md#parserrep)
85 |
86 |
87 |
88 | ## extract
89 |
90 | [Show source in docopt2json.py:55](../../../../cli2gui/tojson/docopt2json.py#L55)
91 |
92 | Get the actions as json for the parser.
93 |
94 | #### Signature
95 |
96 | ```python
97 | def extract(parser: Any) -> list[Group]: ...
98 | ```
99 |
100 | #### See also
101 |
102 | - [Group](../models.md#group)
103 |
104 |
105 |
106 | ## parse
107 |
108 | [Show source in docopt2json.py:76](../../../../cli2gui/tojson/docopt2json.py#L76)
109 |
110 | Parse an option help text, adapted from docopt.
111 |
112 | #### Signature
113 |
114 | ```python
115 | def parse(optionDescription: str) -> tuple[str, str, int, Any, str]: ...
116 | ```
117 |
118 |
119 |
120 | ## parseOpt
121 |
122 | [Show source in docopt2json.py:94](../../../../cli2gui/tojson/docopt2json.py#L94)
123 |
124 | Parse an option help text, adapted from docopt.
125 |
126 | #### Signature
127 |
128 | ```python
129 | def parseOpt(doc: Any) -> list[tuple[str, str, int, Any, str]]: ...
130 | ```
131 |
132 |
133 |
134 | ## parsePos
135 |
136 | [Show source in docopt2json.py:106](../../../../cli2gui/tojson/docopt2json.py#L106)
137 |
138 | Parse positional arguments from docstring.
139 |
140 | #### Signature
141 |
142 | ```python
143 | def parsePos(doc: str) -> list[tuple[str, str]]: ...
144 | ```
145 |
146 |
147 |
148 | ## parseSection
149 |
150 | [Show source in docopt2json.py:67](../../../../cli2gui/tojson/docopt2json.py#L67)
151 |
152 | Taken from docopt.
153 |
154 | #### Signature
155 |
156 | ```python
157 | def parseSection(name: str, source: str) -> list[str]: ...
158 | ```
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/tojson/getopt2json.md:
--------------------------------------------------------------------------------
1 | # Getopt2json
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Tojson](./index.md#tojson) / Getopt2json
4 |
5 | > Auto-generated documentation for [cli2gui.tojson.getopt2json](../../../../cli2gui/tojson/getopt2json.py) module.
6 |
7 | - [Getopt2json](#getopt2json)
8 | - [actionToJson](#actiontojson)
9 | - [catLong](#catlong)
10 | - [catShort](#catshort)
11 | - [convert](#convert)
12 | - [process](#process)
13 |
14 | ## actionToJson
15 |
16 | [Show source in getopt2json.py:13](../../../../cli2gui/tojson/getopt2json.py#L13)
17 |
18 | Convert an arg to json, behave in the same way as argparse hence the large
19 | amount of duplication.
20 |
21 | #### Signature
22 |
23 | ```python
24 | def actionToJson(action: str, widget: ItemType, short: bool = True) -> Item: ...
25 | ```
26 |
27 | #### See also
28 |
29 | - [ItemType](../models.md#itemtype)
30 | - [Item](../models.md#item)
31 |
32 |
33 |
34 | ## catLong
35 |
36 | [Show source in getopt2json.py:28](../../../../cli2gui/tojson/getopt2json.py#L28)
37 |
38 | Categorize long args.
39 |
40 | #### Signature
41 |
42 | ```python
43 | def catLong(actions: list[str]) -> Generator[Item, None, None]: ...
44 | ```
45 |
46 | #### See also
47 |
48 | - [Item](../models.md#item)
49 |
50 |
51 |
52 | ## catShort
53 |
54 | [Show source in getopt2json.py:38](../../../../cli2gui/tojson/getopt2json.py#L38)
55 |
56 | Categorize short args.
57 |
58 | #### Signature
59 |
60 | ```python
61 | def catShort(actions: list[str]) -> Generator[Item, None, None]: ...
62 | ```
63 |
64 | #### See also
65 |
66 | - [Item](../models.md#item)
67 |
68 |
69 |
70 | ## convert
71 |
72 | [Show source in getopt2json.py:64](../../../../cli2gui/tojson/getopt2json.py#L64)
73 |
74 | Convert getopt to a dict.
75 |
76 | #### Arguments
77 |
78 | ----
79 | parser (tuple[list[str], list[str]]): getopt parser
80 |
81 | #### Returns
82 |
83 | -------
84 | - `ParserRep` - dictionary representing parser object
85 |
86 | #### Signature
87 |
88 | ```python
89 | def convert(parser: tuple[list[str], list[str]]) -> ParserRep: ...
90 | ```
91 |
92 | #### See also
93 |
94 | - [ParserRep](../models.md#parserrep)
95 |
96 |
97 |
98 | ## process
99 |
100 | [Show source in getopt2json.py:55](../../../../cli2gui/tojson/getopt2json.py#L55)
101 |
102 | Generate a group (or section).
103 |
104 | #### Signature
105 |
106 | ```python
107 | def process(
108 | group: list[str],
109 | groupName: str,
110 | categorize: Callable[[list[str]], Generator[Item, None, None]],
111 | ) -> list[Group]: ...
112 | ```
113 |
114 | #### See also
115 |
116 | - [Group](../models.md#group)
117 | - [Item](../models.md#item)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/tojson/index.md:
--------------------------------------------------------------------------------
1 | # Tojson
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / Tojson
4 |
5 | > Auto-generated documentation for [cli2gui.tojson](../../../../cli2gui/tojson/__init__.py) module.
6 |
7 | - [Tojson](#tojson)
8 | - [Modules](#modules)
9 |
10 | ## Modules
11 |
12 | - [Argparse2json](./argparse2json.md)
13 | - [Click2json](./click2json.md)
14 | - [Docopt2json](./docopt2json.md)
15 | - [Getopt2json](./getopt2json.md)
16 | - [Optparse2json](./optparse2json.md)
--------------------------------------------------------------------------------
/documentation/reference/cli2gui/tojson/optparse2json.md:
--------------------------------------------------------------------------------
1 | # Optparse2json
2 |
3 | [Cli2gui Index](../../README.md#cli2gui-index) / [Cli2gui](../index.md#cli2gui) / [Tojson](./index.md#tojson) / Optparse2json
4 |
5 | > Auto-generated documentation for [cli2gui.tojson.optparse2json](../../../../cli2gui/tojson/optparse2json.py) module.
6 |
7 | - [Optparse2json](#optparse2json)
8 | - [actionToJson](#actiontojson)
9 | - [categorize](#categorize)
10 | - [convert](#convert)
11 | - [extractGroups](#extractgroups)
12 | - [extractOptions](#extractoptions)
13 |
14 | ## actionToJson
15 |
16 | [Show source in optparse2json.py:40](../../../../cli2gui/tojson/optparse2json.py#L40)
17 |
18 | Generate json for an action and set the widget - used by the application.
19 |
20 | #### Signature
21 |
22 | ```python
23 | def actionToJson(action: optparse.Option, widget: ItemType) -> Item: ...
24 | ```
25 |
26 | #### See also
27 |
28 | - [ItemType](../models.md#itemtype)
29 | - [Item](../models.md#item)
30 |
31 |
32 |
33 | ## categorize
34 |
35 | [Show source in optparse2json.py:58](../../../../cli2gui/tojson/optparse2json.py#L58)
36 |
37 | Catergorise each action and generate json.
38 |
39 | #### Signature
40 |
41 | ```python
42 | def categorize(actions: list[optparse.Option]) -> Generator[Item, None, None]: ...
43 | ```
44 |
45 | #### See also
46 |
47 | - [Item](../models.md#item)
48 |
49 |
50 |
51 | ## convert
52 |
53 | [Show source in optparse2json.py:73](../../../../cli2gui/tojson/optparse2json.py#L73)
54 |
55 | Convert argparse to a dict.
56 |
57 | #### Arguments
58 |
59 | ----
60 | - `parser` *optparse.OptionParser* - optparse parser
61 |
62 | #### Returns
63 |
64 | -------
65 | - `ParserRep` - dictionary representing parser object
66 |
67 | #### Signature
68 |
69 | ```python
70 | def convert(parser: optparse.OptionParser) -> ParserRep: ...
71 | ```
72 |
73 | #### See also
74 |
75 | - [ParserRep](../models.md#parserrep)
76 |
77 |
78 |
79 | ## extractGroups
80 |
81 | [Show source in optparse2json.py:28](../../../../cli2gui/tojson/optparse2json.py#L28)
82 |
83 | Get the actions as json for each item and group under the parser.
84 |
85 | #### Signature
86 |
87 | ```python
88 | def extractGroups(parser: optparse.OptionParser) -> Group: ...
89 | ```
90 |
91 | #### See also
92 |
93 | - [Group](../models.md#group)
94 |
95 |
96 |
97 | ## extractOptions
98 |
99 | [Show source in optparse2json.py:15](../../../../cli2gui/tojson/optparse2json.py#L15)
100 |
101 | Get the actions as json for each item under a group.
102 |
103 | #### Signature
104 |
105 | ```python
106 | def extractOptions(optionGroup: optparse.OptionGroup) -> Group: ...
107 | ```
108 |
109 | #### See also
110 |
111 | - [Group](../models.md#group)
--------------------------------------------------------------------------------
/documentation/tutorials/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Tutorial: How to Use `Cli2Gui` in Your Project
4 |
5 | `Cli2Gui` is a Python package that allows you to transform command-line interface (CLI) applications
6 | into graphical user interfaces (GUIs). It supports popular parsers like `argparse`, and different
7 | GUI libraries, like `freesimplegui`. In this tutorial, we will walk through how to integrate
8 | `Cli2Gui` into your Python project.
9 |
10 | - [Setup](#setup)
11 | - [Basic Usage](#basic-usage)
12 | - [Basic CLI Application](#basic-cli-application)
13 | - [Adding `Cli2Gui`](#adding-cli2gui)
14 | - [Advanced Example with Argument Groups](#advanced-example-with-argument-groups)
15 | - [Customizing the GUI](#customizing-the-gui)
16 |
17 | ## Setup
18 |
19 | For the examples below, install the `Cli2Gui` package by running:
20 |
21 | ```bash
22 | pip install cli2gui[fsg]
23 | ```
24 |
25 | Note: If you need the functionality provided by these libraries, use the following extras:
26 |
27 | - **psg**: For PySimpleGUI support, adding easy-to-build, functional GUIs.
28 | - **fsg**: For FreeSimpleGUI, a lightweight, streamlined GUI option.
29 | - **web**: To run your app in a web browser via PySimpleGUIWeb.
30 | - **qt**: For PySimpleGUIQt, creating polished, native desktop GUIs.
31 | - **pandoc**: To pretty print markdown files and similar
32 |
33 | ## Basic Usage
34 |
35 | The primary way to use `Cli2Gui` is through a decorator that you add to your main function. The
36 | decorator transforms your CLI into a GUI when necessary.
37 |
38 | Here's a simple example of how to integrate `Cli2Gui` into a basic program:
39 |
40 | ### Basic CLI Application
41 |
42 | ```python
43 | import argparse
44 |
45 | def run(args):
46 | print(args.arg)
47 |
48 | def main():
49 | parser = argparse.ArgumentParser(description="Example parser")
50 | parser.add_argument("arg", type=str, help="Positional argument")
51 | args = parser.parse_args()
52 | run(args)
53 |
54 | if __name__ == "__main__":
55 | main()
56 | ```
57 |
58 | In this basic example, we have a CLI app that takes a positional argument. Next, we will enhance
59 | it with `Cli2Gui` to provide a GUI option.
60 |
61 | ### Adding `Cli2Gui`
62 |
63 | We'll now decorate the `main` function with `Cli2Gui`:
64 |
65 | ```python
66 | from cli2gui import Cli2Gui
67 |
68 | # Define the function that will handle the program's logic
69 | def run(args):
70 | print(args.arg)
71 |
72 | # Use Cli2Gui as a decorator to convert CLI into a GUI, using freesimplegui
73 | @Cli2Gui(run_function=run, gui="freesimplegui")
74 | def main():
75 | parser = argparse.ArgumentParser(description="Example parser with GUI support")
76 | parser.add_argument("arg", type=str, help="Positional argument")
77 | args = parser.parse_args()
78 | run(args)
79 |
80 | if __name__ == "__main__":
81 | main()
82 | ```
83 |
84 | The `Cli2Gui` decorator wraps around the `main` function and adds support for both CLI and GUI.
85 | You can now run this program in two ways:
86 |
87 | - **CLI Mode**: `python3 main.py`
88 | - **GUI Mode**: run `python3 main.py --cli2gui` and the GUI will appear, allowing you to enter
89 | the arguments interactively.
90 |
91 | ## Advanced Example with Argument Groups
92 |
93 | Let’s expand on the previous example with a more complex argument structure, including mutually
94 | exclusive groups, optional arguments, and file inputs. We will also demonstrate how to add a
95 | menu to the GUI.
96 |
97 | ```python
98 | from cli2gui import Cli2Gui
99 | import argparse
100 | from pathlib import Path
101 |
102 | # Define the function that will handle the program's logic
103 | def handle(args):
104 | print(args)
105 |
106 | # Define the CLI function with advanced arguments
107 | @Cli2Gui(
108 | run_function=handle,
109 | gui="freesimplegui",
110 | menu={
111 | "File": "path/to/file.md",
112 | "Another File": "path/to/another_file.md",
113 | }
114 | )
115 | def cli():
116 | parser = argparse.ArgumentParser(description="Advanced CLI with GUI support")
117 |
118 | # Positional arguments
119 | parser.add_argument("positional", help="Positional argument")
120 | parser.add_argument("positional_file", type=argparse.FileType("r"), help="Positional file input")
121 |
122 | # Optional arguments
123 | parser.add_argument("--optional", help="Optional argument")
124 | parser.add_argument("--store-true", action="store_true", help="Store true")
125 | parser.add_argument("--store-false", action="store_false", help="Store false")
126 | parser.add_argument("--store", help="Store value")
127 | parser.add_argument("--count", action="count", help="Count occurrences")
128 | parser.add_argument("--choices", choices=["choice1", "choice2"], help="Pick a choice")
129 | parser.add_argument("--somefile", type=argparse.FileType("r"), help="Optional file input")
130 |
131 | # Mutually exclusive group
132 | group = parser.add_argument_group("Image options")
133 | mxg = group.add_mutually_exclusive_group()
134 | mxg.add_argument("--mxg-true", action="store_true", help="Mutually exclusive store true")
135 | mxg.add_argument("--mxg-false", action="store_false", help="Mutually exclusive store false")
136 | mxg.add_argument("--mxg", help="Mutually exclusive store")
137 | mxg.add_argument("--mxg-count", action="count", help="Mutually exclusive count")
138 | mxg.add_argument("--mxg-choices", choices=["choice1", "choice2"], help="Mutually exclusive choice")
139 |
140 | args = parser.parse_args()
141 | handle(args)
142 |
143 | if __name__ == "__main__":
144 | cli()
145 | ```
146 |
147 | In this example:
148 |
149 | - **run_function**: The function to be executed when the user clicks the "Run" button in the GUI. This is the `handle()` function
150 | - **gui**: Specify the GUI framework to use. Supported options are: `"dearpygui"`, `"pysimplegui"`, `"pysimpleguiqt"`, `"pysimpleguiweb"`, `"freesimplegui"`. Defaults to `"dearpygui"`. In the example, this is `"freesimplegui"`.
151 | - **menu**: Add a custom menu to the GUI. Example: `{"File": "/path/to/file.md"}`.
152 |
153 | ## Customizing the GUI
154 |
155 | You can customize various aspects of the GUI, including the theme, icon, and program name. Here’s
156 | how you can apply some customization options:
157 |
158 | ```python
159 | @Cli2Gui(
160 | run_function=handle,
161 | gui="freesimplegui",
162 | theme="one-light.yaml",
163 | darkTheme="one-dark.yaml",
164 | image="path/to/icon.png",
165 | program_name="My Custom Program",
166 | program_description="This is a custom description"
167 | )
168 | def cli():
169 | # Argument parsing logic here
170 | pass
171 | ```
172 |
173 | In this example:
174 |
175 | - **run_function**: The function to be executed when the user clicks the "Run" button in the GUI. This is the `handle()` function
176 | - **gui**: Specify the GUI framework to use. Supported options are: `"dearpygui"`, `"pysimplegui"`, `"pysimpleguiqt"`, `"pysimpleguiweb"`, `"freesimplegui"`. Defaults to `"dearpygui"`. In the example, this is `"freesimplegui"`.
177 | - **theme**: Set a base24 theme or provide a base24 scheme file (e.g., `"one-light.yaml"`).
178 | - **darkTheme**: Specify a dark theme variant using a base24 scheme or file (e.g., `"one-dark.yaml"`).
179 | - **image**: Define the program icon. Supported formats are those compatible with the Python Imaging Library (PIL).
180 | - **program_name**: Override the default program name with a custom name.
181 | - **program_description**: Provide a custom description for the program.
182 | - **menu**: Add a custom menu to the GUI. Example: `{"File": "/path/to/file.md"}`.
183 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "cli2gui"
3 | version = "2025"
4 | description = "Use this module to convert a cli program to a gui"
5 | authors = [{ name = "FredHappyface" }]
6 | requires-python = ">=3.9"
7 | readme = "README.md"
8 | license = "mit"
9 | classifiers = [
10 | "Environment :: Console",
11 | "Environment :: MacOS X",
12 | "Environment :: Web Environment",
13 | "Environment :: Win32 (MS Windows)",
14 | "Environment :: X11 Applications :: Qt",
15 | "Development Status :: 5 - Production/Stable",
16 | "Intended Audience :: Developers",
17 | "Intended Audience :: Education",
18 | "Natural Language :: English",
19 | "Operating System :: OS Independent",
20 | "Programming Language :: Python :: Implementation :: CPython",
21 | "Topic :: Software Development :: Libraries :: Python Modules",
22 | "Topic :: Utilities",
23 | ]
24 | dependencies = [
25 | "dearpygui>=2.0.0",
26 | "getostheme>=2024.0.1",
27 | "pillow>=11.1.0",
28 | "pyyaml>=6.0.2",
29 | ]
30 |
31 | [project.optional-dependencies]
32 | psg = ["pysimplegui<6,>=5.0.3"]
33 | fsg = ["freesimplegui<6,>=5.1.1"]
34 | # fsgweb = ["freesimpleguiWeb>=1.0.0"]
35 | # fsgqt = ["freesimpleguiQt>=1.0.0"]
36 | web = ["PySimpleGUIWeb<2,>=0.39.0"]
37 | qt = ["PySimpleGUIQt<6,>=5.0.0"]
38 | pandoc = ["catpandoc<2026,>=2024"]
39 |
40 | [project.urls]
41 | Homepage = "https://github.com/FHPythonUtils/Cli2Gui"
42 | Repository = "https://github.com/FHPythonUtils/Cli2Gui"
43 | Documentation = "https://github.com/FHPythonUtils/Cli2Gui/blob/master/README.md"
44 |
45 | [dependency-groups]
46 | dev = [
47 | "click>=8.1.8",
48 | "coverage>=7.6.12",
49 | "dephell-argparse>=0.1.3",
50 | "docopt>=0.6.2",
51 | "handsdown>=2.1.0",
52 | "pyright>=1.1.394",
53 | "pytest>=8.3.4",
54 | "ruff>=0.9.6",
55 | "safety>=3.3.0",
56 | ]
57 |
58 | [build-system]
59 | requires = ["hatchling"]
60 | build-backend = "hatchling.build"
61 |
62 | [tool.ruff]
63 | line-length = 100
64 | indent-width = 4
65 | target-version = "py38"
66 |
67 | [tool.ruff.lint]
68 | select = ["ALL"]
69 | ignore = [
70 | "ANN401", # allow dynamically typed expressions (typing.Any)
71 | "COM812", # enforce trailing comma
72 | "D2", # pydocstyle formatting
73 | "ISC001",
74 | "N802", "N803", "N806", "N812", "N813", "N815", # pep8 naming
75 | "PLR09", # pylint refactor too many
76 | "TCH", # type check blocks
77 | "W191" # ignore this to allow tabs
78 | ]
79 | fixable = ["ALL"]
80 |
81 | [tool.ruff.lint.per-file-ignores]
82 | "**/{tests,docs,tools}/*" = ["D", "S101", "E402", "T201", "ERA001"]
83 |
84 | [tool.ruff.lint.flake8-tidy-imports]
85 | ban-relative-imports = "all" # Disallow all relative imports.
86 |
87 | [tool.ruff.format]
88 | indent-style = "tab"
89 | docstring-code-format = true
90 | line-ending = "lf"
91 |
92 | [tool.pyright]
93 | venvPath = "."
94 | venv = ".venv"
95 |
--------------------------------------------------------------------------------
/readme-assets/icons/name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/readme-assets/icons/name.png
--------------------------------------------------------------------------------
/readme-assets/icons/proj-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/readme-assets/icons/proj-icon.png
--------------------------------------------------------------------------------
/readme-assets/screenshots/dearpygui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/readme-assets/screenshots/dearpygui.png
--------------------------------------------------------------------------------
/readme-assets/screenshots/freesimplegui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/readme-assets/screenshots/freesimplegui.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by uv via the following command:
2 | # uv export --no-hashes --no-dev -o requirements.txt
3 | -e .
4 | dearpygui==2.0.0
5 | getostheme==2024.0.1
6 | pillow==11.1.0
7 | pyyaml==6.0.2
8 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/__init__.py
--------------------------------------------------------------------------------
/tests/argparse/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/argparse/__init__.py
--------------------------------------------------------------------------------
/tests/argparse/another_file.md:
--------------------------------------------------------------------------------
1 | ## Decorator
2 |
3 | ```python
4 | @Cli2Gui(run_function, auto_enable=False, parser="argparse", gui="pysimplegui",
5 | theme="", darkTheme="", image="", program_name="",
6 | program_description="", max_args_shown=5, **kwargs)
7 | ```
8 |
9 | ## Using the decorator in your project
10 |
11 | ### run_function (optional)
12 |
13 | The name of the function to call eg. main(args). Defaults to None. If not
14 | specified, program continues as normal (can only run once)
15 |
16 | ```python
17 | def main(args):
18 | print(args.arg)
19 |
20 | @Cli2Gui(run_function=main)
21 | def cli():
22 | parser = argparse.ArgumentParser(description="this is an example parser")
23 | parser.add_argument("arg", type=str,
24 | help="positional arg")
25 | args = parser.parse_args()
26 | main(args)
27 | ```
28 |
29 | ### auto_enable (optional)
30 |
31 | Enable the GUI by default. If enabled by default requires `--disable-cli2gui`, otherwise requires `--cli2gui`
32 |
33 | ```python
34 | @Cli2Gui(auto_enable=False)
35 | ```
36 |
37 | ### parser (optional)
38 |
39 | Override the parser to use, defaults to argparse. Current options are:
40 | "argparse", "getopt", "optparse", "docopt", "dephell_argparse"
41 |
42 | ```python
43 | @Cli2Gui(parser="argparse")
44 | ```
45 |
46 | ### gui (optional)
47 |
48 | Override the gui to use. Current options are:
49 | "pysimplegui", "pysimpleguiqt","pysimpleguiweb". Defaults to "pysimplegui".
50 |
51 | pysimplegui is the recommended option
52 |
53 | ```python
54 | @Cli2Gui(gui="pysimplegui")
55 | ```
56 |
57 | ### theme (optional)
58 |
59 | Set a base24 theme. Can also pass a base24 scheme file. eg. `one-light.yaml`
60 |
61 | ```python
62 | @Cli2Gui(theme=["#e7e7e9", "#dfdfe1", "#cacace", "#a0a1a7", "#696c77",
63 | "#383a42", "#202227", "#090a0b", "#ca1243", "#c18401", "#febb2a",
64 | "#50a14f", "#0184bc", "#4078f2", "#a626a4", "#986801", "#f0f0f1",
65 | "#fafafa", "#ec2258", "#f4a701", "#6db76c", "#01a7ef", "#709af5",
66 | "#d02fcd"])
67 | ```
68 |
69 | ### darkTheme (optional)
70 |
71 | Set a base24 dark theme variant. Can also pass a base24 scheme file. eg.
72 | `one-dark.yaml`
73 |
74 | ```python
75 | @Cli2Gui(darkTheme=["#282c34", "#3f4451", "#4f5666", "#545862", "#9196a1",
76 | "#abb2bf", "#e6e6e6", "#ffffff", "#e06c75", "#d19a66", "#e5c07b",
77 | "#98c379", "#56b6c2", "#61afef", "#c678dd", "#be5046", "#21252b",
78 | "#181a1f", "#ff7b86", "#efb074", "#b1e18b", "#63d4e0", "#67cdff",
79 | "#e48bff"])
80 | ```
81 |
82 | ### image (optional)
83 |
84 | Set the program icon. File extensions can be any that PIL supports
85 |
86 | ```python
87 | @Cli2Gui(image="path/to/image.png")
88 | ```
89 |
90 | ### program_name (optional)
91 |
92 | Override the program name
93 |
94 | ```python
95 | @Cli2Gui(program_name="custom name")
96 | ```
97 |
98 | ### program_description (optional)
99 |
100 | Override the program description
101 |
102 | ```python
103 | @Cli2Gui(program_description="this is a custom description")
104 | ```
105 |
106 | ### max_args_shown (optional)
107 |
108 | Maximum number of args shown before using a scrollbar
109 |
110 | ```python
111 | @Cli2Gui(max_args_shown=5)
112 | ```
113 |
114 | ### menu (optional)
115 |
116 | Add a menu to the program. Defaults to None. eg.
117 |
118 | ```python
119 | THIS_DIR = str(Path(__file__).resolve().parent)
120 | menu={"File": THIS_DIR + "/file.md"}
121 | ```
122 |
123 | Works significantly better with pysimplegui than pysimpleguiqt
124 |
125 | ```python
126 | @Cli2Gui(menu={"File": THIS_DIR + "/file.md", "Another File": THIS_DIR + "/another_file.md", })
127 | ```
128 |
129 | ## Click
130 |
131 | ```python
132 | def Click2Gui(run_function, gui="pysimplegui", theme="", darkTheme="",
133 | image="", program_name="", program_description="",
134 | max_args_shown=5, menu="", **kwargs):
135 | ```
136 |
137 | Very similar to the decorator but with the following differences...
138 |
139 | ### run_function (required)
140 |
141 | Specify the click function to use. (attempts were made to offer full program
142 | support however this behaved very poorly)
143 |
144 | ### parser (not applicable)
145 |
146 | As this is exclusively for click, this option is not present
147 |
--------------------------------------------------------------------------------
/tests/argparse/file.md:
--------------------------------------------------------------------------------
1 | # Example file 1
2 |
3 | Did you know that cli2gui supports a range of parsers, here's a comparison of support vs
4 | some other popular projects
5 |
6 | | Parser | Cli2Gui | Gooey | Quick |
7 | | ---------------- | -------------------- | ------------------ | ------------------ |
8 | | Argparse | ✔ | ✔ | ❌ |
9 | | Optparse | ✔ | ❌ | ❌ |
10 | | DocOpt | ✔ | ❌ | ❌ |
11 | | Click | ✔ * | ❌ | ✔ |
12 | | GetOpt | ✔ | ❌ | ❌ |
13 | | Dephell Argparse | ✔ | ❌ | ❌ |
14 |
--------------------------------------------------------------------------------
/tests/argparse/test_10.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | from pathlib import Path
4 |
5 | THISDIR = Path(__file__).resolve().parent
6 | sys.path.insert(0, str(THISDIR.parent.parent))
7 | from cli2gui import Cli2Gui
8 |
9 |
10 | def run(args: argparse.Namespace) -> None:
11 | print(args)
12 |
13 |
14 | def main() -> None:
15 | parser = argparse.ArgumentParser(description="this is an example parser")
16 | subparsers = parser.add_subparsers(help="types of A")
17 | parser.add_argument(
18 | "-v",
19 | )
20 |
21 | a_parser = subparsers.add_parser("A")
22 | _b_parser = subparsers.add_parser("B")
23 |
24 | a_parser.add_argument("something", choices=["a1", "a2"])
25 |
26 | args = parser.parse_args()
27 | run(args)
28 |
29 |
30 | decorator_function = Cli2Gui(
31 | run_function=run,
32 | auto_enable=True,
33 | )
34 |
35 | gui = decorator_function(main)
36 |
37 | if __name__ == "__main__":
38 | gui()
39 |
--------------------------------------------------------------------------------
/tests/argparse/test_11.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | from pathlib import Path
4 |
5 | THISDIR = Path(__file__).resolve().parent
6 | sys.path.insert(0, str(THISDIR.parent.parent))
7 | from cli2gui import Cli2Gui
8 |
9 |
10 | def run(args: argparse.Namespace) -> None:
11 | print(args.arg)
12 |
13 |
14 | def main() -> None:
15 | parser = argparse.ArgumentParser(description="this is an example parser")
16 | parser.add_argument("--arg", type=str, default="foo", help="keyword arg")
17 | parser.add_argument("--bool", action="store_true", default=True, help="boolean arg")
18 | args = parser.parse_args()
19 | run(args)
20 |
21 |
22 | decorator_function = Cli2Gui(
23 | run_function=run,
24 | auto_enable=True,
25 | )
26 |
27 | gui = decorator_function(main)
28 |
29 | if __name__ == "__main__":
30 | gui()
31 |
--------------------------------------------------------------------------------
/tests/argparse/test_16.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 | try:
18 | args.write_path.write_bytes(b"test")
19 | except AttributeError:
20 | print("Oops! write_path was never specified")
21 |
22 |
23 | @Cli2Gui(
24 | run_function=handle,
25 | menu={
26 | "File": f"{THISDIR}/file.md",
27 | "Another File": f"{THISDIR}/another_file.md",
28 | },
29 | )
30 | def cli() -> None:
31 | """Cli entrypoint."""
32 | parser = argparse.ArgumentParser("Simple Parser")
33 |
34 | # Positional and file
35 | parser.add_argument("positional", help="positional arg")
36 | parser.add_argument(
37 | "positional_file", type=argparse.FileType("r"), help="positional arg for a file"
38 | )
39 | parser.add_argument(
40 | "write_file", type=argparse.FileType("wb"), help="positional arg for a file"
41 | )
42 |
43 | parser.add_argument("write_path", type=Path, help="positional arg for a file")
44 | parser.add_argument(
45 | "write_path_default",
46 | type=Path,
47 | help="positional arg for a file",
48 | default=Path("example.specialext"),
49 | )
50 |
51 | args = parser.parse_args()
52 |
53 | handle(args)
54 |
55 |
56 | cli()
57 |
--------------------------------------------------------------------------------
/tests/argparse/test_18.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from threading import Thread
3 |
4 | from cli2gui import Cli2Gui
5 |
6 | THREAD: Thread = None
7 | import argparse
8 | from pathlib import Path
9 |
10 |
11 | def main() -> None:
12 | parser = argparse.ArgumentParser(description="Web scraper with aria2c output")
13 | parser.add_argument(
14 | "urls_file",
15 | help="Input file containing URLs",
16 | type=argparse.FileType("r", encoding="utf-8"),
17 | )
18 | parser.add_argument(
19 | "aria2c_file",
20 | help="Output file for aria2c download links",
21 | type=Path,
22 | )
23 | parser.add_argument("--timeout", type=int, default=5000, help="Timeout per page (ms)")
24 | parser.add_argument("--max-workers", type=int, default=2, help="Maximum number of workers")
25 | parser.add_argument(
26 | "--save-trace",
27 | action="store_true",
28 | help="Save trace files (for debugging only)",
29 | )
30 | parser.add_argument(
31 | "--skip-edge",
32 | action="store_true",
33 | help="Don't use Edge",
34 | )
35 | args = parser.parse_args()
36 | blocking_run(args)
37 |
38 |
39 | def blocking_run(args: argparse.Namespace) -> None:
40 | asyncio.run(run(args))
41 |
42 |
43 | async def run(args: argparse.Namespace) -> None:
44 | print(args)
45 | print(args.timeout)
46 |
47 |
48 | def wrapper(args: argparse.Namespace) -> None:
49 | global THREAD
50 |
51 | if THREAD is not None and THREAD.is_alive():
52 | print("Task already running")
53 | return
54 |
55 | THREAD = Thread(target=blocking_run, args=(args,))
56 | THREAD.start()
57 |
58 |
59 | decorator_function = Cli2Gui(
60 | run_function=wrapper,
61 | auto_enable=True,
62 | program_name="Test program #18",
63 | )
64 |
65 |
66 | gui = decorator_function(main)
67 |
68 | if __name__ == "__main__":
69 | gui()
70 |
--------------------------------------------------------------------------------
/tests/argparse/test_22.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from threading import Thread
3 |
4 | from cli2gui import Cli2Gui
5 |
6 | THREAD: Thread = None
7 | import argparse
8 | from pathlib import Path
9 |
10 |
11 | def main() -> None:
12 | parser = argparse.ArgumentParser(description="Web scraper with aria2c output")
13 | parser.add_argument(
14 | "urls_file",
15 | help="Input file containing URLs",
16 | type=argparse.FileType("r", encoding="utf-8"),
17 | )
18 | parser.add_argument(
19 | "aria2c_file",
20 | help="Output file for aria2c download links",
21 | type=Path,
22 | )
23 | parser.add_argument("--timeout", type=int, default=5000, help="Timeout per page (ms)")
24 | parser.add_argument("--max-workers", type=int, default=2, help="Maximum number of workers")
25 | parser.add_argument(
26 | "--save-trace",
27 | action="store_true",
28 | help="Save trace files (for debugging only)",
29 | )
30 | parser.add_argument(
31 | "--skip-edge",
32 | action="store_true",
33 | help="Don't use Edge",
34 | )
35 | args = parser.parse_args()
36 | blocking_run(args)
37 |
38 |
39 | def blocking_run(args: argparse.Namespace) -> None:
40 | asyncio.run(run(args))
41 |
42 |
43 | async def run(args: argparse.Namespace) -> None:
44 | print(args)
45 | print(args.timeout)
46 |
47 |
48 | def wrapper(args: argparse.Namespace) -> None:
49 | global THREAD
50 |
51 | if THREAD is not None and THREAD.is_alive():
52 | print("Task already running")
53 | return
54 |
55 | THREAD = Thread(target=blocking_run, args=(args,))
56 | THREAD.start()
57 |
58 |
59 | decorator_function = Cli2Gui(
60 | run_function=wrapper,
61 | auto_enable=True,
62 | program_name="Test program #22",
63 | gui="freesimplegui",
64 | )
65 |
66 |
67 | gui = decorator_function(main)
68 |
69 | if __name__ == "__main__":
70 | gui()
71 |
--------------------------------------------------------------------------------
/tests/argparse/test_24.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 | try:
18 | args.write_path.write_bytes(b"test")
19 | except AttributeError:
20 | print("Oops! write_path was never specified")
21 |
22 |
23 | @Cli2Gui(
24 | run_function=handle,
25 | menu={
26 | "File": f"{THISDIR}/file.md",
27 | "Another File": f"{THISDIR}/another_file.md",
28 | },
29 | gui="freesimplegui",
30 | )
31 | def cli() -> None:
32 | """Cli entrypoint."""
33 | parser = argparse.ArgumentParser("Simple Parser")
34 |
35 | # Positional and file
36 | parser.add_argument("positional", help="positional arg")
37 | parser.add_argument(
38 | "positional_file", type=argparse.FileType("r"), help="positional arg for a file"
39 | )
40 | parser.add_argument(
41 | "write_file", type=argparse.FileType("wb"), help="positional arg for a file"
42 | )
43 |
44 | parser.add_argument("write_path", type=Path, help="positional arg for a file")
45 |
46 | args = parser.parse_args()
47 |
48 | handle(args)
49 |
50 |
51 | cli()
52 |
--------------------------------------------------------------------------------
/tests/argparse/test_advanced.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(
20 | run_function=handle,
21 | menu={
22 | "File": f"{THISDIR}/file.md",
23 | "Another File": f"{THISDIR}/another_file.md",
24 | },
25 | )
26 | def cli() -> None:
27 | """Cli entrypoint."""
28 | parser = argparse.ArgumentParser("Simple Parser")
29 |
30 | # Positional and file
31 | parser.add_argument("positional", help="positional arg")
32 | parser.add_argument(
33 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
34 | )
35 | parser.add_argument("--optional", help="optional arg")
36 |
37 | # Store true, false, store, count, choices
38 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
39 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
40 | parser.add_argument("--store", action="store", help="optional arg store")
41 | parser.add_argument("--count", action="count", help="optional arg count")
42 | parser.add_argument(
43 | "--choices",
44 | action="store",
45 | choices=["choice1", "choice2"],
46 | help="optional arg store with choices",
47 | )
48 | parser.add_argument(
49 | "--somefile",
50 | type=argparse.FileType("r"),
51 | required=False,
52 | )
53 |
54 | group = parser.add_argument_group(
55 | "choose one of the following", "use the following arguments to change the look of the image"
56 | )
57 | mxg = group.add_mutually_exclusive_group()
58 |
59 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
60 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
61 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
62 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
63 | mxg.add_argument(
64 | "--mxg-choices",
65 | action="store",
66 | choices=["choice1", "choice2"],
67 | help="mutually exclusive arg store with choices",
68 | )
69 |
70 | args = parser.parse_args()
71 |
72 | handle(args)
73 |
74 |
75 | cli()
76 |
--------------------------------------------------------------------------------
/tests/argparse/test_advanced_fsg.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(
20 | run_function=handle,
21 | menu={
22 | "File": f"{THISDIR}/file.md",
23 | "Another File": f"{THISDIR}/another_file.md",
24 | },
25 | gui="freesimplegui",
26 | )
27 | def cli() -> None:
28 | """Cli entrypoint."""
29 | parser = argparse.ArgumentParser("Simple Parser")
30 |
31 | # Positional and file
32 | parser.add_argument("positional", help="positional arg")
33 | parser.add_argument(
34 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
35 | )
36 | parser.add_argument("--optional", help="optional arg")
37 |
38 | # Store true, false, store, count, choices
39 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
40 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
41 | parser.add_argument("--store", action="store", help="optional arg store")
42 | parser.add_argument("--count", action="count", help="optional arg count")
43 | parser.add_argument(
44 | "--choices",
45 | action="store",
46 | choices=["choice1", "choice2"],
47 | help="optional arg store with choices",
48 | )
49 | parser.add_argument(
50 | "--somefile",
51 | type=argparse.FileType("r"),
52 | required=False,
53 | )
54 |
55 | group = parser.add_argument_group(
56 | "choose one of the following", "use the following arguments to change the look of the image"
57 | )
58 | mxg = group.add_mutually_exclusive_group()
59 |
60 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
61 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
62 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
63 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
64 | mxg.add_argument(
65 | "--mxg-choices",
66 | action="store",
67 | choices=["choice1", "choice2"],
68 | help="mutually exclusive arg store with choices",
69 | )
70 |
71 | args = parser.parse_args()
72 |
73 | handle(args)
74 |
75 |
76 | cli()
77 |
--------------------------------------------------------------------------------
/tests/argparse/test_advanced_fsgqt.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(
20 | run_function=handle,
21 | menu={
22 | "File": f"{THISDIR}/file.md",
23 | "Another File": f"{THISDIR}/another_file.md",
24 | },
25 | gui="fsgqt",
26 | )
27 | def cli() -> None:
28 | """Cli entrypoint."""
29 | parser = argparse.ArgumentParser("Simple Parser")
30 |
31 | # Positional and file
32 | parser.add_argument("positional", help="positional arg")
33 | parser.add_argument(
34 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
35 | )
36 | parser.add_argument("--optional", help="optional arg")
37 |
38 | # Store true, false, store, count, choices
39 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
40 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
41 | parser.add_argument("--store", action="store", help="optional arg store")
42 | parser.add_argument("--count", action="count", help="optional arg count")
43 | parser.add_argument(
44 | "--choices",
45 | action="store",
46 | choices=["choice1", "choice2"],
47 | help="optional arg store with choices",
48 | )
49 | parser.add_argument(
50 | "--somefile",
51 | type=argparse.FileType("r"),
52 | required=False,
53 | )
54 |
55 | group = parser.add_argument_group(
56 | "choose one of the following", "use the following arguments to change the look of the image"
57 | )
58 | mxg = group.add_mutually_exclusive_group()
59 |
60 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
61 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
62 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
63 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
64 | mxg.add_argument(
65 | "--mxg-choices",
66 | action="store",
67 | choices=["choice1", "choice2"],
68 | help="mutually exclusive arg store with choices",
69 | )
70 |
71 | args = parser.parse_args()
72 |
73 | handle(args)
74 |
75 |
76 | cli()
77 |
--------------------------------------------------------------------------------
/tests/argparse/test_advanced_fsgweb.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(
20 | run_function=handle,
21 | menu={
22 | "File": f"{THISDIR}/file.md",
23 | "Another File": f"{THISDIR}/another_file.md",
24 | },
25 | gui="freesimpleguiweb",
26 | )
27 | def cli() -> None:
28 | """Cli entrypoint."""
29 | parser = argparse.ArgumentParser("Simple Parser")
30 |
31 | # Positional and file
32 | parser.add_argument("positional", help="positional arg")
33 | parser.add_argument(
34 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
35 | )
36 | parser.add_argument("--optional", help="optional arg")
37 |
38 | # Store true, false, store, count, choices
39 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
40 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
41 | parser.add_argument("--store", action="store", help="optional arg store")
42 | parser.add_argument("--count", action="count", help="optional arg count")
43 | parser.add_argument(
44 | "--choices",
45 | action="store",
46 | choices=["choice1", "choice2"],
47 | help="optional arg store with choices",
48 | )
49 | parser.add_argument(
50 | "--somefile",
51 | type=argparse.FileType("r"),
52 | required=False,
53 | )
54 |
55 | group = parser.add_argument_group(
56 | "choose one of the following", "use the following arguments to change the look of the image"
57 | )
58 | mxg = group.add_mutually_exclusive_group()
59 |
60 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
61 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
62 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
63 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
64 | mxg.add_argument(
65 | "--mxg-choices",
66 | action="store",
67 | choices=["choice1", "choice2"],
68 | help="mutually exclusive arg store with choices",
69 | )
70 |
71 | args = parser.parse_args()
72 |
73 | handle(args)
74 |
75 |
76 | cli()
77 |
--------------------------------------------------------------------------------
/tests/argparse/test_advanced_psg.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = Path(__file__).resolve().parent
10 | sys.path.insert(0, str(THISDIR.parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(
20 | run_function=handle,
21 | menu={
22 | "File": f"{THISDIR}/file.md",
23 | "Another File": f"{THISDIR}/another_file.md",
24 | },
25 | gui="pysimplegui",
26 | )
27 | def cli() -> None:
28 | """Cli entrypoint."""
29 | parser = argparse.ArgumentParser("Simple Parser")
30 |
31 | # Positional and file
32 | parser.add_argument("positional", help="positional arg")
33 | parser.add_argument(
34 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
35 | )
36 | parser.add_argument("--optional", help="optional arg")
37 |
38 | # Store true, false, store, count, choices
39 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
40 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
41 | parser.add_argument("--store", action="store", help="optional arg store")
42 | parser.add_argument("--count", action="count", help="optional arg count")
43 | parser.add_argument(
44 | "--choices",
45 | action="store",
46 | choices=["choice1", "choice2"],
47 | help="optional arg store with choices",
48 | )
49 | parser.add_argument(
50 | "--somefile",
51 | type=argparse.FileType("r"),
52 | required=False,
53 | )
54 |
55 | group = parser.add_argument_group(
56 | "choose one of the following", "use the following arguments to change the look of the image"
57 | )
58 | mxg = group.add_mutually_exclusive_group()
59 |
60 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
61 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
62 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
63 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
64 | mxg.add_argument(
65 | "--mxg-choices",
66 | action="store",
67 | choices=["choice1", "choice2"],
68 | help="mutually exclusive arg store with choices",
69 | )
70 |
71 | args = parser.parse_args()
72 |
73 | handle(args)
74 |
75 |
76 | cli()
77 |
--------------------------------------------------------------------------------
/tests/argparse/test_simple.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = str(Path(__file__).resolve().parent)
10 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(run_function=handle)
20 | def cli() -> None:
21 | """Cli entrypoint."""
22 | parser = argparse.ArgumentParser("Simple Parser")
23 |
24 | # Positional and file
25 | parser.add_argument("positional", help="positional arg")
26 | parser.add_argument(
27 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
28 | )
29 | parser.add_argument("--optional", help="optional arg")
30 |
31 | # Store true, false, store, count, choices
32 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
33 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
34 | parser.add_argument("--store", action="store", help="optional arg store")
35 | parser.add_argument("--count", action="count", help="optional arg count")
36 | parser.add_argument(
37 | "--choices",
38 | action="store",
39 | choices=["choice1", "choice2"],
40 | help="optional arg store with choices",
41 | )
42 |
43 | args = parser.parse_args()
44 |
45 | handle(args)
46 |
47 |
48 | cli()
49 |
--------------------------------------------------------------------------------
/tests/click/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/click/__init__.py
--------------------------------------------------------------------------------
/tests/click/test_boolean.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser
2 |
3 | Program from https://click.palletsprojects.com/en/7.x/#documentation
4 | """
5 |
6 | from __future__ import annotations
7 |
8 | import sys
9 | from pathlib import Path
10 |
11 | import click
12 |
13 | THISDIR = str(Path(__file__).resolve().parent)
14 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
15 | from cli2gui import Click2Gui
16 |
17 |
18 | @click.command()
19 | @click.option("--print-version", default=True, help="Print the version?")
20 | def hello(*, print_version: bool) -> None:
21 | if print_version:
22 | print("v1.0")
23 |
24 |
25 | Click2Gui(run_function=hello)
26 | # hello()
27 |
--------------------------------------------------------------------------------
/tests/click/test_choice.py:
--------------------------------------------------------------------------------
1 | """Program from https://click.palletsprojects.com/en/8.1.x/"""
2 |
3 | from __future__ import annotations
4 |
5 | import sys
6 | from pathlib import Path
7 |
8 | import click
9 |
10 | THISDIR = str(Path(__file__).resolve().parent)
11 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
12 | from cli2gui import Click2Gui
13 |
14 |
15 | @click.command()
16 | @click.option("--hash-type", type=click.Choice(["MD5", "SHA1"], case_sensitive=False))
17 | def digest(hash_type: str) -> None:
18 | click.echo(hash_type)
19 |
20 |
21 | Click2Gui(run_function=digest)
22 |
--------------------------------------------------------------------------------
/tests/click/test_feat_switches.py:
--------------------------------------------------------------------------------
1 | """Program from https://click.palletsprojects.com/en/8.1.x/"""
2 |
3 | from __future__ import annotations
4 |
5 | import sys
6 | from pathlib import Path
7 |
8 | import click
9 |
10 | THISDIR = str(Path(__file__).resolve().parent)
11 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
12 | import sys
13 |
14 | from cli2gui import Click2Gui
15 |
16 |
17 | @click.command()
18 | @click.option("--upper", "transformation", flag_value="upper", default=True)
19 | @click.option("--lower", "transformation", flag_value="lower")
20 | def info(transformation) -> None:
21 | click.echo(getattr(sys.platform, transformation)())
22 |
23 |
24 | Click2Gui(run_function=info)
25 |
--------------------------------------------------------------------------------
/tests/click/test_simple.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser
2 |
3 | Program from https://click.palletsprojects.com/en/7.x/#documentation
4 | """
5 |
6 | from __future__ import annotations
7 |
8 | import sys
9 | from pathlib import Path
10 |
11 | import click
12 |
13 | THISDIR = str(Path(__file__).resolve().parent)
14 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
15 | from cli2gui import Click2Gui
16 |
17 |
18 | @click.command()
19 | @click.option("--count", default=1, help="Number of greetings.")
20 | @click.option("--name", prompt="Your name", help="The person to greet.")
21 | def hello(count: int, name: str) -> None:
22 | """Simple program that greets NAME for a total of COUNT times."""
23 | for _index in range(count):
24 | click.echo(f"Hello {name}!")
25 |
26 |
27 | Click2Gui(run_function=hello)
28 | # ⬇️ This is how you call the function without a GUI
29 | # hello()
30 |
--------------------------------------------------------------------------------
/tests/custom(argparse)/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/custom(argparse)/__init__.py
--------------------------------------------------------------------------------
/tests/custom(argparse)/test_advanced.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = str(Path(__file__).resolve().parent)
10 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(parser="input()", run_function=handle)
20 | def cli() -> None:
21 | """Cli entrypoint."""
22 | parser = argparse.ArgumentParser("Simple Parser")
23 |
24 | # Positional and file
25 | parser.add_argument("positional", help="positional arg")
26 | parser.add_argument(
27 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
28 | )
29 | parser.add_argument("--optional", help="optional arg")
30 |
31 | # Store true, false, store, count, choices
32 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
33 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
34 | parser.add_argument("--store", action="store", help="optional arg store")
35 | parser.add_argument("--count", action="count", help="optional arg count")
36 | parser.add_argument(
37 | "--choices",
38 | action="store",
39 | choices=["choice1", "choice2"],
40 | help="optional arg store with choices",
41 | )
42 |
43 | group = parser.add_argument_group(
44 | "choose one of the following", "use the following arguments to change the look of the image"
45 | )
46 | mxg = group.add_mutually_exclusive_group()
47 |
48 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
49 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
50 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
51 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
52 | mxg.add_argument(
53 | "--mxg-choices",
54 | action="store",
55 | choices=["choice1", "choice2"],
56 | help="mutually exclusive arg store with choices",
57 | )
58 |
59 | args = parser.parse_args()
60 |
61 | handle(args)
62 |
63 |
64 | cli()
65 |
--------------------------------------------------------------------------------
/tests/custom(argparse)/test_simple.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = str(Path(__file__).resolve().parent)
10 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: argparse.Namespace) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(parser="input()", run_function=handle)
20 | def cli() -> None:
21 | """Cli entrypoint."""
22 | parser = argparse.ArgumentParser("Simple Parser")
23 |
24 | # Positional and file
25 | parser.add_argument("positional", help="positional arg")
26 | parser.add_argument(
27 | "positional-file", type=argparse.FileType("r"), help="positional arg for a file"
28 | )
29 | parser.add_argument("--optional", help="optional arg")
30 |
31 | # Store true, false, store, count, choices
32 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
33 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
34 | parser.add_argument("--store", action="store", help="optional arg store")
35 | parser.add_argument("--count", action="count", help="optional arg count")
36 | parser.add_argument(
37 | "--choices",
38 | action="store",
39 | choices=["choice1", "choice2"],
40 | help="optional arg store with choices",
41 | )
42 |
43 | args = parser.parse_args()
44 |
45 | handle(args)
46 |
47 |
48 | cli()
49 |
--------------------------------------------------------------------------------
/tests/dephell_argparse/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/dephell_argparse/__init__.py
--------------------------------------------------------------------------------
/tests/dephell_argparse/test_advanced.py:
--------------------------------------------------------------------------------
1 | """Tests an advanced parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | import dephell_argparse
10 |
11 | THISDIR = str(Path(__file__).resolve().parent)
12 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
13 | from cli2gui import Cli2Gui
14 |
15 |
16 | def handle(args: argparse.Namespace) -> None:
17 | """Handle the args."""
18 | print(args)
19 |
20 |
21 | @Cli2Gui(run_function=handle, parser="dephell_argparse")
22 | def cli() -> None:
23 | """Cli entrypoint."""
24 | parser = dephell_argparse.Parser()
25 |
26 | # Positional and file
27 | parser.add_argument("positional", help="positional arg")
28 | parser.add_argument("--optional", help="optional arg")
29 |
30 | # Store true, false, store, count, choices
31 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
32 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
33 | parser.add_argument("--store", action="store", help="optional arg store")
34 | parser.add_argument("--count", action="count", help="optional arg count")
35 | parser.add_argument(
36 | "--choices",
37 | action="store",
38 | choices=["choice1", "choice2"],
39 | help="optional arg store with choices",
40 | )
41 |
42 | group = parser.add_argument_group(
43 | "choose one of the following", "use the following arguments to change the look of the image"
44 | )
45 | mxg = group.add_mutually_exclusive_group()
46 |
47 | mxg.add_argument("--mxg-true", action="store_true", help="mutually exclusive arg store true")
48 | mxg.add_argument("--mxg-false", action="store_false", help="mutually exclusive arg store false")
49 | mxg.add_argument("--mxg", action="store", help="mutually exclusive arg store")
50 | mxg.add_argument("--mxg-count", action="count", help="mutually exclusive arg count")
51 | mxg.add_argument(
52 | "--mxg-choices",
53 | action="store",
54 | choices=["choice1", "choice2"],
55 | help="mutually exclusive arg store with choices",
56 | )
57 |
58 | args = parser.parse_args()
59 |
60 | handle(args)
61 |
62 |
63 | cli()
64 |
--------------------------------------------------------------------------------
/tests/dephell_argparse/test_simple.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import argparse
6 | import sys
7 | from pathlib import Path
8 |
9 | import dephell_argparse
10 |
11 | THISDIR = str(Path(__file__).resolve().parent)
12 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
13 | from cli2gui import Cli2Gui
14 |
15 |
16 | def handle(args: argparse.Namespace) -> None:
17 | """Handle the args."""
18 | print(args)
19 |
20 |
21 | @Cli2Gui(run_function=handle, parser="dephell_argparse")
22 | def cli() -> None:
23 | """Cli entrypoint."""
24 | parser = dephell_argparse.Parser()
25 |
26 | # Positional and file
27 | parser.add_argument("positional", help="positional arg")
28 | parser.add_argument("--optional", help="optional arg")
29 |
30 | # Store true, false, store, count, choices
31 | parser.add_argument("--store-true", action="store_true", help="optional arg store true")
32 | parser.add_argument("--store-false", action="store_false", help="optional arg store false")
33 | parser.add_argument("--store", action="store", help="optional arg store")
34 | parser.add_argument("--count", action="count", help="optional arg count")
35 | parser.add_argument(
36 | "--choices",
37 | action="store",
38 | choices=["choice1", "choice2"],
39 | help="optional arg store with choices",
40 | )
41 |
42 | args = parser.parse_args()
43 |
44 | handle(args)
45 |
46 |
47 | cli()
48 |
--------------------------------------------------------------------------------
/tests/docopt/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/docopt/__init__.py
--------------------------------------------------------------------------------
/tests/docopt/test_11.py:
--------------------------------------------------------------------------------
1 | """
2 | usage:
3 | simple.py [-h] [--optional OPTIONAL] [--store-true] [--store-false]
4 | [--store STORE] [--count] [--choices {choice1,choice2}] PATH
5 |
6 | Arguments:
7 | PATH positional arg
8 |
9 | Options:
10 | -h, --help show this help message and exit
11 | --optional OPTIONAL optional arg
12 | --store-true optional arg store true
13 | --store-false optional arg store false
14 | --store STORE optional arg store [default: store me]
15 | --count optional arg count
16 | --choices {choice1,choice2}
17 | optional arg store with choices
18 |
19 | """
20 |
21 | from __future__ import annotations
22 |
23 | import sys
24 | from pathlib import Path
25 | from typing import Any
26 |
27 | import docopt
28 |
29 | THISDIR = str(Path(__file__).resolve().parent)
30 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
31 | from cli2gui import Cli2Gui
32 |
33 |
34 | def handle(args: dict[str, Any]) -> None:
35 | """Handle the args."""
36 | print(type(args))
37 | print(args)
38 |
39 |
40 | @Cli2Gui(run_function=handle, parser="docopt")
41 | def cli() -> None:
42 | """Cli entrypoint."""
43 | args = docopt.docopt(__doc__)
44 | handle(args)
45 |
46 |
47 | cli()
48 |
--------------------------------------------------------------------------------
/tests/docopt/test_simple.py:
--------------------------------------------------------------------------------
1 | """
2 | usage:
3 | simple.py [-h] [--optional OPTIONAL] [--store-true] [--store-false]
4 | [--store STORE] [--count] [--choices {choice1,choice2}] PATH
5 |
6 | Arguments:
7 | PATH positional arg
8 |
9 | Options:
10 | -h, --help show this help message and exit
11 | --optional OPTIONAL optional arg
12 | --store-true optional arg store true
13 | --store-false optional arg store false
14 | --store STORE optional arg store
15 | --count optional arg count
16 | --choices {choice1,choice2}
17 | optional arg store with choices
18 |
19 | """
20 |
21 | from __future__ import annotations
22 |
23 | import sys
24 | from pathlib import Path
25 | from typing import Any
26 |
27 | import docopt
28 |
29 | THISDIR = str(Path(__file__).resolve().parent)
30 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
31 | from cli2gui import Cli2Gui
32 |
33 |
34 | def handle(args: dict[str, Any]) -> None:
35 | """Handle the args."""
36 | print(args)
37 |
38 |
39 | @Cli2Gui(run_function=handle, parser="docopt")
40 | def cli() -> None:
41 | """Cli entrypoint."""
42 | args = docopt.docopt(__doc__)
43 | handle(args)
44 |
45 |
46 | cli()
47 |
--------------------------------------------------------------------------------
/tests/getopt/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/getopt/__init__.py
--------------------------------------------------------------------------------
/tests/getopt/test_simple.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import getopt
6 | import sys
7 | from pathlib import Path
8 |
9 | THISDIR = str(Path(__file__).resolve().parent)
10 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
11 | from cli2gui import Cli2Gui
12 |
13 |
14 | def handle(args: tuple[list, list]) -> None:
15 | """Handle the args."""
16 | print(args)
17 |
18 |
19 | @Cli2Gui(run_function=handle, parser="getopt")
20 | def cli() -> None:
21 | """Cli entrypoint."""
22 | options = getopt.getopt(
23 | sys.argv[1:],
24 | "ts:c:o",
25 | [
26 | "store-true",
27 | "store=",
28 | "count=",
29 | "choice=",
30 | ],
31 | )
32 | handle(options)
33 |
34 |
35 | cli()
36 |
--------------------------------------------------------------------------------
/tests/optparse/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FHPythonUtils/Cli2Gui/3b0c3a8be83c8b9d0e0f40da8cb56d1fb1944183/tests/optparse/__init__.py
--------------------------------------------------------------------------------
/tests/optparse/test_11.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import optparse
6 | import sys
7 | from pathlib import Path
8 | from typing import Any
9 |
10 | THISDIR = str(Path(__file__).resolve().parent)
11 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
12 | from cli2gui import Cli2Gui
13 |
14 |
15 | def handle(args: tuple[Any, list[str]]) -> None:
16 | """Handle the args."""
17 | print(type(args))
18 | print(args)
19 | print(type(args[0]))
20 | print(args[0])
21 | print(args[1])
22 |
23 |
24 | @Cli2Gui(run_function=handle, parser="optparse")
25 | def cli() -> None:
26 | """Cli entrypoint."""
27 | parser = optparse.OptionParser("Simple Parser")
28 |
29 | parser.add_option("--optional", help="optional arg")
30 |
31 | # Store true, false, store, count, choices
32 | parser.add_option(
33 | "--store-true", action="store_true", help="optional arg store true", default=True
34 | )
35 | parser.add_option(
36 | "--store-false", action="store_false", help="optional arg store false", default=True
37 | )
38 | parser.add_option("--store", action="store", help="optional arg store", default="store me")
39 | parser.add_option("--count", action="count", help="optional arg count", default=1)
40 | parser.add_option(
41 | "--choices",
42 | action="store",
43 | choices=["choice1", "choice2"],
44 | help="optional arg store with choices",
45 | )
46 |
47 | args = parser.parse_args()
48 |
49 | handle(args)
50 |
51 |
52 | cli()
53 |
--------------------------------------------------------------------------------
/tests/optparse/test_simple.py:
--------------------------------------------------------------------------------
1 | """Tests a simple parser"""
2 |
3 | from __future__ import annotations
4 |
5 | import optparse
6 | import sys
7 | from pathlib import Path
8 | from typing import Any
9 |
10 | THISDIR = str(Path(__file__).resolve().parent)
11 | sys.path.insert(0, str(Path(THISDIR).parent.parent))
12 | from cli2gui import Cli2Gui
13 |
14 |
15 | def handle(args: tuple[Any, list[str]]) -> None:
16 | """Handle the args."""
17 | print(args)
18 |
19 |
20 | @Cli2Gui(run_function=handle, parser="optparse")
21 | def cli() -> None:
22 | """Cli entrypoint."""
23 | parser = optparse.OptionParser("Simple Parser")
24 |
25 | parser.add_option("--optional", help="optional arg")
26 |
27 | # Store true, false, store, count, choices
28 | parser.add_option("--store-true", action="store_true", help="optional arg store true")
29 | parser.add_option("--store-false", action="store_false", help="optional arg store false")
30 | parser.add_option("--store", action="store", help="optional arg store")
31 | parser.add_option("--count", action="count", help="optional arg count")
32 | parser.add_option(
33 | "--choices",
34 | action="store",
35 | choices=["choice1", "choice2"],
36 | help="optional arg store with choices",
37 | )
38 |
39 | args = parser.parse_args()
40 |
41 | handle(args)
42 |
43 |
44 | cli()
45 |
--------------------------------------------------------------------------------