easypyui 2.4.0__tar.gz → 3.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.
- {easypyui-2.4.0/easypyui.egg-info → easypyui-3.0.0}/PKG-INFO +49 -2
- easypyui-2.4.0/PKG-INFO → easypyui-3.0.0/README.md +226 -199
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui/__init__.py +7 -1
- easypyui-3.0.0/easypyui/__main__.py +2 -0
- easypyui-3.0.0/easypyui/cli.py +27 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui/core.py +5 -0
- easypyui-3.0.0/easypyui/reactive.py +43 -0
- easypyui-3.0.0/easypyui/shell.py +34 -0
- easypyui-3.0.0/easypyui/theme3.py +15 -0
- easypyui-2.4.0/README.md → easypyui-3.0.0/easypyui.egg-info/PKG-INFO +246 -179
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui.egg-info/SOURCES.txt +8 -1
- easypyui-3.0.0/easypyui.egg-info/entry_points.txt +2 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/pyproject.toml +4 -1
- easypyui-3.0.0/tests/test_public_api.py +5 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/LICENSE +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui/animation.py +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui/largeapp.py +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui/modern.py +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui/productivity.py +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui.egg-info/dependency_links.txt +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/easypyui.egg-info/top_level.txt +0 -0
- {easypyui-2.4.0 → easypyui-3.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easypyui
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: A simple and modern Python GUI library built on tkinter and ttk
|
|
5
5
|
Author: EasyPyUI
|
|
6
6
|
License: MIT
|
|
@@ -123,7 +123,7 @@ MIT License
|
|
|
123
123
|
|
|
124
124
|
## Version
|
|
125
125
|
|
|
126
|
-
Current release: **
|
|
126
|
+
Current release: **3.0.0**
|
|
127
127
|
|
|
128
128
|
2.2.1 is a PyPI metadata and documentation update for the 2.2 series.
|
|
129
129
|
|
|
@@ -197,3 +197,50 @@ table.set_rows([
|
|
|
197
197
|
("Daniel", 95),
|
|
198
198
|
])
|
|
199
199
|
```
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# EasyPyUI 3.0
|
|
203
|
+
|
|
204
|
+
EasyPyUI 3.0 moves beyond a tkinter helper toward a small application framework.
|
|
205
|
+
|
|
206
|
+
## Reactive UI
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
name = state("Noah")
|
|
210
|
+
upper = computed(lambda: name.get().upper(), name)
|
|
211
|
+
|
|
212
|
+
field = minput("Name")
|
|
213
|
+
bind(field, name)
|
|
214
|
+
text_widget = text("")
|
|
215
|
+
upper.bind_text(text_widget)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## AppShell
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
ui = shell("My App")
|
|
222
|
+
|
|
223
|
+
ui.page("home", "Home", lambda parent: __import__("tkinter").Label(parent, text="Home").pack())
|
|
224
|
+
ui.page("settings", "Settings", lambda parent: __import__("tkinter").Label(parent, text="Settings").pack())
|
|
225
|
+
ui.start("home")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Design Tokens
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
design(radius=12, spacing=10, font="Segoe UI", title_size=24)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## CLI
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
easypyui new my_app
|
|
238
|
+
cd my_app
|
|
239
|
+
python app.py
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Included from 2.x
|
|
243
|
+
|
|
244
|
+
Modern widgets, animations, routing, background tasks, forms and validators,
|
|
245
|
+
state management, shortcuts, debounce/throttle, DataTable, Modal, Drawer,
|
|
246
|
+
Accordion, DatePicker and NotificationCenter remain available.
|
|
@@ -1,199 +1,226 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
##
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
##
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
1
|
+
# EasyPyUI
|
|
2
|
+
|
|
3
|
+
**Build modern Python desktop GUIs with much less code than raw tkinter.**
|
|
4
|
+
|
|
5
|
+
EasyPyUI is a lightweight GUI library built on Python's standard `tkinter` and `ttk`.
|
|
6
|
+
It provides a short beginner-friendly API while keeping access to native Tk widgets.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install easypyui
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from easypyui import *
|
|
18
|
+
|
|
19
|
+
app("Hello EasyPyUI", 500, 400, appearance="dark")
|
|
20
|
+
title("Hello!")
|
|
21
|
+
|
|
22
|
+
name = minput("Name", placeholder="Your name")
|
|
23
|
+
mbutton("Say hello", lambda: notify("Hello " + name.get()))
|
|
24
|
+
|
|
25
|
+
fade_in(400)
|
|
26
|
+
run()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Modern UI
|
|
30
|
+
|
|
31
|
+
EasyPyUI 2.x includes `mbutton()`, `minput()`, `mswitch()`, `mcard()`,
|
|
32
|
+
Light/Dark appearance and custom accent colors.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
app("Dashboard", 700, 500, appearance="dark", accent="#8b5cf6")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Animation
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
ring = progress_ring(20, size=110)
|
|
42
|
+
mbutton("Start", lambda: ring.animate_to(100, 1000))
|
|
43
|
+
fade_in(500)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Easing modes: `linear`, `ease_in`, `ease_out`, `ease_in_out`.
|
|
47
|
+
|
|
48
|
+
## Pages / Router
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from easypyui import *
|
|
52
|
+
|
|
53
|
+
app("Pages", 500, 350)
|
|
54
|
+
|
|
55
|
+
def home(ui):
|
|
56
|
+
title("Home")
|
|
57
|
+
on("Settings", lambda: go("settings"))
|
|
58
|
+
|
|
59
|
+
def settings(ui):
|
|
60
|
+
title("Settings")
|
|
61
|
+
on("Back", lambda: go("home"))
|
|
62
|
+
|
|
63
|
+
route("home", home)
|
|
64
|
+
route("settings", settings)
|
|
65
|
+
go("home")
|
|
66
|
+
run()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Background Tasks
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
def work():
|
|
73
|
+
import time
|
|
74
|
+
time.sleep(2)
|
|
75
|
+
return "Done!"
|
|
76
|
+
|
|
77
|
+
run_task(work, done=lambda result: notify(result), loading_text="Working...")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Native tkinter access
|
|
81
|
+
|
|
82
|
+
Most EasyPyUI wrapper objects expose `.tk`:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
btn = mbutton("Test", lambda: print("clicked"))
|
|
86
|
+
btn.tk.bind("<Button-2>", lambda event: print("middle click"))
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Classic API
|
|
90
|
+
|
|
91
|
+
The older API remains available, including `window()`, `text()`, `inputbox()`,
|
|
92
|
+
`button()`, `table()`, `canvas()` and `start()`.
|
|
93
|
+
|
|
94
|
+
## Requirements
|
|
95
|
+
|
|
96
|
+
- Python 3.9+
|
|
97
|
+
- tkinter support in your Python installation
|
|
98
|
+
- No mandatory third-party runtime GUI dependency
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT License
|
|
103
|
+
|
|
104
|
+
## Version
|
|
105
|
+
|
|
106
|
+
Current release: **3.0.0**
|
|
107
|
+
|
|
108
|
+
2.2.1 is a PyPI metadata and documentation update for the 2.2 series.
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
## 2.3 Productivity APIs
|
|
112
|
+
|
|
113
|
+
### Form validation
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
f = form()
|
|
117
|
+
|
|
118
|
+
email = f.add(
|
|
119
|
+
"Email",
|
|
120
|
+
minput("Email"),
|
|
121
|
+
[Validator.required(), Validator.email()]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
password_box = f.add(
|
|
125
|
+
"Password",
|
|
126
|
+
minput("Password", password=True),
|
|
127
|
+
[Validator.required(), Validator.min_length(8)]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def submit():
|
|
131
|
+
ok, errors = f.validate()
|
|
132
|
+
if ok:
|
|
133
|
+
notify(str(f.values()))
|
|
134
|
+
|
|
135
|
+
mbutton("Submit", submit)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Reactive State
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
count = state(0)
|
|
142
|
+
|
|
143
|
+
label_widget = text("0")
|
|
144
|
+
count.bind_text(label_widget)
|
|
145
|
+
|
|
146
|
+
mbutton("+1", lambda: count.update(lambda n: n + 1))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Keyboard shortcuts
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
shortcut("ctrl+s", save)
|
|
153
|
+
shortcut("ctrl+q", close)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Debounce / Throttle
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
search = debounce(300, do_search)
|
|
160
|
+
resize_handler = throttle(100, update_layout)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
## 2.4 Large Application Widgets
|
|
165
|
+
|
|
166
|
+
- `datatable()` — search, sorting and pagination
|
|
167
|
+
- `modal()` — modal window
|
|
168
|
+
- `drawer()` — collapsible side drawer
|
|
169
|
+
- `accordion()` — expandable sections
|
|
170
|
+
- `datepicker()` — date value input
|
|
171
|
+
- `notifications()` — notification center
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
table = datatable(["name", "score"], ["Name", "Score"], page_size=5)
|
|
175
|
+
table.set_rows([
|
|
176
|
+
("Noah", 100),
|
|
177
|
+
("Daniel", 95),
|
|
178
|
+
])
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# EasyPyUI 3.0
|
|
183
|
+
|
|
184
|
+
EasyPyUI 3.0 moves beyond a tkinter helper toward a small application framework.
|
|
185
|
+
|
|
186
|
+
## Reactive UI
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
name = state("Noah")
|
|
190
|
+
upper = computed(lambda: name.get().upper(), name)
|
|
191
|
+
|
|
192
|
+
field = minput("Name")
|
|
193
|
+
bind(field, name)
|
|
194
|
+
text_widget = text("")
|
|
195
|
+
upper.bind_text(text_widget)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## AppShell
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
ui = shell("My App")
|
|
202
|
+
|
|
203
|
+
ui.page("home", "Home", lambda parent: __import__("tkinter").Label(parent, text="Home").pack())
|
|
204
|
+
ui.page("settings", "Settings", lambda parent: __import__("tkinter").Label(parent, text="Settings").pack())
|
|
205
|
+
ui.start("home")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Design Tokens
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
design(radius=12, spacing=10, font="Segoe UI", title_size=24)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## CLI
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
easypyui new my_app
|
|
218
|
+
cd my_app
|
|
219
|
+
python app.py
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Included from 2.x
|
|
223
|
+
|
|
224
|
+
Modern widgets, animations, routing, background tasks, forms and validators,
|
|
225
|
+
state management, shortcuts, debounce/throttle, DataTable, Modal, Drawer,
|
|
226
|
+
Accordion, DatePicker and NotificationCenter remain available.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from .core import App, Value, Widget, Table, Canvas, Container, Row
|
|
2
2
|
|
|
3
|
-
__version__ = "
|
|
3
|
+
__version__ = "3.0.0"
|
|
4
4
|
|
|
5
5
|
_current = None
|
|
6
6
|
|
|
@@ -119,3 +119,9 @@ def accordion(*a, **k): return _app().accordion(*a, **k)
|
|
|
119
119
|
def datatable(*a, **k): return _app().datatable(*a, **k)
|
|
120
120
|
def datepicker(*a, **k): return _app().datepicker(*a, **k)
|
|
121
121
|
def notifications(): return _app().notifications()
|
|
122
|
+
|
|
123
|
+
from .reactive import ComputedState, computed, bind
|
|
124
|
+
from .shell import AppShell
|
|
125
|
+
from .theme3 import ThemeConfig, DEFAULT_TOKENS
|
|
126
|
+
def shell(*a, **k): return _app().shell(*a, **k)
|
|
127
|
+
def design(**tokens): return _app().design(**tokens)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
TEMPLATE = """from easypyui import *
|
|
5
|
+
|
|
6
|
+
app("{title}", 720, 480, appearance="dark")
|
|
7
|
+
title("{title}")
|
|
8
|
+
label("Created with EasyPyUI 3.0")
|
|
9
|
+
mbutton("Hello", lambda: notify("Hello EasyPyUI!"))
|
|
10
|
+
run()
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def main(argv=None):
|
|
14
|
+
parser=argparse.ArgumentParser(prog="easypyui",description="EasyPyUI project tools")
|
|
15
|
+
sub=parser.add_subparsers(dest="command")
|
|
16
|
+
new=sub.add_parser("new",help="Create a new EasyPyUI project")
|
|
17
|
+
new.add_argument("name"); new.add_argument("--title",default=None)
|
|
18
|
+
args=parser.parse_args(argv)
|
|
19
|
+
if args.command=="new":
|
|
20
|
+
root=Path(args.name); root.mkdir(parents=True,exist_ok=True)
|
|
21
|
+
(root/"app.py").write_text(TEMPLATE.format(title=args.title or args.name),encoding="utf-8")
|
|
22
|
+
(root/"README.md").write_text(f"# {args.name}\\n\\nRun with `python app.py`.\\n",encoding="utf-8")
|
|
23
|
+
print(f"Created {root.resolve()}")
|
|
24
|
+
return 0
|
|
25
|
+
parser.print_help(); return 0
|
|
26
|
+
|
|
27
|
+
if __name__=="__main__": raise SystemExit(main())
|
|
@@ -1196,3 +1196,8 @@ install_productivity_api(App, _Builder)
|
|
|
1196
1196
|
|
|
1197
1197
|
from .largeapp import install_largeapp_api
|
|
1198
1198
|
install_largeapp_api(App, _Builder)
|
|
1199
|
+
|
|
1200
|
+
from .shell import install_shell_api
|
|
1201
|
+
install_shell_api(App, _Builder)
|
|
1202
|
+
from .theme3 import install_theme3_api
|
|
1203
|
+
install_theme3_api(App, _Builder)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class ComputedState:
|
|
2
|
+
def __init__(self, func, *deps):
|
|
3
|
+
self.func=func; self._listeners=[]
|
|
4
|
+
self._value=func()
|
|
5
|
+
for dep in deps:
|
|
6
|
+
dep.watch(lambda _v, self=self: self._refresh())
|
|
7
|
+
def _refresh(self):
|
|
8
|
+
new=self.func()
|
|
9
|
+
if new != self._value:
|
|
10
|
+
self._value=new
|
|
11
|
+
for cb in list(self._listeners): cb(new)
|
|
12
|
+
def get(self): return self._value
|
|
13
|
+
def watch(self, callback, immediate=False):
|
|
14
|
+
self._listeners.append(callback)
|
|
15
|
+
if immediate: callback(self._value)
|
|
16
|
+
return lambda: self.unwatch(callback)
|
|
17
|
+
def unwatch(self, callback):
|
|
18
|
+
if callback in self._listeners: self._listeners.remove(callback)
|
|
19
|
+
def bind_text(self, widget):
|
|
20
|
+
def apply(v):
|
|
21
|
+
try: widget.set(v)
|
|
22
|
+
except Exception:
|
|
23
|
+
try: widget.config(text=str(v))
|
|
24
|
+
except Exception: pass
|
|
25
|
+
self.watch(apply, True); return self
|
|
26
|
+
|
|
27
|
+
def computed(func, *deps): return ComputedState(func, *deps)
|
|
28
|
+
|
|
29
|
+
def bind(widget, state, event="<KeyRelease>"):
|
|
30
|
+
try: widget.set(state.get())
|
|
31
|
+
except Exception: pass
|
|
32
|
+
def from_state(v):
|
|
33
|
+
try:
|
|
34
|
+
if widget.get() != v: widget.set(v)
|
|
35
|
+
except Exception: pass
|
|
36
|
+
state.watch(from_state)
|
|
37
|
+
tkobj=getattr(widget,"tk",widget)
|
|
38
|
+
def from_widget(_event=None):
|
|
39
|
+
try: state.set(widget.get())
|
|
40
|
+
except Exception: pass
|
|
41
|
+
try: tkobj.bind(event,from_widget,add="+")
|
|
42
|
+
except Exception: pass
|
|
43
|
+
return widget
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
|
|
4
|
+
class AppShell:
|
|
5
|
+
def __init__(self, app, title="EasyPyUI", sidebar_width=210):
|
|
6
|
+
self.app=app; self.pages={}; self.buttons={}; self.current=None
|
|
7
|
+
try: app.clear_body()
|
|
8
|
+
except Exception: pass
|
|
9
|
+
self.frame=ttk.Frame(app.body); self.frame.pack(fill="both",expand=True)
|
|
10
|
+
self.sidebar=tk.Frame(self.frame,width=sidebar_width)
|
|
11
|
+
self.sidebar.pack(side="left",fill="y"); self.sidebar.pack_propagate(False)
|
|
12
|
+
self.main=ttk.Frame(self.frame); self.main.pack(side="left",fill="both",expand=True)
|
|
13
|
+
tk.Label(self.sidebar,text=title,font=("",16,"bold"),anchor="w",padx=14,pady=14).pack(fill="x")
|
|
14
|
+
self.content=ttk.Frame(self.main,padding=16); self.content.pack(fill="both",expand=True)
|
|
15
|
+
self.tk=self.frame
|
|
16
|
+
def page(self,name,label=None,builder=None):
|
|
17
|
+
self.pages[name]=builder
|
|
18
|
+
b=tk.Button(self.sidebar,text=label or name,anchor="w",relief="flat",
|
|
19
|
+
command=lambda:self.go(name),padx=14,pady=8)
|
|
20
|
+
b.pack(fill="x"); self.buttons[name]=b; return self
|
|
21
|
+
def go(self,name):
|
|
22
|
+
if name not in self.pages: raise KeyError(name)
|
|
23
|
+
for child in self.content.winfo_children(): child.destroy()
|
|
24
|
+
self.current=name
|
|
25
|
+
builder=self.pages[name]
|
|
26
|
+
if builder: builder(self.content)
|
|
27
|
+
return self
|
|
28
|
+
def start(self,name=None):
|
|
29
|
+
target=name or (next(iter(self.pages)) if self.pages else None)
|
|
30
|
+
if target: self.go(target)
|
|
31
|
+
return self
|
|
32
|
+
|
|
33
|
+
def install_shell_api(App,Builder):
|
|
34
|
+
App.shell=lambda self,*a,**k: AppShell(self,*a,**k)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
DEFAULT_TOKENS = {
|
|
2
|
+
"radius": 10, "spacing": 8, "font": "Segoe UI", "font_size": 10,
|
|
3
|
+
"title_size": 22, "control_height": 36
|
|
4
|
+
}
|
|
5
|
+
class ThemeConfig:
|
|
6
|
+
def __init__(self, **tokens):
|
|
7
|
+
self.tokens=dict(DEFAULT_TOKENS); self.tokens.update(tokens)
|
|
8
|
+
def get(self,key,default=None): return self.tokens.get(key,default)
|
|
9
|
+
def update(self,**tokens): self.tokens.update(tokens); return self
|
|
10
|
+
|
|
11
|
+
def install_theme3_api(App,Builder):
|
|
12
|
+
def design(self,**tokens):
|
|
13
|
+
if not hasattr(self,"_design_tokens"): self._design_tokens=ThemeConfig()
|
|
14
|
+
self._design_tokens.update(**tokens); return self._design_tokens
|
|
15
|
+
App.design=design
|
|
@@ -1,179 +1,246 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
EasyPyUI
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
##
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
##
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easypyui
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: A simple and modern Python GUI library built on tkinter and ttk
|
|
5
|
+
Author: EasyPyUI
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/easypyui/
|
|
8
|
+
Keywords: gui,tkinter,ttk,desktop,ui,python-gui
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# EasyPyUI
|
|
22
|
+
|
|
23
|
+
**Build modern Python desktop GUIs with much less code than raw tkinter.**
|
|
24
|
+
|
|
25
|
+
EasyPyUI is a lightweight GUI library built on Python's standard `tkinter` and `ttk`.
|
|
26
|
+
It provides a short beginner-friendly API while keeping access to native Tk widgets.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install easypyui
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from easypyui import *
|
|
38
|
+
|
|
39
|
+
app("Hello EasyPyUI", 500, 400, appearance="dark")
|
|
40
|
+
title("Hello!")
|
|
41
|
+
|
|
42
|
+
name = minput("Name", placeholder="Your name")
|
|
43
|
+
mbutton("Say hello", lambda: notify("Hello " + name.get()))
|
|
44
|
+
|
|
45
|
+
fade_in(400)
|
|
46
|
+
run()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Modern UI
|
|
50
|
+
|
|
51
|
+
EasyPyUI 2.x includes `mbutton()`, `minput()`, `mswitch()`, `mcard()`,
|
|
52
|
+
Light/Dark appearance and custom accent colors.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
app("Dashboard", 700, 500, appearance="dark", accent="#8b5cf6")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Animation
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
ring = progress_ring(20, size=110)
|
|
62
|
+
mbutton("Start", lambda: ring.animate_to(100, 1000))
|
|
63
|
+
fade_in(500)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Easing modes: `linear`, `ease_in`, `ease_out`, `ease_in_out`.
|
|
67
|
+
|
|
68
|
+
## Pages / Router
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from easypyui import *
|
|
72
|
+
|
|
73
|
+
app("Pages", 500, 350)
|
|
74
|
+
|
|
75
|
+
def home(ui):
|
|
76
|
+
title("Home")
|
|
77
|
+
on("Settings", lambda: go("settings"))
|
|
78
|
+
|
|
79
|
+
def settings(ui):
|
|
80
|
+
title("Settings")
|
|
81
|
+
on("Back", lambda: go("home"))
|
|
82
|
+
|
|
83
|
+
route("home", home)
|
|
84
|
+
route("settings", settings)
|
|
85
|
+
go("home")
|
|
86
|
+
run()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Background Tasks
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
def work():
|
|
93
|
+
import time
|
|
94
|
+
time.sleep(2)
|
|
95
|
+
return "Done!"
|
|
96
|
+
|
|
97
|
+
run_task(work, done=lambda result: notify(result), loading_text="Working...")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Native tkinter access
|
|
101
|
+
|
|
102
|
+
Most EasyPyUI wrapper objects expose `.tk`:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
btn = mbutton("Test", lambda: print("clicked"))
|
|
106
|
+
btn.tk.bind("<Button-2>", lambda event: print("middle click"))
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Classic API
|
|
110
|
+
|
|
111
|
+
The older API remains available, including `window()`, `text()`, `inputbox()`,
|
|
112
|
+
`button()`, `table()`, `canvas()` and `start()`.
|
|
113
|
+
|
|
114
|
+
## Requirements
|
|
115
|
+
|
|
116
|
+
- Python 3.9+
|
|
117
|
+
- tkinter support in your Python installation
|
|
118
|
+
- No mandatory third-party runtime GUI dependency
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT License
|
|
123
|
+
|
|
124
|
+
## Version
|
|
125
|
+
|
|
126
|
+
Current release: **3.0.0**
|
|
127
|
+
|
|
128
|
+
2.2.1 is a PyPI metadata and documentation update for the 2.2 series.
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
## 2.3 Productivity APIs
|
|
132
|
+
|
|
133
|
+
### Form validation
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
f = form()
|
|
137
|
+
|
|
138
|
+
email = f.add(
|
|
139
|
+
"Email",
|
|
140
|
+
minput("Email"),
|
|
141
|
+
[Validator.required(), Validator.email()]
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
password_box = f.add(
|
|
145
|
+
"Password",
|
|
146
|
+
minput("Password", password=True),
|
|
147
|
+
[Validator.required(), Validator.min_length(8)]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def submit():
|
|
151
|
+
ok, errors = f.validate()
|
|
152
|
+
if ok:
|
|
153
|
+
notify(str(f.values()))
|
|
154
|
+
|
|
155
|
+
mbutton("Submit", submit)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Reactive State
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
count = state(0)
|
|
162
|
+
|
|
163
|
+
label_widget = text("0")
|
|
164
|
+
count.bind_text(label_widget)
|
|
165
|
+
|
|
166
|
+
mbutton("+1", lambda: count.update(lambda n: n + 1))
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Keyboard shortcuts
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
shortcut("ctrl+s", save)
|
|
173
|
+
shortcut("ctrl+q", close)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Debounce / Throttle
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
search = debounce(300, do_search)
|
|
180
|
+
resize_handler = throttle(100, update_layout)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
## 2.4 Large Application Widgets
|
|
185
|
+
|
|
186
|
+
- `datatable()` — search, sorting and pagination
|
|
187
|
+
- `modal()` — modal window
|
|
188
|
+
- `drawer()` — collapsible side drawer
|
|
189
|
+
- `accordion()` — expandable sections
|
|
190
|
+
- `datepicker()` — date value input
|
|
191
|
+
- `notifications()` — notification center
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
table = datatable(["name", "score"], ["Name", "Score"], page_size=5)
|
|
195
|
+
table.set_rows([
|
|
196
|
+
("Noah", 100),
|
|
197
|
+
("Daniel", 95),
|
|
198
|
+
])
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# EasyPyUI 3.0
|
|
203
|
+
|
|
204
|
+
EasyPyUI 3.0 moves beyond a tkinter helper toward a small application framework.
|
|
205
|
+
|
|
206
|
+
## Reactive UI
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
name = state("Noah")
|
|
210
|
+
upper = computed(lambda: name.get().upper(), name)
|
|
211
|
+
|
|
212
|
+
field = minput("Name")
|
|
213
|
+
bind(field, name)
|
|
214
|
+
text_widget = text("")
|
|
215
|
+
upper.bind_text(text_widget)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## AppShell
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
ui = shell("My App")
|
|
222
|
+
|
|
223
|
+
ui.page("home", "Home", lambda parent: __import__("tkinter").Label(parent, text="Home").pack())
|
|
224
|
+
ui.page("settings", "Settings", lambda parent: __import__("tkinter").Label(parent, text="Settings").pack())
|
|
225
|
+
ui.start("home")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Design Tokens
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
design(radius=12, spacing=10, font="Segoe UI", title_size=24)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## CLI
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
easypyui new my_app
|
|
238
|
+
cd my_app
|
|
239
|
+
python app.py
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Included from 2.x
|
|
243
|
+
|
|
244
|
+
Modern widgets, animations, routing, background tasks, forms and validators,
|
|
245
|
+
state management, shortcuts, debounce/throttle, DataTable, Modal, Drawer,
|
|
246
|
+
Accordion, DatePicker and NotificationCenter remain available.
|
|
@@ -2,12 +2,19 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
easypyui/__init__.py
|
|
5
|
+
easypyui/__main__.py
|
|
5
6
|
easypyui/animation.py
|
|
7
|
+
easypyui/cli.py
|
|
6
8
|
easypyui/core.py
|
|
7
9
|
easypyui/largeapp.py
|
|
8
10
|
easypyui/modern.py
|
|
9
11
|
easypyui/productivity.py
|
|
12
|
+
easypyui/reactive.py
|
|
13
|
+
easypyui/shell.py
|
|
14
|
+
easypyui/theme3.py
|
|
10
15
|
easypyui.egg-info/PKG-INFO
|
|
11
16
|
easypyui.egg-info/SOURCES.txt
|
|
12
17
|
easypyui.egg-info/dependency_links.txt
|
|
13
|
-
easypyui.egg-info/
|
|
18
|
+
easypyui.egg-info/entry_points.txt
|
|
19
|
+
easypyui.egg-info/top_level.txt
|
|
20
|
+
tests/test_public_api.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "easypyui"
|
|
7
|
-
version = "
|
|
7
|
+
version = "3.0.0"
|
|
8
8
|
description = "A simple and modern Python GUI library built on tkinter and ttk"
|
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -27,3 +27,6 @@ Homepage = "https://pypi.org/project/easypyui/"
|
|
|
27
27
|
[tool.setuptools.packages.find]
|
|
28
28
|
where = ["."]
|
|
29
29
|
include = ["easypyui*"]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
easypyui = "easypyui.cli:main"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|