Tkinter recursive behavior with '<Configure>' callback - python

I try to have a tkinter.Frame that have a full screen image and some buttons underneath it
WIDTH, HEIGHT = 800, 600
root = Tk()
mainframe = Frame(root, padding="3 3 12 12")
mainframe.pack(fill=BOTH, expand=True)
infovariable = StringVar()
infovariable_label = Label(mainframe, textvariable=infovariable, anchor=S)
infovariable_label.pack(fill=X, side=TOP)
label = Label(mainframe)
label.pack(fill=BOTH, expand=True)
image_base = Image.open('hello.jpg')
# setting the photo
image = (image_base
.resize(2500, 1000)
.crop(0, 0, WIDTH,HEIGHT))
label.configure(image=photo)
When I do a window resize, I want my photo to be the same dimensions (width/height), if I do that:
def onResize(event):
global WIDTH, HEIGHT
WIDTH = event.width
HEIGHT = max(0, event.height - 50)
# setting the photo
image = (image_base
.resize(2500, 1000)
.crop(0, 0, WIDTH,HEIGHT))
root.bind('<Configure>', onResize)
The resize, makes the image change size, then call the resize again, having a window that infinitely resizes.
I have the same problem as this thread:
odd behavior with '<Configure>' callback

When you bind to the root window, that binding applies to every child of the root window, too, due to how tkinter uses binding tags.
Part of the solution is to change your onResize to only change the size of the image if the event.widget represents the root window. There may be other problems, but that's the first.
You also need to make sure you account for borders. If you make the image the same size of the window, but the label has a one pixel border, that will cause the label to grow, which will cause the root window to grow, which will start the process all over again.
Another answer related to bind tags is here: https://stackoverflow.com/a/2472992/7432

Related

How to resize the Frame widget on tkinter ? -Python

I want the Frame widget to take all the window of tkinter but it seems to have no effect plus when I try to move the label (who is inside the frame) it mysteriously disapear Althought the Frame is supposed to have the same size as root.
I specified the height and width for root because I don't know how to make the global window of the project just defined by the height and width of the Frame widget.(so I used geometry to assign a height and width to root)
I've heard about "frame.pack(fill="both", expand=1)" but I can't use pack because I use grid for the widgets that are inside the Frame ( and I want to keep grid) so I used .place on Frame instead but I don't know how to do the same thing with place() as we did with pack.
If the frame can resize correctly that I suppose the widget Label will not disapear when we drag it (because the space will be bigger)
import tkinter as tk
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry('800x600')
frame = Frame(root,width=800,height=600)
frame.place(x=0,y=0)
label = Label(frame,text='˃LABEL',fg='green')
label.grid(column=0,row=0)
label2 = Label(frame,text='˃LABEL2',fg='green')
label3 = Label(frame,text='˃LABEL3',fg='green')
def change(event):
label['text'] = '˅LABEL'
label2.grid(column=0,row=1)
label3.grid(column=0,row=2)
if label['text'] == '˅LABEL':
label.bind('<Button-1>', hide)
def hide(event):
label['text'] = '˃LABEL'
label2.grid_forget()
label3.grid_forget()
if label['text'] == '˃LABEL':
label.bind('<Button-1>',change)
label.bind('<Button-1>',change)
def make_draggable(widget):
widget.bind("<Button-1>", on_drag_start)
widget.bind("<B1-Motion>", on_drag_motion)
def on_drag_start(event):
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
def on_drag_motion(event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
make_draggable(label)
root.mainloop()
You can try it if you want
FIRST EDIT : "By default, Tkinter Frame fits to its children and thus its width and height depends on its children. You can override this behavior and force a specific width and height to the frame. To force the width and height of frame widget call pack_propagate(0) on the frame."
I can't do it with place() ? (Like I said I can't use pack() for what I want to do)

Way to set a singled dimension of the root window using python - tkinter?

I'm looking to have a window occupy the width of the screen but not (yet) the height of the screen. I have the root established and working but as far as I can tell the geometry method requires at least a width AND height, not one or the other individually. I would like the window to continue to scale dynamically in height as widgets are added/taken away. There is a starting height of the notebook tab and button
Without the rest of the code for the ReaderUI class shown below, this is what the main method looks like so far...
def main():
root=Tk()
tab_control = ttk.Notebook(root)
first_tab = ttk.Frame(tab_control)
tab_control.add(tab1,text="File 1")
tab_control.pack(expand=1,fill="both")
new_tab_button = Button(tab,text="create new tab", command = lambda: new_tab(tab_control))
new_tab_button.pack(padx = 5, pady = 5)
newUI = ReaderUI(tab1)
width=root.winfo_screenwidth()
height = root.winfo_screenheight()
root.geometry("{}x{}+0+0".format(width,height)
root.resizable(False,False)
root.mainloop()
My question boils down to this: is there a way to have the UI build itself in terms of height (as it would without the geometry method in place) and only establish a width as wide as the screen? Potentially a modification to the line
root.geometry("{}x{}+0+0".format(width,height))
so that height is left unset and free to move.
you can use
winfo_screenwidth
- Returns a decimal string giving the width of window's screen, in pixels.
Code
root.winfo_screenwidth()
root being your Tk() instance
then you can lock the x axis by using
root.resizable(False, True) # (X,Y)
Recourse
https://www.astro.princeton.edu/~rhl/Tcl-Tk_docs/tk8.0a1/winfo.n.html

Python with tkinter aspect ratio issue

I am attempting to create a simple window that maintains a square shape when resized, using python 3.6.4 and tkinter 8.6. Here is my code that produces a window, but does not maintain its aspect ratio when resized.
import tkinter as tk
w = tk.Tk()
w.aspect(1,1,1,1)
w.mainloop()
maybe you can use a canvas to make that, he detect event (image size changed) and .place relative x and relative y. I tryed make a script to help you, but you need make somes changes
from tkinter import *
# create a canvas with no internal border
canvas = Canvas(bd=0, highlightthickness=0)
canvas.pack(fill=BOTH, expand=1)
lastw, lasth = canvas.winfo_width(), canvas.winfo_height()
# track changes to the canvas size and draw
# a rectangle which fills the visible part of
# the canvas
def configure(event):
global lastw, lasth
canvas.delete("all")
w, h = event.width, event.height
try:
label.config(font = ('Arial ', int(12 * ((w - lastw) / (h - lasth))))) # -- this formula need change :3
except ZeroDivisionError: pass
lastw, lasth = canvas.winfo_width(), canvas.winfo_height()
canvas.bind("<Configure>", configure)
label = Label(canvas, text = "YOLO")
label.place(relx = 0.5, rely = 0.5) # - this make the widget automatic change her pos
mainloop()
you can see how this work here http://effbot.org/zone/tkinter-window-size.htm

python tkinter hide the window during widget creation

I have a small annoying problem so I come to you to see if you can help to solve it.
I have the following code in Python2.7:
# main window
root = tk.Tk()
root.title('My application')
# create the objects in the main window
w = buildWidgetClass(root)
# size of the screen (monitor resolution)
screenWi = root.winfo_screenwidth()
screenHe = root.winfo_screenheight()
# now that widgets are created, find the widht and the height
root.update()
guiWi = root.winfo_width()
guiHe = root.winfo_height()
# position of the window
x = screenWi / 2 - guiWi / 2
y = screenHe / 2 - guiHe / 2
root.geometry("%dx%d+%d+%d" % (guiWi, guiHe, x, y))
So I create the main window (without any size) I insert widgets inside, then I define the size of the result, and place it in the middle of the screen.
The number of widget in the window may vary, so the resulting size!
So far, so good, it works ! My only problem is that the window first appear in the left top corner of the screen, then repositioned to the center. Not a big deal, but not really professional neither.
So my idea was to hide the main window during the widgets creation time and then make it appearing after having defined the geometry.
So after the first line, I added :
root.withdraw()
then at the end :
root.update()
root.deiconify()
but when the window reappear, it wasn't re-sized by the widget and have a size of 1x1 !!
I tried to replace root.withdraw() by root.iconify(), the window is correctly re-sized but, surprisingly, isn't deiconified at the end !!!
I'm a little bit lost on this ...
Using root.winfo_reqwidth instead of root.winfo_width may help in your case.
Finally with the input of kalgasnik, I have a working code
# main window
root = tk.Tk()
# hide the main window during time we insert widgets
root.withdraw()
root.title('My application')
# create the objects in the main window with .grid()
w = buildWidgetClass(root)
# size of the screen (monitor resolution)
screenWi = root.winfo_screenwidth()
screenHe = root.winfo_screenheight()
# now that widgets are created, find the width and the height
root.update()
# retrieve the requested size which is different as the current size
guiWi = root.winfo_reqwidth()
guiHe = root.winfo_reqheight()
# position of the window
x = (screenWi - guiWi) / 2
y = (screenHe - guiHe) / 2
root.geometry("%dx%d+%d+%d" % (guiWi, guiHe, x, y))
# restore the window
root.deiconify()
Thanks a lot to both of you for your time and your help

How to center a tkinter window and retain the "fit to children" behavior?

I have a window whose content changes. Sometimes the content is larger than the window, so the window expands to fit it's children. However, when I center a window using a call to "geometry", the window no longer resizes. Below, you will find code that illustrates this.
If you comment out the delayed center() function call, you'll notice that the window expands to fit its content. If you leave it as is, the window centers, but no longer expands to fit its content.
Is it possible to center a window AND have it continue to resize to fit its content?
from Tkinter import *
import ttk
def center(root):
w = root.winfo_screenwidth()
h = root.winfo_screenheight()
rootsize = tuple(int(_) for _ in root.geometry().split('+')[0].split('x'))
x = w/2 - rootsize[0]/2
y = h/2 - rootsize[1]/2
root.geometry("%dx%d+%d+%d" % (rootsize + (x, y)))
root = Tk()
var = StringVar()
var.set('Small Text')
label = ttk.Label(root, textvariable=var)
label.grid(column=0, row=0)
# Change the text label in a couple of seconds.
def changeit():
var.set('BIG TXT - ' * 5)
root.after(2000, changeit)
# Comment out this center call and the label expands.
root.after(100, lambda: center(root))
root.mainloop()
When you call the geometry command, don't provide a width and height -- just supply the x/y values. When you give it an explicit width and height, you're telling Tk "I want the window to be exactly this size" so it turns it's auto-resize behavior off.
root.geometry("+%d+%d" % (rootsize + (x, y)))
Also, you can use winfo_width() and winfo_height() to get the actual size of the window, instead of parsing the output of the geometry method.

Categories

Resources