-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwidgets.py
More file actions
624 lines (555 loc) · 25.3 KB
/
Copy pathwidgets.py
File metadata and controls
624 lines (555 loc) · 25.3 KB
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
# encoding = utf-8
from typing import Literal
import platform
from tkinter import * # @UnusedWildImport
import tkinter as tk # When we must use tk
from tkinter.ttk import *
from tkinter import filedialog
from i18n import I18n
class ProcessBar(object):
'''a processbar widget'''
def __init__(self, master, height:int) -> None:
self.frame = Frame(master=master, height=height)
self._bar = Frame(master=self.frame, height=height)
self._bar.configure(width=0)
def setpro(self, proce:float):
'''proce:float type,between 0.0-1.0'''
width = self.frame.winfo_width()
bar_width = width * proce
self._bar.configure(width=bar_width)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
self._bar.pack(side=LEFT,)
self.frame.pack(*args, **kwargs)
def setstyle(self, style:str):
'''set the bar's style,Using TFrame'''
self._bar.config(style=style)
class LabelWidget(Frame):
'''a label and subwidget'''
def __init__(self, master, subwidget:type , label_text:str, widget_width:int=None,
subwidget_kwargs:dict={}, **kwargs) -> None:
super().__init__(master=master, **kwargs)
self.value = StringVar()
self.label = Label(self, text=label_text)
self.subwidget = subwidget(self, width=widget_width,
textvariable=self.value, **subwidget_kwargs)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
self.label.pack(side=LEFT)
# 让子控件占用剩余水平空间,横向填充并可扩展
self.subwidget.pack(side=LEFT, padx=5, fill=X, expand=True)
super().pack(*args, **kwargs)
class LabelEntryButton(Frame):
'''a label, entry and button widget'''
def __init__(self, master, label_text:str, entry_width:int=None, button_text:str=None) -> None:
super().__init__(master=master)
self.value = StringVar()
self.label = Label(self, text=label_text)
self.entry = Entry(self, width=entry_width, textvariable=self.value)
self.button = Button(self, text=button_text)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
self.label.pack(side=LEFT)
self.entry.pack(side=LEFT, padx=5,fill=X,expand=True)
self.button.pack(side=LEFT, padx=5)
super().pack(*args, **kwargs)
def callback(self, func):
'''set button's command'''
self.button.config(command=func)
return func
class RasioButtonGroup(Frame):
'''a group of radio buttons'''
def __init__(self, master, label_text:str, options:list) -> None:
super().__init__(master=master)
self.value = StringVar()
self.label = Label(self, text=label_text)
self.buttons = []
for option in options:
button = Radiobutton(self, text=option, variable=self.value, value=option)
self.buttons.append(button)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
self.label.pack(side=LEFT)
for button in self.buttons:
button.pack(side=LEFT, padx=5)
super().pack(*args, **kwargs)
class LabelCombobox(Frame):
'''a label and combobox widget'''
def __init__(self, master, label_text:str, combo_values:list) -> None:
super().__init__(master=master)
self.value = StringVar()
self.label = Label(self, text=label_text)
self.combo = Combobox(self, values=combo_values, textvariable=self.value)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
self.label.pack(side=LEFT)
self.combo.pack(side=LEFT, padx=5)
super().pack(*args, **kwargs)
class CheckbtnWidget(Frame):
'''a checkbutton and subwidget'''
def __init__(self, master,subwidget:type, check_text:str, widget_width:int) -> None:
super().__init__(master=master)
self.value = StringVar() if subwidget in [Entry] else None
self.picked = BooleanVar()
self.check = Checkbutton(self, text=check_text, variable=self.picked)
self.entry = subwidget(self, textvariable=self.value if self.value else None, width=widget_width)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
self.check.pack(side=LEFT)
self.entry.pack(side=LEFT, padx=5, expand=True, fill=X)
super().pack(*args, **kwargs)
@property
def val(self):
if self.picked.get():
return self.value.get()
else:
return None
class FileChooser(Frame):
'''a file chooser widget'''
def __init__(self, master, #label_text:str,
choose_type:Literal["file", "folder", "saveas"]="file",
file_type:str=None, textvariable: StringVar=None, **kwargs) -> None:
super().__init__(master, **kwargs)
self.value = textvariable if textvariable else StringVar()
self.choose_type = choose_type
self.file_type = file_type
# self.label = Label(self, text=label_text)
self.entry = Entry(self, textvariable=self.value)
self.button = Button(self, text='...', command=self.pick, width=20)
def pack(self, *args, **kwargs) -> None:
'''Pack a widget in the parent widget. Use as options:
after=widget - pack it after you have packed widget
anchor=NSEW (or subset) - position widget according togiven direction
before=widget - pack it before you will pack widget
expand=bool - expand widget if parent size grows
fill=NONE or X or Y or BOTH - fill widget if widget grows in=
master - use master to contain this widget in_=master - see 'in' option description
ipadx=amount - add internal padding in x direction
ipady=amount - add internal padding in y direction
padx=amount - add padding in x direction
pady=amount - add padding in y direction
side=TOP or BOTTOM or LEFT or RIGHT - where to add this widget.'''
# self.label.pack(side=LEFT)
self.entry.pack(side=LEFT, padx=5, expand=True, fill=X)
self.button.pack(side=LEFT, padx=5)
super().pack(*args, **kwargs)
def pick(self):
'''open file chooser dialog'''
if self.choose_type == "file":
filename = filedialog.askopenfilename(filetypes=[(self.file_type, '*.' + self.file_type)]
if self.file_type else [])
elif self.choose_type == "folder":
filename = filedialog.askdirectory()
elif self.choose_type == "saveas":
filename = filedialog.asksaveasfilename(defaultextension='.' + self.file_type if self.file_type else '')
else:
raise ValueError("Invalid choose_type: {}".format(self.choose_type))
self.entry.delete(0, END)
self.entry.insert(0, filename)
self.value.set(filename)
class PlaceholderEntry(Entry):
"""
带提示文字(placeholder)的 Entry 控件。
当控件未获得焦点且内容为空时, 以浅色显示提示文字。
"""
# 类变量, 确保样式只配置一次(可选优化)
_styles_configured = False
def __init__(self, master, placeholder="", placeholder_color="gray",
normal_color="black", fieldbackground="white", **kwargs):
"""
初始化 PlaceholderEntry。
:param master: 父容器
:param placeholder: 提示文字
:param placeholder_color: 提示文字颜色
:param normal_color: 正常输入文字颜色
:param fieldbackground: 字段背景颜色
:param kwargs: 传递给 ttk.Entry 的其他参数
"""
# 保存自定义属性
self.placeholder = placeholder
self.placeholder_color = placeholder_color
self.normal_color = normal_color
self.fieldbackground = fieldbackground
self._placeholder_active = False # 标记当前是否显示 placeholder
if platform.system() == "Windows":
# tk.Entry: 颜色在 Windows 下可稳定生效
tk.Entry.__init__(self, master,
bg=fieldbackground,
fg=normal_color,
insertbackground=normal_color,
disabledbackground=fieldbackground,
disabledforeground=placeholder_color,
relief=FLAT,
highlightthickness=0,
**kwargs)
self.placeholder_style = None
self.normal_style = None
else:
# 非 Windows 继续使用 ttk.Entry
style = Style(master)
self.placeholder_style = f"Placeholder_{id(self)}.TEntry"
self.normal_style = f"Normal_{id(self)}.TEntry"
style.configure(self.placeholder_style, foreground=placeholder_color, fieldbackground=fieldbackground)
style.configure(self.normal_style, foreground=normal_color, fieldbackground=fieldbackground)
style.map(self.placeholder_style,
fieldbackground=[('disabled', fieldbackground), ('!disabled', fieldbackground)],
foreground=[('disabled', placeholder_color), ('!disabled', placeholder_color)])
style.map(self.normal_style,
fieldbackground=[('disabled', fieldbackground), ('!disabled', fieldbackground)],
foreground=[('disabled', normal_color), ('!disabled', normal_color)])
super().__init__(master, style=self.normal_style, **kwargs)
# 绑定焦点事件
self.bind("<FocusIn>", self._on_focus_in)
self.bind("<FocusOut>", self._on_focus_out)
# 初始检查:如果控件无内容, 则显示 placeholder
if not self.get():
self._show_placeholder()
# 启用右键菜单并绑定常见快捷键
try:
self.right_click_menu()
except Exception:
pass
# 键盘快捷键:支持 Ctrl/Cmd + A/C/X/V
# 绑定到 Entry 本身, 回调均接收 event 参数
self.bind("<Control-a>", lambda e: self.select_all(e))
self.bind("<Control-A>", lambda e: self.select_all(e))
self.bind("<Control-c>", lambda e: self.copy(e))
self.bind("<Control-C>", lambda e: self.copy(e))
self.bind("<Control-x>", lambda e: self.cut(e))
self.bind("<Control-X>", lambda e: self.cut(e))
self.bind("<Control-v>", lambda e: self.paste(e))
self.bind("<Control-V>", lambda e: self.paste(e))
# macOS
self.bind("<Command-a>", lambda e: self.select_all(e))
self.bind("<Command-c>", lambda e: self.copy(e))
self.bind("<Command-x>", lambda e: self.cut(e))
self.bind("<Command-v>", lambda e: self.paste(e))
@property
def is_active(self):
return not self._placeholder_active
def _show_placeholder(self):
"""在控件中显示 placeholder(仅当控件为空且未获得焦点时)"""
if self._placeholder_active:
return
# 清空现有内容(如果有)
self.delete(0, END)
# 插入 placeholder 文字
self.insert(0, self.placeholder)
if platform.system() == "Windows":
self.config(fg=self.placeholder_color)
else:
self.config(style=self.placeholder_style)
self._placeholder_active = True
def _remove_placeholder(self):
"""移除 placeholder, 恢复普通输入状态"""
if not self._placeholder_active:
return
# 删除所有文本(即 placeholder)
self.delete(0, END)
if platform.system() == "Windows":
self.config(fg=self.normal_color)
else:
self.config(style=self.normal_style)
self._placeholder_active = False
def _on_focus_in(self, event=None):
"""获得焦点时, 如果当前显示 placeholder, 则清除它"""
if self._placeholder_active:
self._remove_placeholder()
def _on_focus_out(self, event=None):
"""失去焦点时, 如果内容为空, 则重新显示 placeholder"""
if not self.get():
self._show_placeholder()
def set_text(self, text):
"""
程序化设置 Entry 的文本内容, 并自动调整 placeholder 状态。
:param text: 要设置的文本
"""
# 先移除 placeholder(如果处于活动状态)
if self._placeholder_active:
self._remove_placeholder()
# 设置新文本
self.delete(0, END)
self.insert(0, text)
# 如果设置后的文本为空且控件没有焦点, 则显示 placeholder
if not text and not self.focus_get():
self._show_placeholder()
def right_click_menu(self):
right_click_menu = tk.Menu(self, tearoff=0)
right_click_menu.add_command(label=I18n().get("input_menu.copy"),
command=lambda w=self: self.copy())
right_click_menu.add_command(label=I18n().get("input_menu.cut"),
command=lambda w=self: self.cut())
right_click_menu.add_command(label=I18n().get("input_menu.paste"),
command=lambda w=self: self.paste())
right_click_menu.add_separator()
right_click_menu.add_command(label=I18n().get("input_menu.select_all"),
command=lambda w=self: self.select_all())
# 绑定常见的右键事件(不同平台可能不同)
self.bind("<Button-3>", lambda event, m=right_click_menu: self.show_context_menu(event, m))
self.bind("<Button-2>", lambda event, m=right_click_menu: self.show_context_menu(event, m))
def show_context_menu(self, event, right_click_menu):
try:
if event.widget != self.focus_get():
self.tag_add(tk.SEL, "1.0", tk.END)
self.focus_set()
self.mark_set(tk.INSERT, "insert")
except AttributeError:
if event.widget != self.focus_get():
self.selection_range(0, len(self.get()))
self.focus_set()
self.icursor("insert")
right_click_menu.post(event.x_root, event.y_root)
def copy(self, event=None):
if getattr(self, '_placeholder_active', False):
return 'break'
try:
s = self.selection_get()
except Exception:
return 'break'
self.clipboard_clear()
self.clipboard_append(s)
return 'break'
def cut(self, event=None):
if getattr(self, '_placeholder_active', False):
return 'break'
try:
s = self.selection_get()
except Exception:
return 'break'
self.clipboard_clear()
self.clipboard_append(s)
try:
self.delete(tk.SEL_FIRST, tk.SEL_LAST)
except Exception:
pass
return 'break'
def paste(self, event=None):
try:
clip = self.clipboard_get()
except Exception:
return 'break'
# 如果当前处于 placeholder 状态, 先移除 placeholder
if getattr(self, '_placeholder_active', False):
self._remove_placeholder()
try:
self.delete(tk.SEL_FIRST, tk.SEL_LAST)
except Exception:
pass
try:
self.insert(tk.INSERT, clip)
except Exception:
pass
return 'break'
def select_all(self, event=None):
if getattr(self, '_placeholder_active', False):
return 'break'
length = len(self.get())
try:
self.selection_range(0, length)
self.icursor(length)
except Exception:
pass
return 'break'
class Button(tk.Frame):
"""
基于 Frame + Label 的 Windows 10 风格按钮, 修复了初始化顺序问题。
"""
def __init__(self, master, text="Button", command=None,
width=100, height=30,
bg_normal="#e1e1e1", bg_hover="#c7c7c7", bg_press="#a0a0a0",
fg_normal="#000000", fg_hover="#000000", fg_press="#000000",
font=("Segoe UI", 10), borderwidth=1, **kwargs):
super().__init__(master, borderwidth=borderwidth, height=height,
relief=SOLID, **kwargs)
# 保存参数
self.command = command
self.bg_normal = bg_normal
self.bg_hover = bg_hover
self.bg_press = bg_press
self.fg_normal = fg_normal
self.fg_hover = fg_hover
self.fg_press = fg_press
self.state = "normal"
self.width = width
self.height = height
# 先创建内部 Label(注意:此时还未设置 Frame 尺寸)
self.label = tk.Label(self, text=text, font=font,bg=bg_normal, fg=fg_normal)
self.label.place(relx=0.5, rely=0.5, anchor="center", relwidth=1.0, relheight=1.0)
# 固定尺寸, 防止被子控件撑开(Windows 下显式关闭 pack/grid 传播更稳定)
self.pack_propagate(0)
self.grid_propagate(0)
# 现在设置 Frame 尺寸(使用父类方法, 避免调用自定义 configure)
super().config(width=width, height=height)
# 绑定事件
self.bind("<Enter>", self._on_enter)
self.bind("<Leave>", self._on_leave)
self.bind("<ButtonPress-1>", self._on_press)
self.bind("<ButtonRelease-1>", self._on_release)
self.label.bind("<Enter>", self._on_enter)
self.label.bind("<Leave>", self._on_leave)
self.label.bind("<ButtonPress-1>", self._on_press)
self.label.bind("<ButtonRelease-1>", self._on_release)
self._update_style()
def _update_style(self):
"""根据当前状态更新 Frame 和 Label 的样式"""
if self.state == "normal":
bg = self.bg_normal
fg = self.fg_normal
elif self.state == "hover":
bg = self.bg_hover
fg = self.fg_hover
else: # pressed
bg = self.bg_press
fg = self.fg_press
# 使用父类方法修改背景色, 避免递归
super().config(bg=bg)
self.label.config(bg=bg, fg=fg)
def _on_enter(self, event):
if self.state != "pressed":
self.state = "hover"
self._update_style()
def _on_leave(self, event):
if self.state != "pressed":
self.state = "normal"
self._update_style()
def _on_press(self, event):
self.state = "pressed"
self._update_style()
def _on_release(self, event):
if self.state == "pressed":
# 检查鼠标释放时是否仍在按钮区域内
x, y = event.x_root, event.y_root
x0 = self.winfo_rootx()
y0 = self.winfo_rooty()
x1 = x0 + self.winfo_width()
y1 = y0 + self.winfo_height()
if x0 <= x <= x1 and y0 <= y <= y1 and self.command:
self.command()
# 根据鼠标当前位置恢复状态
self.state = "hover" if self._is_mouse_inside() else "normal"
self._update_style()
def _is_mouse_inside(self):
"""检查鼠标是否在按钮区域内"""
x_root = self.winfo_pointerx()
y_root = self.winfo_pointery()
x0 = self.winfo_rootx()
y0 = self.winfo_rooty()
x1 = x0 + self.winfo_width()
y1 = y0 + self.winfo_height()
return x0 <= x_root <= x1 and y0 <= y_root <= y1
def configure(self, **kwargs):
"""动态修改按钮属性(如文本、颜色等)"""
# 先处理自定义属性
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
else: # 其他属性直接传递给父类处理
super().config(**{key: value})
# 更新文本(确保 label 已存在)
if "text" in kwargs and hasattr(self, "label"):
self.label.config(text=kwargs["text"])
# 更新样式
self._update_style()
config = configure
if __name__ == '__main__':
root = Tk()
# root.geometry('400x300')
pro_bar = ProcessBar(root, height=20)
pro_bar.pack(pady=10)
pro_bar.setpro(0.5)
label_entry = LabelWidget(root, Entry, label_text='Name:', widget_width=20)
label_entry.pack(side = TOP)
label_entry_button = LabelEntryButton(root, label_text='Email:', entry_width=20, button_text='Submit')
label_entry_button.pack(pady=10)
radio_group = RasioButtonGroup(root, label_text = 'Gender:', options = ['Male', 'Female', 'Other'])
radio_group.pack(pady=10)
label_combo = LabelCombobox(root, label_text='Country:', combo_values=['USA', 'Canada', 'UK'])
label_combo.pack(pady=10)
check_entry = CheckbtnWidget(root, Entry, check_text='Subscribe to newsletter', widget_width=20)
check_entry.pack(pady=10)
file_entry = LabelWidget(root, FileChooser, label_text='File:',
subwidget_kwargs={'choose_type': 'folder'}, widget_width=20)
file_entry.pack(pady=10)
@label_entry_button.callback
def callback():
print(label_entry.value.get())
print(label_entry_button.value.get())
print(radio_group.value.get())
print(label_combo.value.get())
print(check_entry.value.get())
print(file_entry.value.get())
# demo for placeholder entry
placeholder_entry = PlaceholderEntry(root, placeholder="Enter your username")
placeholder_entry.pack(pady=10)
# demo for set_text
def set_text_example():
placeholder_entry.set_text("New text")
btn = Button(root, text="Set Text", command=set_text_example)
btn.pack(pady=10)
root.mainloop()