Treeview with Columns

TreeView with columns

There are a number of sample controls to be found around the web:

  • TreeViewAdv for .Net
  • TreeView with Columns
  • ContainerListView and TreeListView

But the all-time favorite is probably the ObjectListView, which provides an expandable, multi-column ListView, along with many other incredibly handy features:

   ObjectListView sample

Treeview with multiple columns

I have stumbled upon an easy way to accomplish what I was trying. Without using third-party controls or very complex templates. Thanks to the following link:
http://www.codemag.com/Article/1401031

Since I had a really hard time finding anything usefull on this, I decided to post an answer myself, so others can also make use of it.

Treeview code with template:

<TreeView BorderThickness="0" ItemsSource="{Binding processes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding subProcesses}" >
<Grid>

//Number of columns
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>

//Controls to use in the treeview grid
<TextBlock Text="{Binding procesId}" Grid.Column="0"/>
<TextBlock Text="{Binding procesName}" Grid.Column="1"/>

</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

List to bind to the treeview (Every time you will give the Proces a list of subProcesses it will generate an additional level in the treeview):

List<Proces> processes;

Proces class:

public class Proces {
public Int64 procesId { get; set; }
public String procesName { get; set; }
public List<Proces> subProcesses { get; set; }
}

Material UI TreeView: Display multiple columns data from node in treeview

The solution is a little bit more tricky than MUI TreeView example. While example accepts data as JSON, in your case you have an array of JSON.

Supposing that you want to show name attribute in treeview label, you could write something like this:

<TreeView
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpanded={["root"]}
defaultExpandIcon={<ChevronRightIcon />}
>
{renderTree(data)} //<-- this is the function that recursively inspects your array
</TreeView>

Then the renderTree function:

const renderTree = (nodes) => {
return nodes.map((el) => { //<-- mapping on array
return (
<TreeItem key={el.id} nodeId={el.id.toString()} label={el.id + " " + el.name + " " + el.country}>
{Array.isArray(el.children) && el.children.length > 0
? renderTree(el.children) //<-- recursion in case children has values
: null}
</TreeItem>
);
});
};

The result is:

Sample Image

Here a working example.

Windows forms: TreeView control with multiple columns

No, the standard TreeView does not allow columns, what you are looking for is commonly called a TreeListView. So you'll need a custom control.

There are many listed on CodeProject:

  • TreeListView User Control (Lite).
  • TreeListView (Simple)
  • The TreeListView To End All TreeListViews.

Personally I'd recomend TreeViewAdv on SourceForge

How to add columns to a TTK Treeview using a loop?

Here's the example I mentioned in the comments. This could be formatted better but I'm simply adding onto your code to show how it works. Additionally, there was a bug where you were continuously building onto your tuple with each function call.

import tkinter as tk
from tkinter import ttk

headings = ["Heading0", "Heading1", "Heading2", "Heading3"]

root = tk.Tk()
root.title("Add headings")

frame1 = tk.Frame(root)
frame1.pack()

tree = ttk.Treeview(frame1)
tree["columns"] = ("C1", "C2")
tree.column("#0", width=500, minwidth=400, stretch=tk.NO)
tree.column("C1", width=200, minwidth=200, stretch=tk.NO)
tree.column("C2", width=200, minwidth=200, stretch=tk.NO)
tree.heading("#0", text="Name", anchor=tk.W)
tree.heading("C1", text="Type", anchor=tk.W)
tree.heading("C2", text="Index", anchor=tk.W)

t = {}

for i in range(5):
t[i] = tree.insert("", i, text="Example " + str(i), values=("val1", "val2"))
tree.pack(expand=True, fill="both")

def create():
for i, val in enumerate(headings):
if i == 0:
tree2.column("#0", width=200, minwidth=200, stretch=tk.NO)
elif i == 1:
tree2["columns"] = ("C1", )
tree2.column("C1", width=800, minwidth=200, stretch=tk.NO)
else:
tree2["columns"] = tree2["columns"] + ("C" + str(i), )
tree2.column("C" + str(i), width=800, minwidth=200, stretch=tk.NO)

for i, val in enumerate(headings):
if i == 0:
tree2.heading("#0", text=val, anchor=tk.W)
elif i == 1:
tree2.heading("C1", text=val, anchor=tk.W)
else:
tree2.heading("C" + str(i), text=val, anchor=tk.W)

btn1 = tk.Button(frame1, text="Add", command=create)
btn1.pack(side="top")

tree2 = ttk.Treeview(frame1)

tree2.pack(expand=True, fill="both")

root.mainloop()

PyQt5 treeview with multiple columns how to get info into other columns

Since the standard item model is instantiated with 3 columns the rows can be appended with an item or item(s) so

parent1.appendRow([item1,item2,item3])

will populate all 3 columns, to populate a second column for the parent simply set the item in the model at the desired index

model.setItem(row,col,item)

Sample Image

How to filter columns in Tk treeview based on column value?

I didn't know which column you want to populate with values so I left it as a Combobox selector, Selecting field will populate the values Combobox.

from tkinter import *
from tkinter import ttk, filedialog
import pandas as pd

win = Tk()
win.geometry("700x350")
style = ttk.Style()
style.theme_use('clam')

frame = Frame(win)
frame.pack(pady=20)
filename = None

def open_file():
global df
global filename
filename = filedialog.askopenfilename(title="Open a File")

if filename:
try:
filename = r"{}".format(filename)
df = pd.read_excel(filename)
except ValueError:
label.config(text="File could not be opened")
except FileNotFoundError:
label.config(text="File Not Found")

def create_column_combox(): # Creating the select field and value Combobox
def populate_values_combo(e=None): # populate values combobox with unique column values
state = 'normal'
combo_values = []
if col_combo.get() == 'All': # if option All selected
state = 'disabled' # disabling the option to select values
select_value()
else:
combo_values = ['All'] + df[col_combo.get()].unique().tolist() # adding option All to values

col_values_combo.config(values=combo_values, state=state)

def select_value(e=None): # cleaning tree and populating with df values
tree.delete(*tree.get_children())
if col_combo.get() == 'All' or col_values_combo.get() == 'All': # if All selected as column or value
col_values_combo.current(0) # setting values to All
[tree.insert("", "end", text=str(index), values=list(row)) for index, row in df.iterrows()]
else:
for index, row in df.loc[df[col_combo.get()].eq(col_values_combo.get())].iterrows():
tree.insert("", "end", text=index, values=list(row))

columns = ['All'] + list(df.columns) # Adding option All to list of columns
column_combo_label = Label(frame, text='Select field')
column_combo_label.pack()
col_combo = ttk.Combobox(frame, values=columns, state='readonly')
col_combo.pack()
col_combo.bind("<<ComboboxSelected>>", populate_values_combo)
volume_combo_label = Label(frame, text='Select value')
volume_combo_label.pack()
col_values_combo = ttk.Combobox(frame, state='disabled')
col_values_combo.pack()
col_values_combo.bind("<<ComboboxSelected>>", select_value)

create_column_combox()
clear_treeview()

tree["column"] = list(df.columns)
tree["show"] = "headings"

for col in tree["column"]:
tree.heading(col, text=col)
tree.column("#1", anchor="center", width=200)
tree.column("#2", anchor="center", width=150)
tree.column("#3", anchor="center", width=150)
tree.column("#4", anchor="center", width=150)

df_rows = df.to_numpy().tolist()
for row in df_rows:
tree.insert("", "end", values=row)

tree.pack()

def clear_treeview():
tree.delete(*tree.get_children())

tree = ttk.Treeview(frame)

m = Menu(win)
win.config(menu=m)

file_menu = Menu(m, tearoff=False)
m.add_cascade(label="Menu", menu=file_menu)
file_menu.add_command(label="Open Spreadsheet", command=open_file)

label = Label(win, text='')
label.pack(pady=20)

win.mainloop()


Related Topics



Leave a reply



Submit