Hector
Hector

Reputation: 285

How can I insert a treeview in a child window in Tkinter

Dear All I have been trying to insert a treeview in a child window using Tkinter but it has been quite complicated since I don't know how to put them together. Thank you very much in advance! Héctor

This is what I have done so far:

import Tkinter
from Tkinter import *
import tkFont
import ttk 
root= Tk()

class McListBox(object):

    def __init__(self):
        self.tree = None
        self._setup_widgets()
        self._build_tree()

    def _setup_widgets(self):
        s = """
        """
        msg = ttk.Label(wraplength="4i", justify="right", anchor="n",
            padding=(6, 6, 6, 6, 6 ,6), text=s)
        msg.pack(fill='x')
        container = ttk.Frame()
        container.pack(fill='both', expand=True)
        self.tree = ttk.Treeview(columns=element_header, show="headings")
        vsb = ttk.Scrollbar(orient="vertical", command=self.tree.yview)
        hsb = ttk.Scrollbar(orient="horizontal", command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
        self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
        vsb.grid(column=1, row=0, sticky='ns', in_=container)
        hsb.grid(column=0, row=1, sticky='ew', in_=container)
        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(0, weight=1)

    def _build_tree(self):
        for col in element_header:
            self.tree.heading(col, text=col.title(),
                command=lambda c=col: sortby(self.tree, c, 0))
            self.tree.column(col, width=tkFont.Font().measure(col.title()))
        for item in element_list:
            self.tree.insert('', 'end', values=item)
            for ix, val in enumerate(item):
                col_w = tkFont.Font().measure(val)
                if self.tree.column(element_header[ix], width=None) < col_w:
                    self.tree.column(element_header[ix], width=col_w)
def isnumeric(s):
    for c in s:
        if c in "0000123456789000-.":
            numeric = True
        else:
            return False
    return numeric

def change_numeric(data):
    new_data = []
    if isnumeric(data[0][0]):
        for child, col in data:
            new_data.append((float(child), col))
        return new_data
    return data
def sortby(tree, col, descending):
    data = [(tree.set(child, col), child) for child in tree.get_children('')]
    data =  change_numeric(data)
    data.sort(reverse=descending)
    for ix, item in enumerate(data):
        tree.move(item[1], '', ix)
    tree.heading(col,
        command=lambda col=col: sortby(tree, col, int(not descending)))
element_header = ["Device", "Type", "LETs Threshold (L0)"]
element_list =  [('93L422', 'Bipolar', '0.6')]

mc_listbox = McListBox()

def Child_Window():
    win2 = Toplevel()
    message = "This is the child window"
    Label(win2, text=message).pack()
    Button(win2, text='OK', command=win2.destroy).pack()
Button(root, text='Bring up Message', command=Child_Window).pack()
root.mainloop()

Upvotes: 1

Views: 4766

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 385820

The solution is simple: every widget takes another widget as its first parameter, and this parameter defines the parent of the new widget. The new widget by default will appear in the patent widget when you use pack, grid or place. That's really all you need to know.

So, if you want self.tree to be in win2, win2 must* be given to the treeview constructor as the first argument.

Note that you cannot create a widget in one window and then move it to another (where "window" is defined as "an instance of Tk or Toplevel"). A window and its parent must share the same top-most widget as an ancestor.

* "Must" is a bit too strong, because you can have widgets appear inside widgets other than their immediate parent. I don't think that is relevant to what you are trying to do here, so I won't elaborate to avoid confusion.

Upvotes: 1

Marcin Kowalczyk
Marcin Kowalczyk

Reputation: 649

Treeview should be constructed with parent window as a first argument. I changed Child_Window method so that it displays very simple tree. You can now easily adapt your tree to my example. I would also suggest refactoring code in a way that all methods are contained in McListBox class. I used pack geometry manager instead of grid manager. Here is my example:

def Child_Window():
    win2 = Toplevel()
    message = "This is the child window"
    Label(win2, text=message).pack()
    element_header=['1st','2nd','3rd']
    treeScroll = ttk.Scrollbar(win2)
    treeScroll.pack(side=RIGHT, fill=Y)
    tree = ttk.Treeview(win2,columns=element_header, show="headings", yscrollcommand = treeScroll)
    tree.heading("1st", text="1st")
    tree.pack(side=LEFT, fill=BOTH)
    treeScroll.config(command=tree.yview)

After discussion with Hector I decided to paste all modified class which should work like required. By the way I refactored it so that all functions and variables are members of a class. Works fine with Python 2.7.5.

import Tkinter 
from Tkinter import *
import tkFont
import ttk

class ModifiedMcListBox(object):

    def __init__(self):
        self.root= Tk()
        self.tree = None
        self.element_header = ["Device", "Type", "LETs Threshold (L0)"]
        self.element_list =  [('93L422', 'Bipolar', '0.6')]
        self._setup_widgets()
        self._build_tree()
        self.root.mainloop()

    def _setup_widgets(self):
        s = """
        """
        msg = ttk.Label(wraplength="4i", justify="right", anchor="n",
            padding=(6, 6, 6, 6, 6 ,6), text=s)
        msg.pack(fill='x')
        container = ttk.Frame()
        container.pack(fill='both', expand=True)
        self.tree = ttk.Treeview(columns=self.element_header, show="headings")
        vsb = ttk.Scrollbar(orient="vertical", command=self.tree.yview)
        hsb = ttk.Scrollbar(orient="horizontal", command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
        self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
        vsb.grid(column=1, row=0, sticky='ns', in_=container)
        hsb.grid(column=0, row=1, sticky='ew', in_=container)
        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(0, weight=1)
        Button(self.root, text='Bring up Message', command=self.Child_Window).pack()

    def _build_tree(self):
        for col in self.element_header:
            self.tree.heading(col, text=col.title(),
                command=lambda c=col: self.sortby(self.tree, c, 0))
            self.tree.column(col, width=tkFont.Font().measure(col.title()))
        for item in self.element_list:
            self.tree.insert('', 'end', values=item)
            for ix, val in enumerate(item):
                col_w = tkFont.Font().measure(val)
                if self.tree.column(self.element_header[ix], width=None) < col_w:
                    self.tree.column(self.element_header[ix], width=col_w)

    def isnumeric(self,s):
        for c in s:
            if c in "0000123456789000-.":
                numeric = True
            else:
                return False
        return numeric

    def change_numeric(self,data):
        new_data = []
        if self.isnumeric(data[0][0]):
            for child, col in data:
                new_data.append((float(child), col))
            return new_data
        return data

    def sortby(self,tree, col, descending):
        data = [(tree.set(child, col), child) for child in tree.get_children('')]
        data = self.change_numeric(data)
        data.sort(reverse=descending)
        for ix, item in enumerate(data):
            tree.move(item[1], '', ix)
        tree.heading(col,
            command=lambda col=col: sortby(tree, col, int(not descending)))

    def Child_Window(self):
        win2 = Toplevel()
        message = "This is the child window"
        Label(win2, text=message).pack()
        new_element_header=['1st','2nd','3rd']
        treeScroll = ttk.Scrollbar(win2)
        treeScroll.pack(side=RIGHT, fill=Y)
        tree = ttk.Treeview(win2,columns=new_element_header, show="headings", yscrollcommand = treeScroll)
        tree.heading("1st", text="1st")
        tree.heading("2nd", text="2nd")
        tree.heading("3rd", text="3rd")
        tree.pack(side=LEFT, fill=BOTH)
        treeScroll.config(command=tree.yview)

mc_listbox = ModifiedMcListBox()

Upvotes: 2

Related Questions