I have a problem with tkinter. I need to open a new windows, with some parametrics data when the user press a button. The issues is that the methos "set" of the Entry don't work when I create the new window, but it works when I create the window from the main class. What can I do to resolve this problem?
this is the button:
Button(self.frameTabella, text="prenota", command=lambda id=ConcreteLibro.getIdentificativo():self.prenotaLibro(id)).grid(row=i, column=14)
this is the code of the new window:
class GUIPrestitoLibro:
import datetime
def __init__(self, id):
self.GestioneLibri=GestioneLibri()
self.finestraPrestito=Tk()
self.finestraPrestito.geometry()
self.finestraPrestito.title("Prestito/consultazione")
today = datetime.today()
idLibroLabel=Label(self.finestraPrestito, text="ID libro:").grid(row=1, column=0)
self.idLibro=StringVar(value=id)
idLibro=Entry(self.finestraPrestito, textvariable=self.idLibro, state="readonly").grid(row=1, column=1)
idUtenteLabel=Label(self.finestraPrestito, text="ID utente:").grid(row=2, column=0)
self.idUtente=StringVar()
idUtente=Entry(self.finestraPrestito, textvariable=self.idUtente).grid(row=2, column=1)
dataInizioPrestitoLabel=Label(self.finestraPrestito, text="Data inizio prestito:").grid(row=3, column=0)
self.dataInizioPrestito=StringVar(value=today.strftime("%d/%m/%Y"))
dataInizioPrestito=Entry(self.finestraPrestito, textvariable=self.dataInizioPrestito, state="readonly").grid(row=3, column=1)
dataFinePrestitoLabel=Label(self.finestraPrestito, text="Data fine prestito:").grid(row=4, column=0)
self.dataFinePrestito=StringVar()
dataFinePrestito=Entry(self.finestraPrestito, textvariable=self.dataFinePrestito).grid(row=4, column=1)
registra=Button(self.finestraPrestito, text="conferma", command=self.registraPrestito).grid(row=5, column=0)
resetta=Button(self.finestraPrestito, text="resetta", command=self.resettaCampi).grid(row=5, column=1)
self.finestraPrestito.mainloop()
def resettaCampi(self):
self.idUtente.set("")
self.dataFinePrestito.set("")
def registraPrestito(self):
self.GestioneLibri.prestitoLibro(self.idLibro.get(), self.idUtente.get(), self.dataInizioPrestito.get(), self.dataFinePrestito.get())
self.finestraPrestito.destroy()
how can I make the class "GUIPrestitoLibro" instance of Toplevel?
The problem is that this class creates a second instance of Tk. A tkinter app should always only have a single instance of Tk, and should call mainloop() exactly once.
If you need to create multiple windows, all windows but the root window must be instances of Toplevel, and you should not call mainloop() on any window but the root window.
Related
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()
Based on this question, I wrote the following mwe:
import tkinter as tk
class BaseFrame(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.bmanage = tk.Button(self, text='undock', command = self.undock)
self.bforget = tk.Button(self, text='dock', command = self.dock)
self.bmanage.grid(row=0, column=0, padx=20, pady=20, sticky='nsew')
self.bforget.grid(row=0, column=1, padx=20, pady=20, sticky='nsew')
self.dockable_frame = tk.Frame(self, bg="red", height=100)
self.dockable_frame.grid(row=1, column=0, padx=20, pady=20, columnspan=2, sticky='nsew')
self.label = tk.Label(self.dockable_frame, text="hi")
self.label.grid(row=0, column=0, padx=150, pady=20, sticky='nsew')
def undock(self):
self.parent.wm_manage(self.dockable_frame)
self.dockable_frame.configure(bg='blue')
print(type(self.dockable_frame))
def dock(self):
self.parent.wm_forget(self.dockable_frame)
self.dockable_frame.grid()
if __name__ == "__main__":
root = tk.Tk()
base_frame = BaseFrame(root)
base_frame.grid(row=0, column=0, padx=20, pady=20, sticky='nsew')
root.mainloop()
By clicking the undock button, the red frame is undocked and by clicking the dock button, the frame is docked again. I have two questions:
Why is the type of self.dockable_frame a <class 'tkinter.Frame'> and not a TopLevel since the wm manage documentation says: The widget specified will become a stand alone top-level window?
How can I handle the window close event since self.dockable_frame.protocol("WM_DELETE_WINDOW", insert_function_here) gives an error on my Windows pc?
The error is:
AttributeError: 'Frame' object has no attribute 'protocol'
I understand the error but how to handle the window close event?
The documentation is missleading. As I discovered this feature I thought simliar, the frame becomes a window. In fact that isnt really true which I can proof by my code below.
What really happens, at least under MS-Windows but I expect same functionality under other os, is that frames will be just packed on a different toplevel that will be created by wm_mange for this.
When tkinter defines a Window/Toplevel it always build a child window (frame) for the client area which you will work with. Thats why you need to call the win32gui.GetParent method when you will change your window style.
Code:
import tkinter as tk
import win32gui
def info():
print(f'python id: {id(frame)}')
print(f'tkinterID: {frame.winfo_id()}')
print(f'parent id: {win32gui.GetParent(frame.winfo_id())}')
def undock():
root.wm_manage(frame)
def forget():
root.wm_forget(frame)
frame.pack()
root = tk.Tk()
frame= tk.Frame()
frame.pack()
b1 = tk.Button(frame,text='INFO',command=info)
b2 = tk.Button(frame,text='mnge',command=undock)
b3 = tk.Button(frame,text='nrml',command=forget)
b1.pack()
b2.pack()
b3.pack()
root.mainloop()
Output:
By first appearance:
python id: 67118160
tkinterID: 3412074
parent id: 7867926
after undock
python id: 67118160
tkinterID: 3412074
parent id: 15666896
after forget
python id: 67118160
tkinterID: 3412074
parent id: 7867926
reference:
In Tk, Toplevel windows are basically a special form of a Frame which
are managed by the window manager. The proposal is to add the commands
wm manage and wm forget which will take an arbitrary Frame and allow
it to be managed by the window manager, making it a Toplevel window.
Why is the type of self.dockable_frame a <class 'tkinter.Frame'> and not a TopLevel since the wm manage documentation says: The widget specified will become a stand alone top-level window?
I think it is because self.dockable_frame is a python class and doesn't know that the underlying widget has been changed. Arguably this is a bug in wm_manage.
How can I handle the window close event since self.dockable_frame.protocol("WM_DELETE_WINDOW", insert_function_here) gives an error on my Windows PC?
The simplest way is to call the method directly from the tk.Wm class. It would look like this:
tk.Wm.protocol(self.dockable_frame, "WM_DELETE_WINDOW", self.whatever)
I'm creating a tkinter gui that I would like to run. This app will do some house cleaning of sorts. It would check files, check for updates, give the user some information etc. Then I would like it to start another tkinter application and then close it self.
import tkinter as tk
import os
def StartProgram():
os.startfile("C:/WINDOWS/system32/notepad.exe")
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame, text="QUIT", fg="red", command= lambda: quit())
button.pack( side=tk.LEFT)
button2 = tk.Button(frame,text="Start my exe", command=lambda: StartProgram())
button2.pack(side=tk.LEFT)
root.maxsize(200,200)
root.minsize(200,200)
root.mainloop()
The problem I have so far is when I attempt to close the original app it closes the one it started as well.
EDIT: I also tried root.destroy(), root.quit()
Any suggestion how I might correct this?
I'm somewhat new to tkinter and Python and am working on a semester long project. Basically I have a main tkinter window, then from that window, topLevel windows are called depending on the user input. In the each topLevel window I have a button that performs a function, I also want this button to close the topLevel window after performing that function. What would be the best way to approach this problem?
I have tried to destroy or close the window, but it ends up closing the main window also. I am just looking for a way to close the topLevel window and perform the function with the click of a button
class MainWindow:
# Entry box
self.entry = StringVar()
self.text_box = Entry(master, textvariable=self.entry)
self.text_box.grid(row=1, column=2)
# Displays and binds button, so when clicked, the enter_button function is called
self.input_button = Button(master, text='Enter', command=self.enter_button)
self.input_button.grid(row=1, column=3, sticky='W')
def enter_button(self):
# Get user input and perform the given command
command = self.entry.get()
# Creates a root for a toplevel window
top = Toplevel()
if command == '1':
add_gui = AddPayment(top)
top.mainloop()
elif command == '2':
#rest of classes/commands
main
def main():
root = Tk()
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
AddPayment class
class AddPayment:
def __init__(self,master):
self.master = master
self.add_label = Label(master, text='How much is the payment for?')
# payment box
self.pay = StringVar()
self.pay_box = Entry(master, textvariable=self.pay)
self.add_button = Button(master, text='Add', command=self.add_payment)
# position widgets
self.pay_box.grid(row=1, column=2)
self.add_label.grid(row=1, column=1)
self.add_button.grid(row=1, column=3)
def add_payment(self):
database.add_pay(self.pay.get())
In this example I would like something in the add_payment function to close the topLevel window after the add_pay function is performed somehow. Thanks in advance
You have a couple problems. For one, you should never call mainloop more than once. You need to remove the call to mainloop() from the enter_button function.
The other problem is that you're not saving a reference to the toplevel, so you've made it more-or-less impossible to destroy it. You simply need to save a reference and then call destroy on the reference.
self.top = Toplevel()
...
self.top.destroy()
I am trying to experiment and get the button to only display the label when the button is clicked instead it is opening up another GUI window. The main frame is called secret message. Within this when i click onto the button it should then replace the empty place with the label in row=2.
Could someone explain to me how i can raise the label rather than just opening up a new window. All code is functional but i want another way around this, i am new to python.
from tkinter import *
def topLevel():
top=Toplevel()
top.geometry("300x200")
root=Tk()
root.title("Secret Message")
button1 = Button(text="Push this button to see hidden message!", width =60, command=topLevel)
button1.grid(row=1, column=0)
label1 = Label(width=50, height=10, background="WHITE", text= "There is no secret!")
label1.grid(row=2, column=0)
root.mainloop()
You question title has nothing to do with your question.
To update the geometry of your label you simple need to tell the function where you want the label on the container you set up your label in. In this case you do not define the container so the widgets default to the root window.
Here is a working example that will update the label geometry when you press the button.
from tkinter import *
root=Tk()
root.title("Secret Message")
def grid_label():
label1.config(text="There is no secret!")
Button(root, text="Push this button to see hidden message!", width=60, command=grid_label).grid(row=1, column=0)
label1 = Label(root, width=50, height=10, background="WHITE")
label1.grid(row=2, column=0)
root.mainloop()