tkfluent 0.0.2__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.
tkflu/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ """、
2
+
3
+ Fluent设计的tkinter组件库(模板)
4
+
5
+ -------------
6
+ 作者:XiangQinxi
7
+ -------------
8
+ """
9
+
10
+ from .badge import FluBadge
11
+ from .button import FluButton
12
+ from .entry import FluEntry
13
+ from .frame import FluFrame
14
+ from .label import FluLabel
15
+ from .text import FluText
16
+ from .thememanager import FluThemeManager
17
+ from .togglebutton import FluToggleButton
18
+ from .window import FluWindow
19
+
20
+ FluChip = FluBadge
21
+ FluPushButton = FluButton
22
+ FluTextInput = FluEntry
23
+ FluTextBox = FluText
24
+ FluPanel = FluFrame
25
+
26
+ # 
tkflu/__main__.py ADDED
@@ -0,0 +1,50 @@
1
+ from tkflu import *
2
+ from tkinter import *
3
+ from tkinter.font import *
4
+
5
+ root = FluWindow()
6
+ root.iconify
7
+ root.wincustom(way=0)
8
+ root.wm_geometry("180x360")
9
+
10
+ thememanager = FluThemeManager()
11
+
12
+ frame = FluFrame()
13
+
14
+ badge1 = FluBadge(frame, text="FluBadge", width=60)
15
+ badge1.pack(padx=5, pady=5)
16
+
17
+ badge2 = FluBadge(frame, text="FluBadge (Accent)", width=120, style="accent")
18
+ badge2.pack(padx=5, pady=5)
19
+
20
+ button1 = FluButton(
21
+ frame, text="FluButton", command=lambda: print("FluButton -> Clicked")
22
+ )
23
+ button1.pack(fill="x", padx=5, pady=5)
24
+
25
+ button2 = FluButton(
26
+ frame, text="FluButton (Accent)", command=lambda: print("FluButton (Accent) -> Clicked"), style="accent"
27
+ )
28
+ button2.pack(fill="x", padx=5, pady=5)
29
+
30
+ def toggle1():
31
+ print(f"FluToggleButton -> Toggled -> Checked: {togglebutton1.dcget('checked')}")
32
+ if togglebutton1.dcget('checked'):
33
+ thememanager.mode("dark")
34
+ else:
35
+ thememanager.mode("light")
36
+
37
+ togglebutton1 = FluToggleButton(
38
+ frame, text="FluToggleButton", command=toggle1
39
+ )
40
+ togglebutton1.pack(fill="x", padx=5, pady=5)
41
+
42
+ entry1 = FluEntry(frame)
43
+ entry1.pack(fill="x", padx=5, pady=5)
44
+
45
+ text1 = FluText(frame)
46
+ text1.pack(fill="x", padx=5, pady=5)
47
+
48
+ frame.pack(fill="both", expand="yes", side="right", padx=5, pady=5)
49
+
50
+ root.mainloop()
tkflu/badge.py ADDED
@@ -0,0 +1,169 @@
1
+ from tkdeft.windows.draw import DSvgDraw
2
+ from tkdeft.windows.canvas import DCanvas
3
+ from tkdeft.windows.drawwidget import DDrawWidget
4
+
5
+
6
+ class FluBadgeDraw(DSvgDraw):
7
+ def create_roundrect(self,
8
+ x1, y1, x2, y2, temppath=None,
9
+ fill="transparent", outline="black", width=1
10
+ ):
11
+ drawing = self.create_drawing(x2 - x1, y2 - y1, temppath=temppath)
12
+ drawing[1].add(
13
+ drawing[1].rect(
14
+ (x1, y1), (x2 - x1, y2 - y1), 20, 25,
15
+ fill=fill, stroke_width=width,
16
+ stroke=outline,
17
+ )
18
+ )
19
+ drawing[1].save()
20
+ return drawing[0]
21
+
22
+
23
+ class FluBadgeCanvas(DCanvas):
24
+ draw = FluBadgeDraw
25
+
26
+ def create_round_rectangle(self,
27
+ x1, y1, x2, y2, temppath=None,
28
+ fill="transparent", outline="black", width=1
29
+ ):
30
+ self._img = self.svgdraw.create_roundrect(
31
+ x1, y1, x2, y2, temppath=temppath,
32
+ fill=fill, outline=outline, width=width
33
+ )
34
+ self._tkimg = self.svgdraw.create_tksvg_image(self._img)
35
+ return self.create_image(x1, y1, anchor="nw", image=self._tkimg)
36
+
37
+ create_roundrect = create_round_rectangle
38
+
39
+
40
+ class FluBadge(FluBadgeCanvas, DDrawWidget):
41
+
42
+ def __init__(self, *args,
43
+ text="",
44
+ width=70,
45
+ height=30,
46
+ font=None,
47
+ mode="light",
48
+ style="standard",
49
+ **kwargs):
50
+
51
+ """
52
+
53
+ 初始化类
54
+
55
+ :param args: 参照tkinter.Canvas.__init__
56
+ :param text:
57
+ :param width:
58
+ :param height:
59
+ :param font:
60
+ :param mode: Fluent主题模式 分为 “light” “dark”
61
+ :param style:
62
+ :param kwargs: 参照tkinter.Canvas.__init__
63
+ """
64
+
65
+ self._init(mode, style)
66
+
67
+ super().__init__(*args, width=width, height=height, **kwargs)
68
+
69
+ self.dconfigure(
70
+ text=text,
71
+ )
72
+
73
+ self.bind("<<Clicked>>", lambda event=None: self.focus_set(), add="+")
74
+
75
+ if font is None:
76
+ from tkdeft.utility.fonts import SegoeFont
77
+ self.attributes.font = SegoeFont()
78
+
79
+ def _init(self, mode, style):
80
+ from easydict import EasyDict
81
+
82
+ self.attributes = EasyDict(
83
+ {
84
+ "text": "",
85
+ "command": None,
86
+ "font": None,
87
+
88
+ "back_color": None,
89
+ "border_color": None,
90
+ "border_width": None,
91
+ "text_color": None
92
+ }
93
+ )
94
+
95
+ self.theme(mode, style)
96
+
97
+ def _draw(self, event=None):
98
+
99
+ """
100
+ 重新绘制组件
101
+
102
+ :param event:
103
+ """
104
+
105
+ super()._draw(event)
106
+
107
+ self.delete("all")
108
+
109
+ _back_color = self.attributes.back_color
110
+ _border_color = self.attributes.border_color
111
+ _border_width = self.attributes.border_width
112
+ _text_color = self.attributes.text_color
113
+
114
+ self.element_border = self.create_round_rectangle(
115
+ 0, 0, self.winfo_width(), self.winfo_height(), temppath=self.temppath,
116
+ fill=_back_color, outline=_border_color, width=_border_width
117
+ )
118
+
119
+ self.element_text = self.create_text(
120
+ self.winfo_width() / 2, self.winfo_height() / 2, anchor="center",
121
+ fill=_text_color, text=self.attributes.text, font=self.attributes.font
122
+ )
123
+
124
+ def theme(self, mode, style=None):
125
+ self.mode = mode
126
+ if style:
127
+ self.style = style
128
+ if mode.lower() == "dark":
129
+ if self.style.lower() == "accent":
130
+ self._dark_accent()
131
+ else:
132
+ self._dark()
133
+ else:
134
+ if self.style.lower() == "accent":
135
+ self._light_accent()
136
+ else:
137
+ self._light()
138
+
139
+ def _light(self):
140
+ self.dconfigure(
141
+ back_color="#f0f0f0",
142
+ border_color="#f0f0f0",
143
+ border_width=1,
144
+ text_color="#191919",
145
+ )
146
+
147
+ def _light_accent(self):
148
+ self.dconfigure(
149
+ back_color="#005fb8",
150
+ border_color="#005fb8",
151
+ border_width=1,
152
+ text_color="#ffffff",
153
+ )
154
+
155
+ def _dark(self):
156
+ self.dconfigure(
157
+ back_color="#242424",
158
+ border_color="#242424",
159
+ border_width=1,
160
+ text_color="#ffffff",
161
+ )
162
+
163
+ def _dark_accent(self):
164
+ self.dconfigure(
165
+ back_color="#60cdff",
166
+ border_color="#60cdff",
167
+ border_width=1,
168
+ text_color="#000000",
169
+ )
tkflu/button.py ADDED
@@ -0,0 +1,286 @@
1
+ from tkdeft.windows.draw import DSvgDraw
2
+ from tkdeft.windows.canvas import DCanvas
3
+ from tkdeft.windows.drawwidget import DDrawWidget
4
+
5
+
6
+ class FluButtonDraw(DSvgDraw):
7
+ def create_roundrect(self,
8
+ x1, y1, x2, y2, radius, radiusy=None, temppath=None,
9
+ fill="transparent", outline="black", outline2="black", width=1
10
+ ):
11
+ if radiusy:
12
+ _rx = radius
13
+ _ry = radiusy
14
+ else:
15
+ _rx, _ry = radius, radius
16
+ drawing = self.create_drawing(x2 - x1, y2 - y1, temppath=temppath)
17
+ border = drawing[1].linearGradient(start=(x1, y1), end=(x1, y2), id="DButton.Border")
18
+ border.add_stop_color("0%", outline)
19
+ border.add_stop_color("100%", outline2)
20
+ drawing[1].defs.add(border)
21
+ drawing[1].add(
22
+ drawing[1].rect(
23
+ (x1, y1), (x2 - x1, y2 - y1), _rx, _ry,
24
+ fill=fill, stroke_width=width,
25
+ stroke=f"url(#{border.get_id()})",
26
+ )
27
+ )
28
+ drawing[1].save()
29
+ return drawing[0]
30
+
31
+
32
+ class FluButtonCanvas(DCanvas):
33
+ draw = FluButtonDraw
34
+
35
+ def create_round_rectangle(self,
36
+ x1, y1, x2, y2, r1, r2=None, temppath=None,
37
+ fill="transparent", outline="black", outline2="black", width=1
38
+ ):
39
+ self._img = self.svgdraw.create_roundrect(
40
+ x1, y1, x2, y2, r1, r2, temppath=temppath,
41
+ fill=fill, outline=outline, outline2=outline2, width=width
42
+ )
43
+ self._tkimg = self.svgdraw.create_tksvg_image(self._img)
44
+ return self.create_image(x1, y1, anchor="nw", image=self._tkimg)
45
+
46
+ create_roundrect = create_round_rectangle
47
+
48
+
49
+ class FluButton(FluButtonCanvas, DDrawWidget):
50
+ def __init__(self, *args,
51
+ text="",
52
+ width=120,
53
+ height=32,
54
+ command=None,
55
+ font=None,
56
+ mode="light",
57
+ style="standard",
58
+ **kwargs):
59
+ self._init(mode, style)
60
+
61
+ super().__init__(*args, width=width, height=height, **kwargs)
62
+
63
+ if command is None:
64
+ def empty(): pass
65
+
66
+ command = empty
67
+
68
+ self.dconfigure(
69
+ text=text,
70
+ command=command
71
+ )
72
+
73
+ self.bind("<<Clicked>>", lambda event=None: self.focus_set(), add="+")
74
+ self.bind("<<Clicked>>", lambda event=None: self.attributes.command(), add="+")
75
+
76
+ self.bind("<Return>", lambda event=None: self.attributes.command(), add="+") # 可以使用回车键模拟点击
77
+
78
+ if font is None:
79
+ from tkdeft.utility.fonts import SegoeFont
80
+ self.attributes.font = SegoeFont()
81
+
82
+ def _init(self, mode, style):
83
+
84
+ from easydict import EasyDict
85
+
86
+ self.attributes = EasyDict(
87
+ {
88
+ "text": "",
89
+ "command": None,
90
+ "font": None,
91
+
92
+ "rest": {
93
+ "back_color": "#ffffff",
94
+ "border_color": "#f0f0f0",
95
+ "border_color2": "#d6d6d6",
96
+ "border_width": 1,
97
+ "radius": 6,
98
+ "text_color": "#1b1b1b",
99
+ },
100
+ "hover": {
101
+ "back_color": "#fcfcfc",
102
+ "border_color": "#f0f0f0",
103
+ "border_color2": "#d6d6d6",
104
+ "border_width": 1,
105
+ "radius": 6,
106
+ "text_color": "#1b1b1b",
107
+ },
108
+ "pressed": {
109
+ "back_color": "#fdfdfd",
110
+ "border_color": "#f0f0f0",
111
+ "border_color2": "#f0f0f0",
112
+ "border_width": 1,
113
+ "radius": 6,
114
+ "text_color": "#636363",
115
+ }
116
+ }
117
+ )
118
+
119
+ self.theme(mode=mode, style=style)
120
+
121
+ def _draw(self, event=None):
122
+ super()._draw(event)
123
+
124
+ self.delete("all")
125
+
126
+ if self.enter:
127
+ if self.button1:
128
+ _back_color = self.attributes.pressed.back_color
129
+ _border_color = self.attributes.pressed.border_color
130
+ _border_color2 = self.attributes.pressed.border_color2
131
+ _border_width = self.attributes.pressed.border_width
132
+ _radius = self.attributes.pressed.radius
133
+ _text_color = self.attributes.pressed.text_color
134
+ else:
135
+ _back_color = self.attributes.hover.back_color
136
+ _border_color = self.attributes.hover.border_color
137
+ _border_color2 = self.attributes.hover.border_color2
138
+ _border_width = self.attributes.hover.border_width
139
+ _radius = self.attributes.hover.radius
140
+ _text_color = self.attributes.hover.text_color
141
+ else:
142
+ _back_color = self.attributes.rest.back_color
143
+ _border_color = self.attributes.rest.border_color
144
+ _border_color2 = self.attributes.rest.border_color2
145
+ _border_width = self.attributes.rest.border_width
146
+ _radius = self.attributes.rest.radius
147
+ _text_color = self.attributes.rest.text_color
148
+
149
+ self.element_border = self.create_round_rectangle(
150
+ 0, 0, self.winfo_width(), self.winfo_height(), _radius, temppath=self.temppath,
151
+ fill=_back_color, outline=_border_color, outline2=_border_color2, width=_border_width
152
+ )
153
+ self.element_text = self.create_text(
154
+ self.winfo_width() / 2, self.winfo_height() / 2, anchor="center",
155
+ fill=_text_color, text=self.attributes.text, font=self.attributes.font
156
+ )
157
+
158
+ def theme(self, mode, style=None):
159
+ self.mode = mode
160
+ if style:
161
+ self.style = style
162
+ if mode.lower() == "dark":
163
+ if self.style.lower() == "accent":
164
+ self._dark_accent()
165
+ else:
166
+ self._dark()
167
+ else:
168
+ if self.style.lower() == "accent":
169
+ self._light_accent()
170
+ else:
171
+ self._light()
172
+
173
+ def _light(self):
174
+ self.dconfigure(
175
+ rest={
176
+ "back_color": "#ffffff",
177
+ "border_color": "#f0f0f0",
178
+ "border_color2": "#d6d6d6",
179
+ "border_width": 1,
180
+ "radius": 6,
181
+ "text_color": "#1b1b1b",
182
+ },
183
+ hover={
184
+ "back_color": "#fcfcfc",
185
+ "border_color": "#f0f0f0",
186
+ "border_color2": "#d6d6d6",
187
+ "border_width": 1,
188
+ "radius": 6,
189
+ "text_color": "#1b1b1b",
190
+ },
191
+ pressed={
192
+ "back_color": "#fdfdfd",
193
+ "border_color": "#f0f0f0",
194
+ "border_color2": "#f0f0f0",
195
+ "border_width": 1,
196
+ "radius": 6,
197
+ "text_color": "#636363",
198
+ }
199
+ )
200
+
201
+ def _light_accent(self):
202
+ self.dconfigure(
203
+ rest={
204
+ "back_color": "#005fb8",
205
+ "border_color": "#146cbe",
206
+ "border_color2": "#00396e",
207
+ "border_width": 1,
208
+ "radius": 6,
209
+ "text_color": "#ffffff",
210
+ },
211
+ hover={
212
+ "back_color": "#0359a9",
213
+ "border_color": "#1766b0",
214
+ "border_color2": "#0f4373",
215
+ "border_width": 1,
216
+ "radius": 6,
217
+ "text_color": "#ffffff",
218
+ },
219
+ pressed={
220
+ "back_color": "#005fb8",
221
+ "border_color": "#4389ca",
222
+ "border_color2": "#4389ca",
223
+ "border_width": 1,
224
+ "radius": 6,
225
+ "text_color": "#b4cbe0",
226
+ }
227
+ )
228
+
229
+ def _dark(self):
230
+ self.dconfigure(
231
+ rest={
232
+ "back_color": "#272727",
233
+ "border_color": "#303030",
234
+ "border_color2": "#262626",
235
+ "border_width": 1,
236
+ "radius": 6,
237
+ "text_color": "#ffffff",
238
+ },
239
+ hover={
240
+ "back_color": "#2d2d2d",
241
+ "border_color": "#303030",
242
+ "border_color2": "#262626",
243
+ "border_width": 1,
244
+ "radius": 6,
245
+ "text_color": "#ffffff",
246
+ },
247
+ pressed={
248
+ "back_color": "#212121",
249
+ "border_color": "#2a2a2a",
250
+ "border_color2": "#262626",
251
+ "border_width": 1,
252
+ "radius": 6,
253
+ "text_color": "#cfcfcf",
254
+ }
255
+ )
256
+
257
+ def _dark_accent(self):
258
+ self.dconfigure(
259
+ rest={
260
+ "back_color": "#60cdff",
261
+ "border_color": "#6cd1ff",
262
+ "border_color2": "#56b4df",
263
+ "border_width": 1,
264
+ "radius": 6,
265
+ "text_color": "#000000",
266
+ },
267
+ hover={
268
+ "back_color": "#5abce9",
269
+ "border_color": "#67c1eb",
270
+ "border_color2": "#50a5cc",
271
+ "border_width": 1,
272
+ "radius": 6,
273
+ "text_color": "#000000",
274
+ },
275
+ pressed={
276
+ "back_color": "#52a9d1",
277
+ "border_color": "#60b0d5",
278
+ "border_color2": "#60b0d5",
279
+ "border_width": 1,
280
+ "radius": 6,
281
+ "text_color": "#295468",
282
+ }
283
+ )
284
+
285
+ def invoke(self):
286
+ self.attributes.command()
tkflu/checkbox.py ADDED
File without changes
tkflu/customwindow.py ADDED
@@ -0,0 +1,134 @@
1
+ from tkinter import Tk
2
+
3
+ from ctypes import POINTER, Structure, c_int
4
+ from ctypes.wintypes import HWND, RECT, UINT
5
+
6
+ WM_NCCALCSIZE = 0x0083
7
+ WS_EX_APPWINDOW = 0x00040000
8
+ WS_VISIBLE = 0x10000000
9
+ WS_THICKFRAME = 0x00040000
10
+ WS_CAPTION = 0x00C00000
11
+ WM_SYSCOMMAND = 0x0112
12
+ SC_MINIMIZE = 0xF020
13
+
14
+ SWP_NOSIZE = 0x0001
15
+ SWP_NOREDRAW = 0x0008
16
+ SWP_FRAMECHANGED = 0x0020
17
+
18
+ SW_MAXIMIZE = 3
19
+ SW_NORMAL = 1
20
+
21
+ GWL_EXSTYLE = -20
22
+ GWL_STYLE = -16
23
+ GWL_WNDPROC = -4
24
+
25
+
26
+ class PWINDOWPOS(Structure):
27
+ _fields_ = [
28
+ ("hWnd", HWND),
29
+ ("hwndInsertAfter", HWND),
30
+ ("x", c_int),
31
+ ("y", c_int),
32
+ ("cx", c_int),
33
+ ("cy", c_int),
34
+ ("flags", UINT),
35
+ ]
36
+
37
+
38
+ class NCCALCSIZE_PARAMS(Structure):
39
+ _fields_ = [("rgrc", RECT * 3), ("lppos", POINTER(PWINDOWPOS))]
40
+
41
+
42
+ from ctypes import WINFUNCTYPE, c_char_p, c_uint64, windll
43
+
44
+
45
+ from tkinter import Event, Widget, Tk, Frame
46
+
47
+
48
+ class WindowDragArea(object):
49
+ x, y = 0, 0
50
+
51
+ def __init__(self, window):
52
+ self.window = window
53
+
54
+ def _click(self, event: Event):
55
+ self.x, self.y = event.x, event.y
56
+
57
+ def _window_move(self, event: Event):
58
+ new_x = (event.x - self.x) + self.window.winfo_x()
59
+ new_y = (event.y - self.y) + self.window.winfo_y()
60
+ if new_y <= 0:
61
+ new_y = 0
62
+ self.window.geometry(f"+{new_x}+{new_y}")
63
+ self.window.update()
64
+
65
+ def bind(self, widget: Widget):
66
+ widget.bind("<Button-1>", self._click)
67
+ widget.bind("<B1-Motion>", lambda event: self._window_move(event))
68
+
69
+ def tag_bind(self, widget: Widget, tag):
70
+ widget.tag_bind(tag, "<Button-1>", self._click)
71
+ widget.tag_bind(tag, "<B1-Motion>", lambda event: self._window_move(event))
72
+
73
+
74
+ class CustomWindow(object):
75
+ def __init__(self, window: Tk = None, wait=100):
76
+
77
+ if window is not None:
78
+ self.window: Tk = window
79
+ else:
80
+ from tkinter import _default_root
81
+ self.window: Tk = _default_root
82
+
83
+ self.window.after(wait, self.setup)
84
+
85
+ def bind_drag(self, widget):
86
+ WindowDragArea(self.window).bind(widget)
87
+
88
+ def setup(self):
89
+ def handle(hwnd: any, msg: any, wp: any, lp: any) -> any:
90
+ if msg == WM_NCCALCSIZE and wp:
91
+ sz = NCCALCSIZE_PARAMS.from_address(lp)
92
+ sz.rgrc[0].top -= 6
93
+
94
+ return windll.user32.CallWindowProcW(*map(c_uint64, (globals()[old], hwnd, msg, wp, lp)))
95
+
96
+ self.hwnd = windll.user32.GetParent(self.window.winfo_id())
97
+
98
+ windll.user32.SetWindowLongA(self.hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW)
99
+ windll.user32.SetWindowLongA(self.hwnd, GWL_STYLE, WS_VISIBLE | WS_THICKFRAME)
100
+
101
+ old, new = "old", "new"
102
+ prototype = WINFUNCTYPE(c_uint64, c_uint64, c_uint64, c_uint64, c_uint64)
103
+ globals()[old] = None
104
+ globals()[new] = prototype(handle)
105
+ globals()[old] = windll.user32.GetWindowLongPtrA(self.hwnd, GWL_WNDPROC)
106
+ windll.user32.SetWindowLongPtrA(self.hwnd, GWL_WNDPROC, globals()[new])
107
+
108
+ self.window.wm_iconify()
109
+ self.window.wm_deiconify()
110
+ self.window.focus_force()
111
+ self.window.update()
112
+
113
+
114
+ from tkinter import Tk
115
+
116
+
117
+ class CustomTk(Tk):
118
+ def __init__(self, *args, **kwargs):
119
+ super().__init__(*args, **kwargs)
120
+ self.customwindow = CustomWindow(self, *args, **kwargs)
121
+
122
+
123
+ if __name__ == '__main__':
124
+ from tkinter import Tk, Frame
125
+ root = Tk()
126
+ root.title("Test")
127
+
128
+ frame = Frame(root, width=100, height=25, background="grey")
129
+ frame.pack(fill="x", side="top")
130
+
131
+ customwindow = CustomWindow(root, wait=100)
132
+ customwindow.bind_drag(frame)
133
+
134
+ root.mainloop()