I would like to create a function in Main_Application which would access data from other tabs... how can i achieve it?
I was trying to access it by typing "Page_2.entry2.insert(0, "test")" but it returns Attribute error
I would appreciate any kind of help as i am struggling with it for some time already
class Main_Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("HAT")
first_label = ttk.Label(self, text="lol").pack()
#Notebook
nb = ttk.Notebook(self)
nb.pack()
#Frames
p1 = tk.Frame(nb)
p2 = tk.Frame(nb)
p3 = tk.Frame(nb)
p4 = tk.Frame(nb)
nb.add(p1, text="Page1")
nb.add(p2, text="Page2")
nb.add(p3, text="Page3")
nb.add(p4, text="Page4")
Page_1(p1).pack()
Page_2(p2).pack()
def load_it_all(self):
print(Page_01(self).user_input.get())
Page_02.entry2.insert(0, "test")
# <HERE I WOULD LIKE TO CALL THE DATA FROM
#First one prints the data from Page_01 and second i would like to insert data in Page_02.entry2
class Page_1(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#Stringvar
self.user_input = tk.StringVar()
#Labels
label = ttk.Label(self, text="HU3: ")
#Entries
entry = ttk.Entry(self, textvariable=self.user_input)
#Inserts
entry.insert(0, "4500")
#Buttons
button = ttk.Button(self, text="Apply", command=lambda: Main_Application.load_it_all(self))
#Geo
label.pack()
entry.pack()
button.pack()
def test(self):
print(self.user_input.get())
class Page_2(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_input = tk.StringVar()
label = ttk.Label(self, text="Trend from last cycle ")
#Entries
entry2 = ttk.Entry(self, textvariable=self.user_input)
#Inserts
# self.entry2.insert(0, "4500")
#Buttons
# button = ttk.Button(self, text="Apply", command=self.test2)
#Geo
label.pack()
entry2.pack()
# button.pack()
Your issues included
improperly initiating super
too many containers
randomly using ttk widgets
poor understanding of scope
naming syntax errors
unnecessary vars
Below is a working example of what you are trying to do
import tkinter as tk, tkinter.ttk as ttk
class Main(tk.Tk):
def __init__(self):
#this is how to properly init a super
tk.Tk.__init__(self)
#make notebook fill display
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
#Notebook
nb = ttk.Notebook(self)
nb.grid(row=0, column=0, sticky='nswe')
#keep a reference to the pages
self.p1 = Page_1(self)
self.p2 = Page_2(self)
#tabs
nb.add(self.p1, text="Page 1")
nb.add(self.p2, text="Page 2")
def load_it_all(self):
#use the page references to access and share data
self.p2.input = self.p1.input
class Page_1(tk.Frame):
#property
def input(self):
return self.userinput.get()
#input.setter
def input(self, value):
self.userinput.delete(0, 'end')
self.userinput.insert(0, value)
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
tk.Label(self, text="HU3: ").grid(row=0, column=0)
self.userinput = tk.Entry(self)
self.userinput.grid(row=0, column=1)
self.input="4500"
tk.Button(self, text="Apply", command=master.load_it_all).grid(row=0, column=2)
class Page_2(tk.Frame):
#property
def input(self):
return self.userinput.get()
#input.setter
def input(self, value):
self.userinput.delete(0, 'end')
self.userinput.insert(0, value)
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
tk.Label(self, text="Trend from last cycle ").grid(row=0, column=0)
self.userinput = tk.Entry(self)
self.userinput.grid(row=0, column=1)
if __name__ == "__main__":
root = Main()
root.geometry('800x600')
root.title("HAT")
root.mainloop()
Related
I wrote an app using that basis learned there, and this app reads a text file and removes the redundant lines, and returns a list of numbers. The code below returns a list of numbers in PageOne, But as I want to use that list in PageTwo, it changes to a string. I know I have initially introduced variables as strings, but it does not work under other types as well. How I can keep that as a list?
To run this code you can put a few numbers with space separation in a text file and open it using the app.
import tkinter as tk
from tkinter import filedialog
class Data:
def __init__(self):
self.Tot_grids = tk.IntVar()
self.NO = tk.StringVar()
self.NEW = tk.StringVar()
self.NEW2 = tk.StringVar()
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry("500x200")
container = tk.Frame(self)
container.pack()
self.data = Data()
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self.data)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.frames[PageOne].button2.config(command=self.go_to_page_two)
self.show_frame(PageOne)
def go_to_page_two(self):
self.show_frame(PageTwo)
def show_frame(self, c):
frame = self.frames[c]
frame.tkraise()
class PageOne(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
def opentext():
my_file = filedialog.askopenfilenames(initialdir="/pycharm", title="Select your file")
for T in my_file: # this line should be here when opening multiple files
import re
with open(T, 'r') as infile1:
lines = infile1.read()
nums = []
for n in re.findall(r'(\d+\.\d+|\d+\*\d+)', lines):
split_by_ast = n.split("*")
if len(split_by_ast) == 1:
nums += [float(split_by_ast[0])]
else:
nums += [float(split_by_ast[1])] * int(split_by_ast[0])
nums = nums[1:len(nums)]
data.NO.set(nums)
label3.config(text=nums)
label4.config(text=type(nums))
self.button1 = tk.Button(self, text="Open file(s)", command=opentext)
self.button1.grid(row=0, column=0, columnspan=2, pady=20)
label1 = tk.Label(self, text="imported numbers:")
label1.grid(row=1, column=0, pady=10)
label2 = tk.Label(self, text="type of imported numbers:")
label2.grid(row=2, column=0, pady=5)
label3 = tk.Label(self)
label3.grid(row=1, column=1)
label4 = tk.Label(self)
label4.grid(row=2, column=1, pady=5)
self.button2 = tk.Button(self, text="Continue")
self.button2.grid(row=3, column=0, columnspan=2, pady=10)
class PageTwo(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
self.label5 = tk.Label(self, textvariable=self.data.NEW)
self.label5.pack()
self.label5 = tk.Label(self, textvariable=self.data.NEW2)
self.label5.pack()
self.data.NO.trace_add('write', self.on_grids_updated)
def on_grids_updated(self, *args):
self.data.NEW.set(self.data.NO.get())
self.data.NEW2.set(type(self.data.NO.get()))
app = SampleApp()
app.mainloop()
I also wondered why the type of list in PageOne is shown with some random numbers? thanks
You can use Variable which is the base class of StringVar, IntVar, etc.
Variable.get() does not convert the stored data like StringVar.get() or IntVar.get(). However it seems that list will be converted to tuple by TCL internally.
class Data:
def __init__(self):
self.Tot_grids = tk.IntVar()
self.NO = tk.Variable() # used Variable instead of StringVar
self.NEW = tk.StringVar()
self.NEW2 = tk.StringVar()
Also change the line
label4.config(text=type(nums))
to
label4.config(text=str(type(nums)))
will show the type correctly.
I am trying to get Tkinter to return the value from the input box, but I cannot seem to get it to work.
This is my first attempt trying Tkinter out, s
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\rik\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "n:\regnskab\Afregning\RIK\Udvikling\gui\app2.py", line 43, in step_1
periode_2 = get_period(self)
NameError: name 'get_period' is not defined
import tkinter as tk
from tkinter import ttk
class Mainframe(tk.Tk):
def __init__(self, *args, **kvargs):
super().__init__(*args, **kvargs)
self.title("app")
UserInputFrame(self).grid(row=1, column=0)
ControlsFrame(self).grid(row=2, column=0)
ButtonFrame(self).grid(row=3, column=0)
class UserInputFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_input_year = tk.StringVar(value="2021")
label_entry_year = ttk.Label(self, text="Year")
self.entry_year = ttk.Entry(self, textvariable=self.user_input_year)
label_entry_year.grid(row=2, column=0, sticky="W", padx=10, pady=5)
self.entry_year.grid(row=2, column=1, sticky="EW")
def get_period(self):
print(self.entry_year.get())
class ControlsFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
step_1_button = ttk.Button(self, text="Step 1", command=self.step_1)
step_1_button.grid(row=10, column=0, sticky="EW")
def step_1(self):
periode_2 = get_period(self)
print("step 1")
class ButtonFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
quit_button = ttk.Button(self, text="Quit", command=quit)
quit_button.grid(row=4, column=1, sticky="EW")
def quit(self):
app.destroy
app = Mainframe()
app.mainloop()
You have to learn OOP :)
get_period() is not standalone function but it is function inside class UserInputFrame and you try to access it in class ControlsFrame. You can't access function for one class in other class this way.
You would have to get instance of UserInputFrame,
user_input_frame = UserInputFrame(self)
user_input_frame.grid(row=1, column=0)
send it as argument to ControlsFrame
ControlsFrame(self, user_input_frame).grid(row=2, column=0)
keep it in class variable
class ControlsFrame(ttk.Frame):
def __init__(self, container, user_input_frame):
super().__init__(container)
self.user_input_frame = user_input_frame
and then you can use it
def step_1(self):
self.user_input_frame.get_period() # without `self` in `get_period()`
OR better method
You should assing UserInputFrame to class variable in Mainframe
self.user_input_frame = UserInputFrame(self)
self.user_input_frame.grid(row=1, column=0)
and then other objects (created in Mainframe) may access Mainframe (which is its paret/master) using self.master and then they can access elements in Mainframe like self.master.userinputframe
periode_2 = self.master.user_input_frame.get_period() # without `self` in `get_period()`
import tkinter as tk
from tkinter import ttk
class Mainframe(tk.Tk):
def __init__(self, *args, **kvargs):
super().__init__(*args, **kvargs)
self.title("app")
self.user_input_frame = UserInputFrame(self)
self.user_input_frame.grid(row=1, column=0)
ControlsFrame(self).grid(row=2, column=0)
ButtonFrame(self).grid(row=3, column=0)
class UserInputFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_input_year = tk.StringVar(value="2021")
label_entry_year = ttk.Label(self, text="Year")
self.entry_year = ttk.Entry(self, textvariable=self.user_input_year)
label_entry_year.grid(row=2, column=0, sticky="W", padx=10, pady=5)
self.entry_year.grid(row=2, column=1, sticky="EW")
def get_period(self):
#print(self.entry_year.get())
return self.entry_year.get()
class ControlsFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
step_1_button = ttk.Button(self, text="Step 1", command=self.step_1)
step_1_button.grid(row=10, column=0, sticky="EW")
def step_1(self):
# `self.master` gives access to parent object which here is `Masterframe` (because you used `Masterframe` as `self` when you created `ControlsFrame(self)`
periode_2 = self.master.user_input_frame.get_period() # without `self` in `get_period()`
print("step 1")
class ButtonFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
quit_button = ttk.Button(self, text="Quit", command=self.quit)
quit_button.grid(row=4, column=1, sticky="EW")
def quit(self):
app.destroy()
app = Mainframe()
app.mainloop()
BTW: in ButtonFrame you forgot self. in command=self.quit and () in app.destroy()
EDIT: as #acw1668 noticed function get_period() doesn't return value from entry - it only print it - so it needs return
def get_period(self):
return self.entry_year.get()
I´ve been working on a bhaskara equation solver with Interface(tkinter) on python. I´ve watched some tutorials and I´ve split the Pages into classes. The basic idea of this application is the following:
It starts at StartPage, where the user clicks on the select values button and this opens up PageOne. At this page the user should type the desired values and click the select values button (I´m sorry, I couldn't find a way to save all the values at one, if you know how to do this too, please let me know). After the user saves the values, he goes back to StartPage and clicks the Calculate button. If all values are OK, it shows the result variable.
import tkinter as tk
from tkinter import ttk, END
import math
LARGE_FONT =("Verdana", 12)
def calculate():
global value_a
global value_b
global value_c
value_list = [value_a, value_b, value_c]
if "" in value_list:
return False
else:
delta = (int(value_b.get())**2) - 4*int(value_a.get())*int(value_c.get())
if delta >= 0:
delta_root = math.sqrt(delta)
bhask_pos = int(-value_b.get()) + (delta_root)/2*int(value_a.get())
bhask_neg = int(-value_b.get()) - (delta_root)/2*int(value_a.get())
global result
result = "The equation", int(value_a.get()), "x² +", int(value_b.get()), "x +", int(value_c.get()), "has the results ", int(bhask_pos), "and ", int(bhask_neg)
else:
pass
return True
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self,*args, **kwargs)
#self.geometry("720x360")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_columnconfigure(0, weight=1)
container.grid_rowconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button = ttk.Button(self, text="Insert values", command=lambda: controller.show_frame(PageOne))
button.pack(side="top", padx=10, pady=20, expand=False)
canvas = tk.Canvas(self, width=400, height=200, bg="#C0C0C0", bd="10")
canvas.pack(side="bottom", padx=10, pady=20, expand=False)
if calculate() == False:
canvas.create_text(30, 30, text="Error. Check if you selected all values")
elif calculate() == True:
canvas.create_text(30, 30, text=result)
else:
pass
calculation_button = ttk.Button(self, text="Calculate", command=calculate)
calculation_button.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def get_entry_data_a():
global value_1
value_1 = int(value_a.get())
entry_a.delete(0, END)
print(value_1)#just for debugging
def get_entry_data_b():
global value_2
value_2 = int(value_b.get())
entry_b.delete(0, END)
print(value_2)
def get_entry_data_c():
global value_3
value_3 = int(value_c.get())
entry_c.delete(0, END)
print(value_3)
def event_data_a(event):
value_1 = int(value_a.get())
entry_a.delete(0, END)
print(value_1)
def event_data_b(event):
value_2 = int(value_b.get())
entry_b.delete(0, END)
print(value_2)
def event_data_c(event):
value_3 = int(value_c.get())
entry_c.delete(0, END)
print(value_3)
text_a = tk.Label(self, text="value from a:", padx=10, pady=10)
text_a.grid(row=1, column=1)
text_b = tk.Label(self, text="value from b:", padx=10, pady=10)
text_b.grid(row=2, column=1)
text_c = tk.Label(self, text="value from c", padx=10, pady=10)
text_c.grid(row=3, column=1)
value_a = tk.IntVar()
entry_a = tk.Entry(self, textvariable=value_a)
entry_a.grid(row=1, column=2)
entry_a.delete(0, END)
button_a = ttk.Button(self, text="Save value", command=get_entry_data_a)
button_a.grid(row=1, column=3, padx=10, pady=10)
value_b = tk.IntVar()
entry_b = tk.Entry(self, textvariable=value_b)
entry_b.grid(row=2, column=2)
entry_b.delete(0, END)
button_b = ttk.Button(self, text="Save value", command=get_entry_data_b)
button_b.grid(row=2, column=3, padx=10, pady=10)
value_c = tk.IntVar()
entry_c = tk.Entry(self, textvariable=value_c)
entry_c.grid(row=3, column=2)
entry_c.delete(0, END)
button_c = ttk.Button(self, text="Save value", command=get_entry_data_c)
button_c.grid(row=3, column=3,padx=10, pady=10)
entry_a.bind("<Return>", event_data_a)
entry_b.bind("<Return>", event_data_b)
entry_c.bind("<Return>", event_data_c)
back_button = ttk.Button(self, text="Return to Start Page", command=lambda:controller.show_frame(StartPage))
back_button.grid(row=5, column=2, padx=20, pady=20)
app = App()
app.mainloop()
The results should appear in the canvas, as well as a message error if the user does not select a value. My actual problem is that the canvas object is created in StartPage, so I need to define the function before the StartPage class (because I need the boolean result returned from the function calculate() to create the text in the canvas), but the actual values that the user chooses comes only at the end of the code. How is it possible to use those values? Where should I define the function calculate() in this case?
This is the error message:
line 10, in calculate
value_list = [value_a, value_b, value_c]
NameError: name 'value_a' is not defined
To get the globals working properly, just make sure to give them initial values before their first used. The easiest way to do this is to assign them default values at the module level. For example:
value_a = ""
value_b = ""
value_c = ""
def calculate():
global value_a
global value_b
global value_c
# ...
That should get your code working. But while Python supports globals, they're almost never the right choice. Look at the logic of your application and think of ways to use local scope more. One possibility is to set value_a, value_b, and value_c as instance variable in your App class, since it's accessible as controller in both frames.
For example:
def calculate(value_a, value_b, value_c):
value_list = [value_a, value_b, value_c]
# ...
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self,*args, **kwargs)
self.value_a = ""
self.value_b = ""
self.value_c = ""
# ...
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# ...
if calculate(controller.value_a, controller.value_b, controller.value_c):
# ...
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def get_entry_data_a():
value_1 = int(controller.value_a.get())
entry_a.delete(0, END)
print(value_1)#just for debugging
# ...
controller.value_a = tk.IntVar()
I want to create a custom widget in tkinter such that when instantiated, displays a label and an entry box. Example I created a class named entry and call as.. entry ('name', master ) and this would display a label with text as main along side an entry box.
I have succeeded in doing that but my problem is with the geometry managers. they all seem to mess up everything
Your widget should subclass Frame. Within the frame you can use any geometry manager you want without affecting any other code. It's important that the widget class does not call grid, pack or place on itself -- that's the job of the function that creates the widget. Every widget, or function that creates a widget, should only ever worry about laying out its children.
Here's an example that creates a couple of different custom widgets. Each uses a different geometry manager to illustrate that they don't interfere with each other:
try:
# python 3.x
import tkinter as tk
except ImportError:
# python 2.x
import Tkinter as tk
class CustomWidget(tk.Frame):
def __init__(self, parent, label, default=""):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self, text=label, anchor="w")
self.entry = tk.Entry(self)
self.entry.insert(0, default)
self.label.pack(side="top", fill="x")
self.entry.pack(side="bottom", fill="x", padx=4)
def get(self):
return self.entry.get()
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self)
self.e1 = CustomWidget(self, "First Name:", "Inigo")
self.e2 = CustomWidget(self, "Last Name:", "Montoya")
self.submitButton = tk.Button(self, text="Submit", command=self.submit)
self.e1.grid(row=0, column=0, sticky="ew")
self.e2.grid(row=1, column=0, sticky="ew")
self.label.grid(row=2, column=0, sticky="ew")
self.submitButton.grid(row=4, column=0)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def submit(self):
first = self.e1.get()
last = self.e2.get()
self.label.configure(text="Hello, %s %s" % (first, last))
if __name__ == "__main__":
root = tk.Tk()
Example(root).place(x=0, y=0, relwidth=1, relheight=1)
root.mainloop()
I agree with Mr. Oakley. You should subclass frame to do your job.
The simplest way to do what you want is to create a module with the following code:
# AnnotatedEntry.py
def AnnotatedEntry(master, name="An annoted entry box"):
'''
As a little extra, name is a keyword-argument, which defaults to "An annotated
entry box."
'''
import tkinter as tk
overlord = tk.Frame(master, height=5, width=40)
labeller = tk.Label(overlord, text=name, font="Times 14 bold")
labeller.grid(sticky='new')
inputter = tk.Entry(overlord, font="Times 14 bold")
inputter.grid(sticky='sew', pady=(10,0))
return overlord
This would be used as follows:
# Main program
import tkinter
import AnnotatedEntry
root = tkinter.Tk()
hold = AnnotatedEntry.AnnotatedEntry(root, name="Hello, world!")
hold.grid()
I hereby affirm, on my Scout Honor, that this code has been fully tested, and is guaranteed to work in Python 3.7.4. That being said, there is currently no method for returning the data contained in the Entry; you will have to work that out for yourself.
Based on #Bryan Oakley answer, I do have some modification. I know it's out of topic somehow. This is how to return a value from the widget and it only allows integer up to some number of digits that the user must entered.
#create a global value
global tbVal
tbVal = 0
class CustomWidget(tk.Frame):
def __init__(self, parent, nDigits):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.entry.pack(side="bottom", fill="x", padx=4)
self.entry.configure(validate='all',validatecommand=windows.register(self.sbValidate),'%P','%W',nDigits))
def get(self):
return self.entry.get()
def sbValidate(self, userInput, widget, nDigits):
global tbVal
tbVal = userInput
if userInput == '':
return True
if '.' in userInput or ' ' in userInput:
return False
n = len(userInput)
if n > int(nDigits):
return False
try:
val = int(float(userInput))
except ValueError:
return False
return val
class Example(tk.Frame):
def __init__(self, parent, nDigitsLimit):
tk.Frame.__init__(self, parent)
self.e1 = CustomWidget(self, nDigitsLimit)
self.e1.grid(row=0, column=0, sticky="ew")
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def btnStartClick():
print(tbVal)
nDigitsLimit = 8
tbTest = ttk.Entry(Example(windows, nDigitsLimit).place(x=20, y=20, relwidth=0.25, relheight=0.05))
btnStart = tk.Button(frame, text='Start', command=btnStartClick)
btnStart.place(relx=0.50, rely=0.50)
I am new to Python and Tkinter so unable to figure out which might be the simplest thing to do. Could someone please check the below code and tell me how can I trace value returned by radiobutton defined in child class and pass it to parent class. I get following error after compiling:
AttributeError: Toplevel instance has no attribute 'trace_fun'
I am not sure why am I getting this error since I have defined trace_fun in child class body. I have successfully traced variables in parent class but getting above error while trying to do it in the child class.
from Tkinter import *
class Parent(Frame):
classvar = 0
def __init__(self):
Frame.__init__(self)
self.master.title("Parent WIndow")
self.master.geometry("200x100")
self.grid()
self._button = Button(self, text="Create", width=10, command=self.new_window)
self._button.grid(row=0, column=0, sticky=E+W)
def new_window(self):
self.new = Child()
class Child(Parent, Frame):
def __init__(self):
Parent.__init__(self)
new = Frame.__init__(self)
new = Toplevel(self)
new.title("Child Window")
new.grid()
new._var = IntVar()
new._var.set(0)
new._var.trace("w", new.trace_fun)
new._radioButton = Radiobutton(new, text = "Option 1", variable = new._var, value = 1)
new._radioButton.grid(row=0, column=0, sticky=W, padx=10, pady=10)
new._radioButton2 = Radiobutton(new, text = "Option 2", variable = new._var, value = 2)
new._radioButton2.grid(row=1, column=0, sticky=W, padx=10, pady=10)
new._button = Button(new, text = 'Ok', command=new.destroy)
new._button.grid(row=2, column=0, pady=10)
def trace_fun(new, *args):
print new._var.get()
Parent.classvar = new._var.get()
obj = Parent()
def main():
obj.mainloop()
main()
You've overwritten your new variable here:
new = Frame.__init__(self)
new = Toplevel(self)
After those two statements execute, new is equal to an instance of the Toplevel class.
Next, this code executes:
new._var.trace("w", new.trace_fun)
and in particular:
new.trace_fun
So, you have a Toplevel instance trying to access an attribute named trace_fun. The error message is telling you that the Toplevel class does not have any attribute named trace_fun.
Edit:
You can't call trace_fun on a Toplevel instance--ever. Nor can you call trace_fun on a Parent instance--ever. So print out a copy of your program, then get a pen and circle all the variables that are Toplevel instances; then circle all the variables that are Parent instances. You can't call trace_fun on any of those variables. Alternatively, circle all the variables that are Child instances. You can call trace_fun on those variables.
Here is an example of what you can do:
class Child:
def do_stuff(self): #1) self is an instance of class Child, i.e. the object that is calling this method
self.trace_fun() #2) The Child class defines a method named trace_fun()
#3) Therefore, self can call trace_fun()
x = self.trace_fun #4) ...or you can assign self.trace_fun to a variable
#5) ...or pass self.trace_fun to another function
def trace_fun(self):
print 'hello'
d = Chile()
d.do_stuff()
--output:--
hello
It doesn't look like you have a Parent/Child relationship between your two frames--because the Child frame doesn't use anything inherited from the Parent frame. So you could just create two separate frames for your app. Here is an example:
import Tkinter as tk
class EntryFrame(tk.Frame):
classvar = 0
def __init__(self, root):
tk.Frame.__init__(self, root) #Send root as the parent arg to Frame's __init__ method
root.title("Parent Window")
root.geometry("400x200")
tk.Label(self, text="First").grid(row=0)
tk.Label(self, text="Second").grid(row=1)
e1 = tk.Entry(self)
e2 = tk.Entry(self)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
button = tk.Button(self, text="Create", width=10, command=self.create_new_window)
button.grid(row=2, column=0, sticky=tk.E + tk.W)
self.grid()
def create_new_window(self):
RadioButtonFrame()
class RadioButtonFrame(tk.Frame):
def __init__(self):
new_top_level = tk.Toplevel()
tk.Frame.__init__(self, new_top_level) #Send new_top_level as the parent arg to Frame's __init__ method
new_top_level.title("Radio Button Window")
new_top_level.geometry('400x300+0+300') # "width x height + x + y"
self.int_var = int_var = tk.IntVar()
int_var.trace("w", self.trace_func)
int_var.set(0)
rb1 = tk.Radiobutton(self, text = "Option 1", variable = int_var, value = 1)
rb1.grid(row=0, column=0, sticky=tk.W, padx=10, pady=10)
rb2 = tk.Radiobutton(self, text = "Option 2", variable = int_var, value = 2)
rb2.grid(row=1, column=0, sticky=tk.W, padx=10, pady=10)
button = tk.Button(self, text = 'Ok', command=new_top_level.destroy)
button.grid(row=2, column=0, pady=10)
self.grid()
def trace_func(self, *args):
radio_val = self.int_var.get()
print radio_val
EntryFrame.classvar = radio_val
def main():
root = tk.Tk()
my_frame = EntryFrame(root)
root.mainloop()
main()
By making slight changes, now my code is working perfectly. Posting new code for someone stuck on the same point as me earlier. Changes can be seen in the below code:
import Tkinter as tk
class Parent:
classvar = 0
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.master.title("Parent Window")
self.master.geometry("400x100")
self.frame.grid()
self._button = tk.Button(self.frame, text="Create", width=10, command=self.new_window)
self._button.grid(row=0, column=0, sticky=tk.E+tk.W)
def new_window(self):
self.child_window = tk.Toplevel(self.master)
self.app = Child(self.child_window)
class Child(Parent):
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.master.title("Child Window")
self.frame.grid()
self._var = IntVar()
self._var.set(0)
self._var.trace("w", self.trace_fun)
self._radioButton = tk.Radiobutton(self.frame, text = "Option 1", variable = self._var, value = 1)
self._radioButton.grid(row=0, column=0, sticky=W, padx=10, pady=10)
self._radioButton2 = tk.Radiobutton(self.frame, text = "Option 2", variable = self._var, value = 2)
self._radioButton2.grid(row=1, column=0, sticky=W, padx=10, pady=10)
self._button = tk.Button(self.frame, text = 'Ok', command=self.master.destroy)
self._button.grid(row=2, column=0, pady=10)
def trace_fun(self, *args):
Parent.classvar = self._var.get()
print Parent.classvar
root = tk.Tk()
obj = Parent(root)
def main():
root.mainloop()
main()