Python tkinter grid layout problems - python

I'm creating a very simple UI using Tkinter and python, but I'm having trouble sizing GUI elements and using the grid format to place them correctly. Here's a first-order approximation of what I'm trying to have:
Here's the code I have so far. I keep getting close, but I don't think I really understand what I'm doing. Any help is much appreciated!
#User interface
root = Tk()
window_width = root.winfo_screenwidth()
window_height = root.winfo_screenheight()
root.geometry ("%dx%d"%(window_width,window_height))
menu_bar = Menu(root)
menu = Menu(menu_bar, tearoff=0)
menu.add_command(label="Open", command = open_file)
menu.add_command(label="Save", command = save)
menu.add_separator()
menu.add_command(label="Quit", command = exit)
menu_bar.add_cascade(label="File",menu=menu)
root.config(menu=menu_bar)
#textbox is the window in which the code is written
textbox = Text(root, width=50, height = window_height/20+4)
#canvas is where the car will go
canvas_frame= Frame(root, width = window_width/1.5, height = window_height-200)
canvas_frame.configure(borderwidth=1.5,background='black')
canvas = Canvas(canvas_frame, width = window_width/1.5, height = window_height-200)
#console to print to
console = Text(root, width = int(window_width/1.5), height = 10)
run_button = Button(root, text = "Run", command = lambda:generate_program(textbox.get(1.0,END)))
clear_button = Button(root, text = "Clear text", command = clear)
#add them to frame
textbox.grid(row=0, column=0, rowspan=20, columnspan=10)
run_button.grid(row=21,column=0)
clear_button.grid(row=21,column=1)
canvas_frame.grid(row=0,rowspan=10,column=21,columnspan=25)
canvas.grid(row=0, rowspan=1, column=21, columnspan=25)
console.grid(row = 1, rowspan=1, column = 21, columnspan=25)
root.mainloop()

In my opinion, this is layout can be much easier with the pack geometry manager. One of the problems is that you are trying to make the width and the height of each widget fit in its place with rowspan and columspan options. Also, since canvasis inside a frame, you have to think that it is like inside a new window, so a simple call to canvas.grid() would be enough.
However, with pack() you just have to put textbox, run_button and clear_button inside a new frame:
left_frame = Frame(root)
textbox = Text(left_frame, ...)
run_button = Button(left_frame, ...)
clear_button = Button(left_frame, ...)
canvas_frame= Frame(root, ...)
canvas_frame.configure(borderwidth=1.5,background='black')
canvas = Canvas(canvas_frame, ...)
console = Text(root, ...)
left_frame.pack(side=LEFT)
textbox.pack()
run_button.pack(side=LEFT)
clear_button.pack()
canvas_frame.pack()
canvas.pack()
console.pack()

Related

Create scrollbar in tKinter without displacing widgets to the left (python)

I'm creating a program by learning from youtube tutorials (I'm a complete beginner) and I have come to some difficulties. This time, I'm trying to create a scrollbar, and I want my widgets to stay on the center of my window, not the left (I'm following the Codemy.com tutorial on scrollbars).
Here is the current aspect of my program:
with scrollbar
And here is how I want it to look:
without scrollbar
This is my code right now:
import tkinter as tk
root = tk.Tk()
root.geometry("600x400")
my_canvas = tk.Canvas(root)
my_canvas.pack(side = "left", fill = "both", expand = 1)
my_scrollbar = tk.Scrollbar(root, orient = "vertical", command = my_canvas.yview)
my_scrollbar.pack(side = "right", fill = "y")
my_canvas.configure(yscrollcommand = my_scrollbar.set)
my_canvas.bind("<Configure>", lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
my_frame = tk.Frame(my_canvas)
for i in range(100):
my_label = tk.Label(my_frame, text = "Label")
my_label.pack()
my_canvas.create_window((0,0), window = my_frame, anchor = "nw")
root.mainloop()
Include width = 600, anchor = "nw" in my_canvas declaration.
my_canvas.create_window((0,0), window = my_frame, width = 600, anchor = "nw")

Why isn't this frame in tkinter centered correctly?

I want this entry bar and other contents I'll add to the frame later to be centred correctly, I received this code that supposedly should work but it isn't.
import tkinter as tk
import math
import time
root = tk.Tk()
root.geometry()
root.attributes("-fullscreen", True)
exit_button = tk.Button(root, text = "Exit", command = root.destroy)
exit_button.place(x=1506, y=0)
frame = tk.Frame(root)
main_entry = tk.Entry(root, width = 100, fg = "black")
main_entry.place(x=50, y=50)
frame.place(relx=.5,rely=.5, anchor='center')
root.mainloop()
As you can see the frame isn't centred so how can I fix this?
In order to achieve widget centering on a fullscreen I've had to use grid manager.
The code below works but the exact positioning requires some fiddling with frame padding.
frame padx = w/2-300 and pady = h/2-45 are arbitrary values found using a bit of trial and error.
import tkinter as tk
root = tk.Tk()
root.attributes( '-fullscreen', True )
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
frame = tk.Frame( root )
main_entry = tk.Entry( frame, width = 100 )
main_entry.grid( row = 0, column = 0, sticky = tk.NSEW )
frame.grid( row = 0, column = 0, padx = w/2-300, pady = h/2-45, sticky = tk.NSEW )
exit_button = tk.Button( frame, text = 'Exit', command = root.destroy )
exit_button.grid( row = 1, column = 0, sticky = tk.NSEW )
tk.mainloop()
Frame automatically changes size to size of objects inside Frame (when you use pack()) but you have nothing inside Frame. You put all widgets directly in root - so Frame has no size (width zero, height zero) and it is not visible.
When I use tk.Frame(root, bg='red', width=100, height=100) then I see small red frame in the center.
You have two problems:
(1) you put Entry in wrong parent - it has to be frame instead of root,
(2) you use place() which doesn't resize Frame to its children and it has size zero - so you don't see it. You would have to set size of Frame manully (ie. tk.Frame(..., width=100, height=100)) or you could use pack() and it will resize it automatically.
I add colors for backgrounds to see widgets. blue for window and red for frame.
import tkinter as tk
root = tk.Tk()
root['bg'] = 'blue'
root.attributes("-fullscreen", True)
exit_button = tk.Button(root, text="Exit", command=root.destroy)
exit_button.place(x=1506, y=0)
frame = tk.Frame(root, bg='red')
frame.place(relx=.5, rely=.5, anchor='center')
main_entry = tk.Entry(frame, width=100, fg="black")
main_entry.pack(padx=50, pady=50) # with external margins 50
root.mainloop()

how do i use expand on entrys while i can position them python 3 tkinter

I want to make entrys move while resizing. an example is this code.
i didnt want to upload the 300 line code because it does not have to do anything with the design.
as you can see when you resize it horizontaly the entry2 is moving horizontaly how do i do the same for entry1?
i tried the pack(expand=True) on entry1 but then the entry2 is placed in the position of button.
im sorry if i cant make the problem clear this is my first time asking questions about programming...
import tkinter as tk
app = tk.Tk()
app.geometry("600x350")
Canvas = tk.Canvas(height=600, width=1000, bg="#343434")
Canvas.pack(fill="both", expand=True)
Frame = tk.Frame(Canvas, height=600, width=1000, bg="#1A1A1A")
Frame.pack(fill="both", expand=True)
large_font = ('Verdana', 17)
entry1 = tk.Entry(Frame, bg="#F7F5EB", font=large_font, width=25)
entry1.place(rely=0.3, relx=0.17)
entry1.configure(foreground="gray")
entry2 = tk.Entry(Frame, bg="#F7F5EB", width=22, font=("Verdana", 11))
entry2.pack(expand=True)
entry2.configure(foreground="gray")
generate_button_font = ('Arial', 12)
Generate_button = tk.Button(
Frame, bg="#f0f0f0", font=generate_button_font, foreground="#525252")
Generate_button.place(rely=0.7, relx=0.35, relwidth=0.31, relheight=0.12)
Generate_button.config(text="Generate")
help_button_font = ("Arial", 10)
help_button = tk.Button(Frame, bg="#F7F5EB", font=help_button_font, foreground="#525252")
help_button.place(rely=0.03, relx=0.88, relwidth=0.1, relheight=0.075)
help_button.config(text="Help")
about_button_font = ("Arial", 10)
about_button = tk.Button(Frame, bg="#F7F5EB", font=help_button_font, foreground="#525252")
about_button.place(rely=0.03, relx=0.02, relwidth=0.1, relheight=0.075)
about_button.config(text="About")
app.mainloop()
You can use the .place() geometry manager.
Read more about it here.
Place has 3 main paramaters which I think you need.
The first is relx. This places any widget at a certain position in a widget, relative to the widgets x legnth.
For example, if the relx = 0.5, then no matter how much you resize the widget, the widget stays in the middle.
The second parameter is rely, which works the same as relx, except it applies to the y axis.
The third paramater is anchor. Anchor basically tells the program which part of the widget should go to the specified coordinate. The default is "nw" or top left. You can put it as center, or anything else.
Hopefully this helps!!

python tkinter button in frame

It feels this should be easy but not as much as I would hope. All I want to do is put a button in a frame. My code is coloring the frame so I can validate the button is where I want to put it and as you can see, below, my code is not doing what I want/think. I expect my code to put the radio button inside the yellow frame - not under it.
from tkinter import *
class apiMain:
def main(self):
master=Tk()
topframe = Frame(master, bg="Lemon chiffon", width=500, height=50).pack(side = TOP)
v = IntVar()
crbutton = Radiobutton(topframe, text = "change request", variable = v, value = 'cr')
crbutton.pack(side = LEFT, padx = 10)
mainloop()
When you assign topframe like this:
topframe = Frame(master, bg="Lemon chiffon", width=500, height=50).pack(side = TOP)
You are essentially writing topframe = None, because pack() always returns None. Because of this, you're assigning the master of your radio button to None which defaults to the main window. Split your code up, so that topframe references the actual Frame object:
topframe = Frame(master, bg="Lemon chiffon", width=500, height=50)
topframe.pack(side = TOP)

Scrollbar into a python tkinter discussion

from tkinter import *
window = Tk()
ia_answers= "test\n"
input_frame = LabelFrame(window, text="User :", borderwidth=4)
input_frame.pack(fill=BOTH, side=BOTTOM)
input_user = StringVar()
input_field = Entry(input_frame, text=input_user)
input_field.pack(fill=BOTH, side=BOTTOM)
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
canvas = Canvas(window, borderwidth=0, background="white")
ia_frame = LabelFrame(canvas, text="Discussion",borderwidth = 15, height = 100, width = 100)
ia_frame.pack(fill=BOTH, side=TOP)
scroll = Scrollbar(window, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=scroll.set)
scroll.pack(side=RIGHT, fill=Y)
canvas.pack(fill=BOTH, expand=True)
canvas.create_window((4,4), window=ia_frame, anchor="nw")
ia_frame.bind("<Configure>", lambda event, canvas=canvas:onFrameConfigure(canvas))
user_says = StringVar()
user_text = Label(ia_frame, textvariable=user_says, anchor = NE, justify = RIGHT, bg="white")
user_text.pack(fill=X)
ia_says = StringVar()
ia_text = Label(ia_frame, textvariable=ia_says, anchor = NW, justify = LEFT, bg="white")
ia_text.pack(fill=X)
user_texts = []
ia_texts = []
user_says_list = []
ia_says_list = []
def Enter_pressed(event):
"""Took the current string in the Entry field."""
input_get = input_field.get()
input_user.set("")
user_says1 = StringVar()
user_says1.set(input_get + "\n")
user_text1 = Label(ia_frame, textvariable=user_says1, anchor = NE, justify = RIGHT, bg="white")
user_text1.pack(fill=X)
user_texts.append(user_text1)
user_says_list.append(user_says1)
ia_says1 = StringVar()
ia_says1.set(ia_answers)
ia_text1 = Label(ia_frame, textvariable=ia_says1, anchor = NW, justify = LEFT, bg="white")
ia_text1.pack(fill=X)
ia_texts.append(ia_text1)
ia_says_list.append(ia_says1)
input_field.bind("<Return>", Enter_pressed)
window.mainloop()
Hi, I try to build a GUI with tkinter but I've got two problems, the LabelFrame/Canvas doesn't fill entirely the window and I can't get the scrollbar to automatically scroll down.
Can you help me with that, thank you very much.
Ilan Rossler.
You need to manually control the width of the inner frame since it is being managed by the canvas. You can change the width in a binding to the <Configure> event of the canvas (ie: when the canvas changes size, you must change the size of the frame).
You'll need to be able to reference the window object on the canvas, which means you need to save the id, or give it a tag.
Here's an example of giving it a tag:
canvas.create_window((4,4), window=ia_frame, anchor="nw", tags=("innerFrame",))
And here's how to change the width when the canvas changes size:
def onCanvasConfigure(event):
canvas = event.widget
canvas.itemconfigure("innerFrame", width=canvas.winfo_width() - 8)
canvas.bind("<Configure>", onCanvasConfigure)
To scroll down, call the yview command just like the scrollbar does. You need to make this happen after the window has had a chance to refresh.
For example, add this as the very last line in Enter_pressed:
def Enter_pressed(event):
...
canvas.after_idle(canvas.yview_moveto, 1.0)

Categories

Resources