I'm having some trouble figuring out how to use grid properly with tkinter. I just want two labels to appear side by side.
When I do the following, they appear in a separate window from my app. I'm confused because I have buttons on my app that appear as I want them(not using grid), but I can't quite figure out the labels in a grid.
//this is just a snippet from a function
self.root = tk.Tk()
tk.Label(master=self.root, text=directory).grid(row=0,column=0)
tk.Label(master=self.root, text=directory).grid(row=0,column=1)
The root window is created in a different part of the app, so all I'm doing here is making another one (I think). I just want the labels to appear in the window that has already been created but I can't figure out what I'm supposed to reference it to.
This is in a separate file that includes the file with the code above
from Tkinter import *
import tkinter as tk
import widgetActions
import shutil
class mywidgets(widgetActions.Actions):
def __init__(self,root):
frame = tk.Frame(root)
self.makeMenuBar(frame)
frame.pack()
frame.config(width=400)
self.body()
return
def makeMenuBar(self,frame):
menubar = Frame(frame,relief=RAISED,borderwidth=1)
menubar.pack()
mb_file = Menubutton(menubar,text='file')
mb_file.pack(side=LEFT)
mb_file.menu = Menu(mb_file)
mb_file.menu.add_command(label='open', command = self.openfile)
mb_file.menu.add_command(label='close', command = menubar.quit)
mb_file['menu'] = mb_file.menu
return
def body(self):
self.filename()
def main():
root = tk.Tk()
k=mywidgets(root)
root.title('menu bar')
root.mainloop()
main()
You cannot create two instances of Tk. As you observed, you will get two windows. That's not the only problem, just the most obvious one.
You need to pass in a reference to the winget that is to contain these labels. Or, store the root window as a global variable, or as an attribute of an object.
To position the 2 labels side by side i.e label1 and label2:
label1.grid(column=0, row=0)
label2.grid(column=1, row=0)
That should do it
Related
from tkinter import *
from tkinter.ttk import *
root = Tk()
first_run = True
def update(txt):
global first_run
text1 = Label(root, text='')
if first_run:
text1.pack()
text1['text'] = txt
first_run = False
update('1')
update('2')
update('3')
root.mainloop()
When I run this, the text stays at '1', and the following 2 function calls are ignored. I find out that only if I use pack() again then it will be updated, but it creates a duplicate label and I do not want that.
Of course, I know that I am supposed to use a StringVar, but I have been using this method for all other widgets (buttons, label frames etc) and all of them works. I do not know why this particular case does not work.
Running on Python 3.9.9 on Windows 11
You aren't updating the label, you are creating a new label each time the function is called. To update any widget, use the configure method. For that, you need to create the label outside of the function (or, leave it in the function but add logic so that it's only created once). Usually it's best to create it outside the function so that the function is only responsible for the update.
from tkinter import *
from tkinter.ttk import *
root = Tk()
def update(txt):
text1.configure(text=txt)
text1 = Label(root, text='')
text1.pack()
update('1')
update('2')
update('3')
root.mainloop()
Note: since you call your function multiple times before the window is drawn you'll only see the final value. There are plenty of solutions to that on this site. Without knowing more about what your real program looks like it's hard to recommend the best solution to that problem.
I am doing a simple UI with TkInter in Python. I want a button at the top left, so I did something like
back = Button(explorer, text="Back")
back.pack(side="top", anchor="w")
where explorer is a frame, and I expected to see the button on top left, but it's stuck in the center. Any suggestions? I already tried to add side=TOP but it didn't work. What's the right way to do that?
As mentioned above, your parent frame should fill the x space.
This is an example of the parent frame not filling x:
import tkinter as tk
root = tk.Tk()
root.geometry('200x200')
element = tk.Frame(root)
element.pack() # No fill
tk.Button(element, text='No Fill').pack(anchor='w')
# To show the fill
tk.Label(root, text='Fill X', bg='green').pack(fill='x')
root.mainloop()
And the result is the button is in the center despite the anchor:
https://i.stack.imgur.com/DAgmH.png
But, make this change:
element.pack(fill='x')
And now your button will be in the top left like here:
https://i.stack.imgur.com/HoGGj.png
Your frame has to occupy all the horizontal space if you want to align its children. Something like (if you imported tkinter elements with from tkinter import *):
explorer.pack(fill=X)
Tkinter has 3 modules to set items:
.pack which puts everything more or less random
.grid where you can define the row and column
.place where you define everything in pixels
so you could use something like
import tkinter as tk
from tkinter import *
root = Tk()
button = Button(text = 'back')
button.grid(row = 1, column = 1)
root.mainloop()
This shows how to use the .grid function. Keep in mind that the size of each grid is defined by its largest content. If you have a long entry field , the column its placed in is going to be as wide as the entry.
you could also use the .place function, but this one requires the most work. For me its always guessing and rerunning until im happy with it.
import tkinter as tk
from tkinter import *
root = Tk()
button = Button(text = 'back')
root.geometry("150x100")
button.place(x=30, y=30)
root.mainloop()
So in summary, use .pack if you dont care, .grid if you want some kind of control and .place if you want to be accurate. And keep in mind to use only ONE at a time.
I am using tkinter Frames to divide the window and I have bound Frames using grid.
Now When I bind the widgets inside a frame using .grid(), it come out of frame automatically and get bound to main tkinter window.
If I use .pack(), it says : _tkinter.TclError: cannot use geometry manager pack inside . which already has slaves managed by grid
from tkinter import *
root = Tk()
myFrame = Frame(root,text="Frame1").grid(row=1,column=1)
MyLabel = Label(myFrame,text="Label inside Frame1").pack()
Any other method to bind widgets inside frame?
This is a side effect from laying out a widget on the same line as defining it. It causes the widgets to default to root. Try this:
from tkinter import *
root = Tk()
myFrame = Frame(root,text="Frame1")
myFrame.grid(row=1,column=1)
MyLabel = Label(myFrame,text="Label inside Frame1")
MyLabel.pack()
I am working on a program where I create some widgets in a for loop. So I need to get the name of them dynamically. I have set up is when the mouse enters the frame. Which holds the two text label widgets. I causes a function to run. And I want to change the background color of a widget with the name of noteName. But I seem to have run into a stopping point and I can no figure it out. I have searched online but could not find much. SO does anyone here know how to get the name of a widget?
Code:
def get_children_hover(event):
for widgets in event.widget.winfo_children():
#This is here where I can not seem to figure out how to get the widgets name.
Can someone push me into the right direction.
winfo_children() is the right thing to use but you are using it wrong. It is a method for parent widgets. (i.e. root, frame, canvas etc..)
Also:
If the order doesn’t matter, you can get the same information from the
children widget attribute (it’s a dictionary mapping Tk widget names
to widget instances, so widget.children.values() gives you a list of
instances).
simple example:
import tkinter as tk
def foo():
print ("Frame:", frm.winfo_children())
print ("Root:", root.winfo_children())
print ("children_values:", root.children.values())
root = tk.Tk()
frm = tk.Frame(root)
tk.Label(root,text="foo").pack()
btn = tk.Button(frm,text="FOOO",command=foo)
frm.pack()
btn.pack()
root.mainloop()
about your code:
def get_children_hover(event):
for widgets in root.winfo_children(): #assuming your Tk() instance named root
I used the code below (with different variable names for each section) to create a background image for each tkinter window. Each of these is initiated in a function and both work fine independently.
When loading one function from another however, the second fails to display an image. (I have tried importing all relevant in each function aswell). It works in the case that use tk.destruct(), however if If I want to keep it open, or hide it with . withdraw(), the image fails to display, rendering the second window useless.
background_image=tk.PhotoImage(...)
background_label = tk.Label(parent, image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
Ok I've made up a solution for you. Basically all you need is to use tk.Toplevel() for the second tkinter window and make sure that the 'parent' is root2 so the image will appear in the second window.
I have used buttons for the images, you had labels so you may wish to change this, but buttons gave me a way to open a new tk window easily, I have also used .pack(), not .place(), as it was faster for me. May also be helpful for you to know that I used python 3.3 with windows so you might need a capital T for tkinter.
import tkinter as tk
root1 = tk.Tk()
def new_window():
root2 = tk.Toplevel()
# click the last button and all tk windows close
def shutdown():
root1.destroy()
root2.destroy()
background_image2 = tk.PhotoImage(file = '...')
background_button2 = tk.Button(root2, image = background_image2, command = shutdown)
background_button2.pack()
root2.mainloop()
background_image1 = tk.PhotoImage(file = '...')
# have used a button not a label for me to make another tk window
background_button1 = tk.Button(root1, image = background_image1, command = new_window)
background_button1.pack()
root1.mainloop()
#user2589273 Next time you should add more code so answers can be easily given, and tailored to you, just a suggestion. Hope this helps.