前言:
最近因大家都知道的原因,只能在家自學,之前打印的數學考卷存貨不多了,所以想在電腦上編一個來代替之前的數學考卷版本《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('>>>按隨機鍵退出!')