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/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,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: easypyui
3
+ Version: 2.2.0
4
+ Summary: A concise modern GUI wrapper for tkinter and ttk
5
+ License: MIT
6
+ Keywords: gui,tkinter,ttk,easy,desktop
7
+ Requires-Python: >=3.9
@@ -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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ easypyui