mininterface 0.5.0__tar.gz → 0.6.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 (35) hide show
  1. mininterface-0.6.0/PKG-INFO +311 -0
  2. mininterface-0.6.0/README.md +287 -0
  3. mininterface-0.6.0/mininterface/__init__.py +187 -0
  4. {mininterface-0.5.0 → mininterface-0.6.0}/mininterface/__main__.py +8 -4
  5. {mininterface-0.5.0 → mininterface-0.6.0}/mininterface/auxiliary.py +7 -15
  6. mininterface-0.6.0/mininterface/cli_parser.py +207 -0
  7. mininterface-0.6.0/mininterface/common.py +8 -0
  8. mininterface-0.6.0/mininterface/experimental.py +54 -0
  9. mininterface-0.6.0/mininterface/facet.py +56 -0
  10. mininterface-0.6.0/mininterface/form_dict.py +144 -0
  11. mininterface-0.6.0/mininterface/gui_interface/__init__.py +58 -0
  12. mininterface-0.6.0/mininterface/gui_interface/tk_facet.py +20 -0
  13. mininterface-0.6.0/mininterface/gui_interface/tk_window.py +139 -0
  14. mininterface-0.6.0/mininterface/gui_interface/utils.py +160 -0
  15. mininterface-0.6.0/mininterface/mininterface.py +268 -0
  16. mininterface-0.5.0/mininterface/Redirectable.py → mininterface-0.6.0/mininterface/redirectable.py +6 -2
  17. mininterface-0.6.0/mininterface/tag.py +623 -0
  18. mininterface-0.5.0/mininterface/TextInterface.py → mininterface-0.6.0/mininterface/text_interface.py +9 -7
  19. mininterface-0.6.0/mininterface/textual_interface/__init__.py +59 -0
  20. mininterface-0.6.0/mininterface/textual_interface/textual_app.py +144 -0
  21. mininterface-0.6.0/mininterface/textual_interface/textual_button_app.py +96 -0
  22. mininterface-0.6.0/mininterface/textual_interface/textual_facet.py +18 -0
  23. mininterface-0.6.0/mininterface/textual_interface/widgets.py +71 -0
  24. mininterface-0.6.0/mininterface/types.py +79 -0
  25. mininterface-0.6.0/mininterface/validators.py +143 -0
  26. {mininterface-0.5.0 → mininterface-0.6.0}/pyproject.toml +6 -2
  27. mininterface-0.5.0/PKG-INFO +0 -254
  28. mininterface-0.5.0/README.md +0 -230
  29. mininterface-0.5.0/mininterface/FormDict.py +0 -128
  30. mininterface-0.5.0/mininterface/FormField.py +0 -163
  31. mininterface-0.5.0/mininterface/GuiInterface.py +0 -172
  32. mininterface-0.5.0/mininterface/Mininterface.py +0 -144
  33. mininterface-0.5.0/mininterface/TextualInterface.py +0 -234
  34. mininterface-0.5.0/mininterface/__init__.py +0 -95
  35. mininterface-0.5.0/mininterface/common.py +0 -2
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.1
2
+ Name: mininterface
3
+ Version: 0.6.0
4
+ Summary: A minimal access to GUI, TUI, CLI and config
5
+ Home-page: https://github.com/CZ-NIC/mininterface
6
+ License: GPL-3.0-or-later
7
+ Author: Edvard Rejthar
8
+ Author-email: edvard.rejthar@nic.cz
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: envelope
16
+ Requires-Dist: pyyaml
17
+ Requires-Dist: requests
18
+ Requires-Dist: textual
19
+ Requires-Dist: tkinter-tooltip
20
+ Requires-Dist: tkinter_form (==0.1.5.2)
21
+ Requires-Dist: tyro
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Mininterface – access to GUI, TUI, CLI and config files
25
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
26
+ [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
27
+
28
+ Write the program core, do not bother with the input/output.
29
+
30
+ ![Hello world example: GUI window](asset/hello-world.png "A minimal use case – GUI")
31
+ ![Hello world example: TUI fallback](asset/hello-tui.webp "A minimal use case – TUI fallback")
32
+
33
+ Check out the code, which is surprisingly short, that displays such a window or its textual fallback.
34
+
35
+ ```python
36
+ from dataclasses import dataclass
37
+ from mininterface import run
38
+
39
+ @dataclass
40
+ class Env:
41
+ """ This calculates something. """
42
+
43
+ my_flag: bool = False
44
+ """ This switches the functionality """
45
+
46
+ my_number: int = 4
47
+ """ This number is very important """
48
+
49
+ if __name__ == "__main__":
50
+ env = run(Env, prog="My application").env
51
+ # Attributes are suggested by the IDE
52
+ # along with the hint text 'This number is very important'.
53
+ print(env.my_number)
54
+ ```
55
+
56
+ # Contents
57
+ - [You got CLI](#you-got-cli)
58
+ - [You got config file management](#you-got-config-file-management)
59
+ - [You got dialogues](#you-got-dialogues)
60
+ - [Background](#background)
61
+ - [Installation](#installation)
62
+ - [Docs](#docs)
63
+ - [Examples](#examples)
64
+
65
+ ## You got CLI
66
+ 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.
67
+
68
+ TODO regenerate output and all the images
69
+ TODO it seems that input missing dataclass fields is broken, it just shows it must be set via cli
70
+
71
+ ```bash
72
+ $ ./hello.py
73
+ usage: My application [-h] [--test | --no-test] [--important-number INT]
74
+
75
+ This calculates something.
76
+
77
+ ╭─ options ──────────────────────────────────────────────────────────╮
78
+ │ -h, --help show this help message and exit │
79
+ │ --test, --no-test My testing flag (default: False) │
80
+ │ --important-number INT This number is very important (default: 4) │
81
+ ╰────────────────────────────────────────────────────────────────────╯
82
+ ```
83
+
84
+ ## You got config file management
85
+ 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.
86
+
87
+ ```yaml
88
+ my_number: 555
89
+ ```
90
+
91
+ ## You got dialogues
92
+ 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.
93
+
94
+ ```python
95
+ with run(Env) as m:
96
+ print(f"Your important number is {m.env.my_number}")
97
+ boolean = m.is_yes("Is that alright?")
98
+ ```
99
+
100
+ ![Small window with the text 'Your important number'](asset/hello-with-statement.webp "With statement to redirect the output")
101
+ ![The same in terminal'](asset/hello-with-statement-tui.avif "With statement in TUI fallback")
102
+
103
+ # Background
104
+
105
+ Wrapper between the [tyro](https://github.com/brentyi/tyro) `argparse` replacement and [tkinter_form](https://github.com/JohanEstebanCuervo/tkinter_form/) that converts dicts into a GUI.
106
+
107
+ 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.
108
+
109
+ 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:
110
+ * Call it as `program.py --help` to display full help.
111
+ * Use any flag in CLI: `program.py --my-flag` causes `env.my_flag` be set to `True`.
112
+ * The main benefit: Launch it without parameters as `program.py` to get a full working window with all the flags ready to be edited.
113
+ * Running on a remote machine? Automatic regression to the text interface.
114
+
115
+ # Installation
116
+
117
+ Install with a single command from [PyPi](https://pypi.org/project/mininterface/).
118
+
119
+ ```bash
120
+ pip install mininterface
121
+ ```
122
+
123
+ # Docs
124
+ See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
125
+
126
+ # Examples
127
+
128
+ ## A complex dataclass.
129
+
130
+ ```python3
131
+ from typing import Annotated
132
+ from dataclasses import dataclass
133
+ from mininterface.validators import not_empty
134
+ from mininterface import run, Tag, Validation
135
+
136
+ @dataclass
137
+ class NestedEnv:
138
+ another_number: int = 7
139
+ """ This field is nested """
140
+
141
+ @dataclass
142
+ class Env:
143
+ nested_config: NestedEnv
144
+
145
+ mandatory_str: str
146
+ """ As there is not default value, you will be prompted automatically to fill up the field """
147
+
148
+ my_number: int | None = None
149
+ """ This is not just a dummy number, if left empty, it is None. """
150
+
151
+ my_string: str = "Hello"
152
+ """ A dummy string """
153
+
154
+ my_flag: bool = False
155
+ """ Checkbox test """
156
+
157
+ my_validated: Annotated[str, Validation(not_empty)] = "hello"
158
+ """ A validated field """
159
+
160
+ m = run(Env, title="My program")
161
+ # See some values
162
+ print(m.env.nested_config.another_number) # 7
163
+ print(m.env)
164
+ # Env(nested_config=NestedEnv(another_number=7), my_number=5, my_string='Hello', my_flag=False, my_validated='hello')
165
+
166
+ # Edit values in a dialog
167
+ m.form()
168
+ ```
169
+
170
+ ## Form with paths
171
+
172
+ We have a dict with some paths. Here is how it looks.
173
+
174
+ ```python
175
+ from pathlib import Path
176
+ from mininterface import run, Tag
177
+
178
+ m = run(title="My program")
179
+ my_dictionary = {
180
+ "paths": Tag("", annotation=list[Path]),
181
+ "default_paths": Tag([Path("/tmp"), Path("/usr")], annotation=list[Path])
182
+ }
183
+
184
+ # Edit values in a dialog
185
+ m.form(my_dictionary)
186
+ ```
187
+
188
+ ![List of paths](asset/list_of_paths.avif)
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
@@ -0,0 +1,287 @@
1
+ # Mininterface – access to GUI, TUI, CLI and config files
2
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
3
+ [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
4
+
5
+ Write the program core, do not bother with the input/output.
6
+
7
+ ![Hello world example: GUI window](asset/hello-world.png "A minimal use case – GUI")
8
+ ![Hello world example: TUI fallback](asset/hello-tui.webp "A minimal use case – TUI fallback")
9
+
10
+ Check out the code, which is surprisingly short, that displays such a window or its textual fallback.
11
+
12
+ ```python
13
+ from dataclasses import dataclass
14
+ from mininterface import run
15
+
16
+ @dataclass
17
+ class Env:
18
+ """ This calculates something. """
19
+
20
+ my_flag: bool = False
21
+ """ This switches the functionality """
22
+
23
+ my_number: int = 4
24
+ """ This number is very important """
25
+
26
+ if __name__ == "__main__":
27
+ env = run(Env, prog="My application").env
28
+ # Attributes are suggested by the IDE
29
+ # along with the hint text 'This number is very important'.
30
+ print(env.my_number)
31
+ ```
32
+
33
+ # Contents
34
+ - [You got CLI](#you-got-cli)
35
+ - [You got config file management](#you-got-config-file-management)
36
+ - [You got dialogues](#you-got-dialogues)
37
+ - [Background](#background)
38
+ - [Installation](#installation)
39
+ - [Docs](#docs)
40
+ - [Examples](#examples)
41
+
42
+ ## You got CLI
43
+ 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.
44
+
45
+ TODO regenerate output and all the images
46
+ TODO it seems that input missing dataclass fields is broken, it just shows it must be set via cli
47
+
48
+ ```bash
49
+ $ ./hello.py
50
+ usage: My application [-h] [--test | --no-test] [--important-number INT]
51
+
52
+ This calculates something.
53
+
54
+ ╭─ options ──────────────────────────────────────────────────────────╮
55
+ │ -h, --help show this help message and exit │
56
+ │ --test, --no-test My testing flag (default: False) │
57
+ │ --important-number INT This number is very important (default: 4) │
58
+ ╰────────────────────────────────────────────────────────────────────╯
59
+ ```
60
+
61
+ ## You got config file management
62
+ 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.
63
+
64
+ ```yaml
65
+ my_number: 555
66
+ ```
67
+
68
+ ## You got dialogues
69
+ 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.
70
+
71
+ ```python
72
+ with run(Env) as m:
73
+ print(f"Your important number is {m.env.my_number}")
74
+ boolean = m.is_yes("Is that alright?")
75
+ ```
76
+
77
+ ![Small window with the text 'Your important number'](asset/hello-with-statement.webp "With statement to redirect the output")
78
+ ![The same in terminal'](asset/hello-with-statement-tui.avif "With statement in TUI fallback")
79
+
80
+ # Background
81
+
82
+ Wrapper between the [tyro](https://github.com/brentyi/tyro) `argparse` replacement and [tkinter_form](https://github.com/JohanEstebanCuervo/tkinter_form/) that converts dicts into a GUI.
83
+
84
+ 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.
85
+
86
+ 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:
87
+ * Call it as `program.py --help` to display full help.
88
+ * Use any flag in CLI: `program.py --my-flag` causes `env.my_flag` be set to `True`.
89
+ * The main benefit: Launch it without parameters as `program.py` to get a full working window with all the flags ready to be edited.
90
+ * Running on a remote machine? Automatic regression to the text interface.
91
+
92
+ # Installation
93
+
94
+ Install with a single command from [PyPi](https://pypi.org/project/mininterface/).
95
+
96
+ ```bash
97
+ pip install mininterface
98
+ ```
99
+
100
+ # Docs
101
+ See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
102
+
103
+ # Examples
104
+
105
+ ## A complex dataclass.
106
+
107
+ ```python3
108
+ from typing import Annotated
109
+ from dataclasses import dataclass
110
+ from mininterface.validators import not_empty
111
+ from mininterface import run, Tag, Validation
112
+
113
+ @dataclass
114
+ class NestedEnv:
115
+ another_number: int = 7
116
+ """ This field is nested """
117
+
118
+ @dataclass
119
+ class Env:
120
+ nested_config: NestedEnv
121
+
122
+ mandatory_str: str
123
+ """ As there is not default value, you will be prompted automatically to fill up the field """
124
+
125
+ my_number: int | None = None
126
+ """ This is not just a dummy number, if left empty, it is None. """
127
+
128
+ my_string: str = "Hello"
129
+ """ A dummy string """
130
+
131
+ my_flag: bool = False
132
+ """ Checkbox test """
133
+
134
+ my_validated: Annotated[str, Validation(not_empty)] = "hello"
135
+ """ A validated field """
136
+
137
+ m = run(Env, title="My program")
138
+ # See some values
139
+ print(m.env.nested_config.another_number) # 7
140
+ print(m.env)
141
+ # Env(nested_config=NestedEnv(another_number=7), my_number=5, my_string='Hello', my_flag=False, my_validated='hello')
142
+
143
+ # Edit values in a dialog
144
+ m.form()
145
+ ```
146
+
147
+ ## Form with paths
148
+
149
+ We have a dict with some paths. Here is how it looks.
150
+
151
+ ```python
152
+ from pathlib import Path
153
+ from mininterface import run, Tag
154
+
155
+ m = run(title="My program")
156
+ my_dictionary = {
157
+ "paths": Tag("", annotation=list[Path]),
158
+ "default_paths": Tag([Path("/tmp"), Path("/usr")], annotation=list[Path])
159
+ }
160
+
161
+ # Edit values in a dialog
162
+ m.form(my_dictionary)
163
+ ```
164
+
165
+ ![List of paths](asset/list_of_paths.avif)
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+