mininterface 0.8.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mininterface-1.0.0/PKG-INFO +338 -0
- mininterface-0.8.0/PKG-INFO → mininterface-1.0.0/README.md +107 -72
- {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/__init__.py +89 -63
- mininterface-1.0.0/mininterface/__main__.py +167 -0
- mininterface-1.0.0/mininterface/_lib/__init__.py +0 -0
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/auxiliary.py +56 -3
- mininterface-1.0.0/mininterface/_lib/cli_parser.py +538 -0
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/form_dict.py +66 -36
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/redirectable.py +3 -3
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/showcase.py +10 -12
- mininterface-1.0.0/mininterface/_lib/start.py +132 -0
- {mininterface-0.8.0/mininterface/mininterface → mininterface-1.0.0/mininterface/_mininterface}/__init__.py +223 -84
- mininterface-1.0.0/mininterface/_mininterface/adaptor.py +111 -0
- mininterface-1.0.0/mininterface/_mininterface/mixin.py +42 -0
- {mininterface-0.8.0/mininterface/text_interface → mininterface-1.0.0/mininterface/_text_interface}/__init__.py +33 -32
- mininterface-1.0.0/mininterface/_text_interface/adaptor.py +185 -0
- {mininterface-0.8.0/mininterface/text_interface → mininterface-1.0.0/mininterface/_text_interface}/facet.py +3 -2
- mininterface-1.0.0/mininterface/_textual_interface/__init__.py +28 -0
- mininterface-1.0.0/mininterface/_textual_interface/adaptor.py +138 -0
- mininterface-1.0.0/mininterface/_textual_interface/button_contents.py +71 -0
- {mininterface-0.8.0/mininterface/textual_interface → mininterface-1.0.0/mininterface/_textual_interface}/file_picker_input.py +165 -113
- mininterface-0.8.0/mininterface/textual_interface/textual_app.py → mininterface-1.0.0/mininterface/_textual_interface/form_contents.py +27 -97
- mininterface-1.0.0/mininterface/_textual_interface/secret_input.py +75 -0
- mininterface-1.0.0/mininterface/_textual_interface/style.tcss +50 -0
- mininterface-1.0.0/mininterface/_textual_interface/textual_app.py +69 -0
- mininterface-1.0.0/mininterface/_textual_interface/widgets.py +125 -0
- mininterface-1.0.0/mininterface/_tk_interface/__init__.py +40 -0
- {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/adaptor.py +36 -20
- {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/date_entry.py +6 -5
- {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/facet.py +2 -2
- {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/redirect_text_tkinter.py +1 -1
- {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/secret_entry.py +2 -2
- mininterface-1.0.0/mininterface/_tk_interface/select_input.py +190 -0
- mininterface-1.0.0/mininterface/_tk_interface/utils.py +222 -0
- {mininterface-0.8.0/mininterface/web_interface → mininterface-1.0.0/mininterface/_web_interface}/__init__.py +14 -15
- mininterface-1.0.0/mininterface/_web_interface/app.py +42 -0
- mininterface-1.0.0/mininterface/_web_interface/child_adaptor.py +85 -0
- {mininterface-0.8.0/mininterface/web_interface → mininterface-1.0.0/mininterface/_web_interface}/parent_adaptor.py +32 -13
- mininterface-1.0.0/mininterface/cli.py +195 -0
- {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/exceptions.py +23 -5
- {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/experimental.py +1 -1
- {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/facet/__init__.py +5 -5
- mininterface-1.0.0/mininterface/interfaces.py +129 -0
- mininterface-1.0.0/mininterface/settings.py +74 -0
- mininterface-1.0.0/mininterface/tag/__init__.py +8 -0
- {mininterface-0.8.0/mininterface/types → mininterface-1.0.0/mininterface/tag}/alias.py +12 -5
- mininterface-1.0.0/mininterface/tag/callback_tag.py +84 -0
- mininterface-1.0.0/mininterface/tag/datetime_tag.py +116 -0
- mininterface-1.0.0/mininterface/tag/flag.py +140 -0
- {mininterface-0.8.0/mininterface/types → mininterface-1.0.0/mininterface/tag}/internal.py +3 -0
- mininterface-1.0.0/mininterface/tag/path_tag.py +115 -0
- mininterface-1.0.0/mininterface/tag/secret_tag.py +53 -0
- mininterface-1.0.0/mininterface/tag/select_tag.py +322 -0
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/tag}/tag.py +294 -211
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/tag}/tag_factory.py +32 -10
- {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/validators.py +13 -5
- mininterface-1.0.0/pyproject.toml +140 -0
- mininterface-0.8.0/README.md +0 -228
- mininterface-0.8.0/mininterface/ValidationFail.py +0 -5
- mininterface-0.8.0/mininterface/__main__.py +0 -77
- mininterface-0.8.0/mininterface/cli_parser.py +0 -367
- mininterface-0.8.0/mininterface/interfaces.py +0 -106
- mininterface-0.8.0/mininterface/mininterface/adaptor.py +0 -59
- mininterface-0.8.0/mininterface/options.py +0 -55
- mininterface-0.8.0/mininterface/start.py +0 -95
- mininterface-0.8.0/mininterface/subcommands.py +0 -141
- mininterface-0.8.0/mininterface/text_interface/adaptor.py +0 -158
- mininterface-0.8.0/mininterface/textual_interface/__init__.py +0 -63
- mininterface-0.8.0/mininterface/textual_interface/adaptor.py +0 -91
- mininterface-0.8.0/mininterface/textual_interface/textual_button_app.py +0 -98
- mininterface-0.8.0/mininterface/textual_interface/widgets.py +0 -154
- mininterface-0.8.0/mininterface/tk_interface/__init__.py +0 -57
- mininterface-0.8.0/mininterface/tk_interface/utils.py +0 -221
- mininterface-0.8.0/mininterface/types/__init__.py +0 -4
- mininterface-0.8.0/mininterface/types/flags.py +0 -69
- mininterface-0.8.0/mininterface/types/rich_tags.py +0 -440
- mininterface-0.8.0/mininterface/web_interface/app.py +0 -24
- mininterface-0.8.0/mininterface/web_interface/child_adaptor.py +0 -68
- mininterface-0.8.0/pyproject.toml +0 -65
- {mininterface-0.8.0 → mininterface-1.0.0}/LICENSE +0 -0
- {mininterface-0.8.0/mininterface/textual_interface → mininterface-1.0.0/mininterface/_textual_interface}/facet.py +0 -0
- {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/external_fix.py +0 -0
- {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/tag}/type_stubs.py +0 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mininterface
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A minimal access to GUI, TUI, CLI and config
|
|
5
|
+
License: LGPL-3.0-or-later
|
|
6
|
+
Author: Edvard Rejthar
|
|
7
|
+
Author-email: edvard.rejthar@nic.cz
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Provides-Extra: basic
|
|
17
|
+
Provides-Extra: gui
|
|
18
|
+
Provides-Extra: img
|
|
19
|
+
Provides-Extra: tui
|
|
20
|
+
Provides-Extra: ui
|
|
21
|
+
Provides-Extra: web
|
|
22
|
+
Requires-Dist: annotated-types
|
|
23
|
+
Requires-Dist: autocombobox (==1.4.2) ; extra == "gui" or extra == "ui" or extra == "all"
|
|
24
|
+
Requires-Dist: humanize ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
25
|
+
Requires-Dist: pillow ; extra == "img" or extra == "gui" or extra == "ui" or extra == "all"
|
|
26
|
+
Requires-Dist: pyyaml ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
27
|
+
Requires-Dist: simple_term_menu
|
|
28
|
+
Requires-Dist: textual (<2.0.0) ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
29
|
+
Requires-Dist: textual-serve ; extra == "web" or extra == "ui" or extra == "all"
|
|
30
|
+
Requires-Dist: textual_imageview ; extra == "img" or extra == "tui" or extra == "ui" or extra == "all"
|
|
31
|
+
Requires-Dist: tkcalendar ; extra == "gui" or extra == "ui" or extra == "all"
|
|
32
|
+
Requires-Dist: tkinter-tooltip ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
33
|
+
Requires-Dist: tkinter_form (==0.2.1) ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
34
|
+
Requires-Dist: tkscrollableframe ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
35
|
+
Requires-Dist: tyro (>=0.9,<0.10) ; extra == "basic" or extra == "img" or extra == "tui" or extra == "gui" or extra == "web" or extra == "ui" or extra == "all"
|
|
36
|
+
Project-URL: Homepage, https://github.com/CZ-NIC/mininterface
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# Mininterface – access to GUI, TUI, web, CLI and config files
|
|
40
|
+
[](https://github.com/CZ-NIC/mininterface/actions)
|
|
41
|
+
[](https://pepy.tech/project/mininterface)
|
|
42
|
+
|
|
43
|
+
Write the program core, do not bother with the input/output.
|
|
44
|
+
|
|
45
|
+

|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
Check out the code, which is surprisingly short, that displays such a window or its textual fallback.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from dataclasses import dataclass
|
|
52
|
+
from mininterface import run
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class Env:
|
|
56
|
+
""" This calculates something. """
|
|
57
|
+
|
|
58
|
+
my_flag: bool = False
|
|
59
|
+
""" This switches the functionality """
|
|
60
|
+
|
|
61
|
+
my_number: int = 4
|
|
62
|
+
""" This number is very important """
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
m = run(Env, title="My application")
|
|
66
|
+
m.form()
|
|
67
|
+
# Attributes are suggested by the IDE
|
|
68
|
+
# along with the hint text 'This number is very important'.
|
|
69
|
+
print(m.env.my_number)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
# Contents
|
|
73
|
+
- [You got CLI](#you-got-cli)
|
|
74
|
+
- [You got config file management](#you-got-config-file-management)
|
|
75
|
+
- [You got dialogs](#you-got-dialogs)
|
|
76
|
+
- [Background](#background)
|
|
77
|
+
- [Installation](#installation)
|
|
78
|
+
- [Docs](#docs)
|
|
79
|
+
- [Gallery](#gallery)
|
|
80
|
+
- [Examples](#examples)
|
|
81
|
+
* [Hello world](#hello-world)
|
|
82
|
+
* [Goodbye argparse world](#goodbye-argparse-world)
|
|
83
|
+
|
|
84
|
+
## You got CLI
|
|
85
|
+
It was all the code you need. No lengthy blocks of code imposed by an external dependency. Besides the GUI/TUI/web, you receive powerful YAML-configurable CLI parsing.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
$ ./program.py --help
|
|
90
|
+
usage: program.py [-h] [-v] [--my-flag | --no-my-flag] [--my-number INT]
|
|
91
|
+
|
|
92
|
+
This calculates something.
|
|
93
|
+
|
|
94
|
+
╭─ options ───────────────────────────────────────────────────────────────╮
|
|
95
|
+
│ -h, --help show this help message and exit │
|
|
96
|
+
│ -v, --verbose Verbosity level. Can be used twice to increase. │
|
|
97
|
+
│ --my-flag, --no-my-flag │
|
|
98
|
+
│ This switches the functionality (default: False) │
|
|
99
|
+
│ --my-number INT This number is very important (default: 4) │
|
|
100
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## You got config file management
|
|
104
|
+
Loading config file is a piece of cake. Alongside `program.py`, write some of its arguments to `program.yaml`. They are seamlessly taken as defaults.
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
my_number: 555
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
$ program.py --help
|
|
112
|
+
...
|
|
113
|
+
│ --my-number INT This number is very important (default: 555) │
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## You got dialogs
|
|
117
|
+
Check out several useful methods to handle user dialogs. Here we bound the interface to a `with` statement that redirects stdout directly to the window.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
with run(Env) as m:
|
|
121
|
+
print(f"Your important number is {m.env.my_number}")
|
|
122
|
+
boolean = m.confirm("Is that alright?")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+

|
|
126
|
+

|
|
127
|
+
|
|
128
|
+
# Background
|
|
129
|
+
|
|
130
|
+
Wrapper between various libraries that provide a user interface.
|
|
131
|
+
|
|
132
|
+
Writing a small and useful program might be a task that takes fifteen minutes. Adding a CLI to specify the parameters is not so much overhead. But building a simple GUI around it? HOURS! Hours spent on researching GUI libraries, wondering why the Python desktop app ecosystem lags so far behind the web world. All you need is a few input fields validated through a clickable window... You do not deserve to add hundred of lines of the code just to define some editable fields. *Mininterface* is here to help.
|
|
133
|
+
|
|
134
|
+
The config variables needed by your program are kept in cozy dataclasses. Write less! The syntax of [tyro](https://github.com/brentyi/tyro) does not require any overhead (as its `argparse` alternatives do). You just annotate a class attribute, append a simple docstring and get a fully functional application:
|
|
135
|
+
|
|
136
|
+
* Call it as `program.py --help` to display full help.
|
|
137
|
+
* Use any flag in CLI: `program.py --my-flag` causes `env.my_flag` be set to `True`.
|
|
138
|
+
* The main benefit: Launch it without parameters as `program.py` to get a fully working window with all the flags ready to be edited.
|
|
139
|
+
* Running on a remote machine? Automatic regression to the text interface.
|
|
140
|
+
* Or access your program via [web browser](http://127.0.0.1:8000/Interfaces/#webinterface-or-web).
|
|
141
|
+
|
|
142
|
+
# Installation
|
|
143
|
+
|
|
144
|
+
Install with a single command from [PyPi](https://pypi.org/project/mininterface/).
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
pip install mininterface[all] # GPLv3 and compatible
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Bundles
|
|
151
|
+
|
|
152
|
+
There are various bundles. We mark the least permissive licence in the bundle.
|
|
153
|
+
|
|
154
|
+
| bundle | size | licence | description |
|
|
155
|
+
| ------ | ---- | ----------- | ---- |
|
|
156
|
+
| mininterface | 1 MB | LGPL | minimal – only text dialogs |
|
|
157
|
+
| mininterface[basic] | 25 MB | LGPL | CLI, GUI, TUI |
|
|
158
|
+
| mininterface[web] | 40 MB | LGPL | including [WebInterface](Interfaces.md#webinterface-or-web) |
|
|
159
|
+
| mininterface[img] | 40 MB | LGPL | images |
|
|
160
|
+
| mininterface[tui] | 40 MB | LGPL | images |
|
|
161
|
+
| mininterface[gui] | 70 MB | GPL | images, combobox, calendar |
|
|
162
|
+
| mininterface[ui] | 90 MB | GPL | full installation |
|
|
163
|
+
| mininterface[all] | 90 MB | GPL | full installation, same as `ui`, reserved for future use (big dependencies, optional interfaces) |
|
|
164
|
+
|
|
165
|
+
Apart from the minimal bundle (which lacks CLI and dataclass support), they have the same functionality, differring only in the user experience.
|
|
166
|
+
|
|
167
|
+
!!! tip
|
|
168
|
+
For automated testing (e.g., in CI environments), the `mininterface[basic]` bundle is sufficient.
|
|
169
|
+
|
|
170
|
+
## MacOS GUI
|
|
171
|
+
|
|
172
|
+
If the GUI does not work on MacOS, you might need to launch: `brew install python-tk`
|
|
173
|
+
|
|
174
|
+
# Docs
|
|
175
|
+
See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
|
|
176
|
+
|
|
177
|
+
# Gallery
|
|
178
|
+
|
|
179
|
+
These projects have the code base reduced thanks to the mininterface:
|
|
180
|
+
|
|
181
|
+
* **[deduplidog](https://github.com/CZ-NIC/deduplidog/)** – Find duplicates in a scattered directory structure
|
|
182
|
+
* **[touch-timestamp](https://github.com/CZ-NIC/touch-timestamp/)** – A powerful dialog to change the files' timestamp
|
|
183
|
+
|
|
184
|
+
# Examples
|
|
185
|
+
## Hello world
|
|
186
|
+
|
|
187
|
+
Take a look at the following example.
|
|
188
|
+
|
|
189
|
+
1. We define any Env class.
|
|
190
|
+
2. Then, we initialize mininterface with [`run(Env)`][mininterface.run] – the missing fields will be prompter for
|
|
191
|
+
3. Then, we use various dialog methods, like [`confirm`][mininterface.Mininterface.confirm], [`choice`][mininterface.Mininterface.select] or [`form`][mininterface.Mininterface.form].
|
|
192
|
+
|
|
193
|
+
Below, you find the screenshots how the program looks in various environments ([graphic](Interfaces.md#guiinterface-or-tkinterface-or-gui) interface, [web](Interfaces.md#webinterface-or-web) interface...).
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from dataclasses import dataclass
|
|
197
|
+
from pathlib import Path
|
|
198
|
+
from mininterface import run
|
|
199
|
+
|
|
200
|
+
@dataclass
|
|
201
|
+
class Env:
|
|
202
|
+
my_file: Path # This is my help text
|
|
203
|
+
my_flag: bool = False
|
|
204
|
+
my_number: int = 4
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
# Here, the user will be prompted
|
|
208
|
+
# for missing parameters (`my_file`) automatically
|
|
209
|
+
with run(Env) as m:
|
|
210
|
+
|
|
211
|
+
# You can lean on the typing
|
|
212
|
+
# Ex. directly read from the file object:
|
|
213
|
+
print("The file contents:", m.env.my_file.read_text())
|
|
214
|
+
|
|
215
|
+
# You can use various dialog methods,
|
|
216
|
+
# like `confirm` for bool
|
|
217
|
+
if m.confirm("Do you want to continue?"):
|
|
218
|
+
|
|
219
|
+
# or `choice` for choosing a value
|
|
220
|
+
fruit = m.select(("apple", "banana", "sirup"), "Choose a fruit")
|
|
221
|
+
|
|
222
|
+
if fruit == "apple":
|
|
223
|
+
# or `form` for an arbitrary values
|
|
224
|
+
m.form({
|
|
225
|
+
"How many": 0,
|
|
226
|
+
"Choose another file": m.env.my_file
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Launch with `./program.py`:
|
|
231
|
+
|
|
232
|
+

|
|
233
|
+

|
|
234
|
+

|
|
235
|
+

|
|
236
|
+
|
|
237
|
+
Or at the remote machine `MININTERFACE_INTERFACE=tui ./program.py`:
|
|
238
|
+
|
|
239
|
+

|
|
240
|
+

|
|
241
|
+

|
|
242
|
+

|
|
243
|
+
|
|
244
|
+
Or via the plain text `MININTERFACE_INTERFACE=text ./program.py`:
|
|
245
|
+
|
|
246
|
+

|
|
247
|
+
|
|
248
|
+
Or via web browser `MININTERFACE_INTERFACE=web ./program.py`:
|
|
249
|
+
|
|
250
|
+

|
|
251
|
+
|
|
252
|
+
You can always set Env via CLI or a config file:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
$ ./program.py --help
|
|
256
|
+
usage: program.py [-h] [OPTIONS]
|
|
257
|
+
|
|
258
|
+
╭─ options ──────────────────────────────────────────────────────────────╮
|
|
259
|
+
│ -h, --help show this help message and exit │
|
|
260
|
+
│ -v, --verbose Verbosity level. Can be used twice to increase. │
|
|
261
|
+
│ --my-file PATH This is my help text (required) │
|
|
262
|
+
│ --my-flag, --no-my-flag │
|
|
263
|
+
│ (default: False) │
|
|
264
|
+
│ --my-number INT (default: 4) │
|
|
265
|
+
╰────────────────────────────────────────────────────────────────────────╯
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Goodbye argparse world
|
|
269
|
+
|
|
270
|
+
You want to try out the Mininterface with your current [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser)?
|
|
271
|
+
|
|
272
|
+
You're using positional arguments, subparsers, types in the ArgumentParser... Mininterface will give you immediate benefit. Just wrap it inside the [`run`][mininterface.run] method.
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
#!/usr/bin/env python3
|
|
276
|
+
from argparse import ArgumentParser
|
|
277
|
+
from datetime import time
|
|
278
|
+
from pathlib import Path
|
|
279
|
+
|
|
280
|
+
from mininterface import run
|
|
281
|
+
|
|
282
|
+
parser = ArgumentParser()
|
|
283
|
+
parser.add_argument("input_file", type=Path, help="Path to the input file.")
|
|
284
|
+
parser.add_argument("--time", type=time, help="Given time")
|
|
285
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
286
|
+
sub1 = subparsers.add_parser("build", help="Build something.")
|
|
287
|
+
sub1.add_argument("--optimize", action="store_true", help="Enable optimizations.")
|
|
288
|
+
|
|
289
|
+
# Old version
|
|
290
|
+
# env = parser.parse_args()
|
|
291
|
+
# env.input_file # a Path object
|
|
292
|
+
|
|
293
|
+
# New version
|
|
294
|
+
m = run(parser)
|
|
295
|
+
m.env.input_file # a Path object
|
|
296
|
+
|
|
297
|
+
# Live edit of the fields
|
|
298
|
+
m.form()
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Now, the help text looks much better. Try it in the terminal to see the colours.
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
$ ./program.py --help
|
|
305
|
+
usage: program.py [-h] [OPTIONS] PATH
|
|
306
|
+
|
|
307
|
+
╭─ positional arguments ──────────────────────────────────────────────────╮
|
|
308
|
+
│ PATH Path to the input file. (required) │
|
|
309
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
310
|
+
╭─ options ───────────────────────────────────────────────────────────────╮
|
|
311
|
+
│ -h, --help show this help message and exit │
|
|
312
|
+
│ -v, --verbose Verbosity level. Can be used twice to increase. │
|
|
313
|
+
│ --time HH:MM[:SS[…]] Given time (default: 00:00:00) │
|
|
314
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
315
|
+
╭─ build options ─────────────────────────────────────────────────────────╮
|
|
316
|
+
│ --build.optimize, --build.no-optimize │
|
|
317
|
+
│ Enable optimizations. (default: False) │
|
|
318
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
And what happens when you launch the program? First, *Mininterface* asks you to provide the missing required arguments. Note the button to raise a file picker dialog.
|
|
322
|
+
|
|
323
|
+

|
|
324
|
+
|
|
325
|
+
Then, a `.form()` call will create a dialog with all the fields.
|
|
326
|
+
|
|
327
|
+

|
|
328
|
+
|
|
329
|
+
You will access the arguments through [`m.env`][mininterface.Mininterface.env]
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
print(m.env.time) # -> 14:21
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
If you're sure enough to start using *Mininterface*, convert the argparse into a dataclass. Then, the IDE will auto-complete the hints as you type.
|
|
336
|
+
|
|
337
|
+
!!! warning
|
|
338
|
+
Be aware that in contrast to the argparse, we create default values. This does make sense for most values but might pose a confusion for ex. `parser.add_argument("--path", type=Path)` which becomes `Path('.')`, not `None`.
|
|
@@ -1,41 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Name: mininterface
|
|
3
|
-
Version: 0.8.0
|
|
4
|
-
Summary: A minimal access to GUI, TUI, CLI and config
|
|
5
|
-
License: LGPL-3.0-or-later
|
|
6
|
-
Author: Edvard Rejthar
|
|
7
|
-
Author-email: edvard.rejthar@nic.cz
|
|
8
|
-
Requires-Python: >=3.10,<4.0
|
|
9
|
-
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Provides-Extra: all
|
|
16
|
-
Provides-Extra: gui
|
|
17
|
-
Provides-Extra: img
|
|
18
|
-
Provides-Extra: tui
|
|
19
|
-
Provides-Extra: ui
|
|
20
|
-
Provides-Extra: web
|
|
21
|
-
Requires-Dist: autocombobox (==1.4.2) ; extra == "gui" or extra == "ui" or extra == "all"
|
|
22
|
-
Requires-Dist: humanize
|
|
23
|
-
Requires-Dist: pillow ; extra == "img" or extra == "gui" or extra == "ui" or extra == "all"
|
|
24
|
-
Requires-Dist: pyyaml
|
|
25
|
-
Requires-Dist: simple_term_menu
|
|
26
|
-
Requires-Dist: textual (<2.0.0)
|
|
27
|
-
Requires-Dist: textual-serve ; extra == "web" or extra == "ui" or extra == "all"
|
|
28
|
-
Requires-Dist: textual_imageview ; extra == "img" or extra == "tui" or extra == "ui" or extra == "all"
|
|
29
|
-
Requires-Dist: tkcalendar ; extra == "gui" or extra == "ui" or extra == "all"
|
|
30
|
-
Requires-Dist: tkinter-tooltip
|
|
31
|
-
Requires-Dist: tkinter_form (==0.2.1)
|
|
32
|
-
Requires-Dist: tkscrollableframe
|
|
33
|
-
Requires-Dist: typing_extensions
|
|
34
|
-
Requires-Dist: tyro (>=0.9,<0.10)
|
|
35
|
-
Project-URL: Homepage, https://github.com/CZ-NIC/mininterface
|
|
36
|
-
Description-Content-Type: text/markdown
|
|
37
|
-
|
|
38
|
-
# Mininterface – access to GUI, TUI, CLI and config files
|
|
1
|
+
# Mininterface – access to GUI, TUI, web, CLI and config files
|
|
39
2
|
[](https://github.com/CZ-NIC/mininterface/actions)
|
|
40
3
|
[](https://pepy.tech/project/mininterface)
|
|
41
4
|
|
|
@@ -61,7 +24,7 @@ class Env:
|
|
|
61
24
|
""" This number is very important """
|
|
62
25
|
|
|
63
26
|
if __name__ == "__main__":
|
|
64
|
-
m = run(Env,
|
|
27
|
+
m = run(Env, title="My application")
|
|
65
28
|
m.form()
|
|
66
29
|
# Attributes are suggested by the IDE
|
|
67
30
|
# along with the hint text 'This number is very important'.
|
|
@@ -71,20 +34,22 @@ if __name__ == "__main__":
|
|
|
71
34
|
# Contents
|
|
72
35
|
- [You got CLI](#you-got-cli)
|
|
73
36
|
- [You got config file management](#you-got-config-file-management)
|
|
74
|
-
- [You got
|
|
37
|
+
- [You got dialogs](#you-got-dialogs)
|
|
75
38
|
- [Background](#background)
|
|
76
39
|
- [Installation](#installation)
|
|
77
40
|
- [Docs](#docs)
|
|
78
41
|
- [Gallery](#gallery)
|
|
79
|
-
- [
|
|
42
|
+
- [Examples](#examples)
|
|
43
|
+
* [Hello world](#hello-world)
|
|
44
|
+
* [Goodbye argparse world](#goodbye-argparse-world)
|
|
80
45
|
|
|
81
46
|
## You got CLI
|
|
82
|
-
It was all the code you need. No lengthy blocks of code imposed by an external dependency. Besides the GUI/TUI, you receive powerful YAML-configurable CLI parsing.
|
|
47
|
+
It was all the code you need. No lengthy blocks of code imposed by an external dependency. Besides the GUI/TUI/web, you receive powerful YAML-configurable CLI parsing.
|
|
83
48
|
|
|
84
49
|
|
|
85
50
|
```bash
|
|
86
51
|
$ ./program.py --help
|
|
87
|
-
usage:
|
|
52
|
+
usage: program.py [-h] [-v] [--my-flag | --no-my-flag] [--my-number INT]
|
|
88
53
|
|
|
89
54
|
This calculates something.
|
|
90
55
|
|
|
@@ -98,7 +63,7 @@ This calculates something.
|
|
|
98
63
|
```
|
|
99
64
|
|
|
100
65
|
## You got config file management
|
|
101
|
-
Loading config file is a piece of cake. Alongside `program.py`,
|
|
66
|
+
Loading config file is a piece of cake. Alongside `program.py`, write some of its arguments to `program.yaml`. They are seamlessly taken as defaults.
|
|
102
67
|
|
|
103
68
|
```yaml
|
|
104
69
|
my_number: 555
|
|
@@ -110,13 +75,13 @@ $ program.py --help
|
|
|
110
75
|
│ --my-number INT This number is very important (default: 555) │
|
|
111
76
|
```
|
|
112
77
|
|
|
113
|
-
## You got
|
|
114
|
-
Check out several useful methods to handle user
|
|
78
|
+
## You got dialogs
|
|
79
|
+
Check out several useful methods to handle user dialogs. Here we bound the interface to a `with` statement that redirects stdout directly to the window.
|
|
115
80
|
|
|
116
81
|
```python
|
|
117
82
|
with run(Env) as m:
|
|
118
83
|
print(f"Your important number is {m.env.my_number}")
|
|
119
|
-
boolean = m.
|
|
84
|
+
boolean = m.confirm("Is that alright?")
|
|
120
85
|
```
|
|
121
86
|
|
|
122
87
|

|
|
@@ -126,13 +91,13 @@ with run(Env) as m:
|
|
|
126
91
|
|
|
127
92
|
Wrapper between various libraries that provide a user interface.
|
|
128
93
|
|
|
129
|
-
Writing a small and useful program might be a task that takes fifteen minutes. Adding a CLI to specify the parameters is not so much overhead. But building a simple GUI around it? HOURS! Hours spent on researching GUI libraries, wondering why the Python desktop app ecosystem lags so far behind the web world. All you need is a few input fields validated through a clickable window... You do not deserve to add hundred of lines of the code just to define some editable fields.
|
|
94
|
+
Writing a small and useful program might be a task that takes fifteen minutes. Adding a CLI to specify the parameters is not so much overhead. But building a simple GUI around it? HOURS! Hours spent on researching GUI libraries, wondering why the Python desktop app ecosystem lags so far behind the web world. All you need is a few input fields validated through a clickable window... You do not deserve to add hundred of lines of the code just to define some editable fields. *Mininterface* is here to help.
|
|
130
95
|
|
|
131
96
|
The config variables needed by your program are kept in cozy dataclasses. Write less! The syntax of [tyro](https://github.com/brentyi/tyro) does not require any overhead (as its `argparse` alternatives do). You just annotate a class attribute, append a simple docstring and get a fully functional application:
|
|
132
97
|
|
|
133
98
|
* Call it as `program.py --help` to display full help.
|
|
134
99
|
* Use any flag in CLI: `program.py --my-flag` causes `env.my_flag` be set to `True`.
|
|
135
|
-
* The main benefit: Launch it without parameters as `program.py` to get a
|
|
100
|
+
* The main benefit: Launch it without parameters as `program.py` to get a fully working window with all the flags ready to be edited.
|
|
136
101
|
* Running on a remote machine? Automatic regression to the text interface.
|
|
137
102
|
* Or access your program via [web browser](http://127.0.0.1:8000/Interfaces/#webinterface-or-web).
|
|
138
103
|
|
|
@@ -146,27 +111,23 @@ pip install mininterface[all] # GPLv3 and compatible
|
|
|
146
111
|
|
|
147
112
|
## Bundles
|
|
148
113
|
|
|
149
|
-
There are various bundles. We mark the
|
|
114
|
+
There are various bundles. We mark the least permissive licence in the bundle.
|
|
150
115
|
|
|
151
116
|
| bundle | size | licence | description |
|
|
152
117
|
| ------ | ---- | ----------- | ---- |
|
|
153
|
-
| mininterface |
|
|
154
|
-
| mininterface |
|
|
155
|
-
| mininterface[web] |
|
|
156
|
-
| mininterface[img] | | | images |
|
|
157
|
-
| mininterface[tui] | | | images |
|
|
158
|
-
| mininterface[gui] | | GPL | images, combobox, calendar |
|
|
159
|
-
| mininterface[ui] |
|
|
160
|
-
| mininterface[all] |
|
|
118
|
+
| mininterface | 1 MB | LGPL | minimal – only text dialogs |
|
|
119
|
+
| mininterface[basic] | 25 MB | LGPL | CLI, GUI, TUI |
|
|
120
|
+
| mininterface[web] | 40 MB | LGPL | including [WebInterface](Interfaces.md#webinterface-or-web) |
|
|
121
|
+
| mininterface[img] | 40 MB | LGPL | images |
|
|
122
|
+
| mininterface[tui] | 40 MB | LGPL | images |
|
|
123
|
+
| mininterface[gui] | 70 MB | GPL | images, combobox, calendar |
|
|
124
|
+
| mininterface[ui] | 90 MB | GPL | full installation |
|
|
125
|
+
| mininterface[all] | 90 MB | GPL | full installation, same as `ui`, reserved for future use (big dependencies, optional interfaces) |
|
|
161
126
|
|
|
162
|
-
|
|
127
|
+
Apart from the minimal bundle (which lacks CLI and dataclass support), they have the same functionality, differring only in the user experience.
|
|
163
128
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
pip install --no-dependencies mininterface
|
|
168
|
-
pip install tyro typing_extensions pyyaml simple_term_menu
|
|
169
|
-
```
|
|
129
|
+
!!! tip
|
|
130
|
+
For automated testing (e.g., in CI environments), the `mininterface[basic]` bundle is sufficient.
|
|
170
131
|
|
|
171
132
|
## MacOS GUI
|
|
172
133
|
|
|
@@ -182,16 +143,18 @@ These projects have the code base reduced thanks to the mininterface:
|
|
|
182
143
|
* **[deduplidog](https://github.com/CZ-NIC/deduplidog/)** – Find duplicates in a scattered directory structure
|
|
183
144
|
* **[touch-timestamp](https://github.com/CZ-NIC/touch-timestamp/)** – A powerful dialog to change the files' timestamp
|
|
184
145
|
|
|
185
|
-
#
|
|
146
|
+
# Examples
|
|
147
|
+
## Hello world
|
|
186
148
|
|
|
187
149
|
Take a look at the following example.
|
|
150
|
+
|
|
188
151
|
1. We define any Env class.
|
|
189
152
|
2. Then, we initialize mininterface with [`run(Env)`][mininterface.run] – the missing fields will be prompter for
|
|
190
|
-
3. Then, we use various dialog methods, like [`
|
|
153
|
+
3. Then, we use various dialog methods, like [`confirm`][mininterface.Mininterface.confirm], [`choice`][mininterface.Mininterface.select] or [`form`][mininterface.Mininterface.form].
|
|
191
154
|
|
|
192
155
|
Below, you find the screenshots how the program looks in various environments ([graphic](Interfaces.md#guiinterface-or-tkinterface-or-gui) interface, [web](Interfaces.md#webinterface-or-web) interface...).
|
|
193
156
|
|
|
194
|
-
```
|
|
157
|
+
```python
|
|
195
158
|
from dataclasses import dataclass
|
|
196
159
|
from pathlib import Path
|
|
197
160
|
from mininterface import run
|
|
@@ -212,11 +175,11 @@ if __name__ == "__main__":
|
|
|
212
175
|
print("The file contents:", m.env.my_file.read_text())
|
|
213
176
|
|
|
214
177
|
# You can use various dialog methods,
|
|
215
|
-
# like `
|
|
216
|
-
if m.
|
|
178
|
+
# like `confirm` for bool
|
|
179
|
+
if m.confirm("Do you want to continue?"):
|
|
217
180
|
|
|
218
181
|
# or `choice` for choosing a value
|
|
219
|
-
fruit = m.
|
|
182
|
+
fruit = m.select(("apple", "banana", "sirup"), "Choose a fruit")
|
|
220
183
|
|
|
221
184
|
if fruit == "apple":
|
|
222
185
|
# or `form` for an arbitrary values
|
|
@@ -251,7 +214,7 @@ Or via web browser `MININTERFACE_INTERFACE=web ./program.py`:
|
|
|
251
214
|
You can always set Env via CLI or a config file:
|
|
252
215
|
|
|
253
216
|
```bash
|
|
254
|
-
$
|
|
217
|
+
$ ./program.py --help
|
|
255
218
|
usage: program.py [-h] [OPTIONS]
|
|
256
219
|
|
|
257
220
|
╭─ options ──────────────────────────────────────────────────────────────╮
|
|
@@ -263,3 +226,75 @@ usage: program.py [-h] [OPTIONS]
|
|
|
263
226
|
│ --my-number INT (default: 4) │
|
|
264
227
|
╰────────────────────────────────────────────────────────────────────────╯
|
|
265
228
|
```
|
|
229
|
+
|
|
230
|
+
## Goodbye argparse world
|
|
231
|
+
|
|
232
|
+
You want to try out the Mininterface with your current [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser)?
|
|
233
|
+
|
|
234
|
+
You're using positional arguments, subparsers, types in the ArgumentParser... Mininterface will give you immediate benefit. Just wrap it inside the [`run`][mininterface.run] method.
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
#!/usr/bin/env python3
|
|
238
|
+
from argparse import ArgumentParser
|
|
239
|
+
from datetime import time
|
|
240
|
+
from pathlib import Path
|
|
241
|
+
|
|
242
|
+
from mininterface import run
|
|
243
|
+
|
|
244
|
+
parser = ArgumentParser()
|
|
245
|
+
parser.add_argument("input_file", type=Path, help="Path to the input file.")
|
|
246
|
+
parser.add_argument("--time", type=time, help="Given time")
|
|
247
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
248
|
+
sub1 = subparsers.add_parser("build", help="Build something.")
|
|
249
|
+
sub1.add_argument("--optimize", action="store_true", help="Enable optimizations.")
|
|
250
|
+
|
|
251
|
+
# Old version
|
|
252
|
+
# env = parser.parse_args()
|
|
253
|
+
# env.input_file # a Path object
|
|
254
|
+
|
|
255
|
+
# New version
|
|
256
|
+
m = run(parser)
|
|
257
|
+
m.env.input_file # a Path object
|
|
258
|
+
|
|
259
|
+
# Live edit of the fields
|
|
260
|
+
m.form()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Now, the help text looks much better. Try it in the terminal to see the colours.
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
$ ./program.py --help
|
|
267
|
+
usage: program.py [-h] [OPTIONS] PATH
|
|
268
|
+
|
|
269
|
+
╭─ positional arguments ──────────────────────────────────────────────────╮
|
|
270
|
+
│ PATH Path to the input file. (required) │
|
|
271
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
272
|
+
╭─ options ───────────────────────────────────────────────────────────────╮
|
|
273
|
+
│ -h, --help show this help message and exit │
|
|
274
|
+
│ -v, --verbose Verbosity level. Can be used twice to increase. │
|
|
275
|
+
│ --time HH:MM[:SS[…]] Given time (default: 00:00:00) │
|
|
276
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
277
|
+
╭─ build options ─────────────────────────────────────────────────────────╮
|
|
278
|
+
│ --build.optimize, --build.no-optimize │
|
|
279
|
+
│ Enable optimizations. (default: False) │
|
|
280
|
+
╰─────────────────────────────────────────────────────────────────────────╯
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
And what happens when you launch the program? First, *Mininterface* asks you to provide the missing required arguments. Note the button to raise a file picker dialog.
|
|
284
|
+
|
|
285
|
+

|
|
286
|
+
|
|
287
|
+
Then, a `.form()` call will create a dialog with all the fields.
|
|
288
|
+
|
|
289
|
+

|
|
290
|
+
|
|
291
|
+
You will access the arguments through [`m.env`][mininterface.Mininterface.env]
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
print(m.env.time) # -> 14:21
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
If you're sure enough to start using *Mininterface*, convert the argparse into a dataclass. Then, the IDE will auto-complete the hints as you type.
|
|
298
|
+
|
|
299
|
+
!!! warning
|
|
300
|
+
Be aware that in contrast to the argparse, we create default values. This does make sense for most values but might pose a confusion for ex. `parser.add_argument("--path", type=Path)` which becomes `Path('.')`, not `None`.
|