I have currently two problems with Toplevel instances in Tkinter.
First and most important: I want to display a popup window and place 2 frames in it for better arangement in grid, but it doesn't work as I expect it to work:
import tkinter
root = tkinter.Tk()
tkinter.Button(root, text="ABC").grid(column=0, row=0)
tkinter.Label(root, text="FOO").grid(column=1, row=1)
win = tkinter.Toplevel()
f1 = tkinter.Frame(win).grid(row=0, column=0)
f2 = tkinter.Frame(win).grid(row=1, column=1)
tkinter.Label(f1, text="FRAME 1").grid()
tkinter.Label(f2, text="FRAME 2").grid()
root.mainloop()
I would expect "FRAME 1" and "FRAME 2" to be placed in the Toplevel window, but they are actually placed in root. How do I fix this?
Second, less important: The popup window in the code above is spawning behind the root window, while I would like it to be placed in front of root, how do I achieve this?
You set your frames f1 and f2 to the return-value of the grid() command, which is None, therefore tkinter.Label(f1, text="FRAME 1").grid() does not work as you expect.
Try something like this:
win = tkinter.Toplevel()
f1 = tkinter.Frame(win)
f1.grid(row=0, column=0)
tkinter.Label(f1, text="FRAME 1").grid()
When setting your geometry manager be it grid(), pack() or place() and you need to be able to interact with that widget later you will need to assign the widget to a variable and then apply the geometry manager on a new line using that variable name. This way your variable will not be a value of None but rather the proper widget. This happens because the geometry managers all return None.
Next the reason your labels are on the wrong windows is because when your labels try to connect with f1 and f2 they are not able to find a proper tkinter container due to the values being None so it defaults to the root tkinter window in an attempt to be place on something.
With fixing the None issues you will also fix your label issue.
To address the matter of your top level window not being in front of your root window there are a couple of things you can do. The main reason this is happening is how your code is generating the top level at __init__ rather than later with a button or a timed event.
If you really need your top level window to open at the same time as root you can use after() and a function to do this and it will be placed on top. If you do not need it right when the window opens you may want to assign a command to a button to run a function that builds the top window.
Here is an example with after():
import tkinter as tk
root = tk.Tk()
def create_top():
win = tk.Toplevel(root)
f1 = tk.Frame(win)
f1.grid(row=0, column=0)
f2 = tk.Frame(win)
f2.grid(row=1, column=1)
tk.Label(f1, text="FRAME 1").grid()
tk.Label(f2, text="FRAME 2").grid()
tk.Button(root, text="ABC").grid(column=0, row=0)
tk.Label(root, text="FOO").grid(column=1, row=1)
root.after(10, create_top)
root.mainloop()
Here is an example with a button:
import tkinter as tk
root = tk.Tk()
def create_top():
win = tk.Toplevel(root)
f1 = tk.Frame(win)
f1.grid(row=0, column=0)
f2 = tk.Frame(win)
f2.grid(row=1, column=1)
tk.Label(f1, text="FRAME 1").grid()
tk.Label(f2, text="FRAME 2").grid()
tk.Button(root, text="ABC", command=create_top).grid(column=0, row=0)
tk.Label(root, text="FOO").grid(column=1, row=1)
root.mainloop()
Related
So I made a button to copy something to clipboard, but the button itself is always showing, but the text on it only when its pressed, how to fix it?
(Here the part with the button code:)
canvas1 = tk.Canvas(root, width=300, height=300)
canvas1.pack()
def copy_button():
clip = tk.Tk()
clip.withdraw()
clip.clipboard_clear()
clip.clipboard_append(pw)
clip.destroy()
button1 = tk.Button(text="Copy to Clipboard", command=copy_button, bg="grey", fg="white", font=("Helvetica", 12, "bold"))
canvas1.create_window(150, 150, window=button1)
Your disappearing text issue does not appear in Linux OS but I suspect your issue is related to your button1 = tk.Button(....) statement.
The first argument to a tk.Button widget must be its parent (see this link on this requirement) and not a keyword/option.
Try button1 = tk.Button(canvas1, text="Copy to Clipboard", ....). Doing so, you define the tk.Button to be a child of the tk.Canvas widget.
If you are putting more than a button in the canvas, you might want to consider:
defining a tk.Frame to be a child of the tk.Canvas(e.g. frame1=tk.Frame(canvas1)),
replace window=button1 with window=frame1,
letting button1 to be a child of frame1, e.g. button1 = tk.Button(frame1, text="Copy to Clipboard", ....)
Do let me know if this answer addresses your issue.
Other suggestion:
You can remove clip = tk.Tk(), clip.withdraw(), clip.destroy() and replace the clip term in clip.clipboard_clear() and clip.clipboard_append(pw) with root. Here, I assume that you had earlier defined root = tk.Tk() at the start of your code.
How can I manage to automatic cancel this 1st window when I click the next window button?
sample code:
from tkinter import *
from tkinter import messagebox
root = Tk()
root.title("GUI practice")
def open():
top = Toplevel() # new window
top.title("Kokomi")
labels = Label(top, text="This one automatically close when i click the next window").pack()
button2 = Button(top,text="Close window", command=top.destroy).pack()
button3 = Button(top,text="Next window", command=open2).pack()
def open2():
top = Toplevel() # new window
top.title("Guide")
labels = Label(top, text="end").pack()
button2 = Button(top, text="Close window", command=top.destroy).pack() # destroy to quit things
button = Button(root, text="Open(No need to close this)", command=open).pack()
root.mainloop()
[Click open][1]
[Click Next window and after that this windows should disappear and continue to the 3rd picture][2]
[The 2nd picture goes disappear when i click the next window][3]
[1]: https://i.stack.imgur.com/plS1T.png
[2]: https://i.stack.imgur.com/EFW76.png
[3]: https://i.stack.imgur.com/xSZCp.png
For this specific case of using two functions and two windows, you can just simply rename the Toplevel widgets to different names and then globalize one and then access it from another function and destroy it before the new windows is shown.
def open():
global top
top = Toplevel() # new window
top.title("Kokomi")
Label(top, text="This one automatically close when i click the next window").pack()
Button(top, text="Close window", command=top.destroy).pack()
Button(top, text="Next window", command=open2).pack()
def open2():
top.destroy() # Destroy previously open window
top1 = Toplevel() # new window
top1.title("Guide")
Label(top1, text="end").pack()
Button(top1, text="Close window", command=top1.destroy).pack() # destroy to quit things
If you noticed, I removed the variable names of buttons and labels because its useless to have those as their value is None, read Tkinter: AttributeError: NoneType object has no attribute <attribute name>.
When you wish to use more functions and windows, you have to manually follow this procedure for all the functions. Unless ofcourse there is a better and cleaner way of designing your app using classes and frames.
Alternatively you can also invoke two functions from a single button, this method will get rid of globalization and renaming and might be a bit better than the above mentioned solution:
def open():
top = Toplevel() # new window
top.title("Kokomi")
Label(top, text="This one automatically close when i click the next window").pack()
Button(top, text="Close window", command=top.destroy).pack()
Button(top, text="Next window", command=lambda: [top.destroy(),open2()]).pack()
def open2():
top = Toplevel() # new window
top.title("Guide")
Label(top, text="end").pack()
Button(top, text="Close window", command=top.destroy).pack() # destroy to quit things
I am trying to create a Toplevel window, however, this Toplevel is called from a different file in the same directory within a function.
Apologies I am by no means a tkinter or python guru. Here are the two parts of the code. (snippets)
#File 1 (Main)
import tkinter as tk
from tkinter import *
import comm1
from comm1 import com1
root = tk.Tk()
root.title("")
root.geometry("1900x1314")
#grid Center && 3x6 configuration for correct gui layout
root.grid_rowconfigure(0, weight=1)
root.grid_rowconfigure(11, weight=1)
root.grid_columnconfigure(0, weight=1)
root.grid_columnconfigure(11, weight=1)
#background image
canvas = Canvas(root, width=1900, height=1314)
canvas.place(x=0, y=0, relwidth=1, relheight=1)
bckground = PhotoImage(file='img.png')
canvas.create_image(20 ,20 ,anchor=NW, image=bckground)
#command to create new Toplevel
btn1 = tk.Button(root, text='Top', command=com1, justify='center', font=("Arial", 10))
btn1.config(anchor=CENTER)
btn1.grid(row=4, column=1)
#File 2 (Toplevel)
#command for new window
def com1():
newWindow1 = Toplevel(root)
newWindow1.title("")
newWindow1.geometry("500x500")
entry1 = tk.Entry(root, justify='center' , font=("Arial", 12), fg="Grey")
newWindow1.pack()
newWindow1.mainloop()
The weird part is this worked perfectly for a few minutes and without changing any code it just stopped working.
Where am I going wrong?
You need to pass root as an argument to com1
Also, you only need to start mainloop once, and that should probably be in the main file. You do not need to call it each time you create a new window.
Thanks everyone that answered,
Decided to bypass the problem with better structuring in a single file. :)
When I make a button that closes the current window and opens another, the current window doesn't close.
from tkinter import *
root = Tk()
def new_window():
root.quit()
new_window = Tk()
new_window.mainloop()
Button(root, text="Create new window", command=new_window).pack()
root.mainloop()
(This isn't my program, it's just an example)
You should be able to do it like this:
import tkinter as tk
root = tk.Tk()
def new_window():
root = tk.Tk()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
This will literally keep opening the exact same window with a button. The lambda allows you to call multiple functions. By calling .destroy() on your root window, it destroys your window, but doesn’t stop the program. Then you create a new root window with your function.
You can use this technique on your actual script.
I have two queries in this section.
In my code i have created two frames under root, the first frame have "NEXT" button to go on second frame. In second frame there is Run button, which has mapped with close_window function. It should close all windows properly. But in my case not closing it.
When i click "Run" i need to close all windows and need to execute another script on the same directory. Is that possible to do it ?
from Tkinter import *
def close_window():
frame2.destroy()
frame1.destroy()
def swap_frame(frame):
frame.tkraise()
root = Tk()
root.geometry("900x650+220+20")
root.title("Testing")
root.configure(borderwidth="1", relief="sunken", cursor="arrow", background="#dbd8d7", highlightcolor="black")
root.resizable(width=False, height=False)
frame2 = Frame(root, width=900, height=650)
frame1 = Frame(root, width=900, height=650)
Button1 = Button(frame1, text="Next", width=10, height=2, bg="#dbd8d7", command=lambda: swap_frame(frame2))
Button1.place(x=580, y=580)
Button2 = Button(frame2, text="Run", width=10, height=2, bg="#dbd8d7", command=close_window,)
Button2.place(x=580, y=580)
frame2.grid(row=0, column=0)
frame1.grid(row=0, column=0)
root.mainloop()
what is wrong in the code?
"Destroy window not closing all windows properly"
Nor should it. destroy method destroys a widget, and when a widget is destroyed so are its children.
Since neither frame1 nor frame2 are 'windows' or a window's parents, there's no window destruction taking place.
"When I click "Run" I need to close all windows and need to execute another script in the same directory. Is that possible to do it?"
It is possible. Use quit on any GUI object, instead of destroy. It stops the mainloop, hence destroying the entire GUI. In that it solves the first problem as well. Then import another_script:
...
def close_window():
frame1.quit()
...
root.mainloop()
import another_script