Why is Python Tkinter grid method separating the buttons - python-3.x

I am trying to make a game using python 3.8, where I have 1 canvas/frame and 2 buttons. I want the buttons to be right next to each other but whenever I run the code, there is always white space in between the 2 buttons. I tried using pack() but when you click on the buttons, it makes the buttons go above the canvas/frame instead of to the right of it.
My code:
from tkinter import *
# windows, canvas, and frames
root = Tk()
WatchRun = Canvas(root, bg="green", width=600, height=500)
WatchRun.grid(row=0, column=0, rowspan=25)
Upgrade = Frame(root, bg="yellow", width=600, height=500)
Upgrade.grid_forget()
# button functions
def show_upgrade(widget, widget2):
global upgradeBtn
global WatchRunBtn
widget.grid_forget()
widget2.grid(row=0, column=0, rowspan=25)
def show_watchrun(widget, widget2):
global upgradeBtn
global WatchRunBtn
widget.grid_forget()
widget2.grid(row=0, column=0, rowspan=25)
# variables and buttons
distance = 0
started = 0
money = 0
startImage = PhotoImage(file='start.png')
stopImage = PhotoImage(file='stop.png')
upgradeBtn = Button(root, text="Upgrades", width=9, command=lambda: show_upgrade(WatchRun, Upgrade))
upgradeBtn.grid(row=0, column=1)
WatchRunBtn = Button(root, text="Watch run", width=9, command=lambda: show_watchrun(Upgrade, WatchRun))
WatchRunBtn.grid(row=1, column=1)
#loop
root.mainloop()

It appears you're trying to use rowspan to try to force the widgets apart. That's not a good solution.
While there are multiple ways to solve the problem of the buttons not being together, the one I recommend in this specific case is to treat your GUI as if it had three rows (or maybe four, depending on what you want to happen when you resize the window).
Row 0 holds the first button, row 1 holds the second, and row 2 takes up the rest of the GUI. Then, the canvas can span all three rows.
So, start by adding a non-zero weight to the third row so that all unallocated space goes to it.
root.grid_rowconfigure(2, weight=1)
Next, add your buttons to rows 0 and 1:
upgradeBtn.grid(row=0, column=1)
WatchRunBtn.grid(row=1, column=1)
And finally, have your canvas span all three rows. It will force the window to grow larger, with all extra space being given to the third row. This allows the first two rows to retain their natural small height.
WatchRun.grid(row=0, column=0, rowspan=3)
An arguably better solution involves using pack and an extra frame or two. The UI seems to clearly have two logical sections to it: a canvas on the left and a column of buttons on the right. So, you can create one frame for the left and one for the right. Then, put the buttons in the right frame and the canvas in the left frame.

See my comments that indicates the changes in the code. If you have questions left on it, let me know. You may find this Q&A helpfull as well. PEP 8
import tkinter as tk #no wildcard imports
#free functions under imports to ensure function is defined
def show_frame(widget, widget2):
'''common function for upgrade_btn and watchrun_btn
- packs the appropiated widget in the left_frame'''
widget.pack_forget()
widget2.pack()
distance = 0
started = 0
money = 0
#variable names lowercase
root = tk.Tk()
#split window in two containers/master/frames
left_frame = tk.Frame(root)
right_frame= tk.Frame(root)
#leftframe content
watchrun = tk.Canvas(left_frame, bg="green", width=600, height=500)
upgrade = tk.Frame(left_frame, bg="yellow", width=600, height=500)
#rightframe content
upgrade_btn = tk.Button(right_frame, text="Upgrades", width=9, command=lambda: show_frame(watchrun, upgrade))
watchrun_btn = tk.Button(right_frame, text="Watch run", width=9, command=lambda: show_frame(upgrade, watchrun))
#geometry management
left_frame.pack(side=tk.LEFT)
right_frame.pack(side=tk.RIGHT,fill=tk.Y)
watchrun.pack()
upgrade_btn.pack()
watchrun_btn.pack()
root.mainloop()

Related

How can I make a self-generating button?

I'm attempting to create a function that will create a button named after a variable every time the submit to database function is used (1 button for every dataset submitted to my database). Currently I can get the button to name itself, but I'm having trouble offsetting the line for each time it's used. I could just add a simple counter and set row=n, but at this point I'm not sure my approach to the problem is the best. For every dataset there needs to be a button generated for it. Would it be best to approach this by trying to run a program that makes buttons based on my dataset (not exactly sure how I'd do this) or by having my submit return a button with row=counter? Or is there another solution I haven't thought of?
**Note: Looking back on this my buttons don't stay after the program restarts which makes sense. If I'm going to use the submit/counter solution I'll also have to make the data store itself in the code.
**Minimal reproducible code:
from tkinter import *
root = Tk()
root.title('Button')
root.geometry('400x400')
f_name = Entry(root, width=30)
f_name.grid(row=0, column=1)
f_name_lbl = Label(root, text="First Name:")
f_name_lbl.grid(row=0, column=0)
def gen_button():
auto_button = Button(root, text=f_name.get())
auto_button.grid(row=2, column=0, columnspan=2)
submit_btn = Button(root, text="Submit:", command=gen_button).grid(row=1, column=0, columnspan=2, ipadx=100)
I believe I did that right, I apologize I'm new to python and stack overflow
If you do not specify a row, grid will automatically pick the next row after the last occupied row.
auto_button.grid(column=0, columnspan=2)
Though, if you create a separate frame for these buttons you can use pack which is a bit easier since pack aligns widgets along a side. In your case you want them aligned to the top of the frame, just below any other widgets.
def gen_button():
auto_button = Button(button_frame, text=f_name.get())
auto_button.pack(side="top")
submit_btn = Button(root, text="Submit:", command=gen_button)
submit_btn.grid(row=1, column=0, columnspan=2, ipadx=100)
button_frame = Frame(root)
button_frame.grid(row=2, column=0, columnspan=2, sticky="nsew")

"Sticky" paramter doesnt change the window at all

I am just building my first tkinter window and I am a little puzzled. I created a label and a Button and set them in them same row but different columns.
Now the placement of the button changes depending on the length of the text in the label. To fix this I wanted to "stick" the label and the button to east/west respectively and add a small amount of padding.
Now the issue, no matter what variant of "sticky" I add it doesn't affect the placement of anything at all.
Below are the two code variants that all lead to the same output window:
from tkinter import *
def Btt_ShowAll_clicked():
print("klicked")
#create the window
Main_Window = Tk()
#Modify the Window
Main_Window.title("Ressourcen Verwaltung")
Lbl_Descr_a = Label(Main_Window, text = "Einträge einsehen")#Create Label
Lbl_Descr_a.grid(column=0, row=0, padx=10) #Show Label
Btt_ShowAll_a = Button(Main_Window, text="Einträge anzeigen")
Btt_ShowAll_a.bind("<Button-1>",Btt_ShowAll_clicked)#Button click starts function
Btt_ShowAll_a.grid(column=1, row=0, padx=10, pady=10, sticky=W)
#In the line above Change "W" to "E" ord delet the "sticky = W" alltogether and nothing changes in the window
Main_Window.geometry('350x200') #Window size
#Make the windows stay (loop)
Main_Window.mainloop()
What can I do to get the desired output?
Shouldn't "sticky" stick it to the given side and then with padx I should be able to choose how close it is said side?
As far as I know, you have to type sticky='w' instead of sticky=W, should work then.

Tkinter - How to center a Widget (Python)

I started with the GUI in Python and have a problem.
I've added widgets to my frame, but they're always on the left side.
I have tried some examples from the internet, but I did not manage it .
I tried .place, but it does not work for me. Can one show me how to place the widgets in the middle?
Code:
import tkinter as tk
def site_open(frame):
frame.tkraise()
window = tk.Tk()
window.title('Test')
window.geometry('500x300')
StartPage = tk.Frame(window)
FirstPage = tk.Frame(window)
for frame in (StartPage, FirstPage):
frame.grid(row=0, column=0, sticky='news')
lab = tk.Label(StartPage, text='Welcome to the Assistant').pack()
lab1 = tk.Label(StartPage, text='\n We show you helpful information about you').pack()
lab2 = tk.Label(StartPage, text='\n \n Name:').pack()
ent = tk.Entry(StartPage).pack()
but = tk.Button(StartPage, text='Press', command=lambda:site_open(FirstPage)).pack()
lab1 = tk.Label(FirstPage, text='1Page').pack()
but1 = tk.Button(FirstPage, text='Press', command=lambda:site_open(StartPage)).pack()
site_open(StartPage)
window.mainloop()
After you have created window, add:
window.columnconfigure(0, weight=1)
More at The Grid Geometry Manager
You are mixing two different Layout Managers. I suggest you either use The Grid Geometry Manager or The Pack Geometry Manager.
Once you have decided which one you would like to use, it is easier to help you :)
For example you could use the Grid Geometry Manager with two rows and two columns and place the widgets like so:
label1 = Label(start_page, text='Welcome to the Assistant')
# we place the label in the page as the fist element on the very left
# and allow it to span over two columns
label1.grid(row=0, column=0, sticky='w', columnspan=2)
button1 = Button(start_page, text='Button1', command=self.button_clicked)
button1.grid(row=1, column=0)
button2 = Button(start_page, text='Button2', command=self.button_clicked)
button2.grid(row=1, column=1)
This will lead to having the label in the first row and below the two buttons next to each other.

Can you fit multiple buttons in one grid cell in tkinter?

I'm making a table, and the grid of the table is going to be filled with buttons, Is it possible to fit more than one button in a grid space?
Yes you can. Put a frame inside the cell, and then you can put whatever you want inside the frame. Inside the frame you can use pack, place or grid since it is independent from the rest of the widgets.
For example:
import Tkinter as tk
root = tk.Tk()
l1 = tk.Label(root, text="hello")
l2 = tk.Label(root, text="world")
f1 = tk.Frame(root)
b1 = tk.Button(f1, text="One button")
b2 = tk.Button(f1, text="Another button")
l1.grid(row=0, column=0)
l2.grid(row=0, column=1)
f1.grid(row=1, column=1, sticky="nsew")
b1.pack(side="top")
b2.pack(side="top")
root.mainloop()
#jasonharper already provided the answer, but here's some code to go along with it.
This is just a random example with a bunch of buttons / frames using grid / pack. The pack for the buttons was arbitrary you could have used grid instead Each grid section has a random padx to show that it's in a different column and each different column within the grid contains multiple buttons
import tkinter as tk
root = tk.Tk()
#Now you want another frame
for i in range(5):
gridframe = tk.Frame(root)
for j in range(3):
tk.Button(gridframe, text="%d%d" % (i, j)).pack(side=tk.LEFT)
gridframe.grid(row=0, column=i, padx=20)
root.mainloop()

Raise Frame on top in PanedWindow in tkinter python 3.5

I have a frame in PanedWindow which i need on every tkinter GUI (in this case it's topFrame). Below it are many frames and I want to switch between those frame on button click (just like in any software where the top portion of screen is fixed and clicking on buttons the lower portion of GUI changes).
I know i need grid layout for it. But, it is not happening and i am not getting a solution anywhere.I have researched a lot on this topic everywhere but this solution is nowhere. Here is my code... i have written in comments those code which i feel are not working fine.
#python 3.5
from tkinter import *
#function to raise the frame on button click
def raiseFrame(frame):
frame.tkraise()
m = PanedWindow(height=500, width=1000, orient=VERTICAL)
m.pack(fill=BOTH, expand=1)
#to expand the column and row to fill the extra space
m.grid_columnconfigure(index=0, weight=1) #this code is not working as it should
m.grid_rowconfigure(index=0, weight=1) #this code is not working as it should
#top frame has two buttons which switches the bottom frames
topFrame = Frame(m, bg="blue")
m.add(topFrame)
button1 = Button(topFrame, text="Raise Frame 2", command=lambda: raiseFrame(frame2)) #raises frame 2 on clicking it
button1.pack(side=LEFT)
button2 = Button(topFrame, text="Raise Frame 1", command=lambda: raiseFrame(frame1)) #raises frame 1 on clicking it
button2.pack(side=LEFT)
#bottomframe acts as container for two other frames which i need to switch
bottomframe = Frame(m, bg="orange")
m.add(bottomframe)
frame1 = Frame(bottomframe, bg="yellow")
frame1.grid(row=0, column=0, sticky="news") ## sticky is not working ##
frame2 = Frame(bottomframe, bg="green")
frame2.grid(row=0, column=0, sticky="news") ## sticky is not working ##
label1 = Label(frame1, text="i should change")
label1.pack(padx=10, pady=10)
label2 = Label(frame2, text="i am changed !!")
label2.pack(padx=10, pady=10)
mainloop()
1)Please correct my code.
2)Explain me why in the "topFrame" even though i have not written
topFrame.pack(fill=BOTH, expand=True)
my code is showing the above property and it's expanding as well as filling both X and Y.
Same goes for the bottomFrame, it's orange colour is filling the entire space which does not happen in normal frames. So, is it some special feature of PanedWindow ?
You don't want to call topFrame.pack() and m.add(topFrame). You either pack/place/grid the window, or you add it to a paned window, you don't do both.
Also, if the frame is going to be in the paned window it needs to be a child of the paned window rather than a child of the root window.

Resources