如何调整Tkinter Treeview组件中复选框的尺寸?
调整Treeview复选框尺寸的解决方案
我正在开发一款简易程序,程序中的Treeview组件带有复选框。我希望将这些复选框的尺寸调大一些,以便部分用户能够更轻松地勾选。当前复选框显示尺寸较小,操作不便。以下是我的原实现代码:
import tkinter as tk from tkinter import ttk root = tk.Tk() class CbTreeview(ttk.Treeview): def __init__(self, master=None, **kw): kw.setdefault('style', 'cb.Treeview') kw.setdefault('show', 'headings') # hide column #0 ttk.Treeview.__init__(self, master, **kw) # create checheckbox images self._im_checked = tk.PhotoImage('checked', data=b'GIF89a\x0e\x00\x0e\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x0e\x00\x0e\x00\x00\x02#\x04\x82\xa9v\xc8\xef\xdc\x83k\x9ap\xe5\xc4\x99S\x96l^\x83qZ\xd7\x8d$\xa8\xae\x99\x15Zl#\xd3\xa9"\x15\x00;', master=self ) self._im_unchecked = tk.PhotoImage('unchecked', data=b'GIF89a\x0e\x00\x0e\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x0e\x00\x0e\x00\x00\x02\x1e\x04\x82\xa9v\xc1\xdf"|i\xc2j\x19\xce\x06q\xed|\xd2\xe7\x89%yZ^J\x85\x8d\xb2\x00\x05\x00;', master=self) style = ttk.Style(self) style.configure("cb.Treeview.Heading", font=(None, 13)) # put image on the right style.layout('cb.Treeview.Row', [('Treeitem.row', {'sticky': 'nswe'}), ('Treeitem.image', {'side': 'right', 'sticky': 'e'})]) # use tags to set the checkbox state self.tag_configure('checked', image='checked') self.tag_configure('unchecked', image='unchecked') self.tag_configure('evenrow', background='lightblue') self.tag_configure('oddrow', background='gray') def tag_add(self, item, tags): new_tags = tuple(self.item(item, 'tags')) + tuple(tags) self.item(item, tags=new_tags) def tag_remove(self, item, tag): tags = list(self.item(item, 'tags')) tags.remove(tag) self.item(item, tags=tags) def insert(self, parent, index, iid=None, **kw): item = ttk.Treeview.insert(self, parent, index, iid, **kw) print(kw['values'][0]) if kw['values'][0] % 2 == 0: self.tag_add(item, (item, 'evenrow')) else: self.tag_add(item, (item, 'oddrow')) if kw['values'][2] is True: self.tag_add(item, (item, 'checked')) else: self.tag_add(item, (item, 'unchecked')) self.tag_add(item, (item, 'unchecked')) self.tag_bind(item, '<ButtonRelease-1>', lambda event: self._on_click(event, item)) def _on_click(self, event, item): """Handle click on items.""" if self.identify_row(event.y) == item: if self.identify_column(event.x) == '#4': # click in 'Is Lv 90' column # toggle checkbox image if self.tag_has('checked', item): self.tag_remove(item, 'checked') self.tag_add(item, ('unchecked',)) else: self.tag_remove(item, 'unchecked') self.tag_add(item, ('checked',)) tree = CbTreeview(root, columns=("No.", "Character Name", "Leveled up to 90", "Is Lv 90"), height=20, selectmode="extended") tree.heading('No.', text="No.", anchor=tk.E) tree.heading('Character Name', text="Character Name", anchor=tk.E) tree.heading('Leveled up to 90', text="Leveled up to 90", anchor=tk.E) tree.heading('Is Lv 90', text="Is Lv 90", anchor=tk.E) tree.column('#1', stretch='no', minwidth=0, width=30, anchor=tk.E) tree.column('#2', stretch='no', minwidth=0, width=150, anchor=tk.E) tree.column('#3', stretch='no', minwidth=0, width=0, anchor=tk.E) tree.column('#4', stretch='no', minwidth=0, width=100, anchor=tk.E) tree.pack(fill='both') tree.insert('', 'end', values=(0, 'Amber', True, '')) tree.insert('', 'end', values=(1, 'Jean', False, '')) tree.insert('', 'end', values=(2, 'Lisa', True, '')) tree.insert('', 'end', values=(3, 'Keqing', True, '')) tree.insert('', 'end', values=(4, 'Ganyu', True, '')) tree.insert('', 'end', values=(5, 'Barbara', False, '')) tree.insert('', 'end', values=(6, 'Paimon', True, '')) tree.insert('', 'end', values=(7, 'Lumine', True, '')) tree.insert('', 'end', values=(8, 'Noelle', True, '')) tree.insert('', 'end', values=(9, 'Diluc', False, '')) tree.insert('', 'end', values=(10, 'Ayaka', False, '')) tree.insert('', 'end', values=(11, 'Yae Miko', True, '')) tree.insert('', 'end', values=(12, 'Raiden Shogun', True, '')) tree.insert('', 'end', values=(13, 'Ayato', True, '')) tree.insert('', 'end', values=(14, 'Eula', True, '')) tree.insert('', 'end', values=(15, 'Hu Tao', True, '')) tree.insert('', 'end', values=(16, 'Yoimiya', True, '')) root.mainloop()
修改方案
要调大复选框尺寸,核心是放大用于显示复选状态的图片,同时修正原代码中的逻辑bug:
- 放大复选框图片:利用
PhotoImage的zoom()方法对原有复选框图片进行放大,比如放大2倍; - 修正复选状态覆盖问题:原代码中
insert方法最后重复添加unchecked标签,会覆盖之前根据values[2]设置的状态,需要删除该行; - 可选:调整列宽适配:如果放大后的图片超出列宽,可以适当调大对应列的宽度。
修改后的完整代码:
import tkinter as tk from tkinter import ttk root = tk.Tk() class CbTreeview(ttk.Treeview): def __init__(self, master=None, **kw): kw.setdefault('style', 'cb.Treeview') kw.setdefault('show', 'headings') # hide column #0 ttk.Treeview.__init__(self, master, **kw) # 创建复选框图片并放大2倍 self._im_checked = tk.PhotoImage('checked', data=b'GIF89a\x0e\x00\x0e\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x0e\x00\x0e\x00\x00\x02#\x04\x82\xa9v\xc8\xef\xdc\x83k\x9ap\xe5\xc4\x99S\x96l^\x83qZ\xd7\x8d$\xa8\xae\x99\x15Zl#\xd3\xa9"\x15\x00;', master=self ).zoom(2) # 放大图片 self._im_unchecked = tk.PhotoImage('unchecked', data=b'GIF89a\x0e\x00\x0e\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x0e\x00\x0e\x00\x00\x02\x1e\x04\x82\xa9v\xc1\xdf"|i\xc2j\x19\xce\x06q\xed|\xd2\xe7\x89%yZ^J\x85\x8d\xb2\x00\x05\x00;', master=self).zoom(2) # 放大图片 style = ttk.Style(self) style.configure("cb.Treeview.Heading", font=(None, 13)) # 将图片放在右侧 style.layout('cb.Treeview.Row', [('Treeitem.row', {'sticky': 'nswe'}), ('Treeitem.image', {'side': 'right', 'sticky': 'e'})]) # 用标签设置复选框状态 self.tag_configure('checked', image='checked') self.tag_configure('unchecked', image='unchecked') self.tag_configure('evenrow', background='lightblue') self.tag_configure('oddrow', background='gray') def tag_add(self, item, tags): new_tags = tuple(self.item(item, 'tags')) + tuple(tags) self.item(item, tags=new_tags) def tag_remove(self, item, tag): tags = list(self.item(item, 'tags')) tags.remove(tag) self.item(item, tags=tags) def insert(self, parent, index, iid=None, **kw): item = ttk.Treeview.insert(self, parent, index, iid, **kw) print(kw['values'][0]) if kw['values'][0] % 2 == 0: self.tag_add(item, (item, 'evenrow')) else: self.tag_add(item, (item, 'oddrow')) if kw['values'][2] is True: self.tag_add(item, (item, 'checked')) else: self.tag_add(item, (item, 'unchecked')) # 删除重复设置unchecked的代码,避免覆盖状态 self.tag_bind(item, '<ButtonRelease-1>', lambda event: self._on_click(event, item)) def _on_click(self, event, item): """Handle click on items.""" if self.identify_row(event.y) == item: if self.identify_column(event.x) == '#4': # 点击'Is Lv 90'列 # 切换复选框图片 if self.tag_has('checked', item): self.tag_remove(item, 'checked') self.tag_add(item, ('unchecked',)) else: self.tag_remove(item, 'unchecked') self.tag_add(item, ('checked',)) tree = CbTreeview(root, columns=("No.", "Character Name", "Leveled up to 90", "Is Lv 90"), height=20, selectmode="extended") tree.heading('No.', text="No.", anchor=tk.E) tree.heading('Character Name', text="Character Name", anchor=tk.E) tree.heading('Leveled up to 90', text="Leveled up to 90", anchor=tk.E) tree.heading('Is Lv 90', text="Is Lv 90", anchor=tk.E) tree.column('#1', stretch='no', minwidth=0, width=30, anchor=tk.E) tree.column('#2', stretch='no', minwidth=0, width=150, anchor=tk.E) tree.column('#3', stretch='no', minwidth=0, width=0, anchor=tk.E) tree.column('#4', stretch='no', minwidth=0, width=120, anchor=tk.E) # 适当调大列宽适配放大的图片 tree.pack(fill='both') tree.insert('', 'end', values=(0, 'Amber', True, '')) tree.insert('', 'end', values=(1, 'Jean', False, '')) tree.insert('', 'end', values=(2, 'Lisa', True, '')) tree.insert('', 'end', values=(3, 'Keqing', True, '')) tree.insert('', 'end', values=(4, 'Ganyu', True, '')) tree.insert('', 'end', values=(5, 'Barbara', False, '')) tree.insert('', 'end', values=(6, 'Paimon', True, '')) tree.insert('', 'end', values=(7, 'Lumine', True, '')) tree.insert('', 'end', values=(8, 'Noelle', True, '')) tree.insert('', 'end', values=(9, 'Diluc', False, '')) tree.insert('', 'end', values=(10, 'Ayaka', False, '')) tree.insert('', 'end', values=(11, 'Yae Miko', True, '')) tree.insert('', 'end', values=(12, 'Raiden Shogun', True, '')) tree.insert('', 'end', values=(13, 'Ayato', True, '')) tree.insert('', 'end', values=(14, 'Eula', True, '')) tree.insert('', 'end', values=(15, 'Hu Tao', True, '')) tree.insert('', 'end', values=(16, 'Yoimiya', True, '')) root.mainloop()
说明
zoom(2)会将图片的像素尺寸放大2倍,你可以根据需求调整倍数(比如zoom(1.5)放大1.5倍);- 调大
#4列的宽度是为了让放大后的复选框完整显示,避免被截断; - 原代码中重复添加
unchecked标签的逻辑会导致初始状态错误,删除后才能正确根据values[2]设置复选框状态。
内容的提问来源于stack exchange,提问作者Ricky Shie
相关产品推荐
相关产品推荐

