python clearing text box with button event - python

I am trying to clear the text box i typed already with a Button 'Clear'.But its not clearing the text once the button is clicked!
Please fix my problem!Answer will be appreciated!
import tkinter as tki
class App(object):
def __init__(self,root):
self.root = root
txt_frm = tki.Frame(self.root, width=600, height=400)
txt_frm.pack(fill="both", expand=True)
# ensure a consistent GUI size
txt_frm.grid_propagate(False)
self.txt1 = tki.Text(txt_frm, borderwidth=3, relief="sunken", height=4,width=55)
self.txt1.config(font=("consolas", 12), undo=True, wrap='word')
self.txt1.grid(row=0, column=1, sticky="nsew", padx=2, pady=2)
button1 = tki.Button(txt_frm,text="Clear", command = self.clearBox)
button1.grid(column=2,row=0)
def clearBox(self):
self.txt1.delete(0, END)
root = tki.Tk()
app = App(root)
root.mainloop()

Since END is in tkinter you need to use tki.END or "end" (with quotes, lower case) also your starting index should be "1.0"(thanks to BryanOakley) instead of 0.
This one should work.
def clearBox(self):
self.txt1.delete("1.0", "end")
EDIT: By the way it will be better if you use pack_propagate instead of grid_propagate since you used pack to place your frame.
EDIT2: About that index thing, it is written in here under lines and columns section.
...Line numbers start at 1, while column numbers start at 0...
Note that line/column indexes may look like floating point values, but it’s seldom possible to treat them as such (consider position 1.25 vs. 1.3, for example). I sometimes use 1.0 instead of “1.0” to save a few keystrokes when referring to the first character in the buffer, but that’s about it.

Related

Opening a Toplevel windows in tkinter

I am trying to open a toplevel window with a label from a function call but the label is not showing. What am I doing wrong?
Gratias.
from tkinter import *
import tkinter.font as fonte
def open_top():
top = Toplevel(master)
top.geometry("375x238+789+479")
top.resizable(width=FALSE, height=FALSE)
topFont = fonte.Font(family='Ubuntu', size=40)
label = Label(top, text='world', borderwidth=2, relief="sunk", width=24)
label.config(font = topFont, height=11, wraplength=350)
label.grid(row = 0, column = 0, columnspan=1, rowspan=1, sticky=W+E+N+S)
master.update()
# creating main tkinter window
master = Tk()
master.geometry("374x340+790+100")
master.resizable(width=FALSE, height=FALSE)
myFont = fonte.Font(family='Monospace', size=25)
view = Label(master, text='helo', borderwidth=2, relief="sunk", width=10)
view.config(font = ('Monospace', 36), height=3)
view.grid(row = 4, column = 0, columnspan=2, rowspan=1, sticky=W+E+N+S, padx=5, pady=5)
btn = Button(master, text ='toplevel', command = lambda: open_top())
btn.grid(row=6, column=0, columnspan=1, pady=4, padx=4)
btn = Button(master, text='Quit', command=master.destroy)
btn.grid(row=6, column=1, columnspan=1, pady=4, padx=4)
mainloop()
First about the problem:
When you create the label, you specify the width (and later) height arguments. Since your label contains text the units you pass as values to those arguments represent characters for width and lines for height (You can read about those attributes here)
#TheLizzard mention: wraplength's value also is in characters, and since you have set it to 350 and your font is not that small, it will wrap the text when a huge part of it is out of the window (and quite possibly even out of the screen) so with the current value it is quite useless. (it is also quite useless if you add static text because then you can simply add a newline or sth, a use case would be when you don't know how long the text is, for example, it was taken from user input)
Possible fixes:
Remove the width and height arguments (really the easiest):
label = Label(top, text='world', borderwidth=2, relief="sunk")
label.config(font=topFont, wraplength=350)
The widget can be also configured all at once, you don't need to use its method like this for initialization:
label = Label(top, text='world', borderwidth=2, relief="sunk", font=topFont, wraplength=350)
Change the height and width values (width tho really doesn't have to be used, especially if you use the wraplength argument):
label = Label(top, text='world', borderwidth=2, relief="sunk", font=topFont, wraplength=350, height=1)
Use anchor (really not that much of a fix or anything but will allow you to see the text (to an extent)):
label.config(anchor='nw')
Few other things:
Important (suggestions)
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
Also: I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but use if it is used for assigning a value (variable = 'some value'). Have two blank lines around function and class declarations.

How to add a specific spacing in pixels between tkinter buttons?

I have written some code for some buttons. However, I am not sure how to add a specific number of pixels of spacing for each button. So far is the code I have written. However, I have not yet figured out a reliable way to add spacing between the buttons in pixel sizes.
import tkinter as tk
#from tkinter import PhotoImage
def banana():
print ("Sundae")
def tomato():
print ("Ketchup")
def potato():
print ("Potato chips")
root = tk.Tk()
root.geometry("960x600")
f1 = tk.Frame(root, width=70, height=30)
f1.grid(row=3, column=0, sticky="we")
button_qwer = tk.Button(f1, text="Banana", command=banana)
button_asdf = tk.Button(f1, text="Tomato", command=tomato)
button_zxcv = tk.Button(f1, text="Potato", command=potato)
button_qwer.grid(row=0, column=0)
button_asdf.grid(row=0, column=1)
button_zxcv.grid(row=0, column=2)
root.mainloop()
Adding space between widgets depends on how you are putting the widgets in the window. Since you are using grid, one simple solution is to leave empty columns between the buttons, and then give these columns a minsize equal to the space you want.
Example:
f1.grid_columnconfigure((1, 3), minsize=10, weight=0)
button_qwer.grid(row=0, column=0)
button_asdf.grid(row=0, column=2)
button_zxcv.grid(row=0, column=4)
Using a specific number of pixels of spacing between each Buttondoesn't sound to me like such as good idea because it isn't very flexible nor easily portable to devices with different resolutions.
Nevertheless I've figured-out a way of doing it—namely by putting a do-nothing invisible button between of the each real ones. This got somewhat involved, mostly because it requires putting an image on each Button used this way so its width option argument will be interpreted as number of pixels instead of number of characters (here's some documentation describing the various Button widget configuration options).
import tkinter as tk
# Inline XBM format data for a 1x1 pixel image.
BITMAP = """
#define im_width 1
#define im_height 1
static char im_bits[] = {
0x00
};
"""
root = tk.Tk()
root.geometry("960x600")
bitmap = tk.BitmapImage(data=BITMAP, maskdata=BITMAP)
f1 = tk.Frame(root, width=70, height=30)
f1.grid(row=3, column=0, sticky=tk.EW)
def banana():
print ("Sundae")
def tomato():
print ("Ketchup")
def potato():
print ("Potato chips")
def layout_buttons(parent, buttons, spacing):
if buttons:
first, *rest = buttons
first.grid(row=0, column=0) # Position first Button.
for index, button in enumerate(rest, start=1):
col = 2*index
# Dummy widget to separate each button from the one before it.
separator = tk.Button(parent, relief=tk.FLAT, state=tk.ACTIVE,
image=bitmap, borderwidth=0, highlightthickness=0,
width=spacing)
separator.grid(row=0, column=col-1)
button.grid(row=0, column=col)
buttons = (
tk.Button(f1, text="Banana", command=banana),
tk.Button(f1, text="Tomato", command=tomato),
tk.Button(f1, text="Potato", command=potato),
)
layout_buttons(f1, buttons, 30)
root.mainloop()
Result:
Here's a blow-up showing that the spacing is exactly 30 pixels (as counted in my image editor and indicated by the thin horizontal black line between the adjacent edges of the two Buttons).

Tkinter Grid System: Arranging Elements

I am currently coding a program with will tie in with Discord's rich presence, and I am needing to create a GUI for it in Tkinter. The issue is, I cannot understand how to place elements correctly. It has been quite a pain. Here is what I am planning to have the app sort of look like: https://i.stack.imgur.com/K9Wox.jpg
However, with my current code, this is how abismal the GUI looks... https://i.stack.imgur.com/wGA9A.jpg
Here is my code:
root = tkinter.Tk()
root.title("Matter: A Discord Rich Presence Tool")
root.config(bg='#2C2F33')
root.geometry("560x300")
#root.overrideredirect(1)
# Load images
loadProfileImage = tkinter.PhotoImage(file="resources/loadprofile.png")
saveProfileImage = tkinter.PhotoImage(file="resources/saveprofile.png")
newProfileImage = tkinter.PhotoImage(file="resources/newprofile.png")
# GUI Hell starts here
topCanvas = tkinter.Canvas(root, width=600, height=150)
topCanvas.config(bd=0, highlightthickness=0, relief='ridge', background="#7289DA")
topTextFieldText = tkinter.StringVar(value='Sample top text')
topTextField = tkinter.Entry(root, textvariable=topTextFieldText)
topTextField.config(borderwidth=0, background="#7289DA")
bottomTextFieldText = tkinter.StringVar(value='Sample bottom text')
bottomTextField = tkinter.Entry(root, textvariable=bottomTextFieldText)
bottomTextField.config(borderwidth=0, background="#7289DA")
largeIconName = tkinter.StringVar()
largeIconNameField = tkinter.Entry(root, textvariable=largeIconName)
smallIconName = tkinter.StringVar()
smallIconNameField = tkinter.Entry(root, textvariable=smallIconName)
applicationIDFieldText = tkinter.StringVar()
applicationIDField = tkinter.Entry(root, textvariable=applicationIDFieldText)
applicationIDField.config(borderwidth=0, background="#23272A")
largeIconHoverText = tkinter.StringVar()
largeIconHoverTextField = tkinter.Entry(root, textvariable=largeIconHoverText)
largeIconHoverTextField.config(borderwidth=0, background="#23272A")
smallIconHoverText = tkinter.StringVar()
smallIconHoverTextField = tkinter.Entry(root, textvariable=smallIconHoverText)
smallIconHoverTextField.config(borderwidth=0, background="#23272A")
#greet_button = tkinter.Button(root, text="Run", command=run)
buttonFrame = tkinter.Frame(height=2, bd=0, relief=tkinter.SUNKEN)
newProfileButton = tkinter.Button(root, text="Save to profile", command=save)
newProfileButton.config(image=newProfileImage, borderwidth=0, background="#23272A")
saveButton = tkinter.Button(root, text="Save to profile", command=save)
saveButton.config(image=saveProfileImage, borderwidth=0, background="#23272A")
loadButton = tkinter.Button(root, command=load)
loadButton.config(image=loadProfileImage, borderwidth=0, background="#23272A")
# Grid stuff
topCanvas.grid(row=0, column=1)
applicationIDField.grid(row=3, column=1)
largeIconHoverTextField.grid(row=3, column=2)
smallIconHoverTextField.grid(row=3, column=3)
newProfileButton.grid(row=5, column=1, padx=(20, 5))
saveButton.grid(row=5, column=2, padx=(5, 5))
loadButton.grid(row=5, column=3, padx=(5, 20))
root.mainloop()
Any guidance would be greatly appreciated since I cannot seem to be able to figure out how to use Tkinter's grid to make a layout similar to the images above.
Do not try to put everything into a single grid. Divide your UI up into sections, and then use the right tool for each section.
Start at the root window
I see two major sections to your UI: a top section in blue that has some information, and a bottom section with a black background that has some buttons.
So, I would start by creating those two sections in the root window, and use pack to place one on top of the other:
topFrame = tk.Frame(root, background="#7289DA")
bottomFrame = tk.Frame(root, background="#2C2F33")
topFrame.pack(side="top", fill="both", expand=True)
bottomFrame.pack(side="bottom", fill="both", expand=True)
With that, you will now always have the root divided into two colored regions. The above code gives them an equal size. You may one to change the expand value to False for one or the other, depending on what you want to happen when the user resizes the window.
Don't worry too much about the size, though. It will change once you start adding widgets to each section.
Next, do the bottom section
The bottom also appears to be in two sections: one for inputs and one for buttons. You could use a single grid layout for this whole section, but to illustrate the concept of dividing the UI into sections we'll split the bottom into two. Plus, because everything isn't neatly lined up into rows and columns, this will make things a bit easier.
As I mentioned earlier, you may want to fiddle around with the expand option, depending on if you want these frames to resize equally or stay the same size when the user resizes the window.
inputFrame = tk.Frame(bottomFrame, background="#2C2F33")
buttonFrame = tk.Frame(bottomFrame, background="#2C2F33")
inputFrame.pack(side="top", fill="both", expand=True)
buttonFrame.pack(side="top", fill="both", expand=True)
Note: if you stop right here and try to run your program, you might not see these frames. During development it sometimes helps to give them distinctive colors to help you visualize. Once you get everything working, you can adjust the colors to their final values.
Add the entry widgets
Now we can add the entry widgets to the top half of the bottom section. We can use grid here, since everything is lined up neatly. An important step is to give the rows an equal weight so that they grow and shrink together, though you can make it so that only one column resizes if you wish.
I'll also point out that there's no need to use StringVar instance. You can, but it adds extra objects to keep track of, which in most cases is not necessary.
label1 = tk.Label(inputFrame, text="APPLICATION ID",
foreground="lightgray",
background="#2C2F33")
label2 = tk.Label(inputFrame, text="LARGE IMAGE HOVER",
foreground="lightgray",
background="#2C2F33")
label3 = tk.Label(inputFrame, text="SMALL IMAGE HOVER",
foreground="lightgray",
background="#2C2F33")
# columns should get extra space equally. Give any extra vertical space
# to an empty column below the entry widgets
inputFrame.grid_columnconfigure((0,1,2), weight=1)
inputFrame.grid_rowconfigure(2, weight=1)
appIdEntry = tk.Entry(inputFrame, borderwidth=0,
highlightthickness=0,
background="#23272A", bd=0)
largeImageEntry = tk.Entry(inputFrame,
highlightthickness=0,
background="#23272A", bd=0)
smallImageEntry = tk.Entry(inputFrame, borderwidth=0,
highlightthickness=0,
background="#23272A", bd=0)
label1.grid(row=0, column=0, sticky="w")
label2.grid(row=0, column=1, sticky="w", padx=10)
label3.grid(row=0, column=2, sticky="w")
appIdEntry.grid(row=1, column=0, sticky="ew")
largeImageEntry.grid(row=1, column=1, sticky="ew", padx=10)
smallImageEntry.grid(row=1, column=2, sticky="ew")
This gives us the following:
Notice that the top appears to have shrunk. That's only because it's empty. Tkinter is really good about expanding and shrinking things to fit what's inside. Don't worry too much about it. You can tweak things once you get everything working.
And so on...
I don't want to rewrite your whole program in this answer. The point here is that you should break your UI up into logical chunks, and make each chunk a frame. You are then free to use whatever geometry manager makes the most sense within that frame. Sometimes grid is best, sometimes pack. In either case, it's much easier to manage a few high level frames than it is to try to cram dozens of widgets into a single grid, especially when there are no clear cut rows and/or columns.
This solution also makes it pretty easy to create functions or classes for each section. For example, your main program might look like:
root = tkinter.Tk()
top = topSection(root)
bottom = bottomSection(root)
By doing so, if you decide to completely redesign one section of the UI, you can do so without worrying that you'll mess up the layout in the other sections, since each frame is mostly independent of any other frame.
First you have to explain to the grid geometry manager that you want the canvas to span all three columns:
topCanvas.grid(row=0, column=1, columnspan=3)
Widgets do not automatically expand to fill the entire column (or row) if you don't specify it and it will center itself in a cell if you dont specify where you want it with sticky:
newProfileButton.grid(row=5, column=1, padx=(20, 5), sticky='w')
saveButton.grid(row=5, column=2, padx=(5, 5), sticky='w')
loadButton.grid(row=5, column=3, padx=(5, 20), sticky='w')
This will hopefully give you something to play with although it's not a complete answer.
Here is a good tutorial: Getting Tkinter Grid Sizing Right the first time

Python Tkinter Text Alignment

I am using Tkinter with Python2 to create a new window by clicking on the button. In this new window I want to Display text. Now I have Troubles with the alignment, is it possible to align it to the left? It is always centered and neither anchor=LEFT nor sticky="NSEW" help.
import tkinter as tki
btn3 = tki.Button(self.root, text="HELP", command=self.help, fg="black", bg="white", font=("Courier",22))
btn3.grid(row=1, column=2, padx=10, pady=10, sticky="NSEW" )
def help(self):
self.text = """ Hello.
You can find help here to the following subjects:
- getting started
- installation
- FAQ."""
self.top = tki.Toplevel()
self.top.title("Help")
self.label1 = tki.Label(self.top, text = self.text, height = 0, width=80, fg="black", bg="white", font=("Courier",18))
self.label1.pack()
When you use anchor = n you need to put anchor = "n" i.e. with the n in quotes. You're getting the global name is not defined error because of this.
Alternatively, use anchor = tki.N. Because you used import tkinter as tki you must prefix tkinter variables with tki. in order for python to know what you're referring to. However, I think you might want to try justify=tki.LEFT also if that doesn't work.

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