easypyui 2.2.0__py3-none-any.whl
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/__init__.py +105 -0
- easypyui/animation.py +122 -0
- easypyui/core.py +1192 -0
- easypyui/modern.py +358 -0
- easypyui-2.2.0.dist-info/METADATA +7 -0
- easypyui-2.2.0.dist-info/RECORD +8 -0
- easypyui-2.2.0.dist-info/WHEEL +5 -0
- easypyui-2.2.0.dist-info/top_level.txt +1 -0
easypyui/modern.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import font as tkfont
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _hex_to_rgb(value):
|
|
6
|
+
value = value.lstrip("#")
|
|
7
|
+
if len(value) != 6:
|
|
8
|
+
raise ValueError("顏色必須是 #RRGGBB")
|
|
9
|
+
return tuple(int(value[i:i+2], 16) for i in (0, 2, 4))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _rgb_to_hex(rgb):
|
|
13
|
+
return "#%02x%02x%02x" % tuple(max(0, min(255, int(x))) for x in rgb)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def mix(color1, color2, amount=0.5):
|
|
17
|
+
a = _hex_to_rgb(color1)
|
|
18
|
+
b = _hex_to_rgb(color2)
|
|
19
|
+
return _rgb_to_hex(tuple(a[i] * (1-amount) + b[i] * amount for i in range(3)))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def rounded_rect(canvas, x1, y1, x2, y2, radius=12, **kwargs):
|
|
23
|
+
radius = max(0, min(radius, (x2-x1)//2, (y2-y1)//2))
|
|
24
|
+
points = [
|
|
25
|
+
x1+radius, y1, x2-radius, y1,
|
|
26
|
+
x2, y1, x2, y1+radius,
|
|
27
|
+
x2, y2-radius, x2, y2,
|
|
28
|
+
x2-radius, y2, x1+radius, y2,
|
|
29
|
+
x1, y2, x1, y2-radius,
|
|
30
|
+
x1, y1+radius, x1, y1,
|
|
31
|
+
]
|
|
32
|
+
return canvas.create_polygon(points, smooth=True, **kwargs)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ModernButton:
|
|
36
|
+
def __init__(self, parent, text, command=None, bg="#2563eb", fg="#ffffff",
|
|
37
|
+
hover=None, radius=10, height=38, width=150, font=("Arial", 10, "bold")):
|
|
38
|
+
self.parent = parent
|
|
39
|
+
self.command = command
|
|
40
|
+
self.bg = bg
|
|
41
|
+
self.fg = fg
|
|
42
|
+
self.hover = hover or mix(bg, "#ffffff", 0.12)
|
|
43
|
+
self.radius = radius
|
|
44
|
+
self.height = height
|
|
45
|
+
self.width = width
|
|
46
|
+
self.font = font
|
|
47
|
+
self.enabled = True
|
|
48
|
+
|
|
49
|
+
self.canvas = tk.Canvas(parent, width=width, height=height,
|
|
50
|
+
highlightthickness=0, bd=0, bg=parent.cget("bg") if "bg" in parent.keys() else "#f5f5f5")
|
|
51
|
+
self.tk = self.canvas
|
|
52
|
+
self.widget = self.canvas
|
|
53
|
+
self._draw(self.bg)
|
|
54
|
+
self.canvas.bind("<Enter>", lambda e: self._draw(self.hover) if self.enabled else None)
|
|
55
|
+
self.canvas.bind("<Leave>", lambda e: self._draw(self.bg) if self.enabled else None)
|
|
56
|
+
self.canvas.bind("<Button-1>", self._click)
|
|
57
|
+
|
|
58
|
+
def _draw(self, color):
|
|
59
|
+
self.canvas.delete("all")
|
|
60
|
+
rounded_rect(self.canvas, 1, 1, self.width-1, self.height-1, self.radius, fill=color, outline=color)
|
|
61
|
+
self.canvas.create_text(self.width/2, self.height/2, text=self.text_value if hasattr(self, "text_value") else "",
|
|
62
|
+
fill=self.fg, font=self.font)
|
|
63
|
+
|
|
64
|
+
def set_text(self, text):
|
|
65
|
+
self.text_value = str(text)
|
|
66
|
+
self._draw(self.bg)
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def _click(self, _event=None):
|
|
70
|
+
if self.enabled and self.command:
|
|
71
|
+
self.command()
|
|
72
|
+
|
|
73
|
+
def pack(self, **kwargs):
|
|
74
|
+
self.canvas.pack(**kwargs)
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def grid(self, **kwargs):
|
|
78
|
+
self.canvas.grid(**kwargs)
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def place(self, **kwargs):
|
|
82
|
+
self.canvas.place(**kwargs)
|
|
83
|
+
return self
|
|
84
|
+
|
|
85
|
+
def tooltip(self, text, delay=400):
|
|
86
|
+
from .core import Tooltip
|
|
87
|
+
Tooltip(self.canvas, text, delay)
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
def on(self, event, func, add=None):
|
|
91
|
+
self.canvas.bind(event, func, add)
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def enable(self):
|
|
95
|
+
self.enabled = True
|
|
96
|
+
self._draw(self.bg)
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
def disable(self):
|
|
100
|
+
self.enabled = False
|
|
101
|
+
self._draw(mix(self.bg, "#888888", 0.55))
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
def destroy(self):
|
|
105
|
+
self.canvas.destroy()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ModernEntry:
|
|
109
|
+
def __init__(self, parent, placeholder="", password=False, bg="#ffffff", fg="#111111",
|
|
110
|
+
border="#d1d5db", focus="#2563eb", radius=10, height=40):
|
|
111
|
+
self.frame = tk.Frame(parent, bg=border, padx=1, pady=1)
|
|
112
|
+
self.inner = tk.Frame(self.frame, bg=bg)
|
|
113
|
+
self.inner.pack(fill="both", expand=True)
|
|
114
|
+
self.var = tk.StringVar()
|
|
115
|
+
self.placeholder = placeholder
|
|
116
|
+
self.password = password
|
|
117
|
+
self.normal_fg = fg
|
|
118
|
+
self.placeholder_fg = "#888888"
|
|
119
|
+
self.bg = bg
|
|
120
|
+
self.border = border
|
|
121
|
+
self.focus_color = focus
|
|
122
|
+
self.entry = tk.Entry(
|
|
123
|
+
self.inner, textvariable=self.var, relief="flat", bd=0,
|
|
124
|
+
bg=bg, fg=fg, insertbackground=fg,
|
|
125
|
+
highlightthickness=0, font=("Arial", 11)
|
|
126
|
+
)
|
|
127
|
+
self.entry.pack(fill="both", expand=True, padx=10, pady=8)
|
|
128
|
+
self.tk = self.entry
|
|
129
|
+
self.widget = self.entry
|
|
130
|
+
self._placeholder_active = False
|
|
131
|
+
self._show_placeholder()
|
|
132
|
+
self.entry.bind("<FocusIn>", self._focus_in, add="+")
|
|
133
|
+
self.entry.bind("<FocusOut>", self._focus_out, add="+")
|
|
134
|
+
self.frame.configure(height=height)
|
|
135
|
+
|
|
136
|
+
def _show_placeholder(self):
|
|
137
|
+
if self.placeholder and not self.var.get():
|
|
138
|
+
self._placeholder_active = True
|
|
139
|
+
self.entry.configure(show="", fg=self.placeholder_fg)
|
|
140
|
+
self.var.set(self.placeholder)
|
|
141
|
+
|
|
142
|
+
def _focus_in(self, _event=None):
|
|
143
|
+
self.frame.configure(bg=self.focus_color)
|
|
144
|
+
if self._placeholder_active:
|
|
145
|
+
self.var.set("")
|
|
146
|
+
self._placeholder_active = False
|
|
147
|
+
self.entry.configure(fg=self.normal_fg, show="*" if self.password else "")
|
|
148
|
+
|
|
149
|
+
def _focus_out(self, _event=None):
|
|
150
|
+
self.frame.configure(bg=self.border)
|
|
151
|
+
if not self.var.get():
|
|
152
|
+
self._show_placeholder()
|
|
153
|
+
|
|
154
|
+
def get(self):
|
|
155
|
+
return "" if self._placeholder_active else self.var.get()
|
|
156
|
+
|
|
157
|
+
def set(self, value):
|
|
158
|
+
self._placeholder_active = False
|
|
159
|
+
self.entry.configure(fg=self.normal_fg, show="*" if self.password else "")
|
|
160
|
+
self.var.set(str(value))
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
def pack(self, **kwargs):
|
|
164
|
+
self.frame.pack(**kwargs)
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
def grid(self, **kwargs):
|
|
168
|
+
self.frame.grid(**kwargs)
|
|
169
|
+
return self
|
|
170
|
+
|
|
171
|
+
def place(self, **kwargs):
|
|
172
|
+
self.frame.place(**kwargs)
|
|
173
|
+
return self
|
|
174
|
+
|
|
175
|
+
def focus(self):
|
|
176
|
+
self.entry.focus_set()
|
|
177
|
+
return self
|
|
178
|
+
|
|
179
|
+
def enable(self):
|
|
180
|
+
self.entry.configure(state="normal")
|
|
181
|
+
return self
|
|
182
|
+
|
|
183
|
+
def disable(self):
|
|
184
|
+
self.entry.configure(state="disabled")
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
def tooltip(self, text, delay=400):
|
|
188
|
+
from .core import Tooltip
|
|
189
|
+
Tooltip(self.entry, text, delay)
|
|
190
|
+
return self
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class ModernSwitch:
|
|
194
|
+
def __init__(self, parent, text="", checked=False, command=None,
|
|
195
|
+
accent="#2563eb", bg_off="#9ca3af", text_color="#111111"):
|
|
196
|
+
self.var = tk.BooleanVar(value=checked)
|
|
197
|
+
self.command = command
|
|
198
|
+
self.accent = accent
|
|
199
|
+
self.bg_off = bg_off
|
|
200
|
+
|
|
201
|
+
self.frame = tk.Frame(parent, bg=parent.cget("bg") if "bg" in parent.keys() else "#f5f5f5")
|
|
202
|
+
self.label = tk.Label(self.frame, text=text, bg=self.frame.cget("bg"),
|
|
203
|
+
fg=text_color, font=("Arial", 10))
|
|
204
|
+
self.label.pack(side="left", padx=(0, 8))
|
|
205
|
+
|
|
206
|
+
self.canvas = tk.Canvas(self.frame, width=46, height=24, highlightthickness=0,
|
|
207
|
+
bd=0, bg=self.frame.cget("bg"))
|
|
208
|
+
self.canvas.pack(side="left")
|
|
209
|
+
self.canvas.bind("<Button-1>", self.toggle)
|
|
210
|
+
self.label.bind("<Button-1>", self.toggle)
|
|
211
|
+
self.tk = self.canvas
|
|
212
|
+
self.widget = self.canvas
|
|
213
|
+
self._draw()
|
|
214
|
+
|
|
215
|
+
def _draw(self):
|
|
216
|
+
self.canvas.delete("all")
|
|
217
|
+
on = self.var.get()
|
|
218
|
+
color = self.accent if on else self.bg_off
|
|
219
|
+
rounded_rect(self.canvas, 1, 2, 45, 22, 10, fill=color, outline=color)
|
|
220
|
+
x = 34 if on else 12
|
|
221
|
+
self.canvas.create_oval(x-8, 4, x+8, 20, fill="white", outline="white")
|
|
222
|
+
|
|
223
|
+
def toggle(self, _event=None):
|
|
224
|
+
self.var.set(not self.var.get())
|
|
225
|
+
self._draw()
|
|
226
|
+
if self.command:
|
|
227
|
+
self.command()
|
|
228
|
+
return self
|
|
229
|
+
|
|
230
|
+
def get(self):
|
|
231
|
+
return self.var.get()
|
|
232
|
+
|
|
233
|
+
def set(self, value):
|
|
234
|
+
self.var.set(bool(value))
|
|
235
|
+
self._draw()
|
|
236
|
+
return self
|
|
237
|
+
|
|
238
|
+
def pack(self, **kwargs):
|
|
239
|
+
self.frame.pack(**kwargs)
|
|
240
|
+
return self
|
|
241
|
+
|
|
242
|
+
def grid(self, **kwargs):
|
|
243
|
+
self.frame.grid(**kwargs)
|
|
244
|
+
return self
|
|
245
|
+
|
|
246
|
+
def place(self, **kwargs):
|
|
247
|
+
self.frame.place(**kwargs)
|
|
248
|
+
return self
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class ModernCard:
|
|
252
|
+
def __init__(self, app, parent, title=None, padding=14):
|
|
253
|
+
palette = app.palette()
|
|
254
|
+
self.outer = tk.Frame(parent, bg=palette["surface"], bd=0,
|
|
255
|
+
highlightthickness=1,
|
|
256
|
+
highlightbackground=mix(palette["surface"], palette["text"], 0.12))
|
|
257
|
+
self.outer.pack(fill="x", pady=7)
|
|
258
|
+
if title:
|
|
259
|
+
tk.Label(self.outer, text=title, bg=palette["surface"], fg=palette["text"],
|
|
260
|
+
font=("Arial", 12, "bold")).pack(anchor="w", padx=padding, pady=(padding, 4))
|
|
261
|
+
self.body = tk.Frame(self.outer, bg=palette["surface"])
|
|
262
|
+
self.body.pack(fill="both", expand=True, padx=padding, pady=(4, padding))
|
|
263
|
+
self.app = app
|
|
264
|
+
self.tk = self.outer
|
|
265
|
+
|
|
266
|
+
def text(self, text="", size=11, bold=False):
|
|
267
|
+
p = self.app.palette()
|
|
268
|
+
w = tk.Label(self.body, text=text, bg=p["surface"], fg=p["text"],
|
|
269
|
+
font=("Arial", size, "bold" if bold else "normal"))
|
|
270
|
+
w.pack(anchor="w", pady=3)
|
|
271
|
+
return w
|
|
272
|
+
|
|
273
|
+
def input(self, label=None, password=False, default=""):
|
|
274
|
+
if label:
|
|
275
|
+
self.text(label, size=10)
|
|
276
|
+
p = self.app.palette()
|
|
277
|
+
w = ModernEntry(self.body, password=password, bg=p["surface"], fg=p["text"],
|
|
278
|
+
border=mix(p["surface"], p["text"], 0.18), focus=p["accent"])
|
|
279
|
+
w.pack(fill="x", pady=4)
|
|
280
|
+
if default:
|
|
281
|
+
w.set(default)
|
|
282
|
+
return w
|
|
283
|
+
|
|
284
|
+
def switch(self, text="", checked=False, action=None):
|
|
285
|
+
p = self.app.palette()
|
|
286
|
+
w = ModernSwitch(self.body, text, checked, action, accent=p["accent"],
|
|
287
|
+
text_color=p["text"])
|
|
288
|
+
w.pack(fill="x", pady=4)
|
|
289
|
+
return w
|
|
290
|
+
|
|
291
|
+
def button(self, text, action=None):
|
|
292
|
+
p = self.app.palette()
|
|
293
|
+
w = ModernButton(self.body, text, action, bg=p["accent"])
|
|
294
|
+
w.set_text(text).pack(anchor="w", pady=4)
|
|
295
|
+
return w
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def install_modern_api(App, Builder):
|
|
299
|
+
def modern_button(self, text, action=None, width=150, height=38, radius=10, layout="pack", **kwargs):
|
|
300
|
+
app = self.app if hasattr(self, "app") else self
|
|
301
|
+
p = app.palette()
|
|
302
|
+
w = ModernButton(self._parent, text, action, bg=kwargs.pop("bg", p["accent"]),
|
|
303
|
+
fg=kwargs.pop("fg", "#ffffff"), hover=kwargs.pop("hover", None),
|
|
304
|
+
radius=radius, height=height, width=width)
|
|
305
|
+
w.set_text(text)
|
|
306
|
+
opts = kwargs.pop("layout_opts", {})
|
|
307
|
+
if layout == "grid": w.grid(**opts)
|
|
308
|
+
elif layout == "place": w.place(**opts)
|
|
309
|
+
elif layout != "none":
|
|
310
|
+
defaults = {"anchor": "w", "pady": 4}
|
|
311
|
+
defaults.update(opts)
|
|
312
|
+
w.pack(**defaults)
|
|
313
|
+
return w
|
|
314
|
+
|
|
315
|
+
def modern_input(self, label=None, placeholder="", password=False, default="", layout="pack", **kwargs):
|
|
316
|
+
app = self.app if hasattr(self, "app") else self
|
|
317
|
+
p = app.palette()
|
|
318
|
+
if label:
|
|
319
|
+
try:
|
|
320
|
+
self.text(label, size=10)
|
|
321
|
+
except Exception:
|
|
322
|
+
pass
|
|
323
|
+
w = ModernEntry(self._parent, placeholder=placeholder, password=password,
|
|
324
|
+
bg=p["surface"], fg=p["text"],
|
|
325
|
+
border=mix(p["surface"], p["text"], 0.18), focus=p["accent"])
|
|
326
|
+
if default:
|
|
327
|
+
w.set(default)
|
|
328
|
+
opts = kwargs.pop("layout_opts", {})
|
|
329
|
+
if layout == "grid": w.grid(**opts)
|
|
330
|
+
elif layout == "place": w.place(**opts)
|
|
331
|
+
elif layout != "none":
|
|
332
|
+
defaults = {"fill": "x", "pady": 4}
|
|
333
|
+
defaults.update(opts)
|
|
334
|
+
w.pack(**defaults)
|
|
335
|
+
return w
|
|
336
|
+
|
|
337
|
+
def modern_switch(self, text="", checked=False, action=None, layout="pack", **kwargs):
|
|
338
|
+
app = self.app if hasattr(self, "app") else self
|
|
339
|
+
p = app.palette()
|
|
340
|
+
w = ModernSwitch(self._parent, text, checked, action,
|
|
341
|
+
accent=p["accent"], text_color=p["text"])
|
|
342
|
+
opts = kwargs.pop("layout_opts", {})
|
|
343
|
+
if layout == "grid": w.grid(**opts)
|
|
344
|
+
elif layout == "place": w.place(**opts)
|
|
345
|
+
elif layout != "none":
|
|
346
|
+
defaults = {"fill": "x", "pady": 4}
|
|
347
|
+
defaults.update(opts)
|
|
348
|
+
w.pack(**defaults)
|
|
349
|
+
return w
|
|
350
|
+
|
|
351
|
+
def modern_card(self, title=None, padding=14):
|
|
352
|
+
app = self.app if hasattr(self, "app") else self
|
|
353
|
+
return ModernCard(app, self._parent, title, padding)
|
|
354
|
+
|
|
355
|
+
Builder.modern_button = modern_button
|
|
356
|
+
Builder.modern_input = modern_input
|
|
357
|
+
Builder.modern_switch = modern_switch
|
|
358
|
+
Builder.modern_card = modern_card
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
easypyui/__init__.py,sha256=zazl0yT6uhLFVKr7V4DDkmnvwypWlFMB826kfCtznz4,3708
|
|
2
|
+
easypyui/animation.py,sha256=gjz-YmTeorWpOEWmdNXBEzPYuWYaZgdo6chP-h6hjH0,5316
|
|
3
|
+
easypyui/core.py,sha256=lzWP0BmkAvUT-JQFgRLKUcj5mBuSqMeossU5pktH7YU,37722
|
|
4
|
+
easypyui/modern.py,sha256=-Rn8ltzraslWSxc6W43raYob9wTYTGMQWSmj9EGvCX8,12547
|
|
5
|
+
easypyui-2.2.0.dist-info/METADATA,sha256=Re3peECdSliJcuF7DWNP3zq2q3quQ49H38qN3HFH_yU,192
|
|
6
|
+
easypyui-2.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
7
|
+
easypyui-2.2.0.dist-info/top_level.txt,sha256=-_EeEzk3PwRZX-rNHlYPQGniOoFUIrRQ-VOhfVVV-iU,9
|
|
8
|
+
easypyui-2.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
easypyui
|