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

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.

Related

Changing row-height and column-width in LibreOffice Calc using Python3

I want to write a LibreOffice Calc document from within a Python3 program. Using pyoo I can do almost everything I want, including formatting and merging cells. But I cannot adjust row heights and column widths.
I found Change the column width and row height very helpful, and have been experimenting with it, but I can't seem to get quite the result I want. My present test file, based on the answer mentioned above, looks like this:
#! /usr/bin/python3
import os, pyoo, time, uno
s = '-'
while s != 'Y':
s = input("Have you remembered to start Calc? ").upper()
os.popen("soffice --accept=\"socket,host=localhost,port=2002;urp;\" --norestore --nologo --nodefault")
time.sleep(2)
desktop = pyoo.Desktop('localhost', 2002)
doc = desktop.create_spreadsheet()
class ofic:
sheet_idx = 0
row_num = 0
sheet = None
o = ofic()
uno_localContext = uno.getComponentContext()
uno_resolver = uno_localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", uno_localContext )
uno_ctx = uno_resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
uno_smgr = uno_ctx.ServiceManager
uno_desktop = uno_smgr.createInstanceWithContext( "com.sun.star.frame.Desktop", uno_ctx)
uno_model = uno_desktop.getCurrentComponent()
uno_controller = uno_model.getCurrentController()
uno_sheet_count = 0
doc.sheets.create("Page {}".format(1), index=o.sheet_idx)
o.sheet = doc.sheets[o.sheet_idx]
o.sheet[0, 0].value = "The quick brown fox jumps over the lazy dog"
o.sheet[1, 1].value = o.sheet_idx
uno_controller.setActiveSheet(uno_model.Sheets.getByIndex(uno_sheet_count))
uno_sheet_count += 1
uno_active_sheet = uno_model.CurrentController.ActiveSheet
uno_columns = uno_active_sheet.getColumns()
uno_column = uno_columns.getByName("B")
uno_column.Width = 1000
The main problem with the above is that I have 2 Calc documents on the screen, one of which is created before the Python program gets going; the other is created from Python with a pyoo function. The first document gets the column width change, and the second receives the text input etc. I want just the second document, and of course I want the column width change applied to it.
I am sure the answer must be fairly straightforward, but after hours of experimentation I still can't find it. Could someone point me in the right direction, please?
Your code alternates between pyoo and straight Python-UNO, so it's no wonder that it's giving messy results. Pick one or the other. Personally, I use straight Python-UNO and don't see the benefit of adding the extra pyoo library.
the other is created from Python with a pyoo function
Do you mean this line of code from your question, and is this the "second document" that you want the column change applied to?
doc = desktop.create_spreadsheet()
If so, then get objects from that document instead of whichever window the desktop happens to have selected.
controller = doc.getCurrentController()
sheets = doc.getSheets()
Or perhaps you want the other document, the one that didn't get created from Python. In that case, grab a reference to that document before creating the second one.
first_doc = uno_desktop.getCurrentComponent()
second_doc = desktop.create_spreadsheet()
controller = first_doc.getCurrentController()
sheets = first_doc.getSheets()
If you don't have a reference to the document, you can find it by iterating through the open windows.
oComponents = desktop.getComponents()
oDocs = oComponents.createEnumeration()
Finally, how to resize a column. The link in your question is for Excel and VBA (both from Microsoft), so I'm not sure why you think that would be relevant. Here is a Python-UNO example of resizing columns.
oColumns = oSheet.getColumns()
oColumn = oColumns.getByName("A")
oColumn.Width = 7000
oColumn = oColumns.getByName("B")
oColumn.OptimalWidth = True

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.

Can I use a dictionary in this way

I'm currently revising for my GCSE coursework. The Task I have been asked for is a troubleshooting program, in which the user says their problem; and I evaluate their input and test it for keywords, before pulling up a text file into the code and printing the according solution.
This was my original code:
keywords = ["k1","k2","k3","k4","k5","k6","k7","k8","k9","kk"]
question_about_phone = input("What Seems to be the Problem? Please be percific but don't bombard us with too much info").lower()
file = open('code.txt','r')
solution = [line.strip(',') for line in file.readlines()]
for x in range(0, 10):
if keywords[x] in question_about_phone:
print(solution[x])
However in the middle of my Assessment I realised that u cant have it printing a solution for each keyword. So I decided to make it assign a value to a different list and then have many lines of
if list[1] and list[5] = "true:
print(Solution[1]
and so on ...
however this is inefficient ;( is there anyway i can use a DICTIONARY with values and say something along the lines of:
dictionary = [list[1] list[5], (more combos)
then something like (probably a while loop)
for x in range(0,10):
if dictionary[x] == "TRUE":
print(solutions[x])
end code
You can do
keywords = ["battery", "off", "broken"]
question_about_phone = input("What Seems to be the Problem?")
with open('code.txt', 'r') as file:
solutions = {k:line.strip(',\n') for k, line in zip(keywords, file)}
answers = [v for k, v in solutions.items() if k in question_about_phone]
if answers:
print(answers)
else:
print('Sorry, there are no answers to your question')
which, for example, with a file of
answer 4 battery
answer 4 off
answer 4 broken
...
and an input question of
What Seems to be the Problem? broken battery sunny
produces
['answer 4 broken', 'answer 4 battery']
basically solutions is built pairing the keywords and each line of the file.
Then answers is formed picking the values of those keywords that appear in the question
However, I strongly agree with Tim Seed's approach: it would be much more efficient to only look for the keywords present in the question instead of doing the opposite, since the possible answers outnumber the terms in the question.
In order to achieve that, simply change
answers = [solutions[k] for k in question_about_phone.split() if k in solutions]
You have correctly deduced that iterating through a list (array) is inefficient - and using a dictionary is an option.
So using your Example
keywords = {"k1": "Turn Power on","k2":"Turn Power Off"}
for k in ['Bad','k1','k2','bad']:
if k in keywords:
print("%s: Answer is %s"%(k,keywords[k]))
else:
print("%s: No idea what the issue is"%(k))
You should get Answers for k1,k2 - but not for the others....
Giving you output of
Bad: No idea what the issue is
k1: Answer is Turn Power on
k2: Answer is Turn Power Off
bad: No idea what the issue is
Hope that helps
I assume that there is exactly one answer per keyword (from your example code).
You can then just return after the first answer has been found, as in:
for x in range(0, 10):
if keywords[x] in question_about_phone:
print(solution[x])
return
print("No solution found, be more specific")
You can also iterate in a more general way:
for idx, kw in enumerate(keywords):
if kw in question_about_phone:
print(solutions[idx])
return

Compare two lists in python and obtain non-equality

This piece of code in theory have to compare two lists which have the ID of a tweet, and in this comparison if it already exists in screen printing , otherwise not.
But I print all or not being listed.
Any suggestions to compare these two lists of ID's and if not the ID of the first list in the second then print it ?
Sorry for the little efficient code . ( and my English )
What I seek is actually not do RT ( retweet ) repeatedly when I already have . I use Tweepy library , I read the timeline , and make the tweet RT I did not do RT
def analizarRT():
timeline = []
temp = []
RT = []
fileRT = openFile('rt.txt')
for status in api.user_timeline('cnn', count='6'):
timeline.append(status)
for i in range(6):
temp.append(timeline[i].id)
for a in range(6):
for b in range(6):
if str(temp[a]) == fileRT[b]:
pass
else:
RT.append(temp[a])
for i in RT:
print i
Solved add this function !
def estaElemento(tweetId, arreglo):
encontrado = False
for a in range(len(arreglo)):
if str(tweetId) == arreglo[a].strip():
encontrado = True
break
return encontrado
Its a simple program, don't complicate it. As per your comments, there are two lists:)
1. timeline
2. fileRT
Now, you want to compare the id's in both these lists. Before you do that, you must know the nature of these two lists.
I mean, what is the type of data in the lists?
Is it
list of strings? or
list of objects? or
list of integers?
So, find out that, debug it, or use print statements in your code. Or please add these details in your question. So, you can give a perfect answer.
Mean while, try this:
if timeline.id == fileRT.id should work.
Edited:
def analizarRT():
timeline = []
fileRT = openFile('rt.txt')
for status in api.user_timeline('cnn', count='6'):
timeline.append(status)
for i in range(6):
for b in range(6):
if timeline[i].id == fileRT[b].id:
pass
else:
newlist.append(timeline[i].id)
print newlist
As per your question, you want to obtain them, right?. I have appended them in a newlist. Now you can say print newlist to see the items
your else statement is associated with the for statement, you probably need to add one more indent to make it work on the if statement.

Putting questions into a list and randomly selecting them

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. ^^

Categories

Resources