Dynamically changing label text upon events in another tkinter frame - python-3.x

I'm trying to dynamically change the text of a label within one frame from another frame. Both frames are within an instance of an object class.
This is not my actual problem but it illustrates the principle.
How can I address app.frame0.label1.text from app.frame1.button1.click?
(I appreciate that this is not the correct syntax)
Thank you in advance.
from tkinter import *
txt = 'Hi'
def change():
# idea was to put some code here to change frame0-label1-text
pass
class HST(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
frame0 = Frame(self)
frame0.grid(row=0, column=0)
label1 = Label(frame0, text=txt, width=20, height=5)
label1.grid(row=0, column=0)
frame1 = Frame(self)
frame1.grid(row=1, column=0)
button1 = Button(frame1, text='Click', width=20, height=5, command=change)
button1.grid(row=0, column=1)
app = HST()
app.mainloop()

Managed to solve my own problem! It is often helpful to lay out the issue to explain to someone else. Revised code using StringVar
from tkinter import *
def change():
app.txt.set('hello') # added
pass
class HST(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.txt = StringVar() # added
frame0 = Frame(self)
frame0.grid(row=0, column=0)
label1 = Label(frame0, textvariable=self.txt, width=20, height=5) # changed to use StringVar
label1.grid(row=0, column=0)
frame1 = Frame(self)
frame1.grid(row=1, column=0)
button1 = Button(frame1, text='Click', width=20, height=5, command=change)
button1.grid(row=0, column=1)
app = HST()
app.txt.set('Hi') # added to replace original line txt = 'Hi'
app.mainloop()

Related

How to create a class to close GUI and exit Python

I have been trying to find a way to do this for a while to no avail. I would like to create a class to completely close my GUI in tkinter and I'm not having much luck. I've tried sys.exit and .destroy() a few different ways. I can manage to do what I want without using classes but I'm rather new to OOP. Here is my code:
import sys as system
import tkinter as tk
from tkinter import ttk
class headerFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#setup the grid layout manager
self.columnconfigure(0, weight=1)
self._create_widgets()
def _create_widgets(self):
#header bar
canvas = tk.Canvas(self, bg='#0066cc', highlightthickness=0, height=45, width=600)
canvas.grid(column=0, row=0, sticky=tk.W)
label = ttk.Label(self, text='Production Assistant', background='#0066cc', foreground='White', font=('calibri', 18, 'bold'))
label.grid(row=0, column=0)
class loginFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#setup the grid layout manager
self.columnconfigure(0, weight =1)
self.columnconfigure(0, weight=3)
self._create_widgets()
def _create_widgets(self):
#username
ttk.Label(self, text='Username: ', justify='right').grid(row=0, column=0, sticky=tk.E)
username = ttk.Entry(self, width=33)
username.focus()
username.grid(row=0, column=1, sticky=tk.W)
#password
ttk.Label(self, text='Password: ', justify='right').grid(row=1, column=0, sticky=tk.E)
password = ttk.Entry(self, width=33, show='*')
password.grid(row=1, column=1, sticky=tk.W)
#add padding
for widget in self.winfo_children():
widget.grid(padx=0, pady=5)
class loginButtonFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#setup the grid layout manager
self.columnconfigure(0, minsize=62)
self._create_widgets()
def _create_widgets(self):
#buttons
ttk.Button(self, text='Login', width=15).grid(row=0, column=1)
ttk.Button(self, text='Forgot Login', width=15).grid(row=0, column=2)
ttk.Button(self, text='Request Access', width=15).grid(row=1, column=1)
ttk.Button(self, text='Exit', width=15, command=exitButton).grid(row=1, column=2)
#add padding to buttons
for widget in self.winfo_children():
widget.grid(padx=3, pady=3)
class exitButton():
def exit():
#code to close gui and program
#create the main application
class mainLogin(tk.Tk):
def __init__(self):
super().__init__()
self.title('Login')
self.geometry('325x175')
self.resizable(0, 0)
self.configure(background='#444444')
#windows only (remove the minimize/maximize buttons)
self.attributes('-toolwindow', True)
#TCL to center the screen
self.eval('tk::PlaceWindow . center')
#layout on the root window
self.columnconfigure(0, weight=1)
self._create_Styles()
self._create_widgets()
def _create_Styles(self):
#create styles
s = ttk.Style()
s.configure('TFrame', background='#444444')
s.configure('TLabel', background='#444444', foreground='white')
s.configure('TButton', background='#878683', foreground='black')
def _create_widgets(self):
#create the header frame
_header_frame = headerFrame(self)
_header_frame.grid(column=0, row=0)
#create the login frame
_login_frame = loginFrame(self)
_login_frame.grid(column=0, row=1, sticky=tk.N)
#create the button frame
_login_button_frame = loginButtonFrame(self)
_login_button_frame.grid(column=0, row=2)
if __name__ == '__main__':
app = mainLogin()
app.mainloop()
class exitButton() is what I would like to call from multiple different pages in the application to close everything.
Any help is appreciated, I'm trying to learn as I build so if you have any suggested reading based around Python that would help with this I would appreciate it!

Python tk.StringVar() in a dict won`t change with entry

I'm quite new to Python and have a problem which I can't solve.
I want to write a Class which displays a configuration file (dict) in a tkinter frame. It should choose a tk.widged by type of value and for bool values create checkboxes, and for int and strings it should create entries.
After all it should give back a dict with the same keys, but the values changed to tk.*Var()
However, with the checkboxes there is no problem.
But the int and strings from the entries will not be written through the save_config() function.
It seems the tk.IntVar() and the tk.StringVar() don't update when typing something in the entries.
Hope my question is clear.
Can anybody please help?
code updated like asked by Bryan Oakley:
It also contains the changes suggested by Martin Finke which brings the solution for me!!
#!/usr/bin/env python3.9
import tkinter as tk
from tkinter import ttk
class MainFrame(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self)
self.test_dict = {"name": "Ares"}
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=0)
self.save_config_button = ttk.Button(
self, text="Print Configuration", command=self.save_config)
self.save_config_button.grid(row=0, column=0, padx=5, pady=5)
self.configuration_frame = ConfigurationFrame
self.config_frame = self.configuration_frame(
self, "Configurations", self.test_dict)
self.config_frame.grid(row=1, column=0, padx=5, pady=5)
self.configuration = self.config_frame.get_configuration()
def save_config(self):
conf = {}
for key, value in self.configuration.items():
conf[key] = value.get()
print(conf)
class ConfigurationFrame(tk.LabelFrame):
def __init__(self, parent, widget_name, config, * args, **kwargs):
tk.LabelFrame.__init__(
self, parent, * args, **kwargs)
self.config(bd=2, text=widget_name)
self.grid(sticky=tk.NSEW)
self.configuration = {}
self.rowconfigure(0, weight=0)
self.columnconfigure(0, weight=1)
count = 0
for key, value in config.items():
if type(value) == str:
name = key
entry_text = tk.StringVar(self)
entry_text.set(value)
frame_label = tk.Frame(
self, height=1)
frame_label.grid(
row=count, column=0, padx=10, pady=2, sticky=tk.W)
self.entry_str = ttk.Entry(
frame_label, textvariable=entry_text, text=name, width=10)
self.entry_str.grid(row=0, column=0, padx=5,
pady=2, sticky=tk.W)
self.entry_str.insert(tk.END, str(value))
self.name_label = tk.Label(
frame_label, text=name)
self.name_label.grid(row=0, column=1, padx=5,
pady=2, sticky=tk.W)
self.configuration[name] = self.entry_str
count += 1
def get_configuration(self):
return self.configuration
def main():
MainFrame().mainloop()
if __name__ == "__main__":
main()
Thanks to enyone!
I suggest a simple change in the __ init __ function of the ConfigurationFrame class:
line self.configuration[name] = entry_int_txt replace self.configuration[name] = self.entry_int
line self.configuration[name] = entry_text replace self.configuration[name] = self.entry_str

askopenfilename() disables editing tkinter Entry [duplicate]

This question already has answers here:
No input possible after tk
(2 answers)
Closed 4 years ago.
I'm using the following code to open a file, read its lines into a list and filter them using a substring from an Entry:
def get_entries(self):
"""
Open a file and load entries into a list.
"""
try:
# self.file_name = "p1.py"
self.file_name = askopenfilename(title="Open file")
self.file_handle = open(self.file_name, "r")
except IOError:
messagebox.showinfo("Info", "No file has been openned.")
self.destroy()
else:
self.entry_list = self.file_handle.readlines()
self.update_list()
def update_list(self, *args):
"""
Update the list after each editing of the search filter
"""
search_term = self.search_var.get()
self.lbox.delete(*self.lbox.get_children())
for index, item in enumerate(self.entry_list):
if search_term.lower() in item.lower():
self.lbox.insert('', END, values=(index, item))
Why does it work fine using self.file_name = "p1.py" but using askopenfilename() disables editing the Entry?
Minimizing and restoring the window with the Entry fixes the problem.
I'm using PyCharm on Windows 10
Here is the rest of the code for reference:
from tkinter import *
from tkinter import messagebox
from tkinter import ttk
from tkinter.filedialog import askopenfilename
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.file_name = ""
self.file_handle = ""
self.entry_list = None
self.search_label = Label(self, text="Filter: ")
self.search_var = StringVar()
self.search_var.trace("w", self.update_list)
self.search_entry = Entry(self, textvariable=self.search_var)
self.lbox = ttk.Treeview(self, columns=('indices', 'entries'), displaycolumns='entries', show='headings')
self.lbox.heading('entries', text="Entries", anchor="w")
self.confirm = Button(self, text="Confirm", width=10, command=self.confirm_action)
self.cancel = Button(self, text="Cancel", width=10, command=quit)
self.search_label.grid(row=0, column=0, sticky=E, padx=12, pady=5)
self.search_entry.grid(row=0, column=1, sticky=W, columnspan=4, pady=5)
self.lbox.grid(row=1, column=0, columnspan=3, sticky=(N, W, S, E), padx=12, pady=5)
self.cancel.grid(row=2, column=0, pady=5)
self.confirm.grid(row=2, column=1, sticky=W, padx=12, pady=5)
self.grid_columnconfigure(0, weight=1, uniform="u")
self.grid_columnconfigure(1, weight=1, uniform="u")
self.grid_columnconfigure(2, weight=4, uniform="u")
self.get_entries()
def get_entries(self): ...
def update_list(self, *args): ...
def confirm_action(self): ...
root = Tk()
root.title('Filter Listbox Test')
app = Application(master=root)
app.mainloop()
This seems to be a problem due to calling the askfilename before the root window is drawn. As a workaround you can add self.update() before you call askopenfilename.
class Application(Frame):
def __init__(self, master=None):
# ... stuff ...
self.update()
self.get_entries()
I'll file a bug report about this right now.

UPDATED**TK/ROOT/Not CALLABLE, Multiple problems, AttributeError: 'NoneType' object has no attribute '_root'

I've been struggling for days trying to figure out the problem in my file.
So now I've stripped the whole file down to a minimum with only the problems.
After I added the counting fuction, the problems appeared.
I've been trying several different ways to fix this one without luck.
Searched this site up and down and still can't find any answer or questions thats similar to this one with multiple errors.
EDIT:
The code can now be directly imported. The old import didtnt work as planned.
1st: The problem is that I don't want root to open two windows. But without calling "root=Tk", the "tk.StringVar" will not work.
2nd: The counter only shows the number in console. I want it to show in "l = Label(f3, textvariable=click) # Score"
3rd: What is the root if tk() is allready a "root" without calling root=tk()?
And why do I get the error "AttributeError: 'NoneType' object has no attribute '_root'" when I'm not calling root anything?
-
I'm not that into Python and Tk yet. So I can't figure out a clever answer myself.
Might be a minder issue for someone with more experience in Python and Tk then me.
Would be extremely glad for any help.
EDIT 2:
UPDATE! Found the problem myself after days of struggling.
Needed to add "self." before "click". Removed "root=tk()", removed "from tkinter import*" and added "tk." for every button, checkbutton, labels and frames and now it finally works. The code is now updated aswell.
import tkinter as tk
Large_font= ("arial", 30)
class Myapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Payment")
#root.withdraw()
self.geometry("1280x1024")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Homepage, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Homepage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class Homepage(tk.Frame):
def __init__(self, parent, controller, **kwargs):
Frame.__init__(self, parent, **kwargs)
self.configure(background='grey')
f1 = tk.Frame(self, width=1200, height=100, bd=3, bg="grey", relief="raise")
f1.pack(side="top")
lblInfo = tk.Label(f1, text="MY APP", font=Large_font, bg="grey", fg="white")
lblInfo.pack(side="top")
#=========SUM UP==========
f3 = tk.Frame(self, width=400, height=800, bd=3, bg="grey", relief="raise")
f3.pack(side="right")
def uiPrint():
print("")
print(clickcount)
blankLine()
self.click = tk.IntVar()
self.click.set("6");
def blankLine():
for i in range(0):
print("")
def buttonCommand():
global clickcount
global click
global mult
clickcount += 2 * (mult)
self.click.set(str(clickcount)); # Update score
uiPrint()
def buttonCommand1():
global clickcount
global click
global mult
clickcount -= 1 * (mult)
self.click.set(str(clickcount));
uiPrint()
l = tk.Label(f3, textvariable=click) # Score
l.pack()
plusButton = tk.Button(f3, command = buttonCommand, text="+")
minusButton = tk.Button(f3, command = buttonCommand1, text="-")
plusButton.pack(padx=10, pady=10)
minusButton.pack(padx=10, pady=10)
btn1 = tk.Button(f3, padx=20, pady=20, bd=8, fg="white", bg="green", font=('arial', 30, 'bold'),
text="NEXT")
btn1.pack(padx=10, pady=10)
clickcount = (6)
mult = 1
dcp1 = 0
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.configure(background='grey')
f1 = tk.Frame(self, width=600, height=100, bd=3, bg="grey", relief="raise")
f1.pack()
app = Myapp()
app.mainloop()

Return Value from TkInter Entry with Button

Python novice, here. I've noticed there are a lot of questions around the topic of returning values from a TkInter function, but none of the solutions seem to solve my issue.
I can successfully print self.e1path to the shell from within getPath.submit, but I cannot return it to the rest of my code. I'm using a print statement outside of the class to test whether I've successfully returned the CSV path.
from tkinter import *
import tkinter as tk
class getPath(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label1 = tk.Label(self, text="CSV Path").grid(row=0, column=0)
self.e1 = tk.Entry(self, width=50)
self.e1Grid = self.e1.grid(row=0, column=1)
self.browse = tk.Button(self, text='Browse', command=self.getCSV).grid(row=0, column=2)
self.submit = tk.Button(self, text='Submit', command=self.submit).grid(row=1, column=1)
def getCSV(self):
self.fileName = filedialog.askopenfilename( filetypes = (('Comma Separated Values', '*.csv'), ('All Files', '*.*')), title = "Choose a CSV File")
self.e1.insert(10, self.fileName)
def submit(self):
self.e1Path = self.e1.get()
return self.e1Path
app = getPath()
app.mainloop()
print(app)
I figured it out! I needed to add a self.destroy() to the submit function. This stopped the mainloop and let me call on self.e1path outside of the function using app.e1path. New code:
from tkinter import *
import tkinter as tk
class getPath(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label1 = tk.Label(self, text="CSV Path").grid(row=0, column=0)
self.e1 = tk.Entry(self, width=50)
self.e1Grid = self.e1.grid(row=0, column=1)
self.browse = tk.Button(self, text='Browse', command=self.getCSV).grid(row=0, column=2)
self.submit = tk.Button(self, text='Submit', command=self.submit).grid(row=1, column=1)
def getCSV(self):
self.fileName = filedialog.askopenfilename( filetypes = (('Comma Separated Values', '*.csv'), ('All Files', '*.*')), title = "Choose a CSV File")
self.e1.insert(10, self.fileName)
def submit(self):
self.e1Path = self.e1.get()
self.destroy()
app = getPath()
app.mainloop()
print(app.e1Path)

Resources