简体   繁体   中英

What is a better Tkinter geometry manager than .grid()

My Complaint

I am currently delving deeper than "ever" before into the Tkinter GUI, and I have found the .grid() geometry manager to be inadequate for several reasons:

  1. The plots are based on the largest widget within them - this relativity leads to inaccuracy.

  2. In Windows 7, on Python 2.7.3, the program does not seem to heed my row numbers, instead preferring to use the order of the widgets.

My Code

I am currently working on a really basic text editor, and I want to have multiple buttons on the top of the frame. I have been unable to do this as my widgets are placed either to the far left or right of the massive textbox that dominates the center of the screen.

========Class __init__ Stuff============
def widget(self):#Place widgets here

    #Save Button
    self.saveButton = Button (self, text = "Save", command = self.saveMe)
    self.saveButton.grid(column = 0, row = 0, sticky = W)

    #Open Button
    self.openButton = Button (self, text = "Open", command = self.openMe)
    self.openButton.grid(column = 0, row = 1, sticky = W)
    #Area where you write 
    self.text = Text (self, width = (root.winfo_screenwidth() - 20),
                      height = (root.winfo_screenheight() - 10))
    self.text.grid(row = 2)
==============Mainloop/Command Stuff============

My Question

Is there another way to use the .grid() geometry manager in a way that is more accurate, or should I be using another function altogether?

Thanks!

There are 3 geometry managers that you have available to you -- grid , pack and place . The third is the most general, but also very difficult to use. I prefer grid . Note that you can place widgets inside of other widgets -- Or you can specify columnspan . So, if you want to get the following layout:

  -------------------------
  |    Button1  | Button2 |
  -------------------------
  |     Big Widget        |
  -------------------------

there are 2 canonical ways to do it using .grid . The first method is columnspan :

import Tkinter as Tk
root = Tk.Tk()
b1 = Tk.Button(root,text="Button1")
b1.grid(row=0,column=0)
b2 = Tk.Button(root,text="Button2")
b2.grid(row=0,column=1)
big_widget = Tk.Canvas(root)
big_widget.grid(row=1,column=0,columnspan=2)

*note that there is a completely analogous rowspan option as well.

The second method is to use a Frame to hold your buttons:

import Tkinter as Tk
root = Tk.Tk()
f = Tk.Frame(root)
f.grid(row=0,column=0)
#place buttons on the *frame*
b1 = Tk.Button(f,text="Button1")
b1.grid(row=0,column=0)
b2 = Tk.Button(f,text="Button2")
b2.grid(row=0,column=1)

big_widget = Tk.Canvas(root)
big_widget.grid(row=1,column=0)  #don't need columnspan any more.

This method is SUPER useful for creating complex layouts -- I don't know how you could create a complex layout without using Frame objects like this ...

The "best" geometry manager depends on what you want to do, no geometry manager is best for all situations. For what you're trying to do in this specific case, pack is probably the better choice. Also note that there may be more than one "best" in an application. It's quite normal to use both grid and pack in different parts of your applications. I rarely ever create a GUI where I don't use both grid and pack. And rarely, I also use place.

pack excels at placing things in single rows and single columns. Toolbars, for example, are a perfect case for using pack since you want all your buttons aligned to the left. grid, as it name implies, is best when you want fixed rows and columns. place is useful in those rare cases where neither grid nor pack will do, since it allows you to place widgets at precise fixed or relative locations.

My advice is to divide your application widgets into groups, and use the right geometry manager for each group. In your case you have two logical groups: the toolbar along the top, and a text-widget-and-scrollbar combination in the bottom. So, start with two frames. Since the toolbar is on top and the text widget is below, pack works best.

toolbar = tk.Frame(...)
main = tk.Frame(...)
toolbar.pack(side="top", fill="x", expand=False)
main.pack(side="bottom", fill="both", expand=True)

The above now gives you two areas that are easy to modify independently.

For the toolbar, pack is again most natural. In this case, however, you want the buttons along the left instead of along the top or bottom:

b1 = tk.Button(toolbar, ...)
b2 = tk.Button(toolbar, ...)
b1.pack(side="left")
b2.pack(side="left")
...

Finally, the bottom area. Your example code doesn't show scrollbars, but I assume at some point you'll want to add them. grid works well if you're using both horizontal and vertical scrollbars. I would lay them out like this:

hsb = tk.Scrollbar(main, ..., orient="horizontal", ...)
vsb = tk.Scrollbar(main, ..., orient="vertical", ...)
text = tk.Text(main, ...)
vsb.grid(row=0, column=1, sticky="ns")
hsb.grid(row=1, column=0, sticky="ew")
text.grid(row=0, column=0, sticky="nsew")
main.grid_rowconfigure(0, weight=1)
main.grid_columnconfigure(0, weight=1)

That last part is important -- you always need to give a weight to at least one row and one column. This tells grid which rows and columns should grow and shrink when the window is resized.

I find the place manager the most intuitive and accurate of all three, because of the possibility to specify combinations of absolute and relative lengths for both positions and size:

mywidget.place(x=..., y=..., width=..., height=...) # absolute
mywidget.place(relx=..., rely=..., relwidth=..., relheight=...) # relative

# combination
pad_left=5
pad_right=5
pad_top=5
pad_bottom=5
mywidget.place(relx=0, x=pad_left, rely=0, y=pad_top,\
       relwidth=1, width=-pad_left-pad_right,\
       relheight=1, height=-pad_top-pad_bottom, anchor="e") # use the whole space except some padding

For some reason, the .place manager doesn't seem very liked, but I find it makes a particulary good job to define how your widgets should move around and resize when the parent window is resized.

To ease the construction of complex windows, I sometimes use a function (to kind of emulate a grid behaviour I suppose) to aid in the vertical placement, such as:

def offset_y(line_no):
    if line_no == 0:
        return 0
    else:
        return height_label + (line_no - 1)*height_button

mylabel.place(..., y=offset_y(0))
mybtn1.place(..., y=offset_y(1))
etc.

to pack vertically a Label and then a series of buttons. (Of course, the .pack manager could be used in this case, but the reason I prefer the .place manager is for understanding more accurately how things will move around when the window is resized).

Instead of row,column....etc.,you can use coordinates.

It may be easier than the others.

EXAMPLE:

from Tkinter import*
root=Tk()
Button(root,text="HI").grid(padx=100,pady=50)
root.mainloop()

Try it youself by changing padx and pady values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM