How to set the button sticky property properly? - python

I've been playing around with tkinter a bit and I can't figure out why the "sticky" attribute doesn't seem to be working with my button. I've specified sticky to be NW which should cause my button to stick to the top left edge but for some reason it sticks to the top right. Any idea why?
from tkinter import *
from tkinter import ttk
def test():
name = userName.get()
text = "Hello {0}! Pleased to meet you.".format(name)
greeting.set(text)
window = Tk()
greeting = StringVar()
userName = StringVar()
name = Entry(window, textvariable=userName)
name.grid(column=1, row=1, sticky=NW)
button = Button(window, text="greeting", command=test)
button.grid(column=2, row=1, sticky=NW)
label = Label(window, textvariable=greeting)
label.grid(column=1, row=2, sticky=NW)
#creating a rectangle
canvas = Canvas(window)
canvas.grid(column=1, row=2)
#attributes are x,y coordinates of two points
x = canvas.create_rectangle(5,5,115,115)
mainloop()

The sticky attribute applies to the cell that the widget is in, rather than to the whole grid or whole window. So, the widget is anchored to the nw corner of its cell, it's just that you can't tell because the cell is exactly the same width as the button.
Since you are placing the button in the upper right cell (row 1, column 2) but say you want it in the upper left (of the whole window?) it's hard to know exactly what you want. Without knowing what you're trying to achieve it's hard to make any recommendations.
The easiest way to learn the grid layout manager is with paper and pencil. Get out some gridded graphing paper and draw your widgets on the paper. It then becomes obvious where to put your widgets.
You also need to learn about the rowconfigure and columnconfigure commands, especially with respect to the weight attribute. With this attribute you can identify which rows and columns grown and shrink to take up any extra space. It's also useful to know you can apply these attributes to empty rows and columns. This is useful if you want your interior widgets to stay the same size, and have any extra applied to the edges of your gui (generally not useful, though sometimes it is).
As a rough rule of thumb, each window should have one "major" widget -- the one that dominates the UI. Usually this is a canvas or text widget, but it doesn't have to be. Find that widget and give the row and column it is in a weight of 1 (one) so that it grows and shrinks as the user resizes the window. In your case this would be the canvas in row 2, column 1.

The point is that the grid cell is exactly the same size as the button - you won't notice if it in E or W...You can check that by placing all your widgets below each other (all row 0, and column 0-4) you'll see that in that case the button shows up NW. Hope this helps...

Related

Column width using tkinter in python

for my GUI I want to use a grid with 6 columns. The size of the table is set. How do I fill the width of the grid with the columns? Can you set the size of the columns? I only found padx to change the padding, but not the actual size of the columns.
For now, this is supposed to work on a canvas. Is that even possible?
If you've fixed the size of the table, you can configure the columns to each be 1/6th the width of the window as a whole. The trick is to give each column the same non-zero weight, and use the uniform option. The uniform option takes a string, and all columns with the same value will be the same width.
If you run this code, notice how you can resize the window and the columns will automatically resize as well.
import tkinter as tk
import sys
root = tk.Tk()
root.geometry("600x400")
columns = []
for i in range(6):
frame = tk.Frame(root, borderwidth=1, relief="raised", background="bisque")
columns.append(frame)
root.grid_rowconfigure(0, weight=1)
for column, f in enumerate(columns):
f.grid(row=0, column=column, sticky="nsew")
root.grid_columnconfigure(column, weight=1, uniform="column")
root.mainloop()
With only the information you passed i would recommend you following:
Grid layouts your objects. Its not a table.
If you work with grid here. The simplest thing should be to set the width of the object you put on the wanted grid. For example:
If you add a Label set the label to the width you want it to be. This label will then
be displayed in the grid with its width and height.
If you want to display a table. Or even better load a table from a database. I would recommend a Treeview. Its good for design allows multiselect and its easy to fill and you dont have the layout problems :)

Tkinter: trouble filling entire frame with widget

I am attempting to create three frames: top, middle, and bottom. I successfully added widgets to my top and bottom frames and oriented them how I wanted them.
However, I am simply trying to add two entry widgets in the middle frame that will span across the entire width of the frame/window.
Due to the length of the code for all the widgets in the top and bottom frames, I'm just going to include a snippet of code for how my window and frames are configured:
root = Tk()
root.resizable(False, False)
top_frame = Frame(root)
middle_frame = Frame(root)
bottom_frame = Frame(root)
# I think this block is irrelavant to the question, but including it anyway just incase
rows = 0
while rows < 36:
top_frame.rowconfigure(rows, weight=1)
top_frame.columnconfigure(rows, weight=1)
bottom_frame.rowconfigure(rows, weight=1)
bottom_frame.columnconfigure(rows, weight=1)
rows += 1
top_frame.grid(row=0, column=0)
middle_frame.grid(row=1, column=0)
bottom_frame.grid(row=2, column=0)
Here is the code I'm using for the two entry widgets:
entry_1 = Entry(middle_frame)
entry_2 = Entry(middle_frame)
entry_1.grid(row=0, column=0, sticky=E+W)
entry_2.grid(row=1, column=0, sticky=E+W)
However, they just stick to the center of the middle frame. I've tried many solutions but nothing seems to change how these entry widgets look within the frame--always centered, never changing size. I even tried just packing them and setting their fill to X. I'm probably overlooking something very simple, but I can't quite figure it out.
Here is a picture for reference
The root of the problem is that the middle frame isn't configured to fill the full width of the window, so the widgets inside won't fill the full width of the window.
The first step is to use the sticky option on the middle window:
middle_frame.grid(row=1, column=0, sticky="ew")
Next, you haven't told grid what to do with extra space in the root window. As a rule of thumb any widget that uses grid to manage its children should have at least one row and one column with a weight greater than zero.
To get the middle frame to take up the full width of the window, give the column it is in a non-zero weight:
root.grid_columnconfigure(0, weight=1)
The same problem exists within the middle frame: you aren't instructing grid how to handle extra space. According to the rule of thumb we need to give column zero a weight within the middle frame:
middle_frame.grid_columnconfigure(0, weight=1)
That will allow the entry widgets in the middle frame to fill the middle frame, and we've configure the app so that the middle frame fills the full width of the window.

Tkinter Frame and Grid

For the life of me, I cannot understand grid within Frame. I'm trying to create the below configuration, but I'm getting something different (highlighted area is the troublesome part).
Here is the code:
from tkinter import *
root = Tk()
weather_root = Frame(root,width=1000, height=5, bg = 'white')
weather_root.pack(side=TOP)
quote_root = Frame(root,width=1000, height =5, bg = 'white')
quote_root.pack(side=TOP)
news_root = Frame(root,width=1000, height =100, bg = 'white')
news_root.pack(side=TOP, fill= BOTH)
financial_root= Frame(root,width=1000, height =100, bg = 'white')
financial_root.pack(side=TOP, fill= BOTH)
# PROBLEM BOX
time_root = Frame(root, bg = 'yellow')
time_root.pack(side = RIGHT, fill= BOTH)
I'm very new to this still, so I'm sure it's something obvious, but what is it? (In the picture I have it split as two frames - that's the ultimate goal, but in the near term, I'm just trying to get the frame to show up against the right of the current placed frames). Thanks very much!
The expected output:
The actual output:
The pack geometry manager is not good for laying things out in a grid. Unsurprisingly, grid is the better choice. It is going to be very difficult to do what you want with pack unless you add additional frames specifically to aid in layout.
Doing this with grid is very straight-forward. It would look something like this:
weather_root.grid( row=0, column=0, sticky="nsew")
quote_root.grid( row=1, column=0, sticky="nsew")
news_root.grid( row=2, column=0, sticky="nsew")
financial_root.grid(row=3, column=0, sticky="nsew")
time_root.grid( row=0, column=1, sticky="nsew", rowspan=4)
You would also need to use root.grid_rowconfigure and root.grid_columnconfigure to apply weights to the rows and columns that should grow or shrink when the window is resized.
If you want to use pack, I recommend adding two extra frames: one for the left (gray), and one for the right (yellow). You can use pack for those two. Then, on the left you could use pack to stack the areas top-to-bottom. Whether that's the right solution in your specific case, I don't know.
Notes:
I strongly recommend grouping your calls grid or pack in this way. It's much easier to manage when they are all in one spot rather than interleaved with the other code.
I don't recommend using extra whitespace as showin in the example. I did it just to make it easier for you to see how the rows and columns relate.
For the canonical description of how pack works, see http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26
The easiest way to accomplish the desired output would be to create two separate sub frames. You can pack the weather, quote, news and financial root frames into the left subframe, then pack the time frame into a right subframe. Last, you would pack them both into root, one using SIDE=LEFT and the other using SIDE=RIGHT. Additionally, it is possible to use Grid and Pack effectively within one app, but one individual widget (frame, for example) can only be managed using one layout manager (grid vs pack) at a time. So you can grid widgets such as frames into a frame, then pack that frame into another frame. Or, you could pack things into a subframe, then grid it into the main frame of the window.

Python Tkinter Window/Frame adjustment issues aligning issues 2.7

I am doing a project at home just trying to create a window with 3(technically 4) frames.
I have an upper frame that has 2 frames inside of it(I want a left and right Frame)
then I have a lower Frame that covers everything else.
That lower frame will eventually have an external process in it, but for now an image that will not take up the full space.
The upper space will not split evenly, EVEN THOUGH I split the height and width evenly at one point.
I will send my code and show an image below.
def createFrames(self):
#Main Upper Frame
topFrame = Frame(height=120, width=800, bd=1, relief=SUNKEN)
topFrame.pack(side=TOP)
#Left Frame in Main Upper Frame
topFrameLeft = Frame(topFrame, height=120, width=400)
topFrameLeft.pack(side=LEFT)
#Right Frame in Main Upper Frame
topFrameRight = Frame(topFrame, height=120, width=400)
topFrameRight.pack(side=RIGHT)
#Frame for GPS, Lower
centerFrame = Frame(width=800, height=400, bg="",
colormap="new",bd=3, relief=GROOVE)
centerFrame.pack(side=BOTTOM, fill=BOTH, expand=True)
#photo stuff
photo = PhotoImage(file="GPS_Imitation.gif")
#scale_w = 3
#scale_h = 400/200
#photo = photo.zoom(scale_w, scale_h)
#photo = photo.subsample(1)
Image_Label = Label(centerFrame, image=photo)
Image_Label.photo = photo
Image_Label.pack(fill=BOTH, expand=True)
#Label for Left Frame
Left_Label = Label(topFrameLeft, width=56, text="Audio", bg="gray",
fg="blue")
Left_Label.pack()
#Label for Right Frame
Right_Label = Label(topFrameRight, width=60,
text="Phone/Notification",
bg="Green", fg="Black")
Right_Label.pack()
I posted the function. I had to do some weird stuff with the code to get it to do this much. but the picture won't expand, it just grays out under and above the picture. and I had to modify the topleft and topright labels in width and height at random.
Any help would be appreciated it.
This is written in Python using Tkinter!
It's hard to give a definitive answer because GUI layout really depends a lot on the specifics. What goes in the frames? How do you want them to behave when you resize? What happens if the window is too small?
If I were doing this I would probably get rid of the internal frames and just put everything in a grid since it seems that you have two columns and two or three rows. Though, that decision really depends on what else is going to be put in various rows and columns.
There's nothing wrong with using pack and extra helper frames (this is often my first choice!), grid is arguably the best tool for the job if you want the width of two columns or more columns to be identical.
Grid allows you to configure columns to be in a uniform group. Every column with the same value for the uniform attribute will be the same size. So, for example, to make sure that topFrameLeft and topFrameRight are exactly the same you can put them in a uniform group inside of topFrame.
Start by using grid to place the widgets inside of TopFrame:
topFrameLeft.grid(row=0, column=0, sticky="nsew")
topFrameRight.grid(row=0, column=1, sticky="nsew")
Next, configure the uniform columns. Note: it's a best practice to always give at least one row and one column a weight, even if you use only one row or one column.
topFrame.grid_rowconfigure(0, weight=1)
topFrame.grid_columnconfigure(0, uniform="half", weight=1)
topFrame.grid_columnconfigure(1, uniform="half", weight=1)
Note: You can continue to use pack for all the other widgets. You can freely mix and match pack, place and grid within an application as long as you don't mix them with widgets that share the same parent.

Python: GUI packing widgets

This is my program and i packed my widgets so it would be left but somehow when i run it the widgets are not appearing from the left. The person and colour are appearing in the middle but the scary person and creature is appearing on the left. I want person and colour widget to to appear on the left too.
Here is my program
from Tkinter import *
import Tkinter
import tkMessageBox
class StoryMaker:
def __init__(self):
# Create the main window.
self.main_window = Tkinter.Tk()
#Create nine frames to group widgets.
self.first_frame = Tkinter.Frame()
self.second_frame = Tkinter.Frame()
self.third_frame = Tkinter.Frame()
self.fourth_frame = Tkinter.Frame()
self.fifth_frame = Tkinter.Frame()
# Create the widgets for the first frame.
self.prompt_label = Tkinter.Label(self.first_frame, text='Please enter information for a new story, then click the "Make Story" button.')
# Create the widgets for the second frame.
self.person_label = Tkinter.Label(self.second_frame, text='Person: ')
self.person_entry = Tkinter.Entry(self.second_frame, width= 15)
# Pack the second frame's widgets.
self.person_label.pack(side='left')
self.person_entry.pack(side='left')
# Create the widgets for the third frame.
self.colour_label = Tkinter.Label(self.third_frame, text='Colour: ')
self.colour_entry = Tkinter.Entry(self.third_frame, width= 15)
# Pack the third frame's widgets.
self.colour_label.pack(side='left')
self.colour_entry.pack(side='left')
# Create the widgets for the fourth frame.
self.scary_label = Tkinter.Label(self.fourth_frame, text='Scary person or creature: ', justify=LEFT)
self.scary_entry = Tkinter.Entry(self.fourth_frame, width= 15)
# Pack the fourth frame's widgets.
self.scary_label.pack(side='left')
self.scary_entry.pack(side='left')
# Pack the frames.
self.first_frame.pack()
self.second_frame.pack()
self.third_frame.pack()
self.fourth_frame.pack()
self.fifth_frame.pack()
# Enter the Tkinter main loop.
Tkinter.mainloop()
my_gui = StoryMaker()
The first thing I suggest is that you give each of the frames a distinct background color. This is just temporary, so you can see where each frame begins and ends. I think you'll find the results surprising:
self.first_frame = Tkinter.Frame(background="red")
self.second_frame = Tkinter.Frame(background="pink")
self.third_frame = Tkinter.Frame(background="green")
self.fourth_frame = Tkinter.Frame(background="blue")
self.fifth_frame = Tkinter.Frame(background="yellow")
When you do that you'll quickly see that the problem isn't that the labels are centered, but that the frame they are in is in the center. This is because you packed them with no options, which is the same as saying ...pack(side="top", fill=None, expand=False).
If you add fill="x") when packing first_frame, second_frame, etc, you'll see that your labels and entry widgets are indeed on the left side of their container.
My advice to people who are trying to learn tkinter is to take the "divide and conquer" approach. If you are using intermediate frames to organize your widgets, start by creating only those frames. Get the pack or grid options set so that these containing widgets are where you want them, and resize the way you want them to. Only after doing that should you add the interior widgets. That way, at any one time you're only trying to solve the layout problems of one group of widgets at a time.
If you're laying out a form, you might find it easier to use grid rather than pack. Typically in a form all of the labels in a group of labels will be the same size and aligned with each other. That's much easier to do by using grid and placing all of the widgets in a single frame, than it is to use multiple frames.

Categories

Resources