Connecting methods from outer class - python

I have two classes App(main class) and OptionsWin(class to create a toplevel window).The classes structure is like
import tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
#other methods
self.opt_win = OptionsWin(self.root,'Circle')
def show(self):
#get value of the RadioButton
pass
class OptionsWin():
def __init__(self, root, shape=None):
self.parent = root
self.root = tk.Toplevel(self.parent)
if shape == 'Circle':
self.create_circle_opt()
def create_circle_opt(self):
#Other Widgets
# Types of Circles
self.radVar = tk.IntVar()
self.radVar.set(0)
tk.Radiobutton(self.root, text='Equal Ellipse', variable=self.radVar,
value=0, command=None).grid(column=2, row=0, padx=10,
pady=5, sticky=tk.W)
tk.Radiobutton(self.root, text='Flat Ellipse', variable=self.radVar,
value=10, command=None).grid(column=2, row=1, padx=10,
pady=5, sticky=tk.W)
tk.Radiobutton(self.root, text='Tall Ellipse', variable=self.radVar,
value=20, command=None).grid(column=2, row=2, padx=10,
pady=5, sticky=tk.W)
I want to connect the RadioButtons command arg to the App class show function.
Is there any way to do it or a better way to organize my classes structure to solve this problem

How about making OptionsWin a member in App?
class App():
def __init__(self):
self.root = tk.Tk()
self.options_win = OptionsWin()
#other methods
self.opt_win(self.root,'Circle')

So, you have the value of the radio button stored in a variable on the OptionsWin instance- self.radVar. If you have access to that instance in your App instance, you can easily reference it. It actually looks like thats what you're going for here-
class App():
def __init__(self):
self.root = tk.Tk()
#other methods
self.opt_win(self.root,'Circle')
We don't see a definition of self.opt_win. My guess is that this was supposed to be
self.opt_win = OptionsWin(self.root,'Circle')
In that case, your show method would just be
def show(self):
#get value of the RadioButton
return self.opt_win.radVar
If my guess was wrong about opt_win (maybe its a constructor method), the basis of the answer is keep a reference to the OptionsWin instance so you can access the variables on it
Edit--
To be able to reference the parent from the child as well, you could do-
class OptionsWin():
def __init__(self, app, shape=None):
self.app = app
self.root = tk.Toplevel(app.root)
if shape == 'Circle':
self.create_circle_opt()
def show(self):
self.app.show()
And then your app constructer becomes
class App():
def __init__(self):
self.root = tk.Tk()
# pass self into the child, and then it can reference this object
self.opt_win = OptionsWin(self,'Circle')

Related

How to use parent class, in Toplevel

I defined a GeneralFrame class that inherits from tk.LabelFrame, which contains other widgets like labels and entries:
class GeneralFrame(tk.LabelFrame):
def __init__(self, master, eCount, lCount):
super().__init__()
self.grid(padx=5, pady=5)
self.entry_widget(eCount)
self.label_widget(lCount)
def entry_widget(self, eCount):
self.e = {}
for i in range(0, eCount):
self.e[i] = tk.Entry(self, width=6)
self.e[i].grid(row=i, column=1, sticky='w')
self.e[i].delete(0, tk.END)
def label_widget(self, lCount):
self.l = {}
for i in range(0, lCount):
self.l[i] = tk.Label(self)
self.l[i].grid(row=i, column=0, sticky='w')
How can I use this class in a TopLevel window?
I've tried like this but it places the frame_save in parent window not TopLevel:
def openNewWindow():
newWindow = Toplevel(window)
newWindow.title('Saved Data')
newWindow.geometry('200x200')
frame_save = GeneralFrame(newWindow, eCount=5, lCount=5)
frame_save.configure(text='Saved data',font=("Helvetica",14,"bold"))
frame_save.grid(row=0, column=0)
labels_text = ['label1','label2','label3','label4','label5']
[frame_save.l[i].configure(text=labels_text[i]) for i in range(0,5)]
And general use in parent window:
window = tk.Tk()
window.geometry("980x500")
window.resizable(1,1)
window.title('Calculator')
class GeneralFrame(tk.LabelFrame):
[code]
frame_1 = GeneralFrame(window, eCount=5, lCount=5)
frame_2 = GeneralFrame(window, eCount=5, lCount=5)
def Frame_1():
[code]
def Frame_2():
[code]
Frame_1()
Frame_2()
window.mainloop()
You need to pass master when calling super().__init__. Otherwise, the actual frame has a default master, and the default master is the root window.
super().__init__(master)
Also, I encourage you to not call self.grid inside the __init__. The way tkinter widgets are designed, it's expected that the code that creates the widgets also calls pack, place, or grid on the widget. Otherwise your class can only ever be used in a parent that uses grid.
It works if I do this:
class GeneralFrame(tk.LabelFrame):
def __init__(self, master, eCount, lCount):
#super().__init__()
tk.LabelFrame.__init__(self,master)

How to modify `Text` of the parent from the children level in Tkinter (Python)

I got an app.py file where the main class App(Tk) is being invoked. Within it I create three children, with two being relevant (in the middle and on the right of the image):
self.active_module = main_module.MainModule(self.main)
self.active_module.grid(column=1,row=0)
and
self.preview = Text(self.main,state='disabled',width=50)
self.preview.grid(column=2,row=0,sticky=E)
The active_module is a ttk.Notebook with several ttk.Frame and separated as a main_module.py file. One of those frames got combobox with different values...
...and the question is how to let preview Text be modified upon changing the combobox value? For clarity purposes I also attach the image how it looks alike:
The relevant parts of code are as follows:
app.py
class App(Tk):
def __init__(self):
super().__init__()
[...]
self.active_module = main_module.MainModule(self.main)
self.active_module.grid(column=1,row=0)
self.preview = Text(self.main,state='disabled',width=50)
self.preview.grid(column=2,row=0,sticky=E)
main_module.py
class MainModule(ttk.Notebook):
def __init__(self,parent):
super().__init__(parent)
self.add(General(self),text="General")
class General(ttk.Frame):
def __init__(self,parent):
super().__init__(parent)
self.runtype_label = ttk.Label(self,text="Runtype:")
self.runtype_label.grid(column=0,row=0,sticky=W)
self.runtype_combobox = ttk.Combobox(self,state="readonly",values=("1","2","3"),width=9)
self.runtype_combobox.grid(column=1,row=0,sticky=W)
#self.runtype_combobox.bind('<<ComboboxSelected>>',self.runtype_choice)
You can pass a callback to MainModule class and then pass this callback to General class.
app.py
class App(Tk):
def __init__(self):
super().__init__()
self.main = Frame(self)
self.main.pack()
# pass callback to MainModule class
self.active_module = main_module.MainModule(self.main, self.insert_text)
self.active_module.grid(column=1, row=0)
self.preview = Text(self.main, state='disabled', width=50)
self.preview.grid(column=2, row=0, sticky=E)
def insert_text(self, text):
self.preview.config(state='normal')
self.preview.insert('end', f'{text}\n')
self.preview.config(state='disabled')
main_module.py
class MainModule(ttk.Notebook):
def __init__(self, parent, callback):
super().__init__(parent)
# pass callback to 'General' class
self.add(General(self, callback), text="General")
class General(ttk.Frame):
def __init__(self, parent, callback):
super().__init__(parent)
self.runtype_label = ttk.Label(self, text="Runtype:")
self.runtype_label.grid(column=0, row=0, sticky=W)
self.runtype_combobox = ttk.Combobox(self, state="readonly", values=("1","2","3"), width=9)
self.runtype_combobox.grid(column=1, row=0, sticky=W)
# call the callback whenever item is selected
self.runtype_combobox.bind('<<ComboboxSelected>>', lambda e: callback(self.runtype_combobox.get()))

I can't organize interaction between two classes, belonging to different frames in tkinter

The two classes belong to different frames. The challenge is
to read data from the window ʻent_dataclass a parent ofForLeftFrame in a descendant class of ForRightFrameChild`.
When calling the parent class, a message appears in the console:
"name 'left_frame' is not defined". Can't figure out why?
Everything works in one frame. Please help me figure it out.
The code is as follows:
import tkinter as tk
#-----------
class ForLeftFrame():
def __init__(self, left_frame):
self.left_frame = left_frame
self.ent_data = tk.Entry(left_frame, width=8, bg='#3de',
fg='#dff')
self.ent_data.grid(column=0, row=1)
#-----------
class ForRightFrameChild(ForLeftFrame):
def __init__(self, right_frame):
self.right_frame = right_frame
super().__init__(self, left_frame)
self.left_frame = left_frame
self.transf_button = tk.Button(right_frame, text="Transfer to...",
bg='#489', fg='#dff', command=self.transferTo)
self.transf_button.grid(column=0, row=1)
def transferTo(self):
self.ent_data_st = self.ent_data.get()
print('Transfer to...', self.ent_data_st)
#-----------
class Application(tk.Frame):
"""Главный класс приложения"""
def __init__(self, master):
super().__init__()
left_frame = tk.Frame(master, bg='tan', relief='groove', bd=3)
left_frame.pack(side='left', fill='both', expand=1)
righr_frame = tk.Frame(master, bg='aqua', relief='groove', bd=3)
righr_frame.pack(side='right', fill='both', expand=1)
self.for_left_frame = ForLeftFrame(left_frame)
self.for_right_frame_child = ForRightFrameChild(righr_frame)
#-----------------------------
root = tk.Tk()
app = Application(root)
root.mainloop()
To achive this you need to have references and that is exactly what the error suggests.
So what I did in the code below is first to write your application as a subclass of tk.Tk() so the root parameter or you window is now your own class to modify, also it becomes the controller/director in this ensemble.
Also I created your frames as a subclass of frames, how you did it first with only the application class.
Now, all the magic is in this line and I will explain what happens here for a better understanding.
self.master.left_frame.ent_data.get()
So self reference to the instance of the class which we had bound to self.right_frame in our class named Application.
The class Application is also the master of self/right_frame.
The Application has the attribute left_frame which we did by self.left_frame in the Application class.
self.left_frame was bound to a reference of an instance from the class LeftFrame were we have defined the attribute ent_data which is an tk.Entry that has the method get.
I know it seems confusing in the beginning and you may need to read this text more then onnce, but this is how it works. It is more or less a straight way to go.
import tkinter as tk
#-----------
class LeftFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.ent_data = tk.Entry(self, width=8, bg='#3de',fg='#dff')
self.ent_data.grid(column=0, row=1)
#-----------
class RightFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
self.transf_button = tk.Button(self, text="Transfer to...",
bg='#489', fg='#dff',
command=self.transferTo)
self.transf_button.grid(column=0, row=1)
def transferTo(self):
self.ent_data_st = self.master.left_frame.ent_data.get()
print('Transfer to...', self.ent_data_st)
#-----------
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.left_frame = LeftFrame(self)
self.right_frame = RightFrame(self)
self.left_frame.pack(side='left', fill='both', expand=1)
self.right_frame.pack(side='right', fill='both', expand=1)
#-----------------------------
app = Application()
app.mainloop()
EDIT:
import tkinter as tk
#-----------
class LeftFrame(object):
def __init__(self, frame):
self.f = frame
self.ent_data = tk.Entry(self.f, width=8, bg='#3de',fg='#dff')
self.ent_data.grid(column=0, row=1)
#-----------
class RightFrame(object):
def __init__(self, frame):
self.f = frame
self.transf_button = tk.Button(self.f, text="Transfer to...",
bg='#489', fg='#dff',
command=self.transferTo)
self.transf_button.grid(column=0, row=1)
def transferTo(self):
self.ent_data_st = self.f.master.for_left_frame.ent_data.get()
print('Transfer to...', self.ent_data_st)
#-----------
class Application(tk.Tk):
def __init__(self):
super().__init__()
frame1 = tk.Frame(self)
frame2 = tk.Frame(self)
self.for_left_frame = LeftFrame(frame1)
self.for_right_frame = RightFrame(frame2)
frame1.pack()
frame2.pack()
#-----------------------------
app = Application()
app.mainloop()

return self.func(*args) TypeError: menu() missing 1 required positional argument: 'self'

I keep getting this error and I cant seem to fix it, if anybody could help me I would really appreciate it. I have been looking at it for quite a while now and I can't seem to get my head around it, I am still quite new to programming in an Object Oriented Way.
Thanks & Merry Christmas
Welcome Page
from External_Menu import *
from tkinter import *
class Welcome(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.welcome_button()
self.pack()
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=ExternalMenu.menu)
self.welcome.pack()
self.pack()
if __name__ == "__main__":
root = Tk()
main = Welcome(root)
main.mainloop()
External Menu
from tkinter import *
class ExternalMenu(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.menu()
self.pack()
def menu(self):
self.external_menu_lbl = Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = Button(self, text="Sign Up")
self.sign_up_button.pack()
self.pack()
Your menu is a method so it needs an object it can manipulate in order to work. But you're trying to call it without an object created for it, essentially you're calling it like a function. First you need to create an object of the class that method is defined on:
an_ex_men = ExternalMenu(root)
and then you are able to call menu method on an_ex_men:
an_ex_men.menu()
But since you already call menu under your ExternalMenu's __int__ method it is called as soon as an object instance for that class is created. Shortly, you creating an ExternalMenu object is enough to achieve that as in, even without putting it to a variable to refer later:
ExternalMenu(root)
Since you want to swap between two windows you need an additional method to either hide or completely destroy the other window. Let's say you want to destroy the other window, and for that you could use a method defined for your button that does those 2 actions:
def welcome(self):
self.destroy()
ExternalMenu(root)
As in:
...
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=self.welcome)
self.welcome.pack()
def welcome(self):
self.destroy()
ExternalMenu(root)
...
Below example does exactly what you expect, first I created a parent class App as according to you the two frames you have are in the same level of hierarchy and having a parent for them is in my opinion better structured:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.welcome_frame = Welcome(self)
#I believe it's better to call geometry managers as parent
self.welcome_frame.pack()
#assigning parent method as button command as it affects siblings
self.welcome_frame.button['command'] = self.go_ex_men
def go_ex_men(self):
self.welcome_frame.destroy()
self.ex_men = ExternalMenu(self)
self.ex_men.pack()
class Welcome(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.button = tk.Button(self, text="Welcome!")
self.button.pack()
class ExternalMenu(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.external_menu_lbl = tk.Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = tk.Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = tk.Button(self, text="Sign Up")
self.sign_up_button.pack()
if __name__ == "__main__":
root = App()
root.mainloop()

Python GUI - Linking one GUI in a class to another class

What I am trying to do is to link a GUI from one class in a separate file to another.
My first class is a main menu which will display a few buttons that will link to another window.
The second class displays a different window, but the problem I am having at the moment is that I don't know how to link the button in the first class to call the second class.
Here's the code I have so far:
First file, the main menu:
from tkinter import *
import prac2_link
class main:
def __init__(self,master):
frame = Frame(master, width=80, height=50)
frame.pack()
self.hello = Label(frame, text="MAIN MENU").grid()
self.cont = Button(frame,text="Menu option 1", command=prac2_link.main2).grid(row=1)
root = Tk()
application = main(root)
root.mainloop()
second file:
from tkinter import *
class main2:
def __init__(self):
frame1 = Frame(self, width=80, height=50)
frame1.pack()
self.hello = Label(frame1, text="hello, its another frame").grid(row=0,column=0)
To create a new window, you have to use a Toplevel widget. You can use it as a superclass for your main2 class:
class main2(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.frame= Frame(self, width=80, height=50)
self.label = Label(self.frame, text='this is another frame')
self.frame.grid()
self.label.grid()
Then you only have to create an instance in the event handler of the Button in the other class:
class main1:
def __init__(self, master):
# ...
self.cont = Button(frame,text="Menu option 1", command=self.open_main2).grid(row=1)
def open_main2(self):
prac2_link.main2()

Categories