I'm making a quiz app that involves reading data from a JSON file and storing the user's name and password into an SQL database.
I made 2 classes: one class runs the GUI and the other class handles the quiz logic. How can I call the quiz class into the GUI class from a click of a Tkinter button?
Here is the code below:
import tkinter as tk
from tkinter import tix
from tkinter import ttk
import json
import tkinter.messagebox
import sqlite3
# MAIN GUI DESIGN:
class Main:
def __init__(self):
self.root = tix.Tk()
self.root.title("Quiz App")
self.root.geometry("850x600+500+200")
self.root.resizable(False,False)
# FUNCTIONS AND METHODS:
def frame2to3(self): # THIS IS WHERE I NEED HELP CALLING THE CLASS INTO THIS METHOD
self.frame2.pack_forget()
self.frame3.pack(fill="both",expand=True)
def clearfunc1(self):
self.entry1.delete(0, 'end')
self.entry2.delete(0, 'end')
def clearfunc2(self):
self.entry3.delete(0, 'end')
self.entry4.delete(0, 'end')
def close(self):
if tkinter.messagebox.askyesno("Quiz App","Are you sure you want to exit?") > 0:
self.root.destroy()
return
m = Main()
# QUIZ LOGIC:
class Quiz(Main): # THE CLASS I NEED TO CALL
def __init__(self):
self.q_no =0
self.display_question()
self.opt_selected = IntVar()
self.opts = self.radio_buttons()
self.display_options()
self.buttons()
self.data_size = len(question)
self.correct =0
def display_result(self):
wrong_count = self.data_size - self.correct
correct = f"Correct: {self.correct}"
wrong = f"Wrong: {wrong_count}"
score = int(self.correct/self.data_size*100)
result = f"Score: {score}%"
self.show_result = tk.Label(m.frame3(), text=f"{result}\n{correct}\n{Wrong}")
def check_ans(self,q_no):
if self.opt_selected.get() == answer[q_no]:
return True
def next_btn(self):
if self.check_ans(self.q_no):
self.correct += 1
self.q_no += 1
if self.q_no == self.data_size:
self.display_result()
self.root.destroy()
else:
self.display_question()
self.display_options()
def buttons(self):
self.next_btn = tk.Button(m.frame3(), text = "next" , command=self.next_btn)
self.next_btn.place(x=780,y=550)
self.exitButton3 = tk.Button(m.frame3(),text="Exit", command = self.close)
self.exitButton3.place(x=250,y=140)
def display_options(self):
val = 0
self.opt_selected.set(0)
for option in options[self.q_no]:
self.opts[val]['text'] = option
val += 1
def display_question(self):
ques_no = tk.Label(m.frame3(), text=question[self.q_no])
ques_no.place(x=30,y=90)
def radio_buttons(self):
q_list =[]
y_pos = 150
while len(q_list) < 4:
radio_btn = tk.Radiobutton(m.frame3(), text = "", variable=self.opt_selected,value = len(q_list)+1)
q_list.append(radio_btn)
radio_btn.place(x=100,y=y_pos)
y_pos += 30
return q_list
with open('data1.json') as f:
data1 = json.load(f)
question = (data1['question'])
options = (data1['options'])
answer = (data1[ 'answer'])
q = Quiz()
Beside removing the strange pattern of subclassing Main
, as already mentioned by khelwood, you might want to add a parameter quiz
to the Main
class __init__
. You can then store a reference to the Quiz
instance as an attribute of the Main
instance. The instantiation it self would be in this order:
quiz = Quiz()
main = Main(quiz=quiz)
and the __init__
method:
class Main:
def __init__(self, quiz):
self._quiz = quiz
self.root = tix.Tk()
...
Update:
But as the Quiz
instance is required during Main
instantiation, we might switch the order (and the parameters):
main = Main()
quiz = Quiz(main=main)
main.quiz = quiz
and
class Main:
def __init__(self):
self.root = tix.Tk()
...
and
class Quiz:
def __init__(self, main):
self._main = main
....