在 tkinter ttk treeview 中格式化单个单元格/项目而不是整行

Posted

技术标签:

【中文标题】在 tkinter ttk treeview 中格式化单个单元格/项目而不是整行【英文标题】:Format individual cell/item rather than entire row in tkinter ttk treeview 【发布时间】:2017-07-28 19:56:34 【问题描述】:

我目前正在使用一个函数以电子表格样式格式显示 pandas 数据框。我希望能够添加一些功能来根据它们的内容格式化树视图的各个单元格,例如如果它们包含子字符串 'X' 或者它们的值高于 Y。

目前实现的更新功能如下:

   def updateTree(self, dataframe):
    '''
    Updates the treeview with the data in the dataframe
    parameter
    '''
    #Remove any nan values which may have appeared in the dataframe parameter
    df = dataframe.replace(np.nan,'', regex=True)

    #Currently displayed data
    self.treesubsetdata = dataframe

    #Remove existing items
    for item in self.tree.get_children(): self.tree.delete(item)
    #Recreate from scratch the columns based on the passed dataframe
    self.tree.config(columns= [])
    self.tree.config(columns= list(dataframe.columns))

    #Ensure all columns are considered strings and write column headers
    for col in dataframe.columns:
        self.tree.heading(col,text=str(col))

    #Get number of rows and columns in the imported script
    self.rows,self.cols = dataframe.shape

    #Populate data in the treeview
    for row in dataframe.itertuples():
        self.tree.insert('', 'end',values = tuple(row[1:]))

    #Minimise first column
    self.tree.column('#0',width=0)
    self.tree.update()

任何人都可以确认您实际上可以编辑评论中的单个单元格吗?

如果是,有什么想法可以实现吗?

【问题讨论】:

我认为这不可能。您是否有任何理由不想制作一个简单的 Label 或 Entry 小部件网格? 我使用自定义类来创建、显示、添加滚动条、更新和使用显示数据上的绑定键执行复杂的排序和过滤。我不确定此时我是否可以重新设计框架。如果不可能,我想我将不得不重新考虑功能。 【参考方案1】:

无法在 Treeview 中设置单个单元格的样式;只有整行可以使用标签属性。

如果您只想要一个值表,那么我建议您只使用 ttk.Label 小部件,您可以通过多种方式对其进行格式化。例如:

import Tkinter as tk
import ttk
import pandas as pd
from random import randrange

PADDING = dict(padx=3, pady=3)
class GridView(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.labels = []
        style = ttk.Style()
        style.configure("red.TLabel", background='red')
        style.configure("green.TLabel", background='green')
        style.configure("header.TLabel", font = '-weight bold')

    def set(self, df):
        self.clear()
        for col, name in enumerate(df.columns):
            lbl = ttk.Label(self, text=name, style='header.TLabel')
            lbl.grid(row=0, column=col, **PADDING)
            self.labels.append(lbl)

        for row, values in enumerate(df.itertuples(), 1):
            for col, value in enumerate(values[1:]):
                lbl = ttk.Label(self, text=value, style=self.get_style(value))
                lbl.grid(row=row, column=col, **PADDING)
                self.labels.append(lbl)

    @staticmethod
    def get_style(value):
        if value > 70:
            return "red.TLabel"
        elif value < 30:
            return "green.TLabel"
        else:
            return None

    def clear(self):
        for lbl in self.labels:
            lbl.grid_forget()
        self.labels = []

class GUI(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.table = GridView(self)
        self.table.pack()
        btn = ttk.Button(self, text="populate", command=self.populate)
        btn.pack()
        btn = ttk.Button(self, text="clear", command=self.table.clear)
        btn.pack()

    def populate(self):
        self.table.set(new_rand_df())

def main():
    root = tk.Tk()
    win = GUI(root)
    win.pack()
    root.mainloop()

def new_rand_df():
    width = 5
    height = 5
    return pd.DataFrame([[randrange(100) for _ in range(width)] for _ in range(height)], columns = list('abcdefghijklmnopqrstuvwxyz'[:width]))

if __name__ == '__main__':
    main()

【讨论】:

在传统的 ttk.Treeview 中似乎不可能做我想做的事,所以我接受乔纳森的回答,详细提出替代解决方案。【参考方案2】:

这可以使用 Gtk3 中的自定义单元渲染器来实现,例如:

import gi
gi.require_version( 'Gtk', '3.0' )
from gi.repository import Gtk, GObject


class My_CellRendererText( Gtk.CellRendererText ):
    def __init__( self ):
        super().__init__()

    def do_render( self, cr, widget, background_area, cell_area, flags ):
        cell_text = self.props.text
        self.props.underline = ( "X" in cell_text )
        self.props.weight = 700 if ( cell_text.isdigit() and int( cell_text ) > 3 ) else 400
        return Gtk.CellRendererText.do_render( self, cr, widget, background_area, cell_area, flags )

GObject.type_register( My_CellRendererText )

【讨论】:

这可以与 ttk Treeview 一起使用吗?还是我需要使用 Gtk 从头开始​​构建 GUI? 是的,这个 cellrenderer 需要一个 Gtk.Treeview。我不知道它是否也可以与 ttk 一起使用,但我认为不能。

以上是关于在 tkinter ttk treeview 中格式化单个单元格/项目而不是整行的主要内容,如果未能解决你的问题,请参考以下文章

Python如何改变tkinter.ttk.Treeview表格组件的每行的行高?

强制 Tkinter.ttk Treeview 小部件在缩小其列宽后调整大小

如何使用 Tkinter 清除整个 Treeview

在 tkinter ttk treeview 中格式化单个单元格/项目而不是整行

tkinter ttk treeview 如何设置固定宽度?为啥它会随着列数而变化?

python tkinter与ttk