easypyui 2.2.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.2.0/PKG-INFO +7 -0
- easypyui-2.2.0/README.md +49 -0
- easypyui-2.2.0/easypyui/__init__.py +105 -0
- easypyui-2.2.0/easypyui/animation.py +122 -0
- easypyui-2.2.0/easypyui/core.py +1192 -0
- easypyui-2.2.0/easypyui/modern.py +358 -0
- easypyui-2.2.0/easypyui.egg-info/PKG-INFO +7 -0
- easypyui-2.2.0/easypyui.egg-info/SOURCES.txt +10 -0
- easypyui-2.2.0/easypyui.egg-info/dependency_links.txt +1 -0
- easypyui-2.2.0/easypyui.egg-info/top_level.txt +1 -0
- easypyui-2.2.0/pyproject.toml +14 -0
- easypyui-2.2.0/setup.cfg +4 -0
easypyui-2.2.0/PKG-INFO
ADDED
easypyui-2.2.0/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# EasyPyUI 2.2 — Animation
|
|
2
|
+
|
|
3
|
+
2.2 加入輕量動畫引擎,沒有新增第三方依賴。
|
|
4
|
+
|
|
5
|
+
## 新功能
|
|
6
|
+
|
|
7
|
+
- `fade_in()` / `fade_out()`
|
|
8
|
+
- `animate(..., kind="slide")`
|
|
9
|
+
- easing:linear / ease_in / ease_out / ease_in_out
|
|
10
|
+
- `progress_ring()` 圓形進度
|
|
11
|
+
- `ring.animate_to(80)`
|
|
12
|
+
- Animation 可 `.cancel()`
|
|
13
|
+
- 2.1 現代 UI、2.0 Router、1.x API 全部保留
|
|
14
|
+
|
|
15
|
+
## Fade
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from easypyui import *
|
|
19
|
+
|
|
20
|
+
app("動畫", 500, 400, appearance="dark")
|
|
21
|
+
title("EasyPyUI 2.2")
|
|
22
|
+
mbutton("淡出", lambda: fade_out(500))
|
|
23
|
+
fade_in(500)
|
|
24
|
+
run()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Progress Ring
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
ring = progress_ring(20, size=110)
|
|
31
|
+
mbutton("到 90%", lambda: ring.animate_to(90, 700))
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Slide
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
btn = mbutton("滑入", lambda: None, layout="none")
|
|
38
|
+
|
|
39
|
+
animate(
|
|
40
|
+
btn,
|
|
41
|
+
kind="slide",
|
|
42
|
+
from_x=-160,
|
|
43
|
+
from_y=180,
|
|
44
|
+
to_x=170,
|
|
45
|
+
to_y=180,
|
|
46
|
+
duration=600,
|
|
47
|
+
easing="ease_out"
|
|
48
|
+
)
|
|
49
|
+
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from .core import App, Value, Widget, Table, Canvas, Container, Row
|
|
2
|
+
|
|
3
|
+
__version__ = "2.2.0"
|
|
4
|
+
|
|
5
|
+
_current = None
|
|
6
|
+
|
|
7
|
+
def window(title="EasyPyUI", width=500, height=400, **kwargs):
|
|
8
|
+
global _current
|
|
9
|
+
_current = App(title, width, height, **kwargs)
|
|
10
|
+
return _current
|
|
11
|
+
|
|
12
|
+
def _app():
|
|
13
|
+
global _current
|
|
14
|
+
if _current is None:
|
|
15
|
+
_current = App()
|
|
16
|
+
return _current
|
|
17
|
+
|
|
18
|
+
def text(*a, **k): return _app().text(*a, **k)
|
|
19
|
+
def button(*a, **k): return _app().button(*a, **k)
|
|
20
|
+
def inputbox(*a, **k): return _app().input(*a, **k)
|
|
21
|
+
def password(*a, **k): return _app().password(*a, **k)
|
|
22
|
+
def textbox(*a, **k): return _app().textbox(*a, **k)
|
|
23
|
+
def check(*a, **k): return _app().check(*a, **k)
|
|
24
|
+
def radio(*a, **k): return _app().radio(*a, **k)
|
|
25
|
+
def select(*a, **k): return _app().select(*a, **k)
|
|
26
|
+
def slider(*a, **k): return _app().slider(*a, **k)
|
|
27
|
+
def spin(*a, **k): return _app().spin(*a, **k)
|
|
28
|
+
def progress(*a, **k): return _app().progress(*a, **k)
|
|
29
|
+
def listbox(*a, **k): return _app().listbox(*a, **k)
|
|
30
|
+
def table(*a, **k): return _app().table(*a, **k)
|
|
31
|
+
def canvas(*a, **k): return _app().canvas(*a, **k)
|
|
32
|
+
def separator(*a, **k): return _app().separator(*a, **k)
|
|
33
|
+
def group(*a, **k): return _app().group(*a, **k)
|
|
34
|
+
def row(*a, **k): return _app().row(*a, **k)
|
|
35
|
+
def frame(*a, **k): return _app().frame(*a, **k)
|
|
36
|
+
def scroll_area(*a, **k): return _app().scroll_area(*a, **k)
|
|
37
|
+
def alert(*a, **k): return _app().alert(*a, **k)
|
|
38
|
+
def error(*a, **k): return _app().error(*a, **k)
|
|
39
|
+
def warning(*a, **k): return _app().warning(*a, **k)
|
|
40
|
+
def ask(*a, **k): return _app().ask(*a, **k)
|
|
41
|
+
def prompt(*a, **k): return _app().prompt(*a, **k)
|
|
42
|
+
def open_file(*a, **k): return _app().open_file(*a, **k)
|
|
43
|
+
def open_files(*a, **k): return _app().open_files(*a, **k)
|
|
44
|
+
def save_file(*a, **k): return _app().save_file(*a, **k)
|
|
45
|
+
def folder(*a, **k): return _app().folder(*a, **k)
|
|
46
|
+
def pick_color(*a, **k): return _app().pick_color(*a, **k)
|
|
47
|
+
def later(*a, **k): return _app().later(*a, **k)
|
|
48
|
+
def every(*a, **k): return _app().every(*a, **k)
|
|
49
|
+
def start(): return _app().run()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def switch(*a, **k): return _app().switch(*a, **k)
|
|
53
|
+
def card(*a, **k): return _app().card(*a, **k)
|
|
54
|
+
def badge(*a, **k): return _app().badge(*a, **k)
|
|
55
|
+
def navbar(*a, **k): return _app().navbar(*a, **k)
|
|
56
|
+
def sidebar(*a, **k): return _app().sidebar(*a, **k)
|
|
57
|
+
def toast(*a, **k): return _app().toast(*a, **k)
|
|
58
|
+
def loading(*a, **k): return _app().loading(*a, **k)
|
|
59
|
+
def statusbar(*a, **k): return _app().statusbar(*a, **k)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# EasyPyUI 2.0 short aliases
|
|
63
|
+
def app(title="EasyPyUI", width=500, height=400, **kwargs):
|
|
64
|
+
return window(title, width, height, **kwargs)
|
|
65
|
+
|
|
66
|
+
def title(text_value, size=24, **kwargs):
|
|
67
|
+
return _app().text(text_value, size=size, bold=True, **kwargs)
|
|
68
|
+
|
|
69
|
+
def label(text_value="", **kwargs):
|
|
70
|
+
return _app().text(text_value, **kwargs)
|
|
71
|
+
|
|
72
|
+
def input(prompt_text=None, **kwargs):
|
|
73
|
+
return _app().input(prompt_text, **kwargs)
|
|
74
|
+
|
|
75
|
+
def on(text_value, action, **kwargs):
|
|
76
|
+
return _app().button(text_value, action, **kwargs)
|
|
77
|
+
|
|
78
|
+
def run():
|
|
79
|
+
return start()
|
|
80
|
+
|
|
81
|
+
def notify(*a, **k):
|
|
82
|
+
return _app().notify(*a, **k)
|
|
83
|
+
|
|
84
|
+
def confirm(*a, **k):
|
|
85
|
+
return _app().confirm(*a, **k)
|
|
86
|
+
|
|
87
|
+
def route(name, builder=None):
|
|
88
|
+
return _app().route(name, builder)
|
|
89
|
+
|
|
90
|
+
def go(name):
|
|
91
|
+
return _app().route(name)
|
|
92
|
+
|
|
93
|
+
def run_task(*a, **k):
|
|
94
|
+
return _app().run_task(*a, **k)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def mbutton(*a, **k): return _app().modern_button(*a, **k)
|
|
98
|
+
def minput(*a, **k): return _app().modern_input(*a, **k)
|
|
99
|
+
def mswitch(*a, **k): return _app().modern_switch(*a, **k)
|
|
100
|
+
def mcard(*a, **k): return _app().modern_card(*a, **k)
|
|
101
|
+
|
|
102
|
+
def progress_ring(*a, **k): return _app().progress_ring(*a, **k)
|
|
103
|
+
def fade_in(*a, **k): return _app().fade_in(*a, **k)
|
|
104
|
+
def fade_out(*a, **k): return _app().fade_out(*a, **k)
|
|
105
|
+
def animate(*a, **k): return _app().animate(*a, **k)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import math
|
|
3
|
+
import tkinter as tk
|
|
4
|
+
|
|
5
|
+
EASINGS = {
|
|
6
|
+
"linear": lambda t: t,
|
|
7
|
+
"ease_in": lambda t: t*t,
|
|
8
|
+
"ease_out": lambda t: 1-(1-t)*(1-t),
|
|
9
|
+
"ease_in_out": lambda t: 2*t*t if t < .5 else 1-((-2*t+2)**2)/2,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def _widget(obj):
|
|
13
|
+
return getattr(obj, "tk", getattr(obj, "widget", obj))
|
|
14
|
+
|
|
15
|
+
class Animation:
|
|
16
|
+
def __init__(self, root, duration=300, fps=60, easing="ease_out"):
|
|
17
|
+
self.root = root
|
|
18
|
+
self.duration = max(1, duration)
|
|
19
|
+
self.interval = max(1, int(1000/fps))
|
|
20
|
+
self.ease = EASINGS.get(easing, EASINGS["ease_out"])
|
|
21
|
+
self.cancelled = False
|
|
22
|
+
self.after_id = None
|
|
23
|
+
|
|
24
|
+
def start(self, update, done=None):
|
|
25
|
+
started = time.perf_counter()
|
|
26
|
+
def tick():
|
|
27
|
+
if self.cancelled:
|
|
28
|
+
return
|
|
29
|
+
elapsed = (time.perf_counter()-started)*1000
|
|
30
|
+
raw = min(1.0, elapsed/self.duration)
|
|
31
|
+
update(self.ease(raw))
|
|
32
|
+
if raw >= 1:
|
|
33
|
+
if done: done()
|
|
34
|
+
else:
|
|
35
|
+
self.after_id = self.root.after(self.interval, tick)
|
|
36
|
+
tick()
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def cancel(self):
|
|
40
|
+
self.cancelled = True
|
|
41
|
+
if self.after_id:
|
|
42
|
+
try: self.root.after_cancel(self.after_id)
|
|
43
|
+
except Exception: pass
|
|
44
|
+
|
|
45
|
+
def fade(window, start=0.0, end=1.0, duration=300, easing="ease_out", done=None):
|
|
46
|
+
root = window.winfo_toplevel()
|
|
47
|
+
try: root.attributes("-alpha", start)
|
|
48
|
+
except tk.TclError: return None
|
|
49
|
+
return Animation(root, duration, easing=easing).start(
|
|
50
|
+
lambda t: root.attributes("-alpha", start+(end-start)*t), done)
|
|
51
|
+
|
|
52
|
+
def slide(widget, from_x, from_y, to_x, to_y, duration=350, easing="ease_out", done=None):
|
|
53
|
+
w = _widget(widget); root = w.winfo_toplevel()
|
|
54
|
+
w.place(x=from_x, y=from_y)
|
|
55
|
+
return Animation(root, duration, easing=easing).start(
|
|
56
|
+
lambda t: w.place(x=from_x+(to_x-from_x)*t, y=from_y+(to_y-from_y)*t), done)
|
|
57
|
+
|
|
58
|
+
def animate_value(root, start, end, setter, duration=300, easing="ease_out", done=None):
|
|
59
|
+
return Animation(root, duration, easing=easing).start(
|
|
60
|
+
lambda t: setter(start+(end-start)*t), done)
|
|
61
|
+
|
|
62
|
+
class ProgressRing:
|
|
63
|
+
def __init__(self, parent, value=0, maximum=100, size=90, width=8,
|
|
64
|
+
accent="#2563eb", track="#d1d5db", text=True):
|
|
65
|
+
self.value=float(value); self.maximum=float(maximum); self.size=size
|
|
66
|
+
self.width=width; self.accent=accent; self.track=track; self.show_text=text
|
|
67
|
+
bg = parent.cget("bg") if "bg" in parent.keys() else "#f5f5f5"
|
|
68
|
+
self.canvas=tk.Canvas(parent,width=size,height=size,highlightthickness=0,bd=0,bg=bg)
|
|
69
|
+
self.tk=self.widget=self.canvas
|
|
70
|
+
self.draw()
|
|
71
|
+
|
|
72
|
+
def draw(self):
|
|
73
|
+
c=self.canvas; c.delete("all"); pad=self.width/2+3
|
|
74
|
+
c.create_oval(pad,pad,self.size-pad,self.size-pad,outline=self.track,width=self.width)
|
|
75
|
+
pct=max(0,min(1,self.value/self.maximum if self.maximum else 0))
|
|
76
|
+
c.create_arc(pad,pad,self.size-pad,self.size-pad,start=90,extent=-360*pct,
|
|
77
|
+
style="arc",outline=self.accent,width=self.width)
|
|
78
|
+
if self.show_text:
|
|
79
|
+
c.create_text(self.size/2,self.size/2,text=f"{pct*100:.0f}%",font=("Arial",11,"bold"))
|
|
80
|
+
|
|
81
|
+
def get(self): return self.value
|
|
82
|
+
def set(self,value): self.value=float(value); self.draw(); return self
|
|
83
|
+
def pack(self,**kw): self.canvas.pack(**kw); return self
|
|
84
|
+
def grid(self,**kw): self.canvas.grid(**kw); return self
|
|
85
|
+
def place(self,**kw): self.canvas.place(**kw); return self
|
|
86
|
+
|
|
87
|
+
def animate_to(self,value,duration=500,easing="ease_out"):
|
|
88
|
+
start=self.value
|
|
89
|
+
return animate_value(self.canvas.winfo_toplevel(),start,float(value),self.set,duration,easing)
|
|
90
|
+
|
|
91
|
+
def install_animation_api(App, Builder):
|
|
92
|
+
def progress_ring(self,value=0,maximum=100,size=90,width=8,layout="pack",**kwargs):
|
|
93
|
+
app=self.app if hasattr(self,"app") else self
|
|
94
|
+
p=app.palette()
|
|
95
|
+
ring=ProgressRing(self._parent,value,maximum,size,width,
|
|
96
|
+
kwargs.pop("accent",p["accent"]),
|
|
97
|
+
kwargs.pop("track","#555555" if getattr(app,"_theme_name","light")=="dark" else "#d1d5db"),
|
|
98
|
+
kwargs.pop("text",True))
|
|
99
|
+
opts=kwargs.pop("layout_opts",{})
|
|
100
|
+
if layout=="grid": ring.grid(**opts)
|
|
101
|
+
elif layout=="place": ring.place(**opts)
|
|
102
|
+
elif layout!="none":
|
|
103
|
+
defaults={"anchor":"w","pady":5}; defaults.update(opts); ring.pack(**defaults)
|
|
104
|
+
return ring
|
|
105
|
+
|
|
106
|
+
Builder.progress_ring=progress_ring
|
|
107
|
+
|
|
108
|
+
def fade_in(self,duration=300,easing="ease_out"):
|
|
109
|
+
return fade(self.root,0,1,duration,easing)
|
|
110
|
+
def fade_out(self,duration=300,easing="ease_in",done=None):
|
|
111
|
+
return fade(self.root,1,0,duration,easing,done)
|
|
112
|
+
def animate(self,widget,kind="slide",duration=350,easing="ease_out",**kwargs):
|
|
113
|
+
if kind=="slide":
|
|
114
|
+
return slide(widget,kwargs.get("from_x",-100),kwargs.get("from_y",0),
|
|
115
|
+
kwargs.get("to_x",0),kwargs.get("to_y",0),duration,easing,kwargs.get("done"))
|
|
116
|
+
if kind=="fade":
|
|
117
|
+
return fade(_widget(widget),kwargs.get("start",0),kwargs.get("end",1),
|
|
118
|
+
duration,easing,kwargs.get("done"))
|
|
119
|
+
raise ValueError("kind 必須是 slide 或 fade")
|
|
120
|
+
App.fade_in=fade_in
|
|
121
|
+
App.fade_out=fade_out
|
|
122
|
+
App.animate=animate
|