How to use parent class, in Toplevel - python

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)

Related

How to get variable from a class to another Tkinter window

The goal is to pass the variable string1 from class Display to be used in another Tkinter window.
So when the button named Next [in class Display load function] is clicked, it would open a new Tkinter window. And in the new window, the variable string1 from class Display needs to be retrieved for further action. May i know should i create another class Display2, or should i just add a method in the class Display?
Currently the string variable can be passed as reference from class Display to the class Action_Data. But how can it be passed to another Tkinter window when the button Next is clicked?
I am trying to get the variable via the callback function new_window. Just not sure if it's how it's done. Any pointer would be appreciated. Many thanks.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self):
self.master = tk.Tk() # create another Tk instance
var_string2 = Label(self, text="<<string1 value>>")
var_string2.pack()
print (var_string2)
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command=self.new_window)
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()
Issue
Now the new window is blank and cannot retrieve the value of variable string1 from the load function
Error
Use the lambda function to pass the button's command, that way you can pass the needed string as an argument.
You do not need to create two instances of Tk, if you need another window, create a Toplevel.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self, string):
#self.master.destroy() # close the current window
new_window = tk.Toplevel() # create toplevel
var_string2 = Label(new_window, text=string)
var_string2.pack()
new_window.focus()
print (var_string2.cget("text"))
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command= lambda : self.new_window(string1))
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()

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()

Connecting methods from outer class

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')

Python-Tkinter Place button on left of frame

How do I place the QUIT button in below code to the extreme right of the Frame?
I tried several things like:
padx
and
self.pack(side="top", anchor="e")
but after trying some 15 times both buttons are coming close to each other. Maybe Some help from anyone would be really appreciated. I need one button on extreme right and other on extreme left
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(anchor='e')
self.pack(side="top", anchor="w")
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
You have several problems here.
First, you're using the wrong geometry manager. The pack geometry manager, as the name implies, packs the widgets as close together as possible. That's not what you want. The grid geometry manager lets you put the widgets into a table-like layout with rows and columns. If you put the Browse button into the first column and the Quit button into the last column, you'll be a step closer.
Second, your Application window contains three child widgets and you're only putting two of them into a geometry manager. How that is going to mess you up I don't even want to think about. So I put the label into column 1, the Quit button into column 2, and the Browse button into column 0. The Quit button I gave a "sticky" value of "e" so it will be attached to the east (right) side of its allocated space.
Third, all the geometry managers try to compact the widgets as much as possible unless you specifically tell it to do otherwise. I told the grid manager to expand column 2 so that the extra space gets assigned to the cell that holds the Quit button.
Fourth, you need to tell the pack manager to expand the top widget so that it spans the entire window. The directive for that is fill="x".
Fifth, you have a redundant call to the pack manager at the end of your createWidgets function.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack(fill="x")
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.Label.grid(row=0, column=1)
self.Run_Main.grid(row=0, column=0, sticky="w")
self.QUIT.grid(row=0, column=2, sticky="e")
self.columnconfigure(2, weight=1)
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
These link, link helped. The other option would be to use tkinter's grid manager, it will be more intuitive and keep you more organized in the future.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.Label.pack(side='left')
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right')
self.pack(side="top", fill=tk.BOTH) # changes here
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
There are two simple fixes you can make in order to get the behavior you want.
First, you need to pack Application so that it fills the window:
class Application(...):
def __init__(...):
...
self.pack(fill="x")
Next, simply pack the quick button on the right side of the window:
self.QUIT.pack(side="right", anchor='e')
Even though the above is all you need to do in this specific example, there are additional things you can do to make your job much easier.
I would recommend creating a frame specifically for the buttons. You can pack it at the top. Then, put the buttons inside this frame, and pack them either on the left or right. You'll get the same results, but you'll find it easier to add additional buttons later.
I also find that it makes the code much easier to read, write, maintain, and visualize when you separate widget creation from widget layout.
class Application(...):
...
def createWidgets(self):
toolbar = tk.Frame(self)
toolbar.pack(side="top", fill="x")
self.Run_Main = tk.Button(toolbar)
self.Label = tk.Label(toolbar)
self.QUIT = tk.Button(toolbar)
...
self.Run_Main.pack(side="left")
self.Label.pack(side="left", fill="x")
self.QUIT.pack(side="right")
...

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