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.
Files changed (83) hide show
  1. mininterface-1.0.0/PKG-INFO +338 -0
  2. mininterface-0.8.0/PKG-INFO → mininterface-1.0.0/README.md +107 -72
  3. {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/__init__.py +89 -63
  4. mininterface-1.0.0/mininterface/__main__.py +167 -0
  5. mininterface-1.0.0/mininterface/_lib/__init__.py +0 -0
  6. {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/auxiliary.py +56 -3
  7. mininterface-1.0.0/mininterface/_lib/cli_parser.py +538 -0
  8. {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/form_dict.py +66 -36
  9. {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/redirectable.py +3 -3
  10. {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/_lib}/showcase.py +10 -12
  11. mininterface-1.0.0/mininterface/_lib/start.py +132 -0
  12. {mininterface-0.8.0/mininterface/mininterface → mininterface-1.0.0/mininterface/_mininterface}/__init__.py +223 -84
  13. mininterface-1.0.0/mininterface/_mininterface/adaptor.py +111 -0
  14. mininterface-1.0.0/mininterface/_mininterface/mixin.py +42 -0
  15. {mininterface-0.8.0/mininterface/text_interface → mininterface-1.0.0/mininterface/_text_interface}/__init__.py +33 -32
  16. mininterface-1.0.0/mininterface/_text_interface/adaptor.py +185 -0
  17. {mininterface-0.8.0/mininterface/text_interface → mininterface-1.0.0/mininterface/_text_interface}/facet.py +3 -2
  18. mininterface-1.0.0/mininterface/_textual_interface/__init__.py +28 -0
  19. mininterface-1.0.0/mininterface/_textual_interface/adaptor.py +138 -0
  20. mininterface-1.0.0/mininterface/_textual_interface/button_contents.py +71 -0
  21. {mininterface-0.8.0/mininterface/textual_interface → mininterface-1.0.0/mininterface/_textual_interface}/file_picker_input.py +165 -113
  22. mininterface-0.8.0/mininterface/textual_interface/textual_app.py → mininterface-1.0.0/mininterface/_textual_interface/form_contents.py +27 -97
  23. mininterface-1.0.0/mininterface/_textual_interface/secret_input.py +75 -0
  24. mininterface-1.0.0/mininterface/_textual_interface/style.tcss +50 -0
  25. mininterface-1.0.0/mininterface/_textual_interface/textual_app.py +69 -0
  26. mininterface-1.0.0/mininterface/_textual_interface/widgets.py +125 -0
  27. mininterface-1.0.0/mininterface/_tk_interface/__init__.py +40 -0
  28. {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/adaptor.py +36 -20
  29. {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/date_entry.py +6 -5
  30. {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/facet.py +2 -2
  31. {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/redirect_text_tkinter.py +1 -1
  32. {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/secret_entry.py +2 -2
  33. mininterface-1.0.0/mininterface/_tk_interface/select_input.py +190 -0
  34. mininterface-1.0.0/mininterface/_tk_interface/utils.py +222 -0
  35. {mininterface-0.8.0/mininterface/web_interface → mininterface-1.0.0/mininterface/_web_interface}/__init__.py +14 -15
  36. mininterface-1.0.0/mininterface/_web_interface/app.py +42 -0
  37. mininterface-1.0.0/mininterface/_web_interface/child_adaptor.py +85 -0
  38. {mininterface-0.8.0/mininterface/web_interface → mininterface-1.0.0/mininterface/_web_interface}/parent_adaptor.py +32 -13
  39. mininterface-1.0.0/mininterface/cli.py +195 -0
  40. {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/exceptions.py +23 -5
  41. {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/experimental.py +1 -1
  42. {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/facet/__init__.py +5 -5
  43. mininterface-1.0.0/mininterface/interfaces.py +129 -0
  44. mininterface-1.0.0/mininterface/settings.py +74 -0
  45. mininterface-1.0.0/mininterface/tag/__init__.py +8 -0
  46. {mininterface-0.8.0/mininterface/types → mininterface-1.0.0/mininterface/tag}/alias.py +12 -5
  47. mininterface-1.0.0/mininterface/tag/callback_tag.py +84 -0
  48. mininterface-1.0.0/mininterface/tag/datetime_tag.py +116 -0
  49. mininterface-1.0.0/mininterface/tag/flag.py +140 -0
  50. {mininterface-0.8.0/mininterface/types → mininterface-1.0.0/mininterface/tag}/internal.py +3 -0
  51. mininterface-1.0.0/mininterface/tag/path_tag.py +115 -0
  52. mininterface-1.0.0/mininterface/tag/secret_tag.py +53 -0
  53. mininterface-1.0.0/mininterface/tag/select_tag.py +322 -0
  54. {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/tag}/tag.py +294 -211
  55. {mininterface-0.8.0/mininterface → mininterface-1.0.0/mininterface/tag}/tag_factory.py +32 -10
  56. {mininterface-0.8.0 → mininterface-1.0.0}/mininterface/validators.py +13 -5
  57. mininterface-1.0.0/pyproject.toml +140 -0
  58. mininterface-0.8.0/README.md +0 -228
  59. mininterface-0.8.0/mininterface/ValidationFail.py +0 -5
  60. mininterface-0.8.0/mininterface/__main__.py +0 -77
  61. mininterface-0.8.0/mininterface/cli_parser.py +0 -367
  62. mininterface-0.8.0/mininterface/interfaces.py +0 -106
  63. mininterface-0.8.0/mininterface/mininterface/adaptor.py +0 -59
  64. mininterface-0.8.0/mininterface/options.py +0 -55
  65. mininterface-0.8.0/mininterface/start.py +0 -95
  66. mininterface-0.8.0/mininterface/subcommands.py +0 -141
  67. mininterface-0.8.0/mininterface/text_interface/adaptor.py +0 -158
  68. mininterface-0.8.0/mininterface/textual_interface/__init__.py +0 -63
  69. mininterface-0.8.0/mininterface/textual_interface/adaptor.py +0 -91
  70. mininterface-0.8.0/mininterface/textual_interface/textual_button_app.py +0 -98
  71. mininterface-0.8.0/mininterface/textual_interface/widgets.py +0 -154
  72. mininterface-0.8.0/mininterface/tk_interface/__init__.py +0 -57
  73. mininterface-0.8.0/mininterface/tk_interface/utils.py +0 -221
  74. mininterface-0.8.0/mininterface/types/__init__.py +0 -4
  75. mininterface-0.8.0/mininterface/types/flags.py +0 -69
  76. mininterface-0.8.0/mininterface/types/rich_tags.py +0 -440
  77. mininterface-0.8.0/mininterface/web_interface/app.py +0 -24
  78. mininterface-0.8.0/mininterface/web_interface/child_adaptor.py +0 -68
  79. mininterface-0.8.0/pyproject.toml +0 -65
  80. {mininterface-0.8.0 → mininterface-1.0.0}/LICENSE +0 -0
  81. {mininterface-0.8.0/mininterface/textual_interface → mininterface-1.0.0/mininterface/_textual_interface}/facet.py +0 -0
  82. {mininterface-0.8.0/mininterface/tk_interface → mininterface-1.0.0/mininterface/_tk_interface}/external_fix.py +0 -0
  83. {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
+ [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
41
+ [![Downloads](https://static.pepy.tech/badge/mininterface)](https://pepy.tech/project/mininterface)
42
+
43
+ Write the program core, do not bother with the input/output.
44
+
45
+ ![Hello world example: GUI window](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-gui.avif?raw=True "A minimal use case – GUI")
46
+ ![Hello world example: TUI fallback](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-tui.avif?raw=True "A minimal use case – TUI fallback")
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
+ ![Small window with the text 'Your important number'](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-with-statement.webp?raw=True "With statement to redirect the output")
126
+ ![The same in terminal'](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-with-statement-tui.avif?raw=True "With statement in TUI fallback")
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
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_tk1.avif?raw=True)
233
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_tk2.avif?raw=True)
234
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_tk3.avif?raw=True)
235
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_tk4.avif?raw=True)
236
+
237
+ Or at the remote machine `MININTERFACE_INTERFACE=tui ./program.py`:
238
+
239
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_textual1.avif?raw=True)
240
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_textual2.avif?raw=True)
241
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_textual3.avif?raw=True)
242
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_textual4.avif?raw=True)
243
+
244
+ Or via the plain text `MININTERFACE_INTERFACE=text ./program.py`:
245
+
246
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_text.avif?raw=True)
247
+
248
+ Or via web browser `MININTERFACE_INTERFACE=web ./program.py`:
249
+
250
+ ![Tutorial](https://github.com/CZ-NIC/mininterface/blob/main/asset/tutorial_web.avif?raw=True)
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
+ ![Positional fields](https://github.com/CZ-NIC/mininterface/blob/main/asset/argparse_required.avif?raw=True)
324
+
325
+ Then, a `.form()` call will create a dialog with all the fields.
326
+
327
+ ![Whole form](https://github.com/CZ-NIC/mininterface/blob/main/asset/argparse_form.avif?raw=True)
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
- Metadata-Version: 2.3
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
  [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
40
3
  [![Downloads](https://static.pepy.tech/badge/mininterface)](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, prog="My application")
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 dialogues](#you-got-dialogues)
37
+ - [You got dialogs](#you-got-dialogs)
75
38
  - [Background](#background)
76
39
  - [Installation](#installation)
77
40
  - [Docs](#docs)
78
41
  - [Gallery](#gallery)
79
- - [Hello world](#hello-world)
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: My application [-h] [-v] [--my-flag | --no-my-flag] [--my-number INT]
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`, put `program.yaml` and put there some of the arguments. They are seamlessly taken as defaults.
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 dialogues
114
- Check out several useful methods to handle user dialogues. Here we bound the interface to a `with` statement that redirects stdout directly to the window.
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.is_yes("Is that alright?")
84
+ boolean = m.confirm("Is that alright?")
120
85
  ```
121
86
 
122
87
  ![Small window with the text 'Your important number'](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-with-statement.webp?raw=True "With statement to redirect the output")
@@ -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. `Mininterface` is here to help.
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 full working window with all the flags ready to be edited.
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 bundles with GPL3 dependencies.
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 | 30 MB | LGPL | standard (GUI, TUI) |
154
- | mininterface | 36 MB | LGPL | standard (GUI, TUI) |
155
- | mininterface[web] | 36 MB | | including [WebInterface](Interfaces.md#webinterface-or-web) |
156
- | mininterface[img] | | | images |
157
- | mininterface[tui] | | | images |
158
- | mininterface[gui] | | GPL | images, combobox, calendar |
159
- | mininterface[ui] | 100 MB | GPL | full installation |
160
- | mininterface[all] | 100 MB | GPL | full installation, same as `ui`, reserved for future use (big dependencies, optional interfaces) |
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
- ## Minimal installation
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
- Should you need just the CLI part and you are happy with basic text dialogs, use these commands instead:
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
- # Hello world
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 [`is_yes`][mininterface.Mininterface.is_yes], [`choice`][mininterface.Mininterface.choice] or [`form`][mininterface.Mininterface.form].
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
- ```python3
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 `is_yes` for bool
216
- if m.is_yes("Do you want to continue?"):
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.choice(("apple", "banana", "sirup"), "Choose a fruit")
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
- $ MININTERFACE_INTERFACE=gui ./program.py --help
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
+ ![Positional fields](https://github.com/CZ-NIC/mininterface/blob/main/asset/argparse_required.avif?raw=True)
286
+
287
+ Then, a `.form()` call will create a dialog with all the fields.
288
+
289
+ ![Whole form](https://github.com/CZ-NIC/mininterface/blob/main/asset/argparse_form.avif?raw=True)
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`.