如何在Tkinter的Button小部件中创建多个标签?
问题描述:
我想知道如何在Tkinter中创建具有多个标签的按钮构件,如下图所示。 Buttons with sub-label. 正如您所看到的,在某些按钮中有一个子标签,例如按钮“X”有另一个小标签“A”。我试图寻找解决方案,但没有发现。如何在Tkinter的Button小部件中创建多个标签?
非常感谢您提前。
答
您可以将您的标签放在Frame
中,并将Button
作为该框架的父项。但是,你需要一点点聪明,克服一些问题,如:
- 无法单击按钮正确(你只能点击边缘,因为包含标签帧是在中间) ,这意味着你必须做一些事件处理(点击框架上,标签内需要触发相同的事件,就好像按钮被点击)
- 不同步的颜色悬停在按钮本身
- 和时还有其他一些小细节,比如点击时正确配置按钮的
relief
(不要忘了,您可以点击框架或标签!)等。
下面是一个MCVE:
import sys
import string
import random
try:
import tkinter as tk
from tkinter import ttk
except ImportError:
import Tkinter as tk
import ttk
CHARS = string.ascii_letters + string.digits
class CustomButton(tk.Button):
"""
CustomButton class inherits from tk.Button, which means it
behaves just like an ordinary tk.Button widget, but it also
has some extended functionality.
"""
def __init__(self, parent, *args, **kwargs):
super().__init__()
self.command = kwargs.get('command')
self.frame = tk.Frame(self)
self.frame.pack(fill='none', expand=False, pady=(3, 0))
self.upper_label = ttk.Label(self.frame, text=kwargs.get('upper_text'))
self.upper_label.grid(row=0, column=0)
self.bottom_label = ttk.Label(self.frame, text=kwargs.get('bottom_text'))
self.bottom_label.grid(row=1, column=1)
self.frame.pack_propagate(False)
self.configure(width=kwargs.get('width'), height=kwargs.get('height'))
self.pack_propagate(False)
self.clicked = tk.BooleanVar()
self.clicked.trace_add('write', self._button_cmd)
self.bind('<Enter>', self._on_enter)
self.bind('<Leave>', self._on_leave)
self.frame.bind('<Enter>', self._on_enter)
self.frame.bind('<Button-1>', self._on_click)
self.upper_label.bind('<Button-1>', self._on_click)
self.bottom_label.bind('<Button-1>', self._on_click)
def _button_cmd(self, *_):
"""
Callback helper method
"""
if self.clicked.get() and self.command is not None:
self.command()
def _on_enter(self, _):
"""
Callback helper method which is triggered
when the cursor enters the widget's 'territory'
"""
for widget in (self, self.frame, self.upper_label, self.bottom_label):
widget.configure(background=self.cget('activebackground'))
def _on_leave(self, _):
"""
Callback helper method which is triggered
when the cursor leaves the widget's 'territory'
"""
for widget in (self, self.frame, self.upper_label, self.bottom_label):
widget.configure(background=self.cget('highlightbackground'))
def _on_click(self, _):
"""
Callback helper method which is triggered
when the the widget is clicked
"""
self.clicked.set(True)
self.configure(relief='sunken')
self.after(100, lambda: [
self.configure(relief='raised'), self.clicked.set(False)
])
class KeyboardMCVE(tk.Tk):
"""
MCVE class for demonstration purposes
"""
def __init__(self):
super().__init__()
self.title('Keyboard')
self._widgets = []
self._create_widgets()
def _create_widgets(self):
"""
Instantiating all the "keys" (buttons) on the fly while both
configuring and laying them out properly at the same time.
"""
for row in range(5):
current_row = []
for column in range(15):
button = CustomButton(
self,
width=1, height=2,
upper_text=random.choice(CHARS),
bottom_text=random.choice(CHARS)
)
button.grid(row=row, column=column, sticky='nswe')
current_row.append(button)
self._widgets.append(current_row)
if __name__ == '__main__':
sys.exit(KeyboardMCVE().mainloop())
可替换地,一个简单的解决方法是使用Unicode的上标/下标。
非常感谢。我从来没有想过我可以有一个框架作为一个按钮的孩子。正如你所说,我必须小心处理事件。现在,我有了一个新想法来创建一个图像并将其放在按钮上。 非常感谢。 – Hasan
伟大的方法。这个答案值得更多赞赏 –