user4085386
user4085386

Reputation:

How to get an all sticky grid of Treeview and Scrollbar in Python Tkinter?

What I want in Tkinter in Python 2.7 is the following grid layout:

Grid Layout

However once, I start using the grid() functions instead of pack() functions, nothing is showing on running the script. The following is what I am stuck with:

import Tkinter, ttk

class App(Tkinter.Frame):
    def __init__(self,parent):
        Tkinter.Frame.__init__(self, parent, relief=Tkinter.SUNKEN, bd=2)
        self.parent = parent        
        self.grid(row=0, column=0, sticky="nsew")
        self.menubar = Tkinter.Menu(self)
        try:
            self.parent.config(menu=self.menubar)
        except AttributeError:
            self.tk.call(self.parent, "config", "-menu", self.menubar)    

        self.tree = ttk.Treeview(self.parent)
        self.tree.grid(row=0, column=0, sticky="nsew")

        self.yscrollbar = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
        self.yscrollbar.grid(row=0, column=1, sticky='nse')
        self.tree.configure(yscrollcommand=self.yscrollbar.set)
        self.yscrollbar.configure(command=self.tree.yview)

if __name__ == "__main__":
    root = Tkinter.Tk()
    root.title("MyApp")
    app = App(root)
    app.pack()
    app.mainloop()

Any help will be highly appreciated.

Upvotes: 0

Views: 9672

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 385880

You have several problems that are affecting your layout.

First, some of the widgets inside App use self as the parent, some use self.parent. They should all use self in this particular case. So, the first thing to do is change the parent option of the Treeview to self.

self.tree = ttk.Treeview(self)

Second, since your main code is calling app.pack(), you shouldn't be calling self.grid. Remove the line `self.grid(row=0, column=0, sticky="nsew"). It's redundant.

Third, you are using very unusual code to add the menubar. You need to configure the menu of the root window. There's no need to put this in a try/except block, and there's no reason to use self.tk.call. Simply do this:

self.parent.configure(menu=self.menubar)

This assumes that self.parent is indeed the root window. If you don't want to force that assumption you can use winfo_toplevel() which will always return the top-most window:

self.parent.winfo_toplevel().configure(menu=self.menubar)

Finally, since you are using grid, you need to give at least one row and one column a "weight" so tkinter knows how to allocate extra space (such as when the user resizes a window).

In your case you want to give all of the weight to row and column 0 (zero), since that's where you've placed the widget which needs the most space:

def __init__(self, parent):
    ...
    self.grid_rowconfigure(0, weight=1)
    self.grid_columnconfigure(0, weight=1)

Note: you'll also want to make sure when you call app.pack() that you give it parameters that makes it fill any extra space, too. Otherwise the tree will fill "app", but "app" would not fill the window.

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

Here is a fully working example with all of those changes. I grouped the main layout code together since that makes the code easier to visualize and easier to maintain:

import Tkinter, ttk

class App(Tkinter.Frame):
    def __init__(self,parent):
        Tkinter.Frame.__init__(self, parent, relief=Tkinter.SUNKEN, bd=2)
        self.parent = parent        

        self.menubar = Tkinter.Menu(self)
        self.parent.winfo_toplevel().configure(menu=self.menubar)

        self.tree = ttk.Treeview(self)

        self.yscrollbar = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
        self.tree.configure(yscrollcommand=self.yscrollbar.set)

        self.tree.grid(row=0, column=0, sticky="nsew")
        self.yscrollbar.grid(row=0, column=1, sticky='nse')
        self.yscrollbar.configure(command=self.tree.yview)

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

if __name__ == "__main__":
    root = Tkinter.Tk()
    root.title("MyApp")
    app = App(root)
    app.pack(fill="both", expand=True)
    app.mainloop()

Your question mentioned grid, but in this case you could save a few lines of code by using pack. pack excels in layouts like this, where your gui is aligned top-to-bottom and/or left-to-right. All you need to do is replace the last five lines (the calls to grid, grid_rowconfigureandgrid_columnconfigure`) with these two lines:

self.yscrollbar.pack(side="right", fill="y")
self.tree.pack(side="left", fill="both", expand=True)

Upvotes: 9

Related Questions