How to save a data after closing a window? - python

I'm trying to make some GUI on Python3 with tkinter. So far I have Main Window and 'Test' button on it, which opens second window. Second window has entry, label, save and close buttons. When you type something in entry and press save button, label shows the text you typed in entry. But after closing this window and opening it again, label shows nothing. How do I make this label to show the text that were typed last time before closing? For example, I type 'Hi' in entry, press 'Save', then I press 'Close', then I open this window again and label shows 'Hi'
import tkinter as tk
def save_data(entry, t):
t.config(text = entry.get())
def close_action(current_window):
current_window.destroy()
def insertMainInfo():
new_window = tk.Tk()
new_window.geometry("307x131")
new_window.title("TestWindow")
test_entry = tk.Entry(new_window)
test_entry.place(relx = 0.283, rely = 0.1, height = 24, width = 127)
text = tk.Label(new_window)
text.place(relx = 0.283, rely = 0.25, height = 24, width = 127)
save_button = tk.Button(new_window, command = lambda: save_data(test_entry, text))
save_button.place(relx=0.283, rely=0.45, height=24, width=127)
save_button.configure(text = "Save")
close = tk.Button(new_window, command = lambda: close_action(new_window))
close.place(relx=0.283, rely=0.687, height=24, width=127)
close.configure(text = "Close")
new_window.mainloop()
if __name__ == '__main__':
top = tk.Tk()
top.geometry("307x131+557+330")
top.resizable(width=False, height=False)
top.title("MainWindow")
new_window_button = tk.Button(top, command = insertMainInfo)
new_window_button.place(relx=0.283, rely=0.687, height=24, width=127)
new_window_button.configure(text = "Test")
main_label = tk.Label(top)
main_label.place(relx=0.033, rely=0.153, height=41, width=284)
main_label.configure(text = "TestLabel")
top.mainloop()

I have to confess that your question heading is a bit ambiguity.
If you just want to update a label of last entry, here a simple way of modifying your code.
As advised, as a good practice we only have one Tk() in a program, other new windows or pop-up windows should use Toplevel() of tkinter class; So I use this in your insertMainInfo() function.
The point here is to define a variable, I called last_entry and initially is empty or ‘’. Use this as parameter in new_window button in main program (after if __name__ == '__main__': ) to this variable (I also add lambda function here).
Then we define it as global in save_data function, so as it can be known later by other functions or main program as the last entry before new_window is closed.
Here I modify your code as said above, and I have tested it, and it works as expected.
import tkinter as tk
def save_data(entry, t):
global last_entry
last_entry = entry.get()
t.config(text = last_entry)
def close_action(current_window):
current_window.destroy()
def insertMainInfo(last_entry):
new_window = tk.Toplevel()
new_window.geometry("307x131")
new_window.title("TestWindow")
test_entry = tk.Entry(new_window)
test_entry.place(relx = 0.283, rely = 0.1, height = 24, width = 127)
text = tk.Label(new_window, text=last_entry)
text.place(relx = 0.283, rely = 0.25, height = 24, width = 127)
save_button = tk.Button(new_window, command = lambda: save_data(test_entry, text))
save_button.place(relx=0.283, rely=0.45, height=24, width=127)
save_button.configure(text = "Save")
close = tk.Button(new_window, command = lambda: close_action(new_window))
close.place(relx=0.283, rely=0.687, height=24, width=127)
close.configure(text = "Close")
new_window.mainloop()
# --- A Simple Data Structure ---
last_entry = ''
if __name__ == '__main__':
top = tk.Tk()
top.geometry("307x131+557+330")
top.resizable(width=False, height=False)
top.title("MainWindow")
new_window_button = tk.Button(top, command = lambda: insertMainInfo(last_entry))
new_window_button.place(relx=0.283, rely=0.687, height=24, width=127)
new_window_button.configure(text = "Test")
main_label = tk.Label(top)
main_label.place(relx=0.033, rely=0.153, height=41, width=284)
main_label.configure(text = "TestLabel")
top.mainloop()

Related

How do I make a tkinter button destroy itself?

I made this program in Tkinter in python where a small window pops up when the code is run and a start button would pop up and make the window full screen and show the content after. I want to make the button destroy itself after I press it so it makes a fullscreen and removes the button. I am still a beginner and would like the answer to be simple. The solution I am looking for is to maybe destroy the button completely(preferred) or move it way out of sight in the fullscreen window. Here is the code:
import Tkinter as w
from Tkinter import *
w = Tk()
w.geometry("150x50+680+350")
def w1():
w.attributes("-fullscreen", True)
l1 = Label(w, text = "Loaded!", height = 6, width = 8).pack()
global b1
b1.place(x = -10000, y = -10000)
b1 = Button(w, text = "Start", height = 3, width = 20, command = w1).place(x = 0, y = 10)
b2 = Button(w, text = "Exit", command = w.destroy).place(x = 1506, y = 0)
w.mainloop()
As you can see I want to make button one destroy itself.
Try this:
import tkinter as tk # Use `import Tkinter as tk` for Python 2
root = tk.Tk()
root.geometry("150x50+680+350")
def function():
global button_start
root.attributes("-fullscreen", True)
label = tk.Label(root, text="Loaded!", height=6, width=8)
label.pack()
button_start.place_forget() # You can also use `button_start.destroy()`
button_start = tk.Button(root, text="Start", height=3, width=20, command=function)
button_start.place(x = 0, y = 10)
button_exit = tk.Button(root, text="Exit", command=root.destroy)
button_exit.place(x=1506, y=0)
root.mainloop()
PS: Please read this.
Try:
b1.place_forget()
This will essentially "forget" about the button and hide it from view.
Edit:
If you are getting the error that b1 is None try:
b1 = Button(w, text = "Start", height = 3, width = 20, command = w1)
b1.place(x = 0, y = 10)
You need to add the b1.place() option at the bottom for this to work

Random tkinter window opening on if/else statement

I'm wondering if I got my if else statement wrong or if its a tkinter issue. I want it so that if a 0 is left in any or all boxes, it gives an error message. But after the error message is closed, it opens a random blank window. This is my code. The specific area is the if else statement within the function valueget()
import tkinter as tk
def mainwindow():
mainwindow = tk.Tk()
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
tk.Label(mainwindow, text = 'Enter a', font = ('verdana'), bg='#aaf0d1').grid(row=0)
tk.Label(mainwindow, text = 'Enter b', font = ('verdana'), bg='#aaf0d1').grid(row=1)
tk.Label(mainwindow, text = 'Enter c', font = ('verdana'), bg='#aaf0d1').grid(row=2)
getA = tk.IntVar()
aBox = tk.Entry(mainwindow, textvariable = getA, width=3, bg='#aaf0d1')
aBox.grid(row=0, column=1)
aBox.config(highlightbackground='#aaf0d1')
getB = tk.IntVar()
bBox = tk.Entry(mainwindow, textvariable = getB, width=3, bg='#aaf0d1')
bBox.grid(row=1, column=1)
bBox.config(highlightbackground='#aaf0d1')
getC = tk.IntVar()
cBox = tk.Entry(mainwindow, textvariable = getC, width=3, bg='#aaf0d1')
cBox.grid(row=2, column=1)
cBox.config(highlightbackground='#aaf0d1')
button = tk.Button(mainwindow, text='Obtain roots', command = lambda: valueget(), font = ('verdana'), highlightbackground='#aaf0d1')
button.grid(row=4)
button.config(bg='#aaf0d1')
def valueget():
readA = getA.get()
readB = getB.get()
readC = getC.get()
intA = int(readA)
intB = int(readB)
intC = int(readC)
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
messagewindow = tk.Tk()
messagewindow.geometry('290x50')
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
if readA == 0 or readB==0 or readC==0 or (readA==0 and readB==0 and readC==0):
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
messagewindow.mainloop()
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
mainwindow.mainloop()
def startup():
startpage = tk.Tk()
startpage.title('Solver')
photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage, image = photo, command = lambda: [startpage.destroy(), mainwindow()])
coverbutton.pack()
coverbutton.configure(highlightbackground='#aaf0d1')
startpage.mainloop()
startup()
Here's a basic idea of what I would do:
import tkinter as tk
from tkinter import messagebox
def mainwindow(root):
# Creates a toplevel window
mainwindow = tk.Toplevel()
mainwindow.protocol("WM_DELETE_WINDOW", root.destroy) # This overrides the "X" being clicked to also destroy the root window.
root.withdraw() # "Hides" the root window, leaving it (and mainloop) running in the background.
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
# Since all three of the labels/entries are the same
# we can save space by generating them in a loop
entry_items = ('Enter a', 'Enter b', 'Enter c')
values = []
for x, item in enumerate(entry_items): # Using enumerate and x to assign rows
tk.Label(mainwindow, text = item,
font = ('verdana'), bg='#aaf0d1').grid(row=x) # Row assigned to x.
values.append(tk.StringVar()) # Appended StringVar to list.
tk.Entry(mainwindow,
textvariable = values[-1], # Uses the last value appended to the values list.
highlightbackground='#aaf0d1',
width=3,
bg='#aaf0d1').grid(row=x, column=1) # Row assigned to x.
tk.Button(mainwindow,
text='Obtain roots',
command = lambda vals = values: valueget(vals), # Here the button command is assigned with the values list
font = ('verdana'), bg='#aaf0d1',
highlightbackground='#aaf0d1').grid(row=3) # we know there are 3 items before this.
mainwindow.lift() # This is a method of bringing a window to the front
def valueget(vals):
# This line gets the values from the StringVars, converts them to ints,
# and returns them to their respective variables.
try:
readA, readB, readC = [int(val.get()) for val in vals]
except ValueError:
messagebox.showerror(title="Number Error", message='Values must be numbers')
return
# Here the variables are checked to see if they are 0
# Since each one is being checked if it is 0, there is no need to check if they are all 0.
for val in (readA, readB, readC):
if val == 0:
# If they are 0, shows an error message
messagebox.showerror(title="Zero Error", message='Values must not be zero')
return
# Creates a toplevel to display the results
messagewindow = tk.Toplevel()
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
tk.Label(messagewindow,
text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}',
bg='#aaf0d1',
font = ('verdana')).pack(padx = 5, pady = 2)
tk.Button(messagewindow,
text='Close',
command = messagewindow.destroy, # There is no need for a lambda for this.
font = ('verdana'),
bg = '#aaf0d1',
highlightbackground='#aaf0d1').pack(padx = 5, pady = 2)
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
messagewindow.lift() # This is a method of bringing a window to the front
def startup():
startpage = tk.Tk()
startpage.title('Solver')
# COMMENTED OUT FOR TESTING
#photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage,
# COMMENTED OUT FOR TESTING
#image = photo,
text = "TESTING", # HERE FOR TESTING
highlightbackground='#aaf0d1',
command = lambda root = startpage: mainwindow(root)).pack() # Passes the startpage to the mainwindow function.
startpage.mainloop() # The only mainloop you need.
startup()
I would recommend to improve the readability of the if-else statement for a start.
coefficients = [readA, readB, readC]
if sum(coefficients): # If they all are all zeros this will be False
if min(coefficients): # If any one is zero, this will be False
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
else:
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
errorwindow = tk.messagebox.showerror(message='none').pack()

Python Tkinterprogramm import to a new file throws a error

i have a problem with the user-output of my tkinter gui.
First of all it works, but there is a huge problem with the import to a new py file. It throws a Error ().
Traceback (most recent call last):
File "F:\Python_Projekt\Übung\Plus\test.py", line 4, in <module>
tk = own.get_start()
File "F:\Python_Projekt\Übung\Plus\plus_window_pack2.py", line 31, in get_start
sel = ListBox1.curselection()
File "C:\Users\nox\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 2782, in curselection
return self._getints(self.tk.call(self._w, 'curselection')) or ()
_tkinter.TclError: invalid command name ".!frame.!frame.!listbox"
I know there is a problem with my ListBox but i have no clue how to handle it. I need the used output for my new script. So i could work with it.
from tkinter import*
import tkinter as tk
window = Tk()
rahmen1 = Frame(master = window) #, bg='black'
rahmen1.pack(side='top', padx=5, pady=5)
rahmen2 = Frame(master = rahmen1) #, bg= 'yellow'
rahmen2.pack(side='left', padx=5, pady=5)
def get_start():
selection=ListBox1.curselection()
picked = selection[0]
used = ListBox1.get(picked) #-------user-input
print(used)
return used
####################----------------------------------------------------
List = ['Dax','Dow','EUR/USD', 'Gold', 'Silber','EUR/JPY','USD/JPY']
scrollbar1 = Scrollbar(rahmen2) # ,bg='green'
scrollbar1.pack(padx = 1,side = 'right',fill=Y)
ListBox1 = Listbox(rahmen2,exportselection = False)
ListBox1.config( yscrollcommand = scrollbar1.set, width = 40)
scrollbar1.config( command = ListBox1.yview) # ,bg='blue'
for i in List:
ListBox1.insert(tk.END, str(i))
ListBox1.pack(padx = 1,)
###################------------------------------------------------
Button1 = Button(rahmen2,text='Get Data', font = 'bold')
Button1.config (width=40, height = 3, command = get_start)
Button1.pack( )
window.mainloop()
I changed the code to get the important part. For a better understanding of my problem.
if i want to get the user-input it thorw's me the error.
Try this. I have added a simple check to make sure something's selected (I'm not sure what the error at the top of your question's about though):
def get_start():
selection=ListBox1.curselection()
if len (selection) != 0:
picked = selection[0]
used = ListBox1.get(picked) #-------user-input
print(used)
return used
EDIT:
This checks every x seconds (defined by UPDATE_TIME) for checkbox updates
UPDATE_TIME = 0.1
from tkinter import*
import tkinter as tk
import threading, time
window = Tk()
rahmen1 = Frame(master = window) #, bg='black'
rahmen1.pack(side='top', padx=5, pady=5)
rahmen2 = Frame(master = rahmen1) #, bg= 'yellow'
rahmen2.pack(side='left', padx=5, pady=5)
def get_start():
print (previous)
return previous
def get_update ():
global previous
try:
while True:
selection=ListBox1.curselection()
if len (selection) == 0: previous = None
else:
picked = selection[0]
previous = ListBox1.get(picked) #-------user-input
time.sleep (UPDATE_TIME)
except: pass
####################----------------------------------------------------
List = ['Dax','Dow','EUR/USD', 'Gold', 'Silber','EUR/JPY','USD/JPY']
scrollbar1 = Scrollbar(rahmen2) # ,bg='green'
scrollbar1.pack(padx = 1,side = 'right',fill=Y)
ListBox1 = Listbox(rahmen2,exportselection = False)
ListBox1.config( yscrollcommand = scrollbar1.set, width = 40)
scrollbar1.config( command = ListBox1.yview) # ,bg='blue'
for i in List:
ListBox1.insert(tk.END, str(i))
ListBox1.pack(padx = 1,)
###################------------------------------------------------
Button1 = Button(rahmen2,text='Get Data', font = 'bold')
Button1.config (width=40, height = 3, command = get_start)
Button1.pack ()
threading.Thread (target = get_update).start ()
window.mainloop()

How would I create a reset button for my program relating with the following code?

I am trying to add a reset button but I can't seem to get it to work. I created a main in order to refer back to it when the button is pressed but no luck. Any ideas?
import sys
from tkinter import *
import math
def main():
def closeWin():
myGui.destroy() #Close Window Function
def kiloFunc():
myText = kiloMent.get() #Kilometers to Miles Fuction
convert = 0.62
miles = myText * convert
finalKilo = Label(text = miles,fg='red',justify='center').place(x=200,y=80)
def mileFunc():
myText2 = mileMent.get() #Miles to Kilometers Function
convertTwo = myText2 // 0.62
finalMile = Label(text = convertTwo, fg = 'red',justify='center').place(x=200,y=170)
myGui = Tk()
kiloMent = IntVar()
mileMent = IntVar()
myGui.title("Distance Converter")
myGui.geometry("450x200+500+200")
myLabel = Label(text="Welcome! Please enter your value then choose your option:",fg="blue",justify='center')
myLabel.pack()
kiloEntry = Entry(myGui, textvariable = kiloMent,justify='center').pack()
kilo2milesButton = Button(text = "Kilometers to Miles!", command = kiloFunc).pack()
mileEntry = Entry(myGui, textvariable = mileMent,justify='center').place(x=130,y=105)
miles2kiloButton = Button(text = "Miles to Kilometers!", command = mileFunc).place(x=150,y=135)
reset = Button(text = "Reset Values!", command = main).place(x=10,y=165)
quit = Button(text="Quit", command = closeWin).place(x=385,y=165)
myGui.mainloop()
main()
By calling main() again, you are simply creating another instance of the GUI. What you should do instead is (if I understand correctly), reset the values of the currently existing GUI. You can use the set() method of the GUI objects.
Does
def reset_values():
kiloMent.set(0)
mileMent.set(0)
reset = Button(text="Reset Values!", command=reset_values).place(x=10, y=165)
do the trick?
Looking at your code more thoroughly, however, there are some other problems there, as well. To start with, I would suggest not creating a Label everytime the user tries to convert a value.
This code should work:
from tkinter import *
def main():
def closeWin():
myGui.destroy() # Close Window Function
def kiloFunc():
finalKilo.set(kiloMent.get() * 0.62) # Kilometers to Miles Fuction
def mileFunc():
finalMile.set(mileMent.get() // 0.62) # Miles to Kilometers Function
def clearFunc():
kiloMent.set("0")
mileMent.set("0")
finalKilo.set("")
finalMile.set("")
myGui = Tk()
kiloMent = IntVar()
mileMent = IntVar()
finalKilo = StringVar()
finalMile = StringVar()
myGui.title("Distance Converter")
myGui.geometry("450x200+500+200")
myLabel = Label(text="Welcome! Please enter your value then choose your option:", fg="blue", justify='center')
myLabel.pack()
kiloEntry = Entry(myGui, textvariable=kiloMent, justify='center')
kiloEntry.pack()
kilo2milesButton = Button(text="Kilometers to Miles!", command=kiloFunc)
kilo2milesButton.pack()
mileEntry = Entry(myGui, textvariable=mileMent, justify='center')
mileEntry.place(x=130, y=105)
miles2kiloButton = Button(text="Miles to Kilometers!", command=mileFunc)
miles2kiloButton.place(x=150, y=135)
kiloLabel = Label(textvariable=finalKilo, fg='red', justify='center')
kiloLabel.place(x=200, y=80)
mileLabel = Label(textvariable=finalMile, fg='red', justify='center')
mileLabel.place(x=200, y=170)
reset = Button(text="Reset Values!", command=clearFunc)
reset.place(x=10, y=165)
quit = Button(text="Quit", command=closeWin)
quit.place(x=385, y=165)
myGui.mainloop()
main()
A few notes about your original code besides the ones that Chuck mentioned:
The math and sys imports were unused.
You were setting variables equal to widget.pack() and widget.place(), which are functions that return None.

Python tkinter: access child widgets of a widget

I have a string 'currentMessage' and a Label to display it.
I have a Toplevel widget, which contains a Text widget to provide new value for 'currentMessage':
from tkinter import *
from tkinter import ttk
root = Tk()
mainFrame = ttk.Frame(root)
mainFrame.grid()
currentMessage = 'current Message'
ttk.Label(mainFrame, text = currentMessage).grid(padx = 10, pady = 10)
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.textBox.get(0.0, END)
def changeValues():
popup = Toplevel(mainFrame)
popup.grid()
textBox = Text(popup, width = 20, height = 5)
textBox.grid(column = 0, row = 0)
textBox.insert(END, 'new message here')
b = ttk.Button(popup, command = lambda: updateCurrentMessage(popup))
b.grid(column = 0, row = 1, padx = 5, pady = 5)
b['text'] = 'Update'
theButton = ttk.Button(mainFrame, command = changeValues, text = 'Click')
theButton.grid(padx = 10, pady = 10)
mainFrame.mainloop()
I tried to get the content of 'textBox' Text widget of the Toplevel by using this function:
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.textBox.get(0.0, END)
But I got an error
'Toplevel' object has no attribute 'textBox'
So how do I access content of the widget 'textBox', which is a child widget of 'popup' (this Toplevel widget is only created when function changeValues() is called)?
I think probably this is what you are looking for -- although I'm just guessing, because you are asking for a solution for a specific problem you think you have, however if I were you I would rethink what exactly do I want to do:
from tkinter import *
from tkinter import ttk
# Create Tk Interface root
root = Tk()
# Initialize mainFrame
mainFrame = ttk.Frame( root )
mainFrame.grid()
# Initialize label of mainframe
theLabel = ttk.Label( mainFrame, text='Current Message' )
theLabel.grid( padx=10, pady=10 )
def createPopup():
# Initialize popup window
popup = Toplevel( mainFrame )
popup.grid()
# Initialize text box of popup window
textBox = Text( popup, width=20, height=5 )
textBox.grid( column = 0, row = 0 )
textBox.insert( END, 'New Message Here' )
# Initialize button of popup window
button = ttk.Button( master = popup,
command = lambda: theLabel.config(text=textBox.get(0.0, END)),
text = 'Update')
button.grid( column=0, row=1, padx=5, pady=5 )
# Initialize button of main frame
theButton = ttk.Button( mainFrame, command=createPopup, text='Click' )
theButton.grid( padx=10, pady=10 )
# Enter event loop
mainFrame.mainloop()
There is a way indeed, like this:
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.nametowidget('textBox').get(0.0, END)
def changeValues():
popup = Toplevel(mainFrame)
popup.grid()
textBox = Text(popup, width = 20, height = 5, name = 'textBox')
textBox.grid(column = 0, row = 0)
textBox.insert(END, 'new message here')
b = ttk.Button(popup, command = lambda: updateCurrentMessage(popup))
b.grid(column = 0, row = 1, padx = 5, pady = 5)
b['text'] = 'Update'
You can choose whatever you want for the 'name'.

Categories