Suppose I have two tkinter classes which act as separate windows. How could I edit any given widget from one class in the other tkinter class. ALso, how could I add a widget in one tkinter class from the other tkinter class?
from tkinter import Tk, Label, Button
class MyFirstGUI:
def __init__(self, master):
self.master = master
master.title("A simple GUI")
self.label = Label(master, text="This is
our first GUI!")
self.label.pack()
self.greet_button = Button(master,
text="Greet", command=self.greet)
self.greet_button.pack()
self.close_button = Button(master,
text="Close", command=master.quit)
self.close_button.pack()
def greet(self):
print("Greetings!")
root = Tk()
my_gui = MyFirstGUI(root)
root.mainloop()
from tkinter import Tk, Label, Button
class MyFirstGUI2:
def __init__(self, master):
self.master = master
master.title("A simple GUI")
self.label = Label(master, text="This is
our first GUI!")
self.label.pack()
self.greet_button = Button(master,
text="Greet", command=self.greet)
self.greet_button.pack()
self.close_button = Button(master,
text="Close", command=master.quit)
self.close_button.pack()
def greet(self):
print("Greetings!")
root = Tk()
my_gui = MyFirstGUI2(root)
root.mainloop()
I think it would be better to use a Toplevel widget for your two windows (or at least one of them). Right now your first window will be created and the code will stop when it gets to the root.mainloop() line. The second window will not be created until you close the first one.
And you can pass in a reference from each class.
import tkinter
from tkinter import Tk, Label, Toplevel, Button
class MainWidget:
def __init__(self, master):
self.master = master
self.widgetTwo = None
self.label = Label(self.master, text='Widget One')
self.label.pack()
class WidgetTwo(Toplevel):
def __init__(self, master, mainWidget):
Toplevel.__init__(self, master)
self.master = master
self.mainWidget = mainWidget
self.labelTwo = Label(self, text='Widget Two')
self.labelTwo.pack()
Button(self, text='Change Main Widget Text', command=self.ChangeMainWidgetLabel).pack()
def ChangeMainWidgetLabel(self):
self.mainWidget.label.config(text='Widget One text changed')
mw = Tk()
mainWidget = MainWidget(mw)
widgetTwo = WidgetTwo(mw, mainWidget)
mainWidget.widgetTwo = widgetTwo
mw.mainloop()
Related
I want to modify the code from: Switch between two frames in tkinter. I put the three clases in three separates files but when I call master.switch_frame(master.StartPage) from pageOne.py that give the error :
return getattr(self.tk, attr)
AttributeError: '_tkinter.tkapp' object has no attribute 'StartPage'
Can someone help me with this error? I appreciate any suggestion.
the code is:
main.py
#take from: https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
# Multi-frame tkinter application v2.3
import tkinter as tk
import pageOne as p1
import pageTwo as p2
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.switch_frame(StartPage)
def switch_frame(self, frame_class):
"""Destroys current frame and replaces it with a new one."""
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.pack()
class StartPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Open page one",
command=lambda: master.switch_frame(p1.PageOne)).pack()
tk.Button(self, text="Open page two",
command=lambda: master.switch_frame(p2.PageTwo)).pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
pageOne.py
# Multi-frame tkinter application v2.3
import tkinter as tk
class PageOne(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(master.StartPage)).pack()
if __name__ == "__main__":
app = PageOne()
app.mainloop()# Multi-frame tkinter application v2.3
import tkinter as tk
class PageOne(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(master.StartPage)).pack()
if __name__ == "__main__":
app = PageOne()
app.mainloop()
enter code here
pageTwo.py
# Multi-frame tkinter application v2.3
import tkinter as tk
class PageTwo(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(master.StartPage)).pack()
if __name__ == "__main__":
app = PageTwo()
app.mainloop()
Thank you Bryan Oakley. finally the code that work is:
main.py
#take from: https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
# Multi-frame tkinter application v2.3
import tkinter as tk
from StartPage import StartPage
from pageOne import PageOne
from pageTwo import PageTwo
pages = {
"StartPage": StartPage,
"PageOne": PageOne,
"PageTwo": PageTwo
}
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.switch_frame("StartPage")
def switch_frame(self, page_name):
"""Destroys current frame and replaces it with a new one."""
cls = pages[page_name]
new_frame = cls(master = self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
pageOne.py
# Multi-frame tkinter application v2.3
import tkinter as tk
class PageOne(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page", command=lambda: master.switch_frame("StartPage")).pack()
if __name__ == "__main__":
app = PageOne()
app.mainloop()
pageTwo.py
# Multi-frame tkinter application v2.3
import tkinter as tk
class PageTwo(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page", command=lambda: master.switch_frame("StartPage")).pack()
if __name__ == "__main__":
app = PageTwo()
app.mainloop()
StartPage.py
# Multi-frame tkinter application v2.3
import tkinter as tk
class StartPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Open page one", command=lambda: master.switch_frame("PageOne")).pack()
tk.Button(self, text="Open page two", command=lambda: master.switch_frame("PageTwo")).pack()
I recommend passing a string as the argument to show_frame. That way, other files don't need to import all of the other classes. You can then create a mapping to be used by switch_frame. That file becomes the only file that needs to import the other pages.
It should look something like the following code. In this example I moved StartPage into a separate file for consistency:
from startPage import StartPage
from pageOne import PageOne
from pageTwo import PageTwo
pages = {
"StartPage": StartPage,
"PageOne": PageOne,
"PageTwo": PageTwo
}
class SampleApp(tk.Tk):
...
def switch_frame(self, page_name):
"""Destroys current frame and replaces it with a new one."""
cls = pages[page_name]
new_frame = cls(master=self)
...
Your other pages don't need to import the pages, they just need to use the page name:
class PageTwo(tk.Frame):
def __init__(self, master):
...
tk.Button(..., command=lambda: master.switch_frame("StartPage")).pack()
I am stuck at referencing methods and variables between classes in Tkinter.
Here is an simple example, I have three different types of windows, which I would like to put into different classes.
In the root window, I can click the button to open the second window, where I can input something in to the Text widget.
Also in the 2nd window I want the OK button to read the content in the Text widget and insert the content into another Text widget into the 3rd window. And the Cancel button can close the 2nd window and show the root window again.
There is many bugs in the code, because I couldn't figure out how to make cross references between classes to access the methods and variables.
Could anyone help me to accomplish that? Thanks.
from tkinter import *
from tkinter import scrolledtext
def main():
"""The main app function"""
root = Tk()
root_window = Root(root)
return None
class Root:
def __init__(self, root):
# Main root window configration
self.root = root
self.root.geometry("200x100")
self.btn_ok = Button(self.root, text="Open new window",
command=NewWindow)
self.btn_ok.pack(padx=10, pady=10)
def hide(self):
"""Hide the root window."""
self.root.withdraw()
def show(self):
"""Show the root window from the hide status"""
self.root.update()
self.root.deiconify()
def onClosing(self, window):
window.destroy()
self.show()
class NewWindow:
def __init__(self):
Root.hide()
self.new_window = Toplevel()
lbl = Label(self.new_window, text="Input here:")
lbl.pack(padx=10, pady=(10, 0), anchor=W)
# Create a scrolledtext widget.
self.new_content = scrolledtext.ScrolledText(
self.new_window, wrap=WORD,
)
self.new_content.pack(padx=10, expand=True, fill=BOTH, anchor=W)
# Respond to the 'Cancel' button.
btn_cancel = Button(self.new_window, text="Cancel", width=10,
command=lambda: Root.onClosing(self.new_window))
btn_cancel.pack(padx=10, pady=10, side=RIGHT)
# Add 'OK' button to read sequence
self.btn_ok = Button(self.new_window, text="OK", width=10,
command=WorkingWindow)
self.btn_ok.pack(padx=10, pady=10, side=RIGHT)
def readContent(self):
self.content = self.new_content.get(1.0, END)
self.new_window.destroy()
workwindow = WorkingWindow()
class WorkingWindow:
def __init__(self):
self.work_window = Toplevel()
self.work_content = scrolledtext.ScrolledText(self.work_window, wrap=WORD, font=("Courier New", 11))
self.work_content.pack(padx=10, expand=True, fill=BOTH, anchor=W)
self.work_content.insert(1.0, Root.content)
if __name__ == '__main__':
main()
You were almost there all you have to do is pass the instance of Root class to other class you are calling
Here is the corrected code:
from tkinter import *
from tkinter import scrolledtext
def main():
"""The main app function"""
root = Tk()
root_window = Root(root)
root.mainloop()
class Root:
def __init__(self, root):
# Main root window configration
self.root = root
self.root.geometry("200x100")
self.btn_ok = Button(self.root, text="Open new window",
command=lambda :NewWindow(self))
self.btn_ok.pack(padx=10, pady=10)
def hide(self):
"""Hide the root window."""
self.root.withdraw()
def show(self):
"""Show the root window from the hide status"""
self.root.update()
self.root.deiconify()
def onClosing(self, window):
window.destroy()
self.show()
class NewWindow:
def __init__(self, parent):
parent.hide()
self.new_window = Toplevel()
lbl = Label(self.new_window, text="Input here:")
lbl.pack(padx=10, pady=(10, 0), anchor=W)
# Create a scrolledtext widget.
self.new_content = scrolledtext.ScrolledText(
self.new_window, wrap=WORD,
)
self.new_content.pack(padx=10, expand=True, fill=BOTH, anchor=W)
# Respond to the 'Cancel' button.
btn_cancel = Button(self.new_window, text="Cancel", width=10,
command=lambda: parent.onClosing(self.new_window))
btn_cancel.pack(padx=10, pady=10, side=RIGHT)
# Add 'OK' button to read sequence
self.btn_ok = Button(self.new_window, text="OK", width=10,
command=self.readContent)
self.btn_ok.pack(padx=10, pady=10, side=RIGHT)
def readContent(self):
self.content = self.new_content.get(1.0, END)
self.new_window.destroy()
workwindow = WorkingWindow(self)
class WorkingWindow:
def __init__(self, parent):
self.work_window = Toplevel()
self.work_content = scrolledtext.ScrolledText(self.work_window, wrap=WORD, font=("Courier New", 11))
self.work_content.pack(padx=10, expand=True, fill=BOTH, anchor=W)
self.work_content.insert(1.0, parent.content)
if __name__ == '__main__':
main()
I was trying to build something where I wanted to give separate class to each frames. Now when I called onSet function of AddWidgets class it shows an
ERROR: TypeError: onSet() missing 1 required positional argument: 'text'
What am I doing wrong? And how to solve this? My Code is given below:
import tkinter as tk
from tkinter import ttk
class MainWindow:
def __init__(self, master):
self.master = master
self.master.title("Class Function")
self.master.wm_iconbitmap('icon.ico')
self.master.geometry("360x240+100+100")
self.frame1 = ttk.Frame(self.master)
self.frame1.grid(row=0, column=0, padx=16, pady=16)
self.frame2 = ttk.Frame(self.master)
self.frame2.grid(row=0, column=1, padx=16, pady=16)
self.add = AddWidgets(self.frame1)
self.edit = EditWidgets(self.frame2)
class AddWidgets:
def __init__(self, master):
self.master = master
self.create_widgets()
def create_widgets(self):
self.label1 = ttk.Label(self.master, text="0")
self.label1.grid(row=0, column=0, padx=4, pady=4)
def onSet(self, text):
self.label1.configure(text=text)
class EditWidgets:
def __init__(self, master):
self.master = master
self.create_widgets()
def create_widgets(self):
def onEdit():
self.send_text = self.entry_text.get()
AddWidgets.onSet(self.send_text)
self.entry_text = tk.StringVar()
self.entry1 = ttk.Entry(self.master, textvariable=self.entry_text)
self.entry1.grid(row=0, column=0, padx=4, pady=4)
self.button1 = ttk.Button(self.master, text="edit", command=onEdit)
self.button1.grid(row=0, column=1, padx=4, pady=4)
def main():
root = tk.Tk()
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
I am new to Python and tkinter. I want to build a desktop app/software. I want to develop the software by following MVC pattern. So any resource for MVC in tkinter and suggestions on how can I improve my coding, software development process would be appreciated.
The method onSet requires the instance of the class as a first argument, while you're only passing the text argument. So when you call your onSet method you should pass the instance of the AddWidgets class as first argument.
You're instantiating this class in the MainWindow class:
self.add = AddWidgets(self.frame1)
So you should pass the self.add variable to the onSet function. In order to do that you should have your EditWidget class accept an additional parameter widget:
class MainWindow:
def __init__(self, master):
self.master = master
self.master.title("Class Function")
self.master.wm_iconbitmap('icon.ico')
self.master.geometry("360x240+100+100")
self.frame1 = ttk.Frame(self.master)
self.frame1.grid(row=0, column=0, padx=16, pady=16)
self.frame2 = ttk.Frame(self.master)
self.frame2.grid(row=0, column=1, padx=16, pady=16)
self.add = AddWidgets(self.frame1)
self.edit = EditWidgets(self.frame2, self.add) <--Add parameter self.add
class AddWidgets:
def __init__(self, master):
self.master = master
self.create_widgets()
def create_widgets(self):
self.label1 = ttk.Label(self.master, text="0")
self.label1.grid(row=0, column=0, padx=4, pady=4)
def onSet(self, text):
self.label1.configure(text=text)
class EditWidgets:
def __init__(self, master, widget): <-- add parameter widget
self.master = master
self.create_widgets()
self.widget = widget
def create_widgets(self):
def onEdit():
self.send_text = self.entry_text.get()
self.widget.onSet(self.send_text) <--call onSet from widget instance
[...]
EDIT:
If you want to create different tabs, all with the same widgets, one approach could be the following: you create a MainWindow class, where you add all the tabs to your window using the Notebook class.
Then you create a Add Widgets class that adds the required widgets to a frame. You instantiate this class on each of the created tabs, in order to populate them with the widgets.
In order to get the values of the variables you set in each tab you can write a method getEntryValue (or whatever name) that returns the value set on the entry. Something like this:
class MainWindow:
def __init__(self, master):
self.master = master
self.master.title("Class Function")
self.master.geometry("360x240+100+100")
n = ttk.Notebook(master)
self.f1 = ttk.Frame(n) # Create first tab
self.f2 = ttk.Frame(n) # Create second tab
n.add(self.f1, text='One')
n.add(self.f2, text='Two')
n.pack()
self.widget1 = AddWidgets(self.f1)
self.widget2 = AddWidgets(self.f2)
class AddWidgets:
def __init__(self, master):
self.master = master
self.create_widgets()
def create_widgets(self):
def onEdit():
send_text = self.entry_text.get()
self.label1.configure(text = send_text)
self.label1 = ttk.Label(self.master, text="0")
self.label1.grid(row=0, column=0, padx=4, pady=4)
self.entry_text = tk.StringVar()
self.entry1 = ttk.Entry(self.master, textvariable=self.entry_text)
self.entry1.grid(row=0, column=1, padx=4, pady=4)
self.button1 = ttk.Button(self.master, text="edit", command=onEdit)
self.button1.grid(row=0, column=2, padx=4, pady=4)
def getEntryText(self):
return self.entry_text.get()
Hope it helps.
This is my code.
When i press CTRL+S it popups a TopLevel Window. However, when i press CTRL+S again, it popups up another window on top of it and so on.
How can i improve the code so that only one window Pops up when I press CTRL+S.
from tkinter import *
from tkinter import messagebox
class MainWindow(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.master.title("Tournament Software")
self.menu()
def menu(self):
menu = Menu(self.master)
self.master.config(menu=menu)
participant = Menu(menu, tearoff=False)
participant.add_command(label="Add participant", accelerator="Ctrl+S", command=self.addparticipant_window)
menu.add_cascade(label="Participants", menu=participant)
self.bind_all('<Control-s>', lambda e: menu.after(100,self.addparticipant_window))
def addparticipant_window(self):
participantWindow = Toplevel()
participantWindow.geometry("400x350")
participantWindow.resizable(False, False)
top_frame = Frame(participantWindow, bg='cyan', width=450, height=50, pady=3)
top_frame.grid(row=0, sticky="ew")
root = Tk()
root.iconbitmap("Icon.ico")
root.geometry("500x400")
root.resizable(False, False)
app = MainWindow(root)
root.mainloop()
Thankyou, I am new to this so help me.
from tkinter import *
from tkinter import messagebox
class MainWindow(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.master.title("Tournament Software")
self.menu()
self.popup = None
def open_up(self):
if self.popup is None or not self.popup.top.winfo_exists():
self.popup = addparticipant_window()
else:
self.popup.top.lift(self.master)
def menu(self):
menu = Menu(self.master)
self.master.config(menu=menu)
participant = Menu(menu, tearoff=False)
participant.add_command(label="Add participant", accelerator="Ctrl+S", command=self.open_up)
menu.add_cascade(label="Participants", menu=participant)
self.bind_all('<Control-s>', lambda e: menu.after(100,self.open_up))
class addparticipant_window:
def __init__(self):
self.top = Toplevel()
self.top.geometry("400x350")
self.top.resizable(False, False)
top_frame = Frame(self.top, bg='cyan', width=450, height=50, pady=3)
top_frame.grid(row=0, sticky="ew")
root = Tk()
root.geometry("500x400")
root.resizable(False, False)
app = MainWindow(root)
root.mainloop()
I've just started programming in Tkinter and my question is why does the 'master' method work without needing to assigned an instance of the Tk() class?
In other Tkinter projects I've seen they have a variable that takes Tk() as its parameters? i.e master = Tk()
from tkinter import Tk, Label, Button
class MyFirstGUI:
def __init__(self, master):
self.master = master
master.title("A simple GUI")
self.label = Label(master, text="This is our first GUI!")
self.label.pack()
self.greet_button = Button(master, text="Greet", command=self.greet)
self.greet_button.pack()
self.close_button = Button(master, text="Close", command=master.quit)
self.close_button.pack()
def greet(self):
print("Greetings!")
root = Tk()
my_gui = MyFirstGUI(root)
root.mainloop()
Well, you assign a instance of Tk():
root = Tk()
my_gui = MyFirstGUI(root)
Here, root is an instance of Tk() and the second parameter of the "init"
And you give it the name of "master" in you "init"
No?