How to make a floating Frame in tkinter - python

For my tkinter app I want to make a frame that would on top of other widgets, not taking any space (like html position fixed).
The Frame will have to contain widgets, if Frame is not possible labels or buttons will do.
I have no idea how to do it so haven't tried anything yet. Please help!

Here is a demonstration of place manager.
Remarks in the code explain behaviour.
import tkinter as tk
root = tk.Tk()
root.geometry("868x131")
button = tk.Button(root, text = "A Button")
button.place(x = 2, y = 2)
Frame = tk.LabelFrame(root, text = "A LabelFrame using place", bg="cyan")
# Frame using place with x, y, width, height in absolute coordinates
Frame.place(x = 250, y = 20, width = 600, height = 70)
ButtonInFrame = tk.Button(Frame, text = "A Button IN LabelFrame", bg="white")
# Note: place x,y is referenced to the container (Frame)
# Note: place uses just x,y and allows Button to determine width and height
ButtonInFrame.place(x = 2, y = 2)
Label = tk.Label(root, text = "A Label on LabelFrame\nWith multiple lines\nOf Text.", bg="light green")
# Label sits on top of buttonFrame and Frame
# Note place uses just x,y and allows Label to determine width and height
Label.place(x = 330, y = 60)
root.mainloop()

Related

Python tkinter - place function not working with multiple widgets

When making a program for a game database, I want to have the main menu to have multiple buttons leading to multiple topics. The place function isn't working for me. Here is my code:
windowFU = tk.Tk()
windowFU.title("MHFU Database")
windowFU.geometry("255x200+300+180")
frame = tk.Frame(master = windowFU, width = 255, height = 200)
frame.pack()
icon = tk.PhotoImage(file = "images/icon.png")
windowFU.iconphoto(False, icon)
welcome = tk.Label(
master = frame,
text = "What would you like to view?",
width = 30,
height = 2
)
searchEntry = tk.Entry(
master = frame,
width = 30
)
buttonMonstersFU = tk.Button(
master = frame,
text = "Monsters",
width = 12,
height = 2
)
# Here is the place function
buttonMonstersFU.place(x = 100, y = 100)
welcome.pack()
searchEntry.pack()
buttonMonstersFU.pack()
searchEntry.bind('<Return>', getEntry)
windowFU.mainloop()
Note: Currently I just have the place function set to x = 100, y = 100 to test, it is not my final location.
Here is an image of what I get:
Result
What should I do?
You call buttonMonstersFU.pack() a few lines after you call buttonMonsersFU.place(...). Only one of pack, place, or grid can be responsible for a widget. When you call pack, any work done by place will be thrown away.
If you want to use place, then remove the line that calls buttonMonstersFU.pack().

How to set the size of a label inside a panel in tkinter - python

I have three problems!
Problem 1: I am creating a label inside a panel and its size can be changed. I want to keep it fixed. It's possible?
If you move the cursor over the upper and lower limits of the label, you will see that the cursor changes to the screen adjustment format.
Problem 2: My button is taking up the entire dimension of the panel. How to resize it without creating an empty label below?
Problem 3: The scales also occupy the entire panel horizontally. Is it possible to change its size?
from tkinter import*
import tkinter
root = Tk()
root.geometry('900x500')
var_a = DoubleVar()
var_b = DoubleVar()
############# CREATING PANELS #####################
#----------- General Panel --------------#
panel_1 = PanedWindow(bd=4,orient = HORIZONTAL ,relief="raised")#, bg = "red")
panel_1.pack(fill=BOTH, expand=1)
#----------- Fist Panel --------------#
panel_3 = PanedWindow(panel_1, orient = VERTICAL, relief="raised")#, bg = "yellow")
panel_1.add(panel_3, minsize=200) #inserting on panel_1
#----------- Second Panel --------------#
panel_2 = PanedWindow(panel_1, orient = VERTICAL, relief="raised")#, bg = "blue")
panel_1.add(panel_2, minsize=800) #inserting on panel_1
label2=Label(panel_3,text="Pass the cursor below me")
panel_3.add(label2)
textbox2=Scale(panel_3,orient=HORIZONTAL,variable = var_a)
panel_3.add(textbox2)
label4=Label(panel_3,text="Pass the cursor above me too")
panel_3.add(label4)
textbox4=Scale(panel_3,orient=HORIZONTAL,variable = var_b)
panel_3.add(textbox4)
def bla():
pass
button1 = Button(panel_3,text="Why I have this size?", height = 1, width = 1, command= bla())
panel_3.add(button1)
tkinter.mainloop()
Based on your problem it is clear that when you add the pack() method and the expand parameter in the panel, what is being it is that all widget containers or panels expand according to their parent container, each widget fits according to the container.
From what I was able to infer from your problem what you can do is add the pack() method with the fill=X parameter by referencing that it only fits a horizontal direction to the button and using the widget expansion in the panel by adding the expand=1 parameter.
Here's what I did:
from tkinter import*
import tkinter
root = Tk()
root.geometry('900x500')
var_a = DoubleVar()
var_b = DoubleVar()
############# CREATING PANELS #####################
#----------- General Panel --------------#
panel_1 = PanedWindow(bd=4,orient = HORIZONTAL ,relief="raised")#, bg = "red")
panel_1.pack(fill=BOTH, expand=1)
#----------- Fist Panel --------------#
panel_3 = PanedWindow(panel_1, orient = VERTICAL, relief="raised")#, bg = "yellow")
panel_1.add(panel_3, minsize=200) #inserting on panel_1
#----------- Second Panel --------------#
panel_2 = PanedWindow(panel_1, orient = VERTICAL, relief="raised")#, bg = "blue")
panel_1.add(panel_2, minsize=800) #inserting on panel_1
label2=Label(panel_3,text="Pass the cursor below me")
panel_3.add(label2)
textbox2=Scale(panel_3,orient=HORIZONTAL,variable = var_a)
panel_3.add(textbox2)
label4=Label(panel_3,text="Pass the cursor above me too")
panel_3.add(label4)
textbox4=Scale(panel_3,orient=HORIZONTAL,variable = var_b)
panel_3.add(textbox4)
def bla():
pass
button1 = Button(panel_3,text="Why I have this size?", height = 0, width = 0, command= bla())
panel_3.add(button1)
button1.pack(fill=X, expand=1) # Only fits in X (horizontal direction), expands according to the panel
tkinter.mainloop()
Note that if you want to resize the scale widgets you can using the width and height parameters but this only works for geometry managers such as place(), in this case you can use the geometry manager pack() to maintain the relationship and expansion according to the panel, however you cannot change its width since being a geometry manager that controls and organizes the widgets you can only expand and change it the height of the scale using in this case width to change the height of the scale widget.
For more information see more information about the geometry manager pack()

tkinter useing grid on a frame doesn't seem to work

Im trying to get a tkinter gui that lets the user sign in im really new to tkinter. I made a frame and when i use grid to put another frame widget inside of the frame it does it based off of the root and not the inner_frame thats what I think is happening. In the code I made a grey box to demonstrate and I dont understand why it is below the blue frame and not inside the yellow frame under the "sign in" text. Thanks for the help.
from tkinter import *
root = Tk()
root.title("sighn in test")
#colors
background = "#273E47"
accent = "#d8973c"
red = "#bb4430"
white = "#edf2f4"
#this creates and places the background frame
main_buttons_frame = Frame(root, height = 500, width = 400, bg = background).grid(row = 0, column = 0)
#this creates and places the inner frame
inner_frame = Frame(main_buttons_frame, height = 450, width = 300, bg = accent).grid(row = 0, column = 0)
#this creates and places the "sighn in text"
top_text = Label(inner_frame, text = "sign in", font = ("helvitica", 30, "bold"), bg = accent, fg =
background).grid(row = 0, column = 0)
#this is a test to demonstrate
test_frame = Frame(inner_frame, bg = "grey", height = 100, width = 100).grid(row = 1, column = 0)
root.mainloop()
You have very common mistake of beginners
inner_frame = Frame(...).grid...()
It assigns None to inner_frame because grid()/pack()/place() gives None.
So later Frame(inner_frame, ..) means Frame(None, ..) and it adds to root
You have to do it in two steps
inner_frame = Frame(...)
inner_frame.grid(...)
And now you have Frame assigned to inner_frame
EDIT:
With correctly assigned widgets I get
and now gray box is inside yellow frame but image shows different problem - grid()/pack() automatically calculate position and size and external frame automatically change size to fit to child's size.
Using .grid_propagate(False) you can stop it
But it shows other problem - cells don't use full size of parent so yellow frame is moved to left top corner, not centered :) Empty cells have width = 0 and heigh = 0 so moving all to next row and next column will not change it - it will be still in left top corner :)
You need to assign weight to column and/or row which decide how to use free space. All columns/rows has weight=0 and when you set weight=1 then this row and/or column will use all free space - (this would need better explanation) - and then element inside cell will be centered.
main_buttons_frame.grid_columnconfigure(0, weight=1)
main_buttons_frame.grid_rowconfigure(0, weight=1)
import tkinter as tk # PEP8: `import *` is not preferred
# --- colors ---
background = "#273E47"
accent = "#d8973c"
red = "#bb4430"
white = "#edf2f4"
# --- main ---
root = tk.Tk()
root.title("sighn in test")
main_buttons_frame = tk.Frame(root, height=500, width=400, bg=background) # PEP8: without spaces around `=` inside `( )`
main_buttons_frame.grid(row=0, column=0)
#main_buttons_frame = None
main_buttons_frame.grid_propagate(False)
main_buttons_frame.grid_columnconfigure(0, weight=1)
main_buttons_frame.grid_rowconfigure(0, weight=1)
inner_frame = tk.Frame(main_buttons_frame, height=450, width=300, bg=accent)
inner_frame.grid(row=0, column=0)
#inner_frame = None
inner_frame.grid_propagate(False)
inner_frame.grid_columnconfigure(0, weight=1)
inner_frame.grid_rowconfigure(0, weight=1)
top_text = tk.Label(inner_frame, text="sign in", font=("helvitica", 30, "bold"), bg=accent, fg=background)
top_text.grid(row=0, column=0,)
#top_text = None
#top_text.grid_propagate(False)
#top_text.grid_columnconfigure(0, weight=1)
#top_text.grid_rowconfigure(0, weight=1)
test_frame = tk.Frame(inner_frame, bg="grey", height=100, width=100)
test_frame.grid(row=1, column=0)
#test_frame = None
#test_frame.grid_propagate(False)
#test_frame.grid_columnconfigure(1, weight=1)
#test_frame.grid_rowconfigure(0, weight=1)
root.mainloop()
BTW: PEP 8 -- Style Guide for Python Code

tkinter Canvas method seemilar to Text .see()

I have a Canvas full of buttons, whose ID's I store in a dictionary.
The canvas is very long, with vertical scrollbars.
There is a way to automatically position the view at a given button?
When using a Text, txt.see(position) usually works,
but I see Canvas has no see.
The only possible alternative seems to be .focus(), but cv.focus(ID) doesnt seem to do what I want
There is no ready made function to do that, but you can implement one using yview_moveto(fraction), where fraction is the top fraction of the canvas that will be set off-screen. So, yview_moveto(0) displays the top of the canvas and yview_moveto(1) the bottom.
What we need is to compute the fraction y/h that will display the button identified by iid. h is the height of the content of the canvas and y the height at which the button is in the canvas. I computed them using the canvas bounding box:
def show(iid):
bbox = canvas.bbox('all')
h = bbox[3] - bbox[1]
y = canvas.coords(iid)[1] - bbox[1]
canvas.yview_moveto(y/h)
And below is a small example, type the button ID (between 1 and 20) in the entry and click on 'Show' to shift the view to see it.
import tkinter as tk
def show(iid):
bbox = canvas.bbox('all')
h = bbox[3] - bbox[1]
y = canvas.coords(iid)[1] - bbox[1]
canvas.yview_moveto(y/h)
root = tk.Tk()
canvas = tk.Canvas(root, bg='white')
canvas.pack(fill='both', expand=True)
e = tk.Entry(root)
e.pack()
tk.Button(root, text='Show', command=lambda: show(e.get())).pack()
buttons = {}
for i in range(1, 21):
b = tk.Button(canvas, text='Button %i' % i)
iid = canvas.create_window(0, 30*i, anchor='nw', width=70, height=30, window=b)
buttons[iid] = b
canvas.configure(scrollregion=canvas.bbox('all'))
root.mainloop()

tkinter pack doesn't expand through whole area

Having issues packing two frames to get an expected outcome as defined below:
Frame 1 to have a width of 150 and scale on Y value, color of blue.
Frame 2 to scale on both X and Y, color of red.
So that when the window is resized the frame 1 keeps its x position and the frame 2 will scale.
As seen in picture below:
Expected Outcome
So this was the code used:
import tkinter as tk
frame1 = tk.Frame(bg = 'blue', width= 150, height = 150)
frame2 = tk.Frame(bg = 'red')
frame1.pack(fill = tk.Y, expand = 1, anchor = tk.W, side = tk.LEFT)
frame2.pack_propagate(True)
frame2.pack(fill = tk.BOTH, expand = 1, anchor = tk.E, side = tk.RIGHT)
Though it produces this: Actual Outcome
A requirement for this is I still need to use pack, but stuck on how to get the outcome required.
Your only problem is that frame1 needs expand to be set to False or 0.
By setting it to 1, you're asking Tkinter to expand the area given to the left frame. Since you only have it fill in the "y" direction, the frame on the left doesn't fill up that extra area even though it has been allocated, which is why you see the blank space between the left and the right side.
fairly simple, if you want them to stack side by side you need to keep stacking them from the same side, and the first frame doesn't need expand=1
try the following:
import tkinter as tk
root = tk.Tk()
frame1 = tk.Frame(bg = 'blue', width= 150, height = 150)
frame2 = tk.Frame(bg = 'red')
frame1.pack(fill = tk.Y, anchor = tk.W, side = tk.LEFT)
frame2.pack_propagate(True)
frame2.pack(fill = tk.BOTH, expand = 1, anchor = tk.E, side = tk.LEFT)
root.mainloop()

Categories