Refreshing a window in Tkinter - python

I am trying to make a GUI in Tkinter and am wondering how to refresh a window, namely if I fill in a rectangle, I want the GUI to delete it a specified time later. How would I go about doing this? Documentation on Tkinter seems to be thin...

Each Tkinter widget has a after method, which you can use to call your rectangle delete function e.g. in the example below first I change a msg using after, and then destruct the window using after
from Tkinter import *
def changeMsg():
label.configure(text="I will self destruct in 2 secs")
label.after(2000, root.destroy)
root = Tk()
mainContainer = Frame(root)
label = Label(mainContainer, text="")
label.configure(text="msg will change in 3 secs")
label.pack(side=LEFT, ipadx=5, ipady=5)
mainContainer.pack()
label.after(3000, changeMsg)
root.title("Timed event")
root.mainloop()

Related

Tkinter toplevel window is not defined

I wonder if someone could tell me if its possible to update toplevel windows using external functions. I've replicated my issue below what I need to do is update the Toplevel(master) using the function updatelabel(). I have used similar external function to update items in root which works like a dream. However, with the top level window I always get the
NameError: name 'newWindow' is not defined
The only work around I found was to kill the newWindow using newWindow.destroy() on each load but this method makes the screen pop up and then close again which doesn't look pretty. Any help most welcome thanks.
from tkinter import *
from tkinter.ttk import *
master = Tk()
master.geometry("200x200")
def updatelabel():
Label(newWindow,
text="I changed").pack()
def openNewWindow():
# Toplevel object which will
# be treated as a new window
newWindow = Toplevel(master)
# sets the title of the
# Toplevel widget
newWindow.title("New Window")
# sets the geometry of toplevel
newWindow.geometry("200x200")
# A Label widget to show in toplevel
Label(newWindow,
text="I want to change").pack()
button1 = Button(newWindow,
text="Click me to change label", command=updatelabel).pack()
btn = Button(master,
text="open a new window",
command=openNewWindow)
btn.pack(pady=10)
mainloop()
Your “newWindow” is defined in your “openNewWindow” function and so it basically only exists in there, you could probably fix this by either defining “newWindow” outside of the function, or by using it as an argument(just add it to the brackets and give it a name in the function itself’s brackets) calling “updateLabel”
I think this should work, though I haven’t worked with tkinter in a bit so don’t blame me if it doesn’t
from tkinter import *
from tkinter.ttk import *
master = Tk()
master.geometry("200x200")
def updatelabel(newWindow):
Label(newWindow,
text="I changed").pack()
def openNewWindow():
# Toplevel object which will
# be treated as a new window
newWindow = Toplevel(master)
# sets the title of the
# Toplevel widget
newWindow.title("New Window")
# sets the geometry of toplevel
newWindow.geometry("200x200")
# A Label widget to show in toplevel
Label(newWindow,
text="I want to change").pack()
button1 = Button(newWindow,
text="Click me to change label", command= lambda: updatelabel(newWindow)).pack()
btn = Button(master,
text="open a new window",
command=openNewWindow)
btn.pack(pady=10)
mainloop()

How to make tkinter right click menu work on one widget only?

Is there a simple way to get the right click menu to open on texty only and not the whole window?
This was a quick mashup to illustrate my question. Inheriting from texty on line 25 was a shot in the dark, which didnt work, but it's close to a simple solution, like I am seeking. I was hoping to avoid programming a whole class each time I want to set a right click menu.
from tkinter import *
from tkinter import ttk
def menu_popup(event):
try:
popup.tk_popup(event.x_root, event.y_root, 0)
finally:
popup.grab_release()
win = Tk()
win.geometry("600x550+125+125")
e = Entry(win, width=50, font=('Helvetica', 11))
e.pack()
e.insert(0, "Some text....")
label = Label(win, text="Right-click to see a menu", font= ('Helvetica 18'))
label.pack(pady= 40)
texty=Text(win, height=10)
texty.pack()
popup = Menu(texty, tearoff=0)
popup.add_command(label="New")
popup.add_separator()
popup.add_command(label="Open")
popup.add_separator()
popup.add_command(label="Close")
win.bind("<Button-3>", menu_popup)
button = ttk.Button(win, text="Quit", command=win.destroy)
button.pack()
mainloop()
The widget on which the callback should be executed for the respective event is determined by the widget you call bind on(and the level of bind too*). So if you want the event to be identified within texty, then apply binding to it.
texty.bind("<Button-3>", menu_popup)
* There is bind_all which executes no matter which widget has focus or is called upon. Read 54.1. Levels of binding for more info.

How do I destroy a tkinter frame?

I am trying to make a tkinter frame that will contain an entry field and a submit button. When the submit button is pressed, I want to pass the entry string to the program and destroy the frame. After many experiments, I came up with this script:
from tkinter import *
from tkinter import ttk
import time
root = Tk()
entryframe = ttk.Frame(root)
entryframe.pack()
par = StringVar('')
entrypar = ttk.Entry(entryframe, textvariable=par)
entrypar.pack()
submit = ttk.Button(entryframe, text='Submit', command=entryframe.quit)
submit.pack()
entryframe.mainloop()
entryframe.destroy()
parval = par.get()
print(parval)
time.sleep(3)
root.mainloop()
When the "Submit" button is pressed, the parameter value is passed correctly to the script and printed. However, the entry frame is destroyed only after 3 seconds (set by the time.sleep function).
I want to destroy the entry frame immediately.
I have a slightly different version of the script in which the entry frame does get destroyed immediately (although the button itself is not destroyed), but the value of par is not printed:
from tkinter import *
from tkinter import ttk
import time
root = Tk()
entryframe = ttk.Frame(root)
entryframe.pack()
par = StringVar('')
entrypar = ttk.Entry(entryframe, textvariable=par)
entrypar.pack()
submit = ttk.Button(root, text='Submit', command=entryframe.destroy)
submit.pack()
entryframe.mainloop()
# entryframe.destroy()
parval = par.get()
print(parval)
time.sleep(3)
root.mainloop()
How can I get both actions, namely the entry frame destroyed immediately and the value of par printed?
Note 100% sure what you are trying to do but look at this code:
from tkinter import *
from tkinter import ttk
def print_results():
global user_input # If you want to access the user's input from outside the function
# Handle the user's input
user_input = entrypar.get()
print(user_input)
# Destroy whatever you want here:
entrypar.destroy()
submit.destroy()
# If you want you can also destroy the window: root.destroy()
# I will create a new `Label` with the user's input:
label = Label(root, text=user_input)
label.pack()
# Create a tkitner window
root = Tk()
# Create the entry
entrypar = ttk.Entry(root)
entrypar.pack()
# Create the button and tell tkinter to call `print_results` whenever
# the button is pressed
submit = ttk.Button(root, text="Submit", command=print_results)
submit.pack()
# Run tkinter's main loop
# It will stop only when all tkinter windows are closed
root.mainloop()
# Because of the `global user_input` now we can use:
print("Again, user_input =", user_input)
I defined a function which will destroy the entry and the button. It also creates a new label that displays the user's input.
I was able to accomplish what I wanted using the wait_window method. Here is the correct script:
from tkinter import *
from tkinter import ttk
root = Tk()
entryframe = ttk.Frame(root)
entryframe.pack()
entrypar = ttk.Entry(entryframe)
entrypar.pack()
submit = ttk.Button(entryframe, text='Submit', command=entryframe.destroy)
submit.pack()
entrypar.wait_window()
parval = entrypar.get()
print(parval)
close_button = ttk.Button(root, text='Close', command=root.destroy)
close_button.pack()
root.mainloop()
My intention was not fully apparent in my original question, and I apologize for that. Anyway, the answers did put me on the right track, and I am immensely thankful.

How to disable the movement of the Tkinter Window without removing the title bar

I have been creating an application for taking the test. So, for that, I have to do two things.
First, disable the drag of the Tkinter window and don't let the user focus on other windows rather than my application window. This means I wanted to make my application such that, No other application can be used while my application is in use.
Try this:
import tkinter as tk
class FocusedWindow(tk.Tk):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Force it to be unminimisable
super().overrideredirect(True)
# Force it to always be on the top
super().attributes("-topmost", True)
# Even if the user unfoceses it, focus it
super().bind("<FocusOut>", lambda event: self.focus_force())
# Take over the whole screen
width = super().winfo_screenwidth()
height = super().winfo_screenheight()
super().geometry("%ix%i+0+0" % (width, height))
root = FocusedWindow()
# You can use it as if it is a normal `tk.Tk()`
button = tk.Button(root, text="Exit", command=root.destroy)
button.pack()
root.mainloop()
That removed the title bar but you can always create your own one by using tkinter.Labels and tkinter.Buttons. I tried making it work with the title bar but I can't refocus the window for some reason.
One way to do this is by the following, another could be to overwrite the .geometry() method of tkinter.
In the following code I simply had get the position by using winfo_rootx and winfo_rooty. After this you can force the window by calling the geometry method via binding the event every time the window is configured.
import tkinter as tk
def get_pos():
global x,y
x = root.winfo_rootx()
y = root.winfo_rooty()
def fix_pos():
root.bind('<Configure>', stay_at)
def stay_at(event):
root.geometry('+%s+%s' % (x,y))
root = tk.Tk()
button1 = tk.Button(root, text='get_pos', command=get_pos)
button2 = tk.Button(root, text='fix_pos', command=fix_pos)
button1.pack()
button2.pack()
root.mainloop()

Python tkinter grid manager?

I just learned how to use tkinter in Python (3.2.2), and I'm having some problem using the grid manager. When I put button.grid(sticky=SE), for example, the button is not being put in the bottom-right and is just being put in the upper-left, ignoring the sticky value. What am I doing wrong here? I tried to search it but I couldn't really find out what I am doing wrong.
You probably need to set a minimum size for the widget containing the button.
If you don't, the container widget may shrink to occupy only the space required to display the button. If so, the sticky option will be meaningless since the container widget gives no space to show any difference.
For example, using a tk.Frame as the container widget:
import Tkinter as tk
class SimpleApp(object):
def __init__(self, master, **kwargs):
title = kwargs.pop('title')
frame = tk.Frame(master, borderwidth=5, bg = 'cyan', **kwargs)
frame.grid()
button = tk.Button(frame, text = title)
button.grid(sticky = tk.SE)
frame.rowconfigure('all', minsize = 200)
frame.columnconfigure('all', minsize = 200)
def basic():
root = tk.Tk()
app = SimpleApp(root, title = 'Hello, world')
root.mainloop()
basic()
yields
PS. I don't have tkinter installed in Python3.2 so I can't test this, but I think the only change you need to make this work with Python3.2 is
import tkinter as tk
instead of
import Tkinter as tk
When you say "What am I doing wrong here", you need to post your code, otherwise how would anyone be able to guess what's wrong.
The following works fine, placing the button in the lower right corner (SE) of the grid cell - the default is center, not upper left (NW).
from tkinter import Button, Label, Entry, Tk, SE
root = Tk()
Label(text="Lots o' Stuff", width=30, height=15,
borderwidth=2, relief="raised").grid(rowspan=2)
Entry().grid(row=0, column=2)
Button(text="Hit Me").grid(row=1, column=2, sticky=SE)
root.mainloop()

Categories

Resources