How to get the size of a tkinter Button? - python

How can I get the size of a button object?
If I do:
quitButton = Button(self, text="Quit", command=self.quit)
_x = quitButton.winfo_width()
_y = quitButton.winfo_height()
print _x, _y
It prints 1 1.
What am I doing wrong?

The size will be 1x1 until it is actually drawn on the screen, since the size is partly controlled by how it is managed (pack, grid, etc).
You can call self.update() after you've put it on the screen (pack, grid, etc) to cause it to be drawn. Once drawn, the winfo_width and winfo_height commands will work.

Related

How can i change frame widget width in python

I'm trying to resize a frame in tkinter, but the width does not change and function winfo_width() returns 1. How can i fix it?
from tkinter import *
root = Tk()
root.geometry('400x300')
Frame = LabelFrame(root, text="Test", width = 200)
Frame.grid(row = 0, column = 0)
label = Label(Frame, text = '').grid(row = 0, column=0)
print(Frame.winfo_width()) #output is 1 instead of 200
root.mainloop()
The width is returning 1 because the window hasn't been drawn yet. The actual width depends on the window being drawn since the actual width depends on many factors which can't be known before the window is actually drawn.
If you call root.update() before calling Frame.winfo_width() to force the window to be drawn, you will see it displaying the actual value.
As for how to change the width, that question is too broad to answer. Normally it's not wise to directly set the width of a frame. Tkinter by default will automaticaly resize a frame to fit its children. So, one way to make the frame wider is to add more widgets.
The width can also depend on how it is added to the display - whether you're using pack or grid or place, and how you have configured them. So, another way to make the frame wider is to use non-default options that cause the frame to grow or shrink to fit the space given to it.
If you want to specify an explicit size and ignore tkinter's automatic resizing, you can do that by turning off geometry propagation and then setting the width and height parameters for the frame. Depending on whether you're using grid or pack, you can call grid_propagate or pack_propagate with a False argument to disable propagation (place doesn't support geometry propagation).
Note that turning off geometry propagation is usually the least desirable solution because it requires you to do a lot more work to create a responsive UI. The best way to design GUI with tkinter is to focus on the size of the inner widgets and let tkinter compute the most efficient size for frames and the window itself.
As the others have pointed out how to set a static size frame using grid_propagate() I will show you how to set up your frame to resize automatically.
You need to tell the row and column to expand that the frame is in. This is done with columnconfigure() and rowconfigure(). Then you need to tell the frame to stick to all sides with sticky='nsew'. Adding widgets to the frame is no different then any other container. Simply tell the widget to be in the frame.
One potention issue I see is you are overwriting Frame() on this line: Frame = LabelFrame(root, text="Test", width = 200). This is a good example why you should not use import *. Instead do import tkinter as tk and use the tk. prefix for anything that needs it.
Example:
import tkinter as tk
root = tk.Tk()
root.geometry('400x300')
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame = tk.LabelFrame(root, text='Test', width=200)
frame.grid(row=0, column=0, sticky='nsew')
label = tk.Label(frame, text='label').grid(row=0, column=0)
root.mainloop()
Results:
Update:
If you do want something static make sure you define both height and width. If you only define one or the other then you will not see the frame in the window.
For a testable example for a static frame size:
import tkinter as tk
root = tk.Tk()
root.geometry('400x300')
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
frame = tk.LabelFrame(root, text='Test', height=200, width=200)
frame.grid(row=0, column=0)
frame.grid_propagate(False)
label = tk.Label(frame, text='label').grid(row=0, column=0)
root.mainloop()
Results:
Your frame can propagate on the grid based on the widgets on it, and not have fixed dimensions.
The output of 1 is due there being nothing on the Frame other than an empty Label. (It would still show 1 if there was no Label)
To get the output as 200, set the grid_propagate flag to False (only after setting your height and widht parameters), as follows:
frame = Frame(..., width=200)
frame.grid(row=0, column=0)
frame.grid_propagate(False)

Why does my tkinter object keep changing shape?

I have created an image on a canvas in tkinter that responds to a button event. And, the object is created on position x and position y where that event took place. But the object changes shape constantly.
def leftclick(event):
canvas1=Canvas(play, height=hei, width=wid)
canvas1.grid(row=0, column=0, sticky=W)
canvas1.delete("all")
x=event.x
y=event.y
print(event.x, event.y)
bullet = canvas1.create_oval(x,y, 100,100, fill="red")
xspeed=random.randint(0, 50)
yspeed=random.randint(0,50)
This just draws ovals which are randomly shaped. Why is this happening and how do I fix it?
You should only create your canvas once, but that's not a problem.
The problem is that the tkinter tries to create an oval inside the rectangle. You've specified the 2 points of the rectangle: x,y and 100,100. Just use bullet = canvas1.create_oval(x-50,y-50, x+50,y+50, fill="red") or whatever number you pick instead of 50. Hope that's helpful!

position of line when resizing Tkinter window

The following code:
from tkinter import *
root=Tk()
for x in range(10):
for y in range(10):
canvas=Canvas(root, width='15',height='15',highlightthickness=0,bg='red')
canvas.create_line(canvas.winfo_x(),canvas.winfo_y(),canvas.winfo_x()+15,canvas.winfo_y()+15,width=2,fill='black')
canvas.grid(row=y,column=x,sticky='NESW')
for x in range(10):
for y in range(10):
root.columnconfigure(x,weight=1)
root.rowconfigure(y,weight=1)
root.mainloop()
produces this, which is a 10 by 10 grid filled with canvases; there is a line extending from the top left to the bottom right corner of each canvas.
When I resize the window, the canvas widgets resize correctly, but the lines retain their shape like this. The lines need to adjust according to the window/widget size.
The core of the problem is that the lines are made using the coordinates of the top left corner of the widget, and are extended 15 pixels in each direction. Is there a way of getting the coordinates of the bottom right corner of the widget, so that the lines can change their shape dynamically, or some other way of keeping the lines shape, relative to the widget?
You can get the current width and height of any widget with the winfo_width and winfo_height methods. If you are binding to the <Configure> method to track when the canvas changes size, the event object has a width and height attribute.
For example:
from tkinter import *
def redraw_line(event):
width = event.width
height = event.height
canvas = event.widget
canvas.coords("diagonal", 0, 0, width, height)
root=Tk()
for x in range(10):
for y in range(10):
canvas=Canvas(root, width='15',height='15',highlightthickness=0,bg='red')
canvas.bind("<Configure>", redraw_line)
# coordinates are irrelevant; they will change as soon as
# the widget is mapped to the screen.
canvas.create_line(0,0,0,0, tags=("diagonal",))
canvas.grid(row=y,column=x,sticky='NESW')
for x in range(10):
for y in range(10):
root.columnconfigure(x,weight=1)
root.rowconfigure(y,weight=1)
root.mainloop()

Python tkinter scrollbar/graph scale slowing down scrolling

I have been toying around with putting a scrollbar on my data graph. I have it on there and its scrolling the data but its also scrolling the scale(data values) on the right hand side of the screen. I've have toyed around this morning with the idea of creating two separate windows, one for the data graph and one for the scale. It looks rather unusual compared to what you normally see but I do notice one thing in particular when I do this. With the scale on the data graph, one gui, the scrolling is very slow as long as the scale still remains on the screen. Once the scale moves off the screen the scrolling speed picks up to what I would normally expect. When I move the scale to a completely separate gui the scrolling speed is consistently fine all the time. How do I overcome this problem?
I'm not sure why the scale is having any kind of effect on the scrolling speed. It's nothing more than:
self.DrawArea.create_line((1298, 12), (1300, 12), fill = "white")
self.DrawArea.create_line((1290, 25), (1300, 25), fill = "white")
self.DrawArea.create_line((1298, 37), (1300, 37), fill = "white")
self.DrawArea.create_text((1320, 25), text = "5.0", fill = 'white')
self.DrawArea.create_text((1320, 50), text = "4.5", fill = 'white')
self.DrawArea.create_text((1320, 75), text = "4.0", fill = 'white')
going down the screen(yes 5 to -5 marked out every every .125...labelled once every .5).
It is feasible to have the scale and the graph data on the same gui and still keep the scrolling speed. I haven't changed the font, either size or type as I'm not sure how to since nothing is really indicated in the tkinter documentation.
Also is there a way that I can limit where the graph data gets displayed. With one gui, I have the graph setup for 1350x615(600 with the bottom 15 being the scrollbar). 1300 should be the display data with the other 50 being the scale. Right now I have the issue that the data gets graphed underneath the scale(scale is obviously put on last). Is there any way I can limit it so the data only gets shown 0-1300 while the scale gets display 1301-1350? I've been toying around with Frames as well this morning but I have had no luck thus far at resolving this issue.
Edited:
When I was trying to use the keyboard for scrolling I was using the .move() command but when I went to change to using the scrollbar I wasn't using the keyboard at all and just using the scrollbar. When I have both the graph and scale on the same gui as long as the scale is on the screen(hasn't been scrolled off yet) the graph moves very slowly across the screen. Once it's off the screen the pace picks up and moves as though I didn't have the scale on the screen at all. It's the same way when I test with two separate windows. The scale on the main graph slows the scrolling down.
Moving the scale to another gui still doesn't help the load speed or the zoom in/out speed for displaying the graph though.
If you want to scroll with the scrollbar and not have the scale scroll, you should probably use two separate windows. I'm not sure what you mean by "unusual compared to what you normally see". If you put the two canvases side-by-side with no space between them and with the same background color, the user would have no way of knowing you're using two canvas widgets at the same time.
The tkinter canvas can scroll several thousand items before it starts to get sluggish, so it's hard to say without seeing your actual code.
here's an example that plots 10,000 points, with the scale in a separate canvas:
import Tkinter as tk
from random import randrange
class Example(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.DrawArea = tk.Canvas(self, width=1000, height=600, background="black",
borderwidth=0, highlightthickness=0)
self.scale = tk.Canvas(self, width=30, height=600, background="black",
borderwidth=0, highlightthickness=0)
self.hsb = tk.Scrollbar(self, orient="horizontal", command=self.DrawArea.xview)
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.DrawArea.yview)
self.DrawArea.configure(yscrollcommand=self.vsb.set, xscrollcommand=self.hsb.set)
self.DrawArea.grid(row=0, column=0, sticky="nsew")
self.scale.grid(row=0, column=1, sticky="nsew")
self.vsb.grid(row=0, column=2, sticky="ns")
self.hsb.grid(row=1, column=0, columnspan=2,sticky="ew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.draw_scale()
self.draw_points()
def draw_scale(self):
value = 5.0
for y in range(25, 600, 25):
self.scale.create_text((25, y), text=str(value), fill="white", anchor="ne")
value -= 0.5
def draw_points(self):
import math
for x in range(5,10000):
y = randrange(600)
color = "green" if (300 > y > 200) else "red"
self.DrawArea.create_rectangle(x-2,y-2,x+2,y+2, fill=color)
self.DrawArea.configure(scrollregion = self.DrawArea.bbox("all"))
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

change size of Frame even if there widget python

hi is there any way to change width and height of widget even if there's widget?
i have code like this
form = Tk()
form.geometry("500x500")
def click():
global frame
frame.config(height = 0 ,width = 0)
frame = LabelFrame(form , text = "vaaja")
frame.place(x = 20 , y = 30)
Label(frame, text ="1").grid(row = 0,column = 0 )
Label(frame, text = "2").grid(row = 1 ,column = 0 )
Button(form , text="Click", command = click).place(x = 200 , y = 200)
form.mainloop()
and when I click the button the size of the frame is the same ( I'cant use grid_forget() for labels and then change the size of frame)
Because you are using place, you have two solutions: you can use place to set the width and height to zero, or you can turn geometry propagation off.
Using place to set the width and height
place allows you to define the width and the height of the placed widget, so in your click function you can do this:
def click():
frame.place_configure(width=0, height=0)
Turning geometry propagation off
A frame is resized to fit its contents by something called "geometry propagation". If you turn this off, you can control the size of the frame with the width and height options of the frame itself. Usually it's better to let Tkinter decide the size for you, but sometimes there's a need to have an explicit size, which is why it's possible to turn geometry propagation off.
Since you are using grid to manage the widgets internal to the frame, you need to use grid_propagate(False) to turn geometry propagation off for that frame:
frame.grid_propagate(False)
By doing so, you're responsible for setting the initial width and height of the widget, though you could leave propagation on to get the initial size, then turn it off with the button click in order to work around that issue.
There's an interesting bug (or feature...) in that if you set the width and height to zero, Tkinter won't redraw the window. At least, it doesn't on the Mac. I don't recall the workaround for that because I never, ever need to set a widget to a zero size, but setting it to 1x1 pixel makes it nearly invisible.

Categories

Resources