I am currently using a function to display a pandas dataframe in a spreadsheet style format. I would like to be able to add some functionality to format individual cells of the treeview based on their content e.g. if they contain substring 'X' or if their value is higher than Y.
The update function currently implemented is as follows:
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()
Can anyone confirm that you can in fact edit an individual cell in a treview?
If yes are there any ideas as to how this could be implemented?
It's not possible to set the style of individual cells in Treeview; only entire rows can use the tag attribute.
If you just want a table of values then I'd recommend just using ttk.Label widgets, which you can format in a huge number of ways. For example:
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()