How can I stack buttons rather than queue them in Tkinter? - python

At the moment I have 3 basic buttons being displayed:
from tkinter import *
root = Tk()
topFrame = Frame(root)
topFrame.pack(side=TOP)
leftFrame = Frame(root)
leftFrame.pack(side=LEFT)
botFrame = Frame(root)
botFrame.pack(side=BOTTOM)
button1 = Button(leftFrame, text="Button 1", fg="Black")
button2 = Button(leftFrame, text="Button 2", fg="Black")
button3 = Button(leftFrame, text="Button 3", fg="Black")
button1.pack(side=LEFT)
button2.pack(side=LEFT)
button3.pack(side=LEFT)
root.mainloop()
The 3 buttons at the moment will stick to the left frame on the window, however then will queue next to each other rather than stack one on top of the other, how do i fix this?

You're explicitly telling them to be side-by-side with side=LEFT. You want side=TOP so that they are placed at the top of the empty space in the frame.
button1.pack(side=TOP)
button2.pack(side=TOP)
button3.pack(side=TOP)
When you use pack, the values TOP, LEFT, RIGHT and BOTTOM tell the widget which side of the remaining space they should occupy. The first time you use LEFT it will reserve the left side of the whole frame for the widget. The next time you use LEFT, that refers to the space remaining in the widget excluding the left edge since that already has a widget in it. The net effect is that LEFT causes widgets to be arranged left-to-right, RIGHT arranges them right-to-left, and so on.

Explore the grid function. Change your pack statements to
button1.grid(row=0,column=0)
button2.grid(row=1,column=0)
button3.grid(row=2,column=0)

Related

How to change location of the buttons on tkinter pack

My buttons are shown in the order of "previous image" and "next image" on the left side and "exit" and "next set" on the right side. I would like to have the next set button still be on the right side overall but be to the left of the exit button. So I would like the "next set" and "exit" buttons to switch spots.
I was also wondering if it was possible to show the exit button on the bottom right of the screen. The second image I provided shows what happens whenever I use .pack(side=tk.BOTTOM), as the exit button doesn't really go underneath the whole image.
fram = tk.Frame(self)
tk.Button(fram, text="Previous Image", bg="green", command=self.prev).pack(side=tk.LEFT)
tk.Button(fram, text=" Next Image ", command=self.next).pack(side=tk.LEFT)
tk.Button(fram, text=" Next set ", command=self._load_dataset).pack(side=tk.RIGHT)
tk.Button(fram, text=" Exit ", command=self.destroy, fg = "grey").pack(side=tk.RIGHT)
tk.Label(fram, textvariable=self.clickStatus, font='Helvetica 18 bold').pack(side=tk.RIGHT)
# inside or outside
fram.pack(side=tk.TOP, fill=tk.BOTH)
enter image description here
So I would like the "next set" and "exit" buttons to switch spots.
The packer works by placing widgets along one of the sides of the unallocated space in the master widget. Order matters, since each widget causes the unallocated space to change. For example, if you want a widget on the far right side of a frame, you should pack it on the right before you pack any other widgets.
In your case, the solution is as simple as changing the order in which you call pack.
tk.Button(fram, text=" Exit ", command=self.destroy, fg = "grey").pack(side=tk.RIGHT)
tk.Button(fram, text=" Next set ", command=self._load_dataset).pack(side=tk.RIGHT)
I was also wondering if it was possible to show the exit button on the bottom right of the screen.
Yes, it's possible. The simplest solution is to divide your window into three areas: a top set of buttons, a buttom set of buttons, and the image area.
For example, the following code creates three frames for these three areas, and uses pack to arrange them. Once you do these, it becomes trivial to add buttons to the top and bottom frames in whatever order you want.
top_frame = tk.Frame(self)
bottom_frame = tk.Frame(self)
image_frame = tk.Frame(self)
top_frame.pack(side="top", fill="x")
bottom_frame.pack(side="bottom", fill="x")
image_frame.pack(side="top", fill="both", expand=True)
exit_button = tk.Button(bottom_frame, text="Exit", ...)
exit_button.pack(side="right")
previous_button = tk.Button(top_frame, text="Previous Image", ...)
next_button = tk.Button(top_frame, text="Next Image", ...)
next_set_button = tk.Button(top_frame, text="Next Set", ...)
previous_button.pack(side="left")
next_button.pack(side="left")
next_set_button.pack(side="right")
Another way to do this is to make all of the buttons a child of self which makes it easy to define them all in the same block of code, and use the in_ parameter to specify where they go.
exit_button = tk.Button(self, text="Exit", ...)
previous_button = tk.Button(self, text="Previous Image", ...)
next_button = tk.Button(self, text="Next Image", ...)
next_set_button = tk.Button(self, text="Next Set", ...)
exit_button.pack(in_=bottom_frame, side="right")
previous_button.pack(in_=top_frame, side="left")
next_button.pack(in_=top_frame, side="left")
next_set_button.pack(in_=top_frame, side="right")
For the definitive description of how the packer works, see Packer Algorithm in the official tk documentation.
An alternative to pack is to use the button's grid attributes to place them in the frame.
I have used separate frames for placing buttons in my app:
frame_buttons = ttk.Frame(tab_details)
frame_buttons.grid(column = 0, row = 3, sticky = tk.EW)
frame_buttons.columnconfigure(0, weight = 1)
frame_buttons.columnconfigure(1, weight = 1)
button_process = ttk.Button(frame_buttons, text = 'Process', command = text_to_xml)
button_process.grid(column = 0, row = 0) # in the centre of the left column
button_clear = ttk.Button(frame_buttons,text = 'Clear', command = clear_entries)
button_clear.grid(column = 1, row = 0) # in the centre of the right column
The grid is described here in the TkDocs site:
TkDocs

Tkinter - Grid elements next to each other

I'm trying to make some UI in python with tkinter.
This is a sample of the code I'm using:
root = Tk()
root.geometry("1000x700x0x0")
canvas = Canvas(root, width = 700, height = 700, bg ='white').grid(row = 0, column = 0)
button1 = Button(root, text = "w/e", command = w/e).grid(row = 0, column = 1)
button2 = Button(root, text = "w/e", command = w/e).grid(row = 1, column = 1)
This is what i'm getting:
and this is what I want:
Any help on how can I do it?
Thanks!
Since your GUI seems to have two logical groups of widgets, I would organize it as such. Start by placing the canvas on the left and a frame on the right. You can use pack, place, grid, or a paned window to manage them. For a left-to-right orientation, pack is a good choice due to its simplicity
Note that you don't have to do it this way, but experience has taught me it makes layout problems much easier to solve.
In the following example I set expand to False for the button frame, which means that the canvas will grow and shrink when the user resizes (because it has expand=True), but the buttons will only take up exactly as much space as they need.
canvas = Canvas(root, ...)
buttonframe = Frame(root, ...)
canvas.pack(side="left", fill="both", expand=True)
buttonframe.pack(side="right", fill="both", expand=False)
Next, you can put all of the buttons in the right side without having to worry how their placement might affect objects on the left.
The important thing to remember when using grid is that you should designate at least one row and at least one column to be given any extra space. This can be a row and/or column that contains widgets, or it can be an empty row and column on an edge.
button1 = Button(buttonframe, ...)
button2 = Button(buttonframe, ...)
button3 = Button(buttonframe, ...)
...
button1.grid(row=0, column=0)
button2.grid(row=0, column=1)
button3.grid(row=1, column=0)
...
buttonframe.grid_rowconfigure(100, weight=1)
buttonframe.grid_columnconfigure(2, weight=1)
note: if you need to keep a reference to a widget, you must create the widget and call grid (or pack or place) on two separate lines. This is because Button(...).grid(...) returns the value of the last function call, and grid(...) returns None

Tkinter: grid or pack inside a grid?

I am building a GUI for a software and want to achieve this:
######################################
# | some title #
# menu upper |----------------------#
# | #
# | CANVAS #
# menu lower | #
# | #
#------------------------------------#
# statusbar #
######################################
Menu upper has some high level functionality, menu lower is changing in dependency of user input. Statusbar changes its contents often.
Unfortunately, Tkinter refuses to work.
Using the grid layout manager I were unable to create a stable design and adding content like labels and buttons to the menu on the left side:
self.root = tk.Tk()
self.root.resizable(width=0, height=0)
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="ns")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_upper.grid(row=0, column=0)
# this label breaks the design
#self.test = tk.Label(self.menu_left_upper, text="test")
#self.test.pack()
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.menu_left_lower.grid(row=1, column=0)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title_frame.grid(row=0, column=1, sticky="we")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
self.root.mainloop()
This design worked without contents in the menu on the left side. Whenever I added something in self.menu_left_upper or self.menu_left_lower, like the test label, my design got broke: the menu frames disappeared.
Additionally, even with columnspan, I had to remove the statusbar, because when it was added the menus on the left disappeared, again.
Using pack layout manager I got this:
######################################
# | some title #
# |----------------------#
# menu upper | #
# | CANVAS #
# | #
# menu lower | #
# |----------------------#
# | statusbar #
######################################
Since I wanted the menu frame on the left to consume the full y-space I made it grow with pack(side="left", expand=True, fill="both"), but this setup always denies the statusbar to go for the full width.
Besides, the pure pack manager code looks "ugly". I think a design with a grid manager is "clearer". Therefore I thought a grid or a pack layout inside a grid layout would be nice?
Can anyone help? I am stuck in the GUI-hell :/
The key to doing layout is to be methodical, and to use the right tool
for the job. It also means that sometimes you need to be creative.
That means using grid when laying things out in a
grid, and using pack when laying things out top-to-bottom or
left-to-right.
The other key is to group your layout code together. It's much, much
easier to visualize and modify the layout when it's all in one block
of code.
In your case you seem to have three or four distinct areas, depending
on how you count. If you want to use grid, it will be easiest to
combine "menu upper" and "menu lower" into a frame, and treat that
whole frame as a table cell. It looks like you're already doing that,
which is good.
So, let's start with those main areas:
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
# you don't have a status bar in the example code, but if you
# did, this is where you would put it
# self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
Any time you use grid, you need to give at least one row and one
column a positive weight so that tkinter knows where to use any
unallocated space. Usually there is one widget that is the "main"
widget. In this case it's the canvas. You want to make sure that the
row and column for the canvas has a weight of 1 (one):
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
note: using pack instead of grid would save you two lines of code, since pack doesn't require you to set weights the way grid does.
Next, we need to solve the problem of the menu areas. By default,
frames shrink to fit their contents, which is why adding the label
broke your layout. You weren't telling tkinter what to do with extra space, so the frames shrunk to fit, and extra space went unused.
Since you want "menu_upper" and "menu_lower" to
each share 50% of that area, pack is the simplest solution. You can
use grid, but it requires more lines of code to add the row and column weights.
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
Here is a functioning version, with statusbar. Notice how it behaves exactly as it should when you resize the window:
import Tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.test = tk.Label(self.menu_left_upper, text="test")
self.test.pack()
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(self.root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.root.mainloop()
Example()
On an unrelated note: I would strongly encourage you to not remove the ability for the user to resize the window. They know better than you what their requirements are. If you use grid and pack properly, the GUI will resize perfectly.
Adding the following code right before self.root.mainloop() achieves what you're looking for
self.some_status = tk.Label(self.root, text="status bar", bg="#dfdfdf")
self.some_status.grid(row=3, column=0, columnspan=2, sticky="we")
By putting in the line:
menu_left_upper.grid_propagate(False)
In between your menu_left_upper Frame and menu_left_upper.mainloop()
This works as:
By default, a container widget expands or collapses to be just big enough to hold its contents. Thus, when you call pack, it causes the frame to shrink. This feature is called geometry propagation.
For the vast majority of applications, this is the behavior you want. For those rare times when you want to explicitly set the size of a container you can turn this feature off. To turn it off, call either pack_propagate or grid_propagate on the container (depending on whether you're using grid or pack on that container), giving it a value of False.
See link to another question where this came from
To get your status bar just implement another frame and grid method:
status_bar_frame = Frame(root, bg="#dfdfdf")
status_bar_frame.grid(row=3, column=0, columnspan=2, sticky="we")
status_bar = Label(status_bar_frame, text="status bar", bg="#dfdfdf")
status_bar.pack()
Then your plan works.
Hope it helps :)
PS. Also why all the self attributes?
EDIT:
TO work you need to do:
menu_left_upper = Frame(menu_left, width=225, height=225, bg="red")
menu_left_upper.grid_propagate(False)
menu_left_upper.grid(row=0, column=0)
# this label breaks the design
test = Label(menu_left_upper, text="test", bg='red')
test.grid()
My result

How can I put 2 buttons with images Vertically in a frame

Im using tkinter and trying to create a toolbar located on the left side going vertically, I already have a toolbar on the top of the frame filled in going horizontaly however can't figure out how to make a second one on the left, with all the buttons.
This is the code that I have:
infobar = Frame(master, bg="#ecf0f1", bd=1, relief=GROOVE)
infobar.pack(side=LEFT, fill=BOTH, expand=None)
infobarr = Label(toolbar, bg="#ecf0f1", text=' ')
infobarr.pack(side=LEFT, fill=Y)
poundToKgButton = Button(infobar, highlightbackground="#ecf0f1", image=eimg20, relief=FLAT, command=self.scale)
poundToKgButton.image = eimg20
createToolTip(poundToKgButton, "Conversion - Pound To KG")
poundToKgButton.pack(side=LEFT)
calculatorButton = Button(infobar, highlightbackground="#ecf0f1", image=eimg19, bd=1, relief=FLAT, command=self.calc)
calculatorButton.image = eimg19
createToolTip(calculatorButton, "Calculator")
calculatorButton.pack(side=LEFT, anchor="sw")
Use side=TOP if you want things stacked vertically. The containing widget has empty space. When you use pack, you're telling tkinter which side of that empty space to put the widget.
Here is a good example of exactly what happens when you call pack: http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26

Tkinter Grid: How to position widgets so they are not stuck together

I'm trying to create two Label widgets that are in the top left and top right corners of my test UI. The problem is that the widgets are stuck together and I'd like there to be space between them.
In my research, I came across suggestions to use the sticky, padx, and pady options. But no matter what arguments I pass to .grid() , I can't seem to be able to create space between my widgets. I understand that regardless of the number of columns and rows between two widgets, if said rows/columns are empty, then its as if they didn't exist and the widgets appear glued together.
Using the .grid() method, how can I position widgets so that they aren't stuck together?
Here is my code so far:
#!/usr/bin/python
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent)
self.main_container.grid(row=0, rowspan=2, column=0, columnspan=4)
self.top_frame = Frame(self.main_container)
self.top_frame.grid(row=0, column=0, columnspan=4)
self.top_left = Frame(self.top_frame)
self.top_left.grid(row=0, column=0, columnspan=2)
self.top_right = Frame(self.top_frame)
self.top_right.grid(row=0, column=2, columnspan=2)
self.bottom_frame = Frame(self.main_container)
self.bottom_frame.grid(row=2, column=0, columnspan=4)
self.top_left_label = Label(self.top_left, text="Top Left")
self.top_left_label.grid(row=0, column=0, sticky='W', padx=2, pady=2)
self.top_right_label = Label(self.top_right, text="Top Right")
self.top_right_label.grid(row=0, column=4, sticky='E', padx=2, pady=2)
self.text_box = Text(self.bottom_frame, height=5, width=40)
self.text_box.grid(row=0, column=0)
root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
~~Update~~
I tried the following but it did not work:
self.top_left = Frame(self.top_frame)
self.top_left.grid(row=0, column=0, columnspan=2)
for c in range(2):
self.top_left.columnconfigure(c, weight=2)
self.top_right = Frame(self.top_frame)
self.top_right.grid(row=0, column=2, columnspan=2)
for c in range(2):
self.top_right.columnconfigure(c, weight=2)
You need to use grid_columnconfigure to give the columns in the middle some weight. With more weight than the other columns, they will expand and contract to fill any extra space you have. However, in your case there's really no need to use grid. Everything in your GUI is aligned along particular sides for which pack is a more natural fit.
I'll show how to use both pack and grid, starting with pack since it's the easiest. Even if you insist on using grid, read through the next section to understand how to break one big layout problem into many smaller layout problems.
Divide and conquer
The best way to do layout in Tkinter is "divide and conquer". Start with the outermost widgets and get them exactly the way you want. Then, tackle each of these one at a time.
In your case you have one outermost widget - the main container. Since it is the only widget in the window, pack is the simplest way to get it to fill the whole container. You can use grid as well, but it requires a little extra work:
self.main_container = Frame(parent. background="bisque")
self.main_container.pack(side="top", fill="both", expand=True)
It helps to temporarily give your frames distinctive colors so you can visualize them as you develop. Add just the above code to your __init__, run your app, then resize the window to see that the main frame properly grows and shrinks.
Next, you have two frames -- top_frame and bottom_frame. By their names and judging by how you've attempted to use grid, I assume they should fill the GUI in the x direction. Also, I'm guessing the top frame is some sort of toolbar, and the bottom frame is the real "main" part of your GUI. Thus, let's make the bottom widget take up all the extra space.
Since these are stacked on top of each other, pack is again the natural choice. Add the following code -- and only the following code -- to make sure these areas occupy the parts of the window that you expect, and that they have the proper resize behavior.
self.top_frame = Frame(self.main_container, background="green")
self.bottom_frame = Frame(self.main_container, background="yellow")
self.top_frame.pack(side="top", fill="x", expand=False)
self.bottom_frame.pack(side="bottom", fill="both", expand=True)
Next comes the frames for the labels. Again, these occupy space along the edges of their container so pack makes the most sense. And again, add just the following bit of code, run your program, and make sure things are still resizing properly and filling the right parts of the window:
self.top_left = Frame(self.top_frame, background="pink")
self.top_right = Frame(self.top_frame, background="blue")
self.top_left.pack(side="left", fill="x", expand=True)
self.top_right.pack(side="right", fill="x", expand=True)
Next, you have your "corner" labels. Again, since the container is but a single row of widgets, pack makes it easy. Since you want the labels in the corners, we'll set the sticky attribute a little different for each:
self.top_left_label = Label(self.top_left, text="Top Left")
self.top_right_label = Label(self.top_right, text="Top Right")
self.top_left_label.pack(side="left")
self.top_right_label.pack(side="right")
Finally, you have the text widget. It fills the entire bottom frame, so once again pack is our friend:
self.text_box = Text(self.bottom_frame, height=5, width=40, background="gray")
self.text_box.pack(side="top", fill="both", expand=True)
pack or grid?
You used grid for your original code and asked how to fix it. Why did I use pack for my examples?
When you use pack, all of the configuration options can be wrapped up in a single call. With grid along with putting the widgets in their containers you must also take care to give your various columns and rows "weight" so that they resize properly. When you are simply stacking widgets on top of each other or aligning them in a row, pack is much easier to use.
In my GUIs, I almost always use a combination of grid and pack. They are both powerful, and excel at different things. The only thing to remember -- and this is crucial -- is that you can't use them in the same parent. Using your code as an example, you can't use pack for the top_left frame and grid for the top_right frame, since they both share the same parent. You can, however, mix them within the same application.
Once more, using grid
Ok, so maybe you really want to use grid: maybe this is a school assignment, or maybe you just want to focus on one geometry manager at a time. That's cool. Here's how I would do it. Again, you must divide and conquer:
Start with the main frame. Replace the one statement where we pack the main container with the following lines. Notice that we have to configure the rows and columns on the parent, not the frame that we created.
self.main_container.grid(row=0, column=0, sticky="nsew")
self.myParent.grid_rowconfigure(0, weight=1)
self.myParent.grid_columnconfigure(0, weight=1)
Ok, now for the top and bottom frames. Remove the pack, and add the following lines. You still only have a single column, but this time there are a couple of rows. Notice which row gets a weight of 1:
self.top_frame.grid(row=0, column=0, sticky="ew")
self.bottom_frame.grid(row=1, column=0,sticky="nsew")
self.main_container.grid_rowconfigure(1, weight=1)
self.main_container.grid_columnconfigure(0, weight=1)
The corner frames -- at last, a container with more than one column! Let's create a third column in the middle to take up all the slack. Replace the pack statements with the following, paying close attention to what is given "weight":
self.top_left.grid(row=0, column=0, sticky="w")
self.top_right.grid(row=0, column=2, sticky="e")
self.top_frame.grid_columnconfigure(1, weight=1)
Next, the labels in their frames. Since we don't need them to expand, we can keep the default weight of zero. You may think it odd that both labels are in column zero. Remember that they are in different parents, and they are the only widgets in their parents so there's only one column in each:
self.top_left_label.grid(row=0, column=0, sticky="w")
self.top_right_label.grid(row=0, column=0, sticky="e")
Finally we have the text widget which is the only widget in the bottom frame. Replace the final pack statements with the following:
self.text_box.grid(row=0, column=0, sticky="nsew")
self.bottom_frame.grid_rowconfigure(0, weight=1)
self.bottom_frame.grid_columnconfigure(0, weight=1)
Conclusion
Laying out widgets is easy, but you have to be systematic about it. Think about what you are doing, organize your widgets into groups, and then tackle the groups one at a time. When you do that, it should become apparent which geometry manager you should use.
As you can see, grid requires a few more lines of code but it's the right choice when you really do have a grid. In your case you had already sub-divided your GUI into sections, and so even though the end result was a grid, each section was either packed on top or below another, or to the left or right edges. In these cases, pack is a bit easier to use since you don't have to worry about row and column weights.
If you add bg = "red" to both top_left and top_right constructors, you will see how the Frames are stuck on the centre, even using the sticky option. If they are not going to contain anything else than a single widget, I recommend not to use them.
#!/usr/bin/python
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.top_left_label = Label(parent, text="Top Left")
self.top_left_label.grid(row=0, column=0, padx=2, pady=2, sticky=N+S+W)
self.top_right_label = Label(parent, text="Top Right")
self.top_right_label.grid(row=0, column=1, padx=2, pady=2, sticky=N+S+E)
self.text_box = Text(parent, height=5, width=40)
self.text_box.grid(row=1, column=0, columnspan=2)
root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
Here is an easy way to do it:
First design your gui on paper or using any tool that works for you
Use grid layout manager
Wherever you want to create empty space, use columnspan or rowspan
property of layout, in combination of sticky property
columnspan or rowspan let you assign more than one cell of the grid to a gui component, and sticky property let you force that element to stick to one side(s) and leave empty space on the other side(s)
Better late than never ;)
Tkinter's "grid" wants to put everything together. Thats why you are not able to skip cells. You have to specify the widths and then anchor the text. I added color to help show cells. I put the bd in the frame. Then I had to give width to the cells left and right so grid would give weight to them. Then anchor text West or East. Im not sure why I had to add extra 2 spaces for each cell but I figured it was a font issue.
I believe Rodas is cleaner and simpler but I tried to stay within the parameters you asking about.
Pack is easier and faster for small stuff.
from tkinter import *
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent, bg="green")
self.main_container.grid()
self.top_frame = Frame(self.main_container)
self.top_frame.grid()
self.top_left = Frame(self.top_frame, bd=2)
self.top_left.grid(row=0, column=0)
self.top_right = Frame(self.top_frame, bd=2)
self.top_right.grid(row=0, column=2)
self.top_left_label = Label(self.top_left, bd=2, bg="red", text="Top Left", width=22, anchor=W)
self.top_left_label.grid(row=0, column=0)
self.top_right_label = Label(self.top_right, bd=2, bg="blue", text="Top Right", width=22, anchor=E)
self.top_right_label.grid(row=0, column=0)
self.bottom_frame = Frame(self.main_container, bd=2)
self.bottom_frame.grid(row=2, column=0)
self.text_box = Text(self.bottom_frame, width=40, height=5)
self.text_box.grid(row=0, column=0)
root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
If you need to have full control on the placement of your gui component, try place layout; a highly manageable approach is to organize your components in several frame, each with grid or pack layout, and then organizing all those frames using place layout.

Categories