Preface:
In the individual event, the early realization of small high math topic and requires only command-line program, but needs to pair programming with a graphical user interface, and the user object is no longer out of the question the teacher, but the beginning of a small high students. Because in the individual event, the two were using C ++ and Python language written in C ++ and feel of the graphical user interface obscure, all unanimously decided to use Python to develop pair programming project, using tools for Python comes with Tkinter
Pair programming project requirements:
user:
Elementary, middle and high school students.
Features:
1, user registration function. The user's phone number, click on the registration will receive a registration code, the user can use this registration code to complete the registration;
2, after the completion of user registration, password prompt interface, user input password matches the password twice after the success. Password 6-10, the case must contain letters and numbers. Users can change the password, enter the correct password in the log in the original state, change your password after successfully re-enter the same new password twice;
3, after the password has been set, jump to the selection interface, display interface elementary, middle and high school three options, the user clicks on one of them, prompting the user to enter the number of items need to be generated;
4, the number of items the user input, generating a piece of paper (with a roll can not have the same title, all entitled multiple choice), the interface displays the first question and casual working four options, the user selects one of the four options submission interface displays the second question, ... until the last question;
5, after the submission of the last question, the display interface, the score is calculated based on the percentage of correct answers;
6, the user interface can opt out of the score or continue to do problems;
7, small high math problem and asked to see the beginning of a personal project.
Project Prototype:
Before the official start, we drew on paper first software prototype map, as follows:
With a red line which the project is divided into three parts, two of the division of writing
The first part, visit:
The failure to grasp the handle of the database, save the account password when using an Excel spreadsheet to achieve
在登陆界面只会验证Excel中是否有该手机号,以及手机号对应的密码是否正确,若没有找到手机号,或者找到手机号但密码对不上,则会弹出一个对话框提示手机号或密码输入错误
若没有注册手机号,可以点击“注册账号”按钮进入注册界面。在注册界面里,当点击“注册”按钮后会验证手机号、密码是否满足格式要求,这里使用正则表达式来实现验证功能,然后会验证两次输入的密码是否一致,以及验证码是否与发送到手机号的验证码一致。点击“获取验证码按钮”会发送验证码到对应手机号上,这里使用了腾讯云的免费试用发送短信功能,免费试用100条短信。注册完成后,注册界面会关闭,在登陆界面输入正确的手机号和密码即可登陆。
第二部分,选择:
登陆成功后会进入选择界面,在界面右上角可以修改密码,可以在“小学”,“初中”,“高中”三种难度中选择一种,选择后输入生成题目数量,会验证题目数量是否大于0。
第三部分,做题:
会按照选择的难度和输入题目的数量生成题目,每次生成一道题,生成一道题的代码复用的是C++的,代码如下:
1 //level为0代表难度为小学,1代表初中,2代表高中 2 string GetOneQuestion(int level) 3 { 4 stringstream ss; 5 int bracketNum = 0; //括号的数量,单位为对 6 int operateNum; //操作数 7 bool leftBracket; //判断该操作数前是否有左括号,若有,则在该操作数后不加右括号,因为要避免出现“(?)”的情况 8 bool need = false; //判断是否满足备注要求,初中题目至少一个根号或平方(概率皆为1/2),高中题目至少一个三角函数 9 if (level == 0) 10 { 11 operateNum = 2+rand()%4; //小学题目2~5个操作数 12 } 13 else 14 { 15 operateNum = 1+rand()%5; //初中和高中题目1~5个操作数 16 } 17 18 if (operateNum == 1) 19 { 20 if (level == 1) 21 { 22 if (rand()%2 == 0) 23 { 24 ss << "√" << 1+rand()%100; 25 } 26 else 27 { 28 ss << 1+rand()%100 << "^2"; 29 } 30 } 31 else if (level == 2) 32 { 33 string fun; 34 int num; 35 //tan值要存在 36 do 37 { 38 fun = g_triFun[rand()%3]; 39 num = g_triNum[rand()%46]; 40 } 41 while (fun == "tan(" && (num+90)%180 == 0); 42 ss << fun << num << "°" << ')'; 43 } 44 } 45 else 46 { 47 //生成第一个操作数 48 if (rand()%4 == 0) 49 { 50 ss << '('; 51 bracketNum++; 52 } 53 54 if (level == 1 && rand()%4 == 0) //根号 55 { 56 need = true; 57 ss <<"√"; 58 } 59 60 if (level == 2 && rand()%4 == 0) //操作数,或者sin(?)cos(?)tan(?) 61 { 62 need = true; 63 string fun; 64 int num; 65 //tan值要存在 66 do 67 { 68 fun = g_triFun[rand()%3]; 69 num = g_triNum[rand()%46]; 70 } 71 while (fun == "tan(" && (num+90)%180 == 0); 72 ss << fun << num << "°" << ')'; 73 } 74 else 75 { 76 ss << 1+rand()%100; 77 } 78 79 if (level == 1 && rand()%4 == 0) //平方 80 { 81 need = true; 82 ss << "^2"; 83 } 84 85 //生成后续运算符与操作数 86 for (int i = 0; i < operateNum; i++) 87 { 88 char c = g_operators[rand()%4]; //运算符 89 ss << c; 90 91 if (level == 1 && (rand()%4 == 0 || (!need && i == operateNum-1 && rand()%2 == 0))) //根号 92 { 93 need = true; 94 ss <<"√"; 95 } 96 97 leftBracket = false; 98 if (rand()%4 == 0 && i != operateNum-1) //左括号 99 { 100 ss << '('; 101 leftBracket = true; 102 bracketNum++; 103 } 104 105 if (level == 2 && (rand()%4 == 0 || (!need && i == operateNum-1))) //操作数,或者sin(?)cos(?)tan(?) 106 { 107 need = true; 108 string fun; 109 int num; 110 //不能发生除0的情况,并且tan值要存在 111 do 112 { 113 fun = g_triFun[rand()%3]; 114 num = g_triNum[rand()%46]; 115 } 116 while ((c == '/' && ((fun == "sin(" && num%180 == 0) || (fun == "cos(" && (num+90)%180 == 0))) 117 ||(fun == "tan(" && (num+90)%180 == 0)); 118 ss << fun << num << "°" << ')'; 119 } 120 else 121 { 122 ss << 1+rand()%100; 123 } 124 125 if (level == 1 && rand()%8 == 0) //平方在操作数右边 126 { 127 need = true; 128 ss << "^2"; 129 } 130 if (rand()%4 == 0 && bracketNum > 0 && !leftBracket) //右括号 131 { 132 ss << ')'; 133 bracketNum--; 134 } 135 if (level == 1 && (rand()%8 == 0 || (!need && i == operateNum-1))) //平方有可能在括号右边 136 { 137 need = true; 138 ss << "^2"; 139 } 140 } 141 while (bracketNum--) 142 { 143 ss << ')'; 144 } 145 } 146 147 ss << '='; 148 if (g_questions.find(ss.str()) != g_questions.end()) 149 { 150 return GetOneQuestion(level); 151 } 152 else 153 { 154 g_questions.insert(ss.str()); 155 return ss.str(); 156 } 157 }
转换为Python代码时会有些不同,因为在个人项目中,只需要考虑出题,而在结对编程中还需要考虑出正确答案与干扰选项。生成题目的流程还是一样的,然后会用到Python的内置函数eval(),函数会把字符串当作正确的表达式来运行,可以借此来获得随机生成的表达式的值,当然前提是在调用eval()函数前要把表达式的根号换成sqrt函数,把圆周率π变成p,其中p=pi,把平方符号换成**2,这样才符合Python的正确表达式形式。通过修改该表达式中某一运算符,分别改成加减乘除,可以得到四个表达式,也就得到四个值,返回四个表达式与四个对应的值。这里没有决定哪个是正确选项,返回之后,程序再生成随机数来决定哪一个表达式是正确答案。
具体Python代码如下:
1 operators = ['+', '-', '*', '/'] 2 triFun = ['sin(', 'cos(', 'tan('] 3 triNum = ['0', 'π/6', 'π/4', 'π/3', 'π/2'] 4 5 def createOneQuestion(level): 6 question = '' 7 questions = [] 8 answers = [] 9 p = pi 10 bracketNum = 0 11 need = False 12 if level==0: 13 operateNum = randint(2,5) 14 else: 15 operateNum = randint(1,5) 16 17 if operateNum==1: 18 if level==1: 19 s = set() 20 while (len(s)<4): 21 if randint(0,7)==0: 22 s.add('√('+str(randint(1,100))+')²') 23 elif randint(0,6)==0: 24 s.add('√('+str(randint(1,100))+'²)') 25 elif randint(0,1)>0: 26 s.add('√('+str(randint(1,100))+')') 27 else: 28 s.add(str(randint(1,100))+'²') 29 questions = list(s) 30 for i in range(4): 31 answers.append(eval(questions[i].replace('√','sqrt').replace('²','**2'))) 32 else: 33 s = set() 34 while (len(s)<4): 35 fun = triFun[randint(0,2)] 36 num = triNum[randint(0,4)] 37 while (fun=='tan(' and num=='π/2'): 38 fun = triFun[randint(0,2)] 39 num = triNum[randint(0,4)] 40 s.add(fun+num+')') 41 questions = list(s) 42 for i in range(4): 43 answers.append(eval(questions[i].replace('π','p'))) 44 45 return questions, answers 46 else: 47 #第一个操作数 48 if randint(0,3)==0: 49 question += '(' 50 bracketNum += 1 51 if level==1 and randint(0,3)==0: 52 need = True 53 question += '√(' 54 bracketNum += 1 55 56 if level==2 and randint(0,3)==0: 57 need = True 58 fun = triFun[randint(0,2)] 59 num = triNum[randint(0,4)] 60 while (fun=='tan(' and num=='π/2'): 61 fun = triFun[randint(0,2)] 62 num = triNum[randint(0,4)] 63 question += fun+num+')' 64 else: 65 question += str(randint(1,100)) 66 67 if level==1 and randint(0,3)==0: 68 question += '²' 69 70 #生成后续运算符与操作数 71 indexs = [] 72 for i in range(0,operateNum): 73 c = operators[randint(0,3)] 74 question += c 75 indexs.append(len(question)-1) 76 77 if level==1 and (randint(0,3)==0 or (not need and i==operateNum-1 and randint(0,1)==0)): 78 need = True 79 question += '√(' 80 bracketNum += 1 81 82 if randint(0,3)==0 and i!=operateNum-1: 83 question += '(' 84 bracketNum += 1 85 86 if level==2 and (randint(0,3)==0 or (not need and i==operateNum-1)): 87 need = True 88 fun = triFun[randint(0,2)] 89 num = triNum[randint(0,4)] 90 while ((fun=='tan(' and num=='π/2') or (c=='/' and (((fun=='sin(' or fun=='tan(') and num=='0') or (fun=='cos(' and num=='π/2')))): 91 fun = triFun[randint(0,2)] 92 num = triNum[randint(0,4)] 93 question += fun+num+')' 94 else: 95 question += str(randint(1,100)) 96 97 if level==1 and randint(0,7)==0: 98 need = True 99 question += '²' 100 if randint(0,3)==0 and bracketNum>0: 101 question += ')' 102 bracketNum -= 1 103 if level==1 and (randint(0,7)==0 or (not need and i==operateNum-1)): 104 need = True 105 question += '²' 106 107 question += ')'*bracketNum 108 # print(indexs)#输出题目的运算符的位置 109 index = indexs[randint(0,operateNum-1)] 110 temp = operators.copy() 111 shuffle(temp) 112 for i in range(4): 113 question = question[:index]+temp[i]+question[index+1:] 114 try: 115 if level==0: 116 answers.append(eval(question)) 117 elif level==1: 118 answers.append(eval(question.replace('√','sqrt').replace('²','**2'))) 119 elif level==2: 120 answers.append(eval(question.replace('π','p'))) 121 except (ZeroDivisionError, ValueError): 122 answers.append('不存在') 123 questions.append(question) 124 if len(set(answers))<4: 125 # print('有相同答案') 126 # for i in range(4): 127 # print(questions[i], answers[i]) 128 return createOneQuestion(level) 129 return questions, answers
做题时会记录正确与否,做完题后会按照百分制输出分数。然后可以退出程序或者继续做题。
经验教训:
在开发图形用户界面的时候,先画出一个软件原型图会使得工作事半功倍,编程界面的时候一下子就完成了。但画完原型图还有一个工作要做,因为是分工合作,需要给出互相调用的接口,命名要一致。比如我做了登陆界面,登陆成功要跳转到选择界面,选择界面是队友在搞,这时候需要调用他写的类,类名要一致,这样当我们编好程后直接导入模块就可以进行测试了。
编写用户界面的时候,最好使用类来编写,这样类成员变量就相当于全局变量,类成员函数之间就可以直接使用类成员变量,而不需要global全局变量,也不需要考虑不同函数摆放的位置。一开始我们直接用函数来生成图形用户界面,但遇到了很多bug,换成类来编写逻辑就十分清晰明了。