Putting questions into a list and randomly selecting them - python

I've had this problem for a while where my quiz won't work, where certain tkinter windows wouldn't close even though the code was there. However, after speaking to my teacher, he said that it was possible to read my text file to get the questions and answers, then place them in a list so that I could randomly select them to be displayed in the window (That way no question appears twice). He then said that you could have the window refresh after each question is answered, putting a different question up with different answers (Maybe even a "Correct"/"Wrong" inbetween each question). If anyone has an idea how to do ths is would be a massive help. Thanks.
def quiz_easy():
global question
questions = []
correctanswers = []
possibleanswer1s = []
possibleanswer2s = []
possibleanswer3s = []
easy_questions = open("sup.txt", "r")
count = 1
for line in easy_questions:
if count == 1:
questions.append(line.rstrip())
elif count == 2:
correctanswers.append(line.rstrip())
elif count == 3:
possibleanswer1s.append(line.rstrip())
elif count == 4:
possibleanswer2s.append(line.rstrip())
elif count == 5:
possibleanswer3s.append(line.rstrip())
if count == 5:
count = 1
else:
count = count + 1
print(questions)
print (correctanswers)
randno1 = random.randint(0,len(questions)-1)
master = Tk()
text = Text(master)
text.insert(INSERT, questions[randno1])
text.insert(END, "?")
text.pack()
text.tag_add("here","5.0", "1.4")
text.tag_add("start", "1.8", "1.13")

Think OOP intead of sequencial
Your Quiz should be a class.
Avoiding MVC approach, stay simple the class directly handle the UI, the data and the process.
The constructor read the question file and update your internal data representation (That may be reworked too), and start the Root Tk instance
Create a method to display the question (a Label) and the area dedicated to the answer (an Entry)
Add a button "OK" or bind the key on the Entry widget to start a method dedicated to the control of the answer and displaying of the correct answer
Create this method
Add a method for the Tkinter mainloop
In the main of the script instanciate the quizz, call the method "display a question" and then call the method for mainloop()
We are waiting for your code ;-) to assist
tips: 1: Prefer:
import Tkinter as tk # instead of from Tkinter import * to keep your code explicit.
2: Forget the global directive in your code .. non sense here
3: the file reading is rought, the format of your file is not optimised.
to not going too quickly in XML paradygm ..
think about prefix ruler
QUE translate pig in french
COR cochon
ALT proc
ALT truie
ALT verrat
ALT porcelet
ALT cochonet
ALT gorret
ALT nourrin
ALT nourrain
ALT cochette
for each line you just append a possible answer in a list of possible answers
Ready to run your brain ... go. ^^

Related

execute multiple variable functions(var_1,var_2,var_3)

I got another little question...
I want to make multiple variables which I create with 'setattr'
That works quite fine. It creates these variables:
self.sectionButton_1 = Button(text=x)
self.sectionButton_2 = Button(text=x)
self.sectionButton_3 = Button(text=x)
Now I want them to get displayed on the window with tkinter so that this should happen:
self.sectionButton_1.grid(row=i, column=0)
self.sectionButton_2.grid(row=i, column=0)
and so on..
But how do I have to edit the loop that the sectionButtons gonna be created with .grid from tkinter in a loop without writing the above ten times.
# Display Section selection
def checkSection(self):
# Read all sections from config
self.sections = config.sections()
self.sectionsCount = str(len(self.sections))
self.i = 0
self.text = Label(text="Choose Section:" + self.sectionsCount)
self.text.grid(row=1, column=0)
for x in self.sections:
i = +1
setattr(self, 'sectionButton_' + str(i), Button(text=x))
I'm not that good at explaining but hopefully its enough to understand my problem ^^
If not, just comment, I will try to answer it
If you have a group of related variables of the same type and you're doing the same operations to each one then that's a natural place to switch to using a list instead of individual variables.
Your code would become more like:
self.sectionButtons = []
for i, x in enumerate(self.sections):
button = Button(text=x)
button.grid(row=i+1, column=0)
self.sectionButtons.append(button)
This also has the advantage of no longer needing to construct the variable names as strings and use setattr, which is often a sign there's a better way.

Using txt files to retrieve questions and multiple answers [duplicate]

This question already has answers here:
How to read a text file into a list or an array with Python
(6 answers)
Closed 1 year ago.
I am currently making a quiz through python and Tkinter. I’m trying to use txt files, one for my questions and one for my set of answers for the said question, as well as one for explanations for the answers. However, I’m not sure how to implement this into my code. I’m not sure how to retrieve a question from the txt file and represent it as a label while also having the correct set of answers for that question represented as buttons for the user to choose. As well as display the text for the correct explanation for the answer. Is there a simple way to do this? Should I rather use an array instead?
This should get you started on the idea of how I think you should proceed.
Store the question and answer and id in a dictionary like:
data = {question:[id,answer]}
# So it would be like
data = {'Who was the first prime minister of india?':[1,'Jawaharlal Nehru'],
'Tallest building in the world':[2,'Burj Khalifa'],
'Largest country in the world':[3,'Russia']}
Create a file explanation.txt and then store the id and explanation in the form of:
id - explanation
So the text file for explanation(explnation.txt) would be something like:
1 - Your explanation goes here: Tryst with destiny
2 - UAE
3 - WC 2018
So then the code for all this together would be something like:
import tkinter as tk
import random
root = tk.Tk()
# Store it here
data = {'Who was the first prime minister of india?':[1,'Jawaharlal Nehru'],
'Tallest building in the world':[2,'Burj Khalifa'],
'Largest country in the world':[3,'Russia']}
score = 0 # Score of correct answers
def get_question():
global id, answer, explanation
exp_label.config(text='') # Clear the previous explanation
question = random.choice(list(data.keys())) # Get the question
item = data[question] # Get the corresponding list
id = item[0] # Get the id from the list
answer = item[1] # Get the answer from the list
explanation = get_explanation(id) # Find the explanation using the id
q_label.config(text=question) # Update the question to this
def submit():
global score
if answer_ent.get().lower() == answer.lower(): # If correct answer
score += 1 # Increase the score by 1
score_label.config(text=f'Score: {score}') # Update the score label
else: # If wrong answer
exp_label.config(text=explanation) # Show the explanation
answer_ent.delete(0,'end') # Clear the entry
def get_explanation(id):
with open('explanation.txt','r') as file: # Open the file
lst = file.readlines() # Read each line and make it a list
for i in lst: # Looping through that list
fetched_id = i.split(' - ')[0] # Split the txt with ' - ' and get the id
if int(fetched_id) == id: # If fetched and our question id are same
explanation = i.split(' - ')[1][:-1] # Get the explanation and trim the \n
return explanation # Return it
q_label = tk.Label(root,font=(0,21))
q_label.grid(row=0,column=0)
answer_ent = tk.Entry(root)
answer_ent.grid(row=1,column=0,pady=10,padx=20)
exp_label = tk.Label(root,font=(0,13))
exp_label.grid(row=3,column=0,pady=10)
score_label = tk.Label(root,text='Score: 0',font=(0,13))
score_label.grid(row=4,column=0)
tk.Button(root,text='Submit',command=submit).grid(row=5,column=0,pady=10)
tk.Button(root,text='Next',command=get_question).grid(row=6,column=0,pady=10)
get_question() # Call the function immediately
root.mainloop()
I have explained the code using comments to make it understandable on-the-go. This is just a small scale example that you can take and expand and more features, like making sure no same question is repeated and so on. This way just seems easy for me using tkinter.

Self Limiting Repition Function

I'm writing a program that is basically a study guide/ practice test for the current section of my A&P class (it keeps me more engaged than just rereading notes over and over). The test works without any problems, but I have an issue where some of my questions use an "enterbox" input, I can have the question loop if the answer is incorrect, but I can't get it to break without a correct answer.
I figured out a way to make it work by putting the entire function back into the initial "else" tree, so that right or wrong you advance to the next question but it looks incredibly ugly and I can't believe there isn't a better way.
So my "solution" looks like such:
def question82():
x = "which type of metabolism provides the maximum amount of ATP needed for contraction?"
ques82 = enterbox(msg = x, title = version)
#version is a variable defined earlier
if ques82.lower() in ["aerobic"]:
add() #a function that is explained in the example further below
question83()
else:
loss() #again its a housecleaning function shown below
ques82b = enterbox(msg = x, title = version)
if ques82b.lower() in ["aerobic"]:
add()
question83()
else:
loss()
question83()
Okay so it worked, but using a nested if tree for each "enterbox" question looks kinda sloppy. I'm self taught so it may be the only solution but if there is something better I would love to learn about it.
So here is a complete section from my program:
from easygui import *
import sys
version = 'A&P EXAM 3 REVIEW'
points = 0
def add():
global points
msgbox("Correct", title = version)
points = points + 1
def loss():
global points
msgbox("Try Again", title = version)
points = points - 1
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version)
if ques81.lower() in ["creatine"]:
add()
question82()
else:
loss()
question81()
It works as is so any errors from what's provided are probably my fault from copy and pasting.
Also I'm running it in python 2.7rc1 if that helps.
Thanks for any help in advance.
I don't know if there is a way to combine "enterbox" that has a button for "skip" as that would also be a solution.
Consider the following approach:
We define a list of question and answer pairs. We do this in one place so it's easy to maintain and we don't have to search all over the file to make changes or re-use this code for a different questionset.
We create an ask_question function that we can call for all of our questions. This way, if we want to make a change about how we implement our question logic, we only have to make it in one spot (and not in each of the questionXX functions).
We compare user input to our answer using == and not in (in will do something else, not what you expect).
We create an object to keep track of our answer results. Here, it's an instance of ResultsStore, but it can be anything really, let's just try to get away from global variables.
Use a loop when prompting for answers. The loop will repeat if the answer given was incorrect (and if retry_on_fail is False).
Allow for the user to enter some "skip" keyword to skip the question.
Display the results once the "test" is complete. Here, we do that by defining and calling the store.display_results() method.
So, what about:
from easygui import enterbox
question_answer_pairs = [
("1 + 1 = ?", "2"),
("2 * 3 = ?", "6"),
("which type of metabolism provides the maximum amount of ATP needed for contraction?", "aerobic")
]
VERSION = 'A&P EXAM 3 REVIEW'
class ResultStore:
def __init__(self):
self.num_correct = 0
self.num_skipped = 0
self.num_wrong = 0
def show_results(self):
print("Results:")
print(" Correct:", self.num_correct)
print(" Skipped:", self.num_skipped)
print(" Wrong: ", self.num_wrong)
def ask_question(q, a, rs, retry_on_fail=True):
while True:
resp = enterbox(msg=q, title=VERSION)
# Force resp to be a string if nothing is entered (so .lower() doesn't throw)
if resp is None: resp = ''
if resp.lower() == a.lower():
rs.num_correct += 1
return True
if resp.lower() == "skip":
rs.num_skipped += 1
return None
# If we get here, we haven't returned (so the answer was neither correct nor
# "skip"). Increment num_wrong and check whether we should repeat.
rs.num_wrong += 1
if retry_on_fail is False:
return False
# Create a ResultsStore object to keep track of how we did
store = ResultStore()
# Ask questions
for (q,a) in question_answer_pairs:
ask_question(q, a, store)
# Display results (calling the .show_results() method on the ResultsStore object)
store.show_results()
Now, the return value currently doesn't do anything, but it could!
RES_MAP = {
True: "Correct!",
None: "(skipped)",
False: "Incorrect" # Will only be shown if retry_on_fail is False
}
for (q,a) in question_answer_pairs:
res = ask_question(q, a, store)
print(RES_MAP[res])
Quick and dirty solution could be using the default value "skip" for the answer:
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version, default = "skip")
if ques81.lower() == 'creatine':
add()
question82()
elif ques81 == 'skip':
# Do something
else:
loss()
question81()
But you should really study the answer given by jedwards. There's a lot to learn about
program design. He's not giving you the fish, he's teaching you to fish.

Python GUI creating a list item with Text and checkboxes

I am trying to create a list like in outlook. With list items with an layout like this:
Don't get me wrong this isn't a "give me the full answer" question. I just have the problem of the right naming. I would appreciate it a lot if some could throw in the right words and I will look for them by my own.
I used tkinter at the moment but in that it seems like there isn't a solution for that.
Kind regards.
I think Tkinter can do this by using a bit of object oriented programming you could define how one list element should look like and then with static variables you could define a position of a new line relative to the position of the previous line. Something like:
from tkinter import *
class Line:
y = 0 # static variable for the y position
def __init__(self):
self.y_local = Line.y
Line.y = Line.y + 40 # increase the static variable for the next line
print(self.y_local)
def draw(self, tk, h_line="Headline", a_info="Addtional Information", date="01/01/1970"):
self.label_head = Label(text=h_line)
self.label_head.place(x=20, y=self.y)
self.label_info = Label(text=a_info)
self.label_info.place(x=20, y=self.y+20)
self.label_date = Label(text='Date')
self.label_date.place(x=200, y=self.y)
self.label_ndate = Label(text=date)
self.label_ndate.place(x=200, y=self.y+20)
self.chkbtn = Checkbutton(tk, text="Checkbox")
self.chkbtn.place(x=300, y=self.y+20)
tk = Tk()
data = [
["News", "WWIII has been avoided", "02/04/2018"],
["Python", "Python solves 42 riddles", "02/04/2018"]
]
for line in data:
temp = Line()
temp.draw(tk, line[0], line[1], line[2])
mainloop()
Hope I understood your question well. That you have a list with information and want to display that in an easy and scalable way. I have not looked to add the lines around the information as I've never done that before I know there are separators I've used vertical once before but I wouldn't be surprised if you can draw a box around each line either.

Tkinter radio button clear / reset values

I am writing a Tkinter program for the first time and have a question on radio buttons. What I am trying to do is this:
open a set of images (one at a time).
When an image is opened, annotate a value using the radio button.
Collect this value in a list
So, in this example I have 2 compounds and the list would have 2 annotations.
The problem I have is, if by mistake the user clicks radiobutton 2 instead of one, and then corrects him/herself, the list will have 4 items (3 for the first image, 1 for the second). How do I handle this, so that the list will have only 2 values?
import Tkinter as tk
from PIL import ImageTk, Image
from tkFileDialog import askopenfilename
cmp_list = ["VU435DR","VU684DR"]
li = []
li_final = []
def sel():
selection = str(var.get())
if selection == "1":
li.append("Antagonist")
elif selection == "2":
li.append("Agonist")
for i in range(len(cmp_list)):
root = tk.Tk()
var = tk.IntVar()
ig = str(cmp_list[i] + '.png')
img = ImageTk.PhotoImage(Image.open(ig))
panel = tk.Label(root,image=img)
panel.pack(side = "top",fill="none",expand="no")
#w = tk.Text(height=2,width=50)
#w.pack(side='right")
q = tk.Radiobutton(root,text="Antagonist",command=sel,value=1,variable=var)
q.pack()
r = tk.Radiobutton(root,text="Agonist",command=sel,value=2,variable=var)
r.pack()
root.mainloop()
print li
Your code is creating more than one instance of tk.Tk(). This is not how Tkinter was designed to work, and it will yield unpredictable behavior. A proper Tkinter program always has exactly one instance of tk.Tk().
If you need more than one window, for the second and subsequent windows you should create an instance of tk.Toplevel.
To answer your specific question about how to handle someone first hitting one radiobutton and then the other -- the problem is that you are unconditionally appending to your list each time they click on a radiobutton. The solution is to use some sort of flag or indicator to know whether one of the radiobuttons has been clicked, or change your code so that it doesn't matter.
Let's look at that second option - make it so it doesn't matter. When you open up a new image you can automatically append a value to your list. In this case, set it to None to say that nothing has been picked yet. Then, in sel, you would always replace the last element rather than append a new element, since you know that the last element always refers to the current compound.

Categories

Resources