I'm trying to create a simple UI with Tkinter and I have run into a problem. My code looks like this:
class UIController(tk.Tk):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (StartPage, BrowsePage, StudentPage):
frame = F(self, container)
self.frames[F] = frame
frame.title("StudyApp")
self.showFrame(StartPage)
self.centerWindow()
def showFrame(self, c):
frame = self.frames[c]
frame.tkraise()
def centerWindow(self):
w = 300
h = 350
sw = self.master.winfo_screenwidth()
sh = self.master.winfo_screenheight()
x = (sw - w)/2
y = (sh - h)/2
self.master.geometry('%dx%d+%d+%d' % (w, h, x, y))
class StartPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.pack()
self.L1 = Label(self, text="Search by credits:")
self.L1.place(x=18, y=45)
self.startYear = Entry(self, bd=2)
self.startYear.place(x=20, y=70)
self.startYear.bind("<Return>", View.enter(startYear.get()))
self.quitButton = Button(self, text="Quit", command=sys.exit)
self.quitButton.pack(side="bottom", padx=5, pady=5, fill=X)
self.searchButton = Button(self, text="Search")
self.searchButton.pack(side="bottom", padx=5, pady=0, fill=X)
class BrowsePage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
class StudentPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
root = tk.Tk()
root.resizable(width=False, height=False)
uicontrol = UIController(root)
root.mainloop()
It gives a TypeError that the constructor takes 2 arguments but 3 were given. What I'm trying to do is have the 3 pages (StartPage, BrowsePage and StudentPage) in the frame 'container', and bring them up as needed with button pushes and such. I don't understand why I'm getting this error.
EDIT:
Added the UIController call.
EDIT2:
Added the page classes StartPage, BrowsePage and StudentPage. The latter two classes are only husks at this point.
I think this is the line that is causing the issue, you cannot pass the self instance to the constructor.
frame = F(self, container)
Can you please check and add more information to the question to understand what you are trying to achieve.
You can just add in __init__ one more argument, controller.
This works for me:
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
Related
In my gui I want to import data from a file, display it in a listbox, do a computation, show the result in a listbox and then export the data as a csv.
I have been able to do most of the GUI but the interaction between classes makes no sense to me, here is my code:
class MainApplication(tk.Tk):
def __init__(self, master, *args, **kwargs):
self.master = master
self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True)
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.frames["computation"] = computation(master=self.container, controller=self)
self.frames["openFile"] = openFile(master=self.container, controller=self)
...
def get_page(self, c):
return self.frames[c]
class computation(tk.Frame):
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
self.master = master
self.controller = controller
self.listbox = Listbox(self.master)
self.listbox.pack()
def add_listbox_item(self):
for i in df.columns:
self.listbox.insert(END,i)
class openFile(tk.Frame):
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
self.master = master
self.controller = controller
self.openfile = Button(self.master, command=self.import_df)
def import_df(self):
self.filename = filedialog.askopenfilename(...)
global df
df = pd.read_csv(self.filename)
computation = self.controller.get_page("computation")
computation.add_listbox_item()
Everything seems to be executing, even a print statement in add_listbox_item but the listbox just wont be filled with the data it's being fed, any ideas?
I am making a game with levels and in each level, I will need to be using different operators and/or different ranges. My problem is that I don't know how to change the variables in a function from a different class. I would like to do this so I don't need to copy and paste my code making it lengthy. I'd like to use self.Answer and self.strQuestion for mulitple scope.
The code below is just to make the classes functional.
from tkinter import *
import tkinter as tk
import random
from Tkinter import messagebox
class BattleMaths(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
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 (StartPage, levelone, leveltwo):
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)
lvl1_button = Button(self, text="LEVEL 1", command=lambda: controller.show_frame(levelone))
lvl1_button.place(relx=0.5, rely=0.5, anchor='center')
I want to put the questions def into class leveltwo while changing it to self.Answer = int(numOne) * int(numTwo) and self.strQuestion = "{} x {}".format(str(numOne), str(numTwo))
class levelone(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def widgets(self):
#widgets here
def question(self):
self.UserAnswer = ''
numOne = random.randrange(1,10)
numTwo = random.randrange(1,10)
self.Answer = int(numOne) + int(numTwo) #change this
self.strQuestion = "{} + {}".format(str(numOne), str(numTwo)) #and change this
def answer(self):
#answer checker
class leveltwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#question def here
root = BattleMaths()
root.title("Battle Maths")
root.geometry("400x250")
root.resizable(0,0)
root.mainloop()
Create the variables you want in the main class (BattleMaths), then you can alter them in the child classes via controller.my_variable.
Example: self.Answer created in BattleMaths and accessed in levelone via controller.Answer
I am making an app for a project called the organizer. It is an organization app. I have an issue that when you check one check box, they all check off.
How do I fix this?
After ran go to: Checklist -> Enter A Value -> Click "Add Assignment" -> Repeat A few times -> try to click one
Also, my .update() works, but still seems to cause an error? Do you know why?
Thanks!
import tkinter as tk
root = tk
AsgnList = []
#Initialization
class TheOrganizer(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
self.geometry('500x500')
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 (StartPage, AddAsgnPage):
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()
def RunApp():
app = TheOrganizer()
app.title('The Organizer')
app.mainloop()
#Making New Pages
'''
Make sure for very new page, you add it to the 'for loop'
'''
HeadFont = ("Verdana", 40)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
LabTitle = tk.Label(self, text="The Organizer", font=HeadFont)
LabTitle.pack()
AddAsgnBtn = tk.Button(self, text='Checklist', command=lambda: controller.show_frame(AddAsgnPage))
AddAsgnBtn.place(x=100,y=250)
class AddAsgnPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
LabTitle = tk.Label(self, text="Assignments", font=HeadFont)
LabTitle.pack()
#Input Assignment
def getEntry():
entryInput = AsgnEntry.get()
AsgnList.append(entryInput)
yVal = 300
while 1:
var = tk.IntVar()
for z in AsgnList:
tk.Checkbutton(self, text=z, variable=var).place(x=200,y=yVal)
yVal += 25
TheOrganizer.update()
AsgnEntry = tk.Entry(self)
AsgnEntry.place(x=175,y=205)
SubBtn = tk.Button(self, text='Add Assignment', command=getEntry)
SubBtn.place(x=25,y=200)
BackBtn = tk.Button(self, text='Back', command=lambda: controller.show_frame(StartPage))
BackBtn.place(x=250,y=400)
You are using the same variable for both checkboxes. Move the variable creation to inside the loop, to use different variables.
var_list = []
for z in AsgnList:
var = tk.IntVar()
tk.Checkbutton(self, text=z, variable=var).place(x=200,y=yVal)
yVal += 25
var_list.append(var)
That said, you probably want to store the variables so you can check which checkboxes are marked later. So I added a var_list list object to store all created vars.
I'm able to move data from one class frame to another, unfortunately, I can't update the text in the new frame. I can only pack the new data below the original pack
class LabelApp(tk.Frame):
def __init__(self, master, xy_motion=[-1,-1]):
tk.Frame.__init__(self, master )
self.pack()
print('LapApp: ' + str(xy_motion[0]))
self.master = master
self.xy_motion=xy_motion
self.xlabel = tk.Label(text=self.xy_motion[0])
self.xlabel.pack()
def update_label(self, xy_motion ):
self.xlabel.config(text=xy_motion[0])
the print statement above shows correct data
code that is passing over data
class VideoDisplayApp(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.pack()
self.master = master
self.image_label = tk.Label(master=self.master) # label for the vide.
..
.
.
def motion(self, event):
x, y = event.x, event.y
self.xy_motion = [x,y]
print('{}, {}'.format(x, y))
self.motion=tk.Frame(self.master)
self.app = LabelApp(self.motion, self.xy_motion )
Main window
class MainWindow(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.pack(padx=15, pady=15)
self.master.title("Mob")
self.master.resizable(False, False)
self.master.tk_setPalette(background='#e6e6e6')
frame_video = tk.Frame(self)
frame_video.grid(row=0, column=0)
# frame_video.geometry("200x200")
VideoDisplayApp(frame_video)
#
frame_bottom = tk.Frame(self)
frame_bottom.grid(row=1, column=0)
LabelApp(frame_bottom)
#
frame_left = tk.Frame(self)
frame_left.grid(row=0, column=1)
ClickOnFrame(frame_left)
I need to access the temperature variable from Monitor Class and print on Graph Class. How can I do that? Please see code below which should compile.
from tkinter import *
import tkinter as tk
import time
class ScientificPumpGui(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True)
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (MonitorPage, GraphPage):
frame = F(self.container)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MonitorPage)
self.create_buttons()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def exit_app(self):
exit()
def create_buttons(self):
main_b_height = 2
main_b_width = 20
page_button_pady = 10
self.page_button_main_toolbar = tk.Frame(self, borderwidth=1)
self.page_button_main_toolbar.pack(side=TOP, anchor=CENTER, fill=X)
self.page_button_toolbar = tk.Frame(self.page_button_main_toolbar, borderwidth=1)
self.page_button_toolbar.pack(side=TOP, anchor=CENTER)
self.monitor_page_button = Button(self.page_button_toolbar, text="Monitor Page", width=main_b_width, height=main_b_height, command=lambda: self.show_frame(MonitorPage))
self.monitor_page_button.pack(side=LEFT, anchor=CENTER, pady=page_button_pady)
self.graph_page_button = Button(self.page_button_toolbar, text="Graph Page", width=main_b_width, height=main_b_height, command=lambda: self.show_frame(GraphPage))
self.graph_page_button.pack(side=LEFT, anchor=CENTER, pady=page_button_pady)
self.exit_app_button = Button(self.page_button_toolbar, text="Exit App", width=main_b_width, height=main_b_height, command=lambda: ScientificPumpGui.exit_app(0))
self.exit_app_button.pack(side=LEFT, anchor=CENTER, pady=page_button_pady)
class MonitorPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.monitor_data_counter = 0
self.page_label = tk.Label(self, text="Monitor Page")
self.page_label.pack(pady=10, padx=10)
def value_function(self):
self.temperature = 100
class GraphPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Graph Page!")
label.pack(pady=5, padx=10)
app = ScientificPumpGui()
app.mainloop()
When I tried to read temperature using:
monitor_page=MonitorPage(ScientificPumpGui())
print(str(monitor_page.temperature))
monitor_page.mainloop()
The error I get is:
AttributeError: 'MonitorPage' object has no attribute 'temperature'
Your MonitorPage class does not declaretemperature in the constructor function, but in value_function.
You can either declare temperature inside __init__ function, or call value_function before reading temperature.
You get this error because the member temperature is initialized in value_function method, which is not getting called.
As you didn't called to this method, the member temperature is not initialized and therefore you get the error.
In order to prevent this error, you should define the member temperature in your __init__ method with a default value.
You also can fix it by calling to the value_function method in order to initialize the member temperature.