前言:
最近因大家都知道的原因,只能在家自学,之前打印的数学考卷存货不多了,所以想在电脑上编一个来代替之前的数学考卷版本《Python:大班数学自动生成器》。
想了2个版本:第一个是利用pyqt5来做个GUI版本,但是苦于对GUI不熟,所以一直没成功;第二个版本则比较简单,使用了CMD的窗口,但是因为我比较虐娃,所以加了1分钟的倒计时,时间到了但还没做的话就本道题按0分处理。要加这个功能就必须用到多线程和线程关闭的技巧了。由于我之前也没怎么研究过多线程,所以正好虐娃前先自虐,还好最后成功了。
注:目前线程关闭的方法抄了网上的,但是如果一个线程用了input的话,即使关闭了该线程也必须输入内容才能继续,为了解决这个问题,只能在另一个线程里再次加入了input的语句,提示“回车两次继续”,如果有人有更好的解决办法请留言!
代码:
import random
import traceback
import threading
import inspect
import ctypes
import time
class MyThread(threading.Thread):
def __init__(self,func,args=()):
super(MyThread,self).__init__()
self.func = func
self.args = args
def run(self):
self.result = self.func(*self.args)
def get_result(self):
try:
return self.result
except Exception:
return None
def _async_raise(tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
def stop_thread(thread):
_async_raise(thread.ident, SystemExit)
def math_question(max_question_num, min_range, max_range, min_answer, max_answer):
'''数学题目生成器'''
question_list = []
answer_list = []
question_num = 0
while True:
if question_num >= max_question_num :
break
else:
question = str(random.randint(min_range, max_range)) # 这里选择单个数字的取数范围
j = int(max_num)
while j - 1:
question = question + random.choice(['+', '-']) # 这里选择运算符的范围
question = question + str(random.randint(min_range, max_range))
j = j - 1
answer = eval(question)
if min_answer <= answer <= max_answer: # 这里选择答案的取数范围
this_question = question + '='
if '*' in this_question:
this_question = this_question.replace('*', '×')
if this_question not in question_list:
question_list.append(this_question)
answer_list.append(answer)
question_num = question_num + 1
return question_list, answer_list
def custom_mode():
'''自定义模式'''
# 1、考题数量
max_question_num = input('>>>请输入考题数量(如20):(直接回车默认20)')
if not max_question_num:
max_question_num = 20
# 2、单个数字的取值范围
num_range = input('>>>请输入单个数字的取值范围(如1,20):(直接回车默认1-20)')
if not num_range:
min_range, max_range = 1, 20
else:
min_range, max_range = num_range.split(',')
# 3、答案的取值范围
answer_range = input('>>>请输入答案的取值范围(如1,20):(直接回车默认1-20)')
if not answer_range:
min_answer, max_answer = 1, 20
else:
min_answer, max_answer = answer_range.split(',')
# 4、单道题目中数字的最大数量(比如1+2+3就是3个数字)
max_num = input('>>>请输入单道题目中数字的最大数量(如2):(直接回车默认2)')
if not max_num:
max_num = 2
return max_question_num, min_range, max_range, min_answer, max_answer, max_num
def mode_choice():
'''模式选择'''
mode_choice = int(input('>>>请选择模式(1:小班,2:中班,3:大班,4:小学,5:自定义):'))
if mode_choice == 1:
max_question_num, min_range, max_range, min_answer, max_answer, max_num = 10, 0, 9, 0, 9, 2
elif mode_choice == 2:
max_question_num, min_range, max_range, min_answer, max_answer, max_num = 20, 0, 10, 0, 20, 2
elif mode_choice == 3:
max_question_num, min_range, max_range, min_answer, max_answer, max_num = 50, 1, 20, 1, 20, 2
elif mode_choice == 4:
max_question_num, min_range, max_range, min_answer, max_answer, max_num = 100, 1, 100, 1, 100, 2
elif mode_choice == 5:
max_question_num, min_range, max_range, min_answer, max_answer, max_num = custom_mode()
max_question_num, min_range, max_range, min_answer, max_answer, max_num = int(max_question_num), int(min_range), int(max_range), int(min_answer), int(max_answer), int(max_num)
return max_question_num, min_range, max_range, min_answer, max_answer, max_num
def make_your_answer():
'''输入答案'''
your_answer = input('>>>请输入答案:')
try:
your_answer = int(your_answer)
return your_answer
except:
print('>>>请输入整数!')
return make_your_answer()
def check_answer(answer, your_answer, score):
if your_answer == answer:
print('>>>恭喜你,答对啦!真棒!')
print('-' * 100)
score += 1
return score
else:
print('>>>真可惜,答错啦!')
print('>>>"{}"的答案是:{}'.format(question, answer))
print('-' * 100)
return score
def make_answer():
'''答题'''
your_answer = make_your_answer()
stop_thread(thread2) #关闭线程2,也就是倒计时
return your_answer
def countdown():
'''倒计时'''
for i in range(0, 60):
time.sleep(1)
print('\n>>>时间到!')
stop_thread(thread1) #关闭线程1,也就是答题
print('>>>按2次回车键继续!')
a = input('>>>') #这里也是无奈之举
return 0
if __name__ =="__main__":
try:
max_question_num, min_range, max_range, min_answer, max_answer, max_num = mode_choice()
# print(sign_list, min_range, max_range, min_answer, max_answer)
question_list, answer_list = math_question(max_question_num, min_range, max_range, min_answer, max_answer)
score = 0 #答对的
overtime = 0 #超时的
n = 1
for question, answer in zip(question_list, answer_list):
print('>>>第{}题是:{}'.format(n, question))
n += 1
thread1 = MyThread(make_answer)
thread2 = MyThread(countdown)
thread1.start()
thread2.start()
thread2.join()
your_answer = thread1.get_result()
if your_answer is None: #如果超时的话,返回的就是None
overtime += 1
# print(your_answer)
score = check_answer(answer, your_answer, score)
wrong_score = max_question_num - score - overtime
accuracy = score / max_question_num
if accuracy < 0.6: evalute = 'D'
elif 0.6<= accuracy < 0.8: evalute = 'C'
elif 0.8<= accuracy <0.9: evalute = 'B'
elif 0.9<= accuracy < 1: evalute = 'A'
elif accuracy == 1: evalute = '100分'
accuracy = format(accuracy, '.0%')
print('>>>考试完成!\n题目总数:{}\n答对:{}\n答错:{}\n未答:{}\n准确率:{}\n评价:{}'.format(max_question_num, score, wrong_score, overtime, accuracy, evalute))
except:
traceback.print_exc()
finally:
a = input('>>>按随机键退出!')