when I am adding canvas to my GUI there are appear some weird gaps between buttons. It doesn't appear without canvas(red one).
for i in range(1,30):
self.przyciskiZawodnikow.append(Button(root, width=w, text="chuj", compound="bottom", height=h, image=self.obrazy_wlosow['blond'], borderwidth=b, command =lambda i=i: self.aktual_stat(i)))
self.przyciskiZawodnikow[-1].grid(row=0, column=i-1)
self.boisko = Canvas(master, width=200, height=100, background='red')
self.boisko.grid(row=5, sticky='S')
self.boisko = Canvas(master, width=200, height=100, background='red')
self.boisko.grid(row=5, sticky='S')
Your creating a grid, which means every cell in a column will be the same width, and ever cell in a row will be the same height.
When you put the canvas in column 0 (by virtue of not specifying the column), that forces column 0 to be as wide as the canvas. Since your buttons aren't as wide as the canvas, and you haven't specified any other options, the buttons will be centered in their cell. Thus, any extra space will be allocated equally on each side of the button in column zero.
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.
MCVE
import Tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=100, height=100)
canvas.grid(row=0, column=0)
def print_click(event):
print event.x, event.y
canvas.bind('<Button-1>', print_click)
root.mainloop()
Issue
Clicking the canvas on the very top left prints (0, 0).
Clicking the canvas on the very bottom right prints (100, 100). I expected (99, 99).
This means the canvas is actually 101 pixels wide and high, not 100.
In my real program, I am showing an array (as an image) on the canvas and need the precise click position. If that position does not exist in the underlying image (i.e. (100, 100) for an 100x100 array), the program will crash.
Questions
Am I doing something wrong creating the canvas? Why is it one wider and higher than expected?
Is the simple fix here to just subtract 1 from width and height whenever setting up a canvas that needs to have width width and height height?
There are other things that contribute to the overall width and height of a widget besides just the width and height attributes. For example, both borderwidth and highlightthickness contribute to the overall size of the widget. Since you aren't setting those to zero, you're relying on the defaults for your platform, and those defaults apparently aren't zero.
You need to explicitly set those attributes to zero:
canvas = tk.Canvas(root, width=100, height=100, highlightthickness=0, borderwidth=0)
I'm new to tkinter and I've having trouble getting notebooks to fill horizontal space. I would like to place two notebooks at the top of the window, side-by-side. Each notebook should occupy half of the horizontal space of the window.
I initially tried using pack, but that made the notebooks expand unevenly, the right notebook took up more space than the left notebook. I thought that grid would work better, but I can't figure out how to get them to expand, even with passing weight=1 to columnconfigure.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry("800x100")
status = tk.Frame(root)
left_tabs = ttk.Notebook(status)
tab_a = tk.Frame(left_tabs)
tab_b = tk.Frame(left_tabs)
left_tabs.add(tab_a, text="a")
left_tabs.add(tab_b, text="b")
# left_tabs.pack(side=tk.LEFT, fill=tk.X, expand=True)
left_tabs.grid(row=0, column=0)
right_tabs = ttk.Notebook(status)
tab_c = tk.Frame(right_tabs)
tab_d = tk.Frame(right_tabs)
tab_e = tk.Frame(right_tabs)
right_tabs.add(tab_c, text="c")
right_tabs.add(tab_d, text="d")
right_tabs.add(tab_e, text="e")
# right_tabs.pack(side=tk.LEFT, fill=tk.X, expand=True)
right_tabs.grid(row=0, column=1)
tk.Label(tab_a, text="testing").pack()
tk.Label(tab_c, text="testing").pack()
status.columnconfigure(0, weight=1)
status.columnconfigure(1, weight=1)
status.pack(fill=tk.X)
title = tk.Label(root, text="title", bg="grey")
title.pack(fill=tk.X)
tk.mainloop()
grid allows you to request that two or more rows or columns have a uniform width or height. This option name is uniform - if two or more columns have the same setting for uniform, they will always be the same width. The value of uniform doesn't matter, as long as it is the same.
status.columnconfigure(0, weight=1, uniform="x")
status.columnconfigure(1, weight=1, uniform="x")
In addition, you need to use the sticky option so that your notebooks fill the space that has been allocated to them.
left_tabs.grid(row=0, column=0, sticky="nsew")
right_tabs.grid(row=0, column=1, sticky="nsew")
To make a gridded layout have all columns have the same width, you've got to configure those columns to have the same weight and to be in the same uniform group. This configuration is associated with the master widget, not any of the contained widgets (because columns can contain many widgets, of course).
sticky
Defines how to expand the widget if the resulting cell is larger than
the widget itself. This can be any combination of the constants S, N,
E, and W, or NW, NE, SW, and SE.
For example, W (west) means that the widget should be aligned to the
left cell border. W+E means that the widget should be stretched
horizontally to fill the whole cell. W+E+N+S means that the widget
should be expanded in both directions. Default is to center the widget
in the cell.
specifying the sticky argument when you do grid:
left_tabs.grid(row=0, column=0, sticky="NESW")
status.columnconfigure(0, weight=1, uniform="group_name")
right_tabs.grid(row=0, column=1, sticky="NESW")
status.columnconfigure(1, weight=1, uniform="group_name")
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 2 frames in a column. The top frame should fill x and be fixed y. This works good. The bottom frame should fill the remaining space, but setting fill to both and expand to True doesn't seem to work the way I expected it to. The bottom frame expands, but not fully, leaving gray background of the root between the frames.
Here is minimum code to reproduce the problem:
import Tkinter as tk
if __name__ == "__main__":
root = tk.Tk()
top_frame = tk.Frame(root, height=50, bg="blue")
top_frame.pack(anchor="n", fill="x", expand=True)
bot_frame = tk.Frame(root, bg="red")
bot_frame.pack(anchor="n", fill="both", expand=True)
root.mainloop()
Ideally, the bottom frame should also start with a minimum size, but I will probably be able to figure it out once this problem is solved.
Remove expand=True from first frame because it informs pack() to use extra free space with this widget too.
import Tkinter as tk
if __name__ == "__main__":
root = tk.Tk()
top_frame = tk.Frame(root, height=50, bg="blue")
top_frame.pack(anchor="n", fill="x") # without expand=True
bot_frame = tk.Frame(root, bg="red")
bot_frame.pack(anchor="n", fill="both", expand=True)
root.mainloop()
expand and fill are completely independent of each other. expand answers the question "do I get extra space?", and fill answers the question "how do I use the extra space that was given to me?".
So, you have two frames both of which have expand=True. That means that tkinter will give half of the extra space to one frame, and half the extra space to the other, regardless of how those widgets plan to use the extra space.
Since the top frame only fills in the X direction, the extra space it has been given in the Y direction goes unused. That is why it appears gray below the defined height of the frame.
The solution to this specific problem is to have expand be false for the top frame because you do not want it to be given extra space.