I am trying to get a label to be center justified between a given width, but it's not working. What am I doing wrong?
from tkinter import *
from tkinter.ttk import *
def main():
root = Tk()
root.geometry("200x100")
root.minsize(0,0)
root.resizable(0,0)
a = Label(master=root, text="Hello World", justify="center", background="red")
a.pack()
a.place(x=0,y=0, width=120)
mainloop()
main()
The text is properly justified in the label. The problem is that you didn't tell the label to stretch to fill the window. To do that, pack it like this:
a.pack(fill="x")
Also, it serves no purpose to call pack and then immediately call place -- only the last one will have any effect. Plus, you should avoid using place unless you have no other choice. Place is fine, but it makes your program harder to maintain, and harder to get it to grow and shrink.
Related
I'm aware this is probably a newb question, but I have yet to be able to find an answer. Here's a snippet of my code, that has a root window containing a button to open a Toplevel. The Toplevel pulls a random line from a text file to function as a sort of idea generator.
import random, fileinput
import tkinter as tk
from tkinter import *
root = tk.Tk()
root.title('Daydreamer')
#fname should be the file name of the image in working directory
fname = "bg.gif"
bg_image = tk.PhotoImage(file=fname)
#get width and height of image
w = bg_image.width()
h = bg_image.height()
#size window correctly
root.geometry("500x400")
cv = tk.Canvas(width=w, height=h)
cv.pack(side='top', fill='both', expand='yes')
cv.create_image(0,0,image=bg_image,anchor='nw')
#add a frame for text
mainframe=tk.Frame(root)
#new window for inspirations
def inspirations():
top = Toplevel(root)
top.geometry=("100x100")
top.title("Inspiration")
def idea():
textidea=None
for line in fileinput.input('textlist.txt'):
if random.randrange(fileinput.lineno())==0:
textidea=line
entrytext=tk.Text(top)
entrytext.insert(INSERT, textidea)
entrytext.insert(END, "Or press the Inspire Me button again for another idea!")
entrytext.pack()
idea()
top.mainloop()
#add buttons
btn1 = tk.Button(cv, text="Inspire Me", command=inspirations)
btn1.pack(side='left', padx=10, pady=5, anchor='sw')
root.mainloop()
Problem is, that Toplevel always comes out absolutely huge (larger than my root window), which looks incredibly silly for the small amount of content being displayed in it. Am I missing something really minute and stupid here? Help much appreciated.
The problem is that you aren't calling the geometry method, you're replacing it with a string.
Change this:
top.geometry=("100x100")
to this:
top.geometry("100x100")
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.
Hi I didn't really understand how furas made the below code work. Why didn't he get an error message about grid and pack on the same root when he added a box? In the addbox function he sets a frame to the root which is pack already and even uses the pack inside the function and then uses the grid.
Can someone please explain to me how this "magic" works?
a link to the his answer:
Creating new entry boxes with button Tkinter
from Tkinter import *
#------------------------------------
def addBox():
print "ADD"
frame = Frame(root)
frame.pack()
Label(frame, text='From').grid(row=0, column=0)
ent1 = Entry(frame)
ent1.grid(row=1, column=0)
Label(frame, text='To').grid(row=0, column=1)
ent2 = Entry(frame)
ent2.grid(row=1, column=1)
all_entries.append( (ent1, ent2) )
#------------------------------------
def showEntries():
for number, (ent1, ent2) in enumerate(all_entries):
print number, ent1.get(), ent2.get()
#------------------------------------
all_entries = []
root = Tk()
showButton = Button(root, text='Show all text', command=showEntries)
showButton.pack()
Thanks
There's no magic, it's just working as designed. The code uses pack in the root window, and uses grid inside a frame. Each widget that acts as a container for other widgets can use either grid or pack. You just can't use both grid and pack together for widgets that have the same master.
not really an answer but I think you will be helped by the link.
tkinter and it's layout is indeed a bit hard to understand.
I never understood how to deal with it until I stumbled over this presentation which explained the layout particulars in a way where I finally could get the hang of it.
Just putting it out there for others to find as well.
tkinter tutorial by beazley
I think you miss out on what pack and grid actually are. Consider such code:
import tkinter as tk
root = tk.Tk()
myFrame = tk.Frame(root)
myFrame.pack()
myButton1 = tk.Button(myFrame, text='This is button 1')
myButton2 = tk.Button(myFrame, text='This is button 2')
myButton1.grid(row=0, column=0)
myButton2.grid(row=1, column=0)
root.mainloop()
By creating root we create a new window. In this window we will put everything else. Then we create myFrame. Note, that the actual "thing" (in more adequate terms - widget) is created in line myFrame = tk.Frame(root). Note, that we have to specify where we are going to put this widget in brackets and we've written that it is going to be root - our main window. Blank frame probably isn't the best example since you can not see it being placed (not unless you use some more specifications at least), but still. We have created it, but not placed it in our user interface. The we use .pack() to place it. Now you refer to widgets as being used as packs or grids. That is not true though. Pack and grid are just the set of rules, on which the widgets are being placed inside some kind of window. Because of that, if you want to add something more to the root in our case, you will have to use .pack() again. Why? If you will give two sets of rules on how to place things on the screen for your computer - they will most likely conflict with each other. However, if we go one more level down and now want to place something inside our myFrame, we can again choose which set of rules to use. It is because it does not matter, where our frame is going to end up inside root, we now just want to specify where our Buttons 1 and 2 are going to end up inside the frame. Therefore we can again use .pack() or switch to .grid().
To conclude: .pack(), .grid() and .place() are sets of rules on how place widgets inside other widgets. In more general terms though these are rules on how place boxes in other boxes. One boxes in which we arrange other boxes can only have one set of rules.
I hope this example helps.
What is the equal function of PyQt setGeometry() in tkinter? Or Is there any function works like that? I searched a bit but couldn't find, all of examples looks like tkinter works on a specific widget and we just can set width-height.
EDIT:
The setGeometry() does two things. It locates the window on the
screen and sets its size. The first two parameters are the x and y
positions of the window. The third is the width and the fourth is the
height of the window.
The nearest thing tkinter has is probably the place geometry manager. With it you can set the x,y coordinates (either absolute or relative) and the width and height attributes (also absolute or relative).
For example, to place a label at 100,100 and with a width and height 50% of its parent you would do something like this:
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text="Hello, world")
label.place(x=100, y=100, relwidth=.5, relheight=.5)
root.mainloop()
However, place is very rarely the right choice. Tkinter is very smart about picking the right size for widgets, and for laying them out. Without knowing the actual problem you're trying to solve it's hard to give good recommendations, but almost certainly, pack or grid will work better.
You can also do something similar to #BryanOakley's answer by the grid geometry manager:
from tkinter import *
root = Tk()
label = Label(root, bg='cyan')
label.grid(ipadx=100, ipady=50, padx=50, pady=50)
UPDATE: Please feedback why you think this answer is not useful.
I'm trying to source out code for a GUI program. I made a simple test and I cannot change the text value on the GUI, no error and nothing happens. Some issue with the mainloop of Tkinter?
serial.py:
import gui
gui.chiplabel.config(text="A.3f V" )
gui.py:
from Tkinter import *
root = Tk()
chiplabel = Label(root, relief=RIDGE, width = 9 , text ="Unknown",
padx=0, pady=0).grid(row = 0,column=5, sticky =W)
root.mainloop()
You have two main problems with your code. It needs to be restructured, and you're making a very common mistake with laying out your widgets.
Organizing your code
The way you have your code structured, your call to configure happens after mainloop exits, and after the widgets have been destroyed. You need to reorganize your code so that the call to mainloop is the last line of code that is executed.
In my opinion this is best accomplished by using classes and objects, but that's not strictly necessary. You simply need to not have any code after you call mainloop.
Laying out the widgets
The problem is this line:
chiplabel = Label( root, relief=RIDGE, width = 9 , text ="Unknown", padx=0, pady=0).grid(row = 0,column=5, sticky =W)
In python, when you do x=y().z(), x is given the value of z(). So, when you do chiplabel = Label(...).grid(...), chiplabel is given the value of grid(...). Grid always returns None, so chiplabel will always be None. Because of this, you can't reconfigure it because you've lost the reference to the widget.
The solution is to create the widget and lay out the widget in two steps.
One way to do this would be to create the UI in a class, e.g.:
import Tkinter as tk # note don't use wildcard imports
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.chiplabel = tk.Label(
self,
padx=0,
pady=0,
relief=tk.RIDGE,
text="Unknown",
width=9,
) # note alphabetical order and consistent spacing
self.chiplabel.grid(
column=5,
row=0,
sticky=tk.W,
) # note grid is separate step
and don't run it in-place, so that you can import the class without running anything. Then your serial.py looks more like:
from gui import GUI
interface = GUI()
interface.chiplabel.config(text="A.3f V")
interface.mainloop()
If you want multiple frames, you could do something like Switching between frames in tkinter menu.