I am trying to make a website in Python and I have 5 frames to separate what I want.
By themselves the website looks like this:
and it has this code:
from tkinter import *
root = Tk()
root.geometry(str(root.winfo_screenwidth()) + "x" + str(root.winfo_screenheight()) + "+" + "0" + "+" + "0")
top = Frame(root, bg="lightGreen").grid(row=0, column=0, columnspan=3, stick=NSEW)
title = Label(top, text="website", bg="lightGreen", font=("Courier", 50, "bold"))
title.place(x=0, y=0)
left = Frame(root, bg="lightBlue").grid(row=1, column=0, stick=NSEW)
center = Frame(root, bg="pink", height=50)
center.grid(row=1, column=1, stick=NSEW)
right = Frame(root, bg="yellow", height=50).grid(row=1, column=2, stick=NSEW)
bottom = Frame(root, bg="silver").grid(row=2, column=0, columnspan=3, stick=NSEW)
# configurations and mainloop and binding
root.columnconfigure(0, weight=2)
root.columnconfigure(1, weight=5)
root.columnconfigure(2, weight=2)
root.rowconfigure([0, 2], weight=1)
root.rowconfigure(1, weight=5)
root.mainloop()
Then when I add stuff into the pink Frame, it looks like this:
The code is the same except with this small piece of coded added with it:
Label(center, text="Welcome", bg="pink", font=("Courier", 50, "bold")).pack(side=TOP, anchor=W)
If you look closely, the blue and yellow frames are smaller and the pink is bigger when the text is added. Why does this happen and is there any way to solve this problem?
If you look closely, the blue and yellow frames are smaller and the pink is bigger when the text is added. Why does this happen and is there any way to solve this problem?
When you use weight, it does not define the ratio of all space to give to each column. It defines how much of the extra space is given to each column. Since none of your widgets have a defined size, they all expand according to the rules you've set forth so it appears that the weight defines the actual width.
When you add a widget to the center section, that label requires a certain amount of space, and thus the center frame now requires a certain amount of space. That means that there is less extra space to be allocated, and this extra space will be added to the space already being used by the label.
When you start out, none of the frames have a width. Let's say we end up setting the width of the window to 900 pixels. Because of the weights, this 900 pixels is split up into 200 pixels on the left, 500 in the middle, and 200 on the right, like you see in your first picture.
Now, let's say that the middle label is added and it takes up 100 pixels. That means there are now 800 extra pixels that have to be distributed, instead of 900. The left and right columns each get 2/9ths of 800 or about 178 pixels. The center section gets 5/9th of that extra space or about 444 pixels. These 444 pixels are added to the existing 100 pixels required by the middle, resulting in a middle section that gets 544 pixels. Thus, the side columns are narrower and the middle column is wider than when there was nothing in the center.
As to the fix, it's hard to say. The fix for this specific case where you have nothing in the left and right is going to be different than the case where you have widgets in the left and right.
As a general rule, don't depend on the weight to define the actual widths, because that's not what it is for. Design your GUI by giving minimum widths to the widgets inside the columns and letting tkinter compute the actual widths. Then, the weight can be used to decide how to distribute extra space when the window grows. Alternately, you can use the minsize attribute of the columnconfigure command to force the left and right sizes to have a minimum width.
Related
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.
I am making a Python program, and I am looking for ways to make a nice centre-aligned title at the top of inside my program. However, I am having trouble doing this because with the current tools I have, I would have to experiment my label spacing length to match how many characters I have for my title. Is there a way to make a Python label be certain amount of pixels long regardless of length of the text? I am specifically looking for a way of specifically making a label be a specific number of pixels long so I don't have to experiment with all these different lengths.
I tried using the "padx" and "pady" parameters, but I have found that the lengths will always change depending on the length of the text of the label itself. I also tried the "width" parameter, but it only extends by a number of textlengths long.
from tkinter import *
root = Tk()
root.geometry("960x600")
label_toptitle = Label(root,
text="Program Name",
padx=40,
pady=40,
font=(None, 40),
#height=2,
#width=30
)
root.mainloop()
This is my current code right here, but if I change the name "Program Name" to another name, the padx and pady and/or width or height functions will become not the same amount of pixels long. I need a way to make the label a fixed amount of pixels long.
Your question title mentions setting the length of a label, but it seems your real goal is to center a label without having to calculate margins.
The best solution for that is to not use padding or margins to center the widget. Just tell tkinter you want things centered when you use grid or pack, which is actually the default. You don't have to do much at all.
For example, the following will give a window with a centered title:
import tkinter as tk
root = tk.Tk()
root.geometry("960x600")
label_toptitle = tk.Label(root,
text="Program Name",
font=(None, 40),
)
label_toptitle.pack(side="top", fill="x")
root.mainloop()
Grid can center things too, though the exact syntax depends on what other rows and columns you have. Without knowing that it's hard to give a useful example.
If you really need to specify an exact width in pixels for a label, you can add a transparent one-pixel image to the label and hide it behind the text. When you add an image, the width attribute represents a width in pixels rather than characters.
Example:
pixel = tk.PhotoImage(width=1, height=1)
label = tk.Label(root, image=pixel, text="Hello", compound="center", width=100)
You could make a variable that declares the max size of a label and then use it to fix the width/height issues on all label declarations as follows:
maxSizeX = 100
maxSizeY = 100
labelTitleText = "Program Name"
labelTitleTextSize = len(labelTitleText)
labelTitle = Label(root,
text=labelTitleText,
width=(maxSizeX-labelTitleTextSize),
height=maxSizeY,
font=(None, 40)
)
Although I'm unsure as to how you would get character pixel width on the display if you could then you could change the above to:
maxSizeX = 100
maxSizeY = 100
labelTitleText = "Program Name"
labelTitleTextSize = len(labelTitleText) * pixelWidth
labelTitle = Label(root,
text=labelTitleText,
padx=(maxSizeX-labelTitleTextSize),
pady=maxSizeY,
font=(None, 40)
)
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.
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.
I have been toying around with putting a scrollbar on my data graph. I have it on there and its scrolling the data but its also scrolling the scale(data values) on the right hand side of the screen. I've have toyed around this morning with the idea of creating two separate windows, one for the data graph and one for the scale. It looks rather unusual compared to what you normally see but I do notice one thing in particular when I do this. With the scale on the data graph, one gui, the scrolling is very slow as long as the scale still remains on the screen. Once the scale moves off the screen the scrolling speed picks up to what I would normally expect. When I move the scale to a completely separate gui the scrolling speed is consistently fine all the time. How do I overcome this problem?
I'm not sure why the scale is having any kind of effect on the scrolling speed. It's nothing more than:
self.DrawArea.create_line((1298, 12), (1300, 12), fill = "white")
self.DrawArea.create_line((1290, 25), (1300, 25), fill = "white")
self.DrawArea.create_line((1298, 37), (1300, 37), fill = "white")
self.DrawArea.create_text((1320, 25), text = "5.0", fill = 'white')
self.DrawArea.create_text((1320, 50), text = "4.5", fill = 'white')
self.DrawArea.create_text((1320, 75), text = "4.0", fill = 'white')
going down the screen(yes 5 to -5 marked out every every .125...labelled once every .5).
It is feasible to have the scale and the graph data on the same gui and still keep the scrolling speed. I haven't changed the font, either size or type as I'm not sure how to since nothing is really indicated in the tkinter documentation.
Also is there a way that I can limit where the graph data gets displayed. With one gui, I have the graph setup for 1350x615(600 with the bottom 15 being the scrollbar). 1300 should be the display data with the other 50 being the scale. Right now I have the issue that the data gets graphed underneath the scale(scale is obviously put on last). Is there any way I can limit it so the data only gets shown 0-1300 while the scale gets display 1301-1350? I've been toying around with Frames as well this morning but I have had no luck thus far at resolving this issue.
Edited:
When I was trying to use the keyboard for scrolling I was using the .move() command but when I went to change to using the scrollbar I wasn't using the keyboard at all and just using the scrollbar. When I have both the graph and scale on the same gui as long as the scale is on the screen(hasn't been scrolled off yet) the graph moves very slowly across the screen. Once it's off the screen the pace picks up and moves as though I didn't have the scale on the screen at all. It's the same way when I test with two separate windows. The scale on the main graph slows the scrolling down.
Moving the scale to another gui still doesn't help the load speed or the zoom in/out speed for displaying the graph though.
If you want to scroll with the scrollbar and not have the scale scroll, you should probably use two separate windows. I'm not sure what you mean by "unusual compared to what you normally see". If you put the two canvases side-by-side with no space between them and with the same background color, the user would have no way of knowing you're using two canvas widgets at the same time.
The tkinter canvas can scroll several thousand items before it starts to get sluggish, so it's hard to say without seeing your actual code.
here's an example that plots 10,000 points, with the scale in a separate canvas:
import Tkinter as tk
from random import randrange
class Example(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.DrawArea = tk.Canvas(self, width=1000, height=600, background="black",
borderwidth=0, highlightthickness=0)
self.scale = tk.Canvas(self, width=30, height=600, background="black",
borderwidth=0, highlightthickness=0)
self.hsb = tk.Scrollbar(self, orient="horizontal", command=self.DrawArea.xview)
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.DrawArea.yview)
self.DrawArea.configure(yscrollcommand=self.vsb.set, xscrollcommand=self.hsb.set)
self.DrawArea.grid(row=0, column=0, sticky="nsew")
self.scale.grid(row=0, column=1, sticky="nsew")
self.vsb.grid(row=0, column=2, sticky="ns")
self.hsb.grid(row=1, column=0, columnspan=2,sticky="ew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.draw_scale()
self.draw_points()
def draw_scale(self):
value = 5.0
for y in range(25, 600, 25):
self.scale.create_text((25, y), text=str(value), fill="white", anchor="ne")
value -= 0.5
def draw_points(self):
import math
for x in range(5,10000):
y = randrange(600)
color = "green" if (300 > y > 200) else "red"
self.DrawArea.create_rectangle(x-2,y-2,x+2,y+2, fill=color)
self.DrawArea.configure(scrollregion = self.DrawArea.bbox("all"))
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()