我使用
Treeview
编写了一个表类:
class SimpleDataTable(tk.Frame):
def __init__(self, parent, colHeadings, colWidths, height):
tk.Frame.__init__(self, parent)
self.parent = parent
self.colHeadings = colHeadings
self.colWidths = colWidths
self.height = height
self.dataDict = {}
self.tree = None
self.vsb = None
self.hsb = None
self.setupUI()
def setupUI(self):
self.tree = ttk.Treeview(self, columns=self.colHeadings, show="headings", height=self.height)
self.tree.grid(column=0, row=0, sticky='news')
self.vsb = ttk.Scrollbar(self, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=self.vsb.set)
self.vsb.grid(column=1, row=0, sticky='ns')
self.hsb = ttk.Scrollbar(self, orient="horizontal", command=self.tree.xview)
self.tree.configure(xscrollcommand=self.hsb.set)
self.hsb.grid(column=0, row=1, sticky='ew')
for i in range(2):
self.grid_columnconfigure(i, weight=1)
self.grid_rowconfigure(i, weight=1)
self.setupColumns()
self.populateData()
# TODO: motion binding callbacks when the user resizes row/column
# self.tree.bind("<Configure>", self.onTreeviewConfigure) --> this doesn't work
# TODO: A way to delete data [and delete from the report]
def addData(self, key, values: List[Any]):
self.dataDict[key] = self.processValuesForDisplay(values)
self.updateData()
def setupColumns(self):
for i,col in enumerate(self.colHeadings):
self.tree.heading(col, text=col, anchor=tk.CENTER)
self.tree.column(col, width=self.colWidths[i], anchor=tk.CENTER)
def processValuesForDisplay(self, values: List[Any]) -> List[Any]:
for i,val in enumerate(values):
if isinstance(val, float):
values[i] = round(val, 2)
return values
def populateData(self):
for key, values in self.dataDict.items():
self.tree.insert("", "end", values=tuple(values), text=str(key))
def updateData(self):
# When new data is added, the whole table gets re-wriiten. Works with simple cases with a few rows and cols
# Clear the existing data, self.tree.get_children() returns a tuple which we'd need to unpacks
self.tree.delete(*self.tree.get_children())
self.populateData()
def selectRowByKey(self, key):
for item in self.tree.get_children():
if self.tree.item(item, 'text') == str(key):
self.tree.selection_set(item)
self.tree.focus(item)
self.tree.tag_configure('selected', background='lightblue') # Change the background color to light blue
self.tree.item(item, tags=('selected',))
break
def onTreeviewConfigure(self, event):
# Resize columns when the treeview is configured (e.g., on window resize)
for i, col in enumerate(self.colHeadings):
width = self.tree.column(col, option="width")
self.colWidths[i] = width
self.resizeColumns()
def resizeColumns(self):
# Resize the columns based on stored widths
for i, col in enumerate(self.colHeadings):
width = self.colWidths[i]
print(width)
self.tree.column(col, width=width, anchor=tk.CENTER)
我通过以下代码将此表放入框架中:
self.dataTable = SimpleDataTable(self, colHeadings = table_titles, colWidths = [350] * len(table_titles) ,height = 3)
self.dataTable.grid(row=5, column=0, columnspan=12, pady=10, padx=(20, 10))
最初,表格具有预期的外观。列数是动态的。如果列超过框架大小,滚动条就会激活,我可以使用它来查看所有列。 现在,如果用户想要调整任何单个列的大小,滚动条就会消失,并且所有列都会缩小到父框架大小内。 我尝试了不同类型的回调(在代码中显示),因此当事件发生时会重新绘制表格,但什么也没有发生。我能做些什么? 我希望用户调整列宽并且滚动条仍然保持活动状态。
请注意,所有网格均在相关框架中配置。
设置宽度时我必须添加
stretch=0
:
self.tree.column(col, width=self.colWidths[i], anchor=tk.CENTER, stretch=0)