摘要:
本篇内容是基于上一次个人项目,两人共同完成的结对项目,关于项目要求详见结对项目需求文档。
总体分析:
本出题软件项目基于python,采用Tkinter模块实现GUI框架,每一个界面都采用一个Class实现(具体界面见代码注释);切换界面时,摧毁当前界面画布,并调用其它Class生成新的界面;该项目中的验证码采用榛子云实现,仅供测试;用户信息的保存采用了pickle模块读取或保存;函数answer_item_get(item_product)用于题目选项的生成,正确答案随机分配选项。
具体分析:
首先从出题功能开始分析,由于个人项目笔者与同伴都采用的是c++实现,故本项目对于个人项目的复用主要是对于思路的复用。
answer_item_get()函数是用于生成题目选项的函数,首先他会计算该题目的答案然后随机的将其分配到A,B,C,D内的某个选项中,剩下的三个选项也会被分配与答案不相同的值其输入为生成的题目,输出的是一个选项的列表。
1 def answer_item_get(item_product): # item_product为生成的题目 2 l = len(item_product) # 题目的长度 3 z = 0 # z和i都是在生成题目时用到的下标 4 i = 0 5 ans_str1 = "" # 生成的答案运算表达式 6 # 答案运算的逻辑就是整体先将三角函数,根号,平方先进行运算,运算完后得到的一个表达式,可以直接用python的eval函数算出值 7 while i < l: 8 r = 0 # 用于计算三角函数时,指向三角符号的第一个字符的下标,如sin的s 9 if item_product[i] == '√': 10 z = i + 1 # z和i都为根号后第一个数字的位置 11 i = i + 1 12 # 对根号后的字符进行遍历,直到访问的字符不是数字,然后对前面的数字形成的数进行开根号运算 13 # 随后将其保存到ans_str1中 14 while i < l: 15 if item_product[i] >= '0' and item_product[i] <= '9': 16 i = i + 1 # 这里不是数字时,循环结束,此时i的位置指向一个非数字字符 17 # item_product[z:i]的切片正好为根号后的整个数,接下来的三角函数和平方运算也是如此 18 else: 19 break 20 num = int(item_product[z:i]) 21 one_ans = str(num ** 0.5) 22 ans_str1 += one_ans 23 # 对于平方的运算,由于一个数最大为100,故每次访问一个字符时,先判断其 24 # 是否为数字,然后判断其后1,2,3个位置是否有平方(注意数组的长度,防止其溢出) 25 # 随后和根号一样,对其进行运算,将结果保存到ans_str1中 26 elif (item_product[i] >= '0' and item_product[i] <= '9') and ( 27 (i + 1 < l and item_product[i + 1] == '^') or (i + 2 < l and item_product[i + 2] == '^') or 28 (i + 3 < l and item_product[i + 3] == '^')): 29 z = i 30 while i < l: 31 if item_product[i] >= '0' and item_product[i] <= '9': 32 i = i + 1 33 else: 34 break 35 num = int(item_product[z:i]) 36 one_ans = str(num ** 2) 37 ans_str1 += one_ans 38 i = i + 1 39 # 计算三角函数和计算根号类似,这里用到了一个sy模块,其可以计算三角函数 40 elif item_product[i:i + 3] == "sin" or item_product[i:i + 3] == "cos" or item_product[i:i + 3] == "tan": 41 r = i 42 z = i + 3 # z和i此时都为三角符号后第一个数字的位置 43 i = i + 3 44 while i < l: 45 if item_product[i] >= '0' and item_product[i] <= '9': 46 i = i + 1 47 else: 48 break 49 angle_str = item_product[r:z] # angle_str保存的是三角符号,sin,cos,tan 50 one_ans = "" # 计算后的答案 51 num_str = int(item_product[z:i]) 52 if angle_str == "sin": 53 one_ans = sy.sin(num_str / 180 * np.pi) 54 elif angle_str == "cos": 55 one_ans = sy.cos(num_str / 180 * np.pi) 56 elif angle_str == "tan": 57 one_ans = sy.tan(num_str / 180 * np.pi) 58 one_ans_str = str(one_ans) 59 ans_str1 += one_ans_str 60 else: 61 ##若符号非三角函数,平方,根号,如*,+,-或不带需要计算符号的数字,就将其直接添加到字符串内 62 ans_str1 += item_product[i] 63 i = i + 1 64 ans_position = random.randint(0, 3) # 正确答案的位置 65 ans_product = [] 66 answer_str = "" 67 # 将选项添加到列表内,函数返回该列表。 68 if ans_position == 0: 69 answer_str = 'A.' # mystr保存着某选项 70 answer_str += str(eval(ans_str1)) 71 # 当ans_position=0时,正确答案就为A,将A和正确答案构成的字符串 72 # 添加到列表里面,随后在将其他选项和错误答案也添加进列表, 73 # 这里的错误答案选择的是正确答案+1,+2,+3 74 ans_product.append(answer_str) 75 answer_str = 'B.' 76 answer_str += str(eval(ans_str1) + 1) 77 ans_product.append(answer_str) 78 answer_str = 'C.' 79 answer_str += str(eval(ans_str1) + 2) 80 ans_product.append(answer_str) 81 answer_str = 'D.' 82 answer_str += str(eval(ans_str1) + 3) 83 ans_product.append(answer_str) 84 if ans_position == 1: 85 answer_str = 'A.' 86 answer_str += str(eval(ans_str1) + 1) 87 ans_product.append(answer_str) 88 answer_str = 'B.' 89 answer_str += str(eval(ans_str1)) 90 ans_product.append(answer_str) 91 answer_str = 'C.' 92 answer_str += str(eval(ans_str1) + 2) 93 ans_product.append(answer_str) 94 answer_str = 'D.' 95 answer_str += str(eval(ans_str1) + 3) 96 ans_product.append(answer_str) 97 if ans_position == 2: 98 answer_str = 'A.' 99 answer_str += str(eval(ans_str1) + 1) 100 ans_product.append(answer_str) 101 answer_str = 'B.' 102 answer_str += str(eval(ans_str1) + 2) 103 ans_product.append(answer_str) 104 answer_str = 'C.' 105 answer_str += str(eval(ans_str1)) 106 ans_product.append(answer_str) 107 answer_str = 'D.' 108 answer_str += str(eval(ans_str1) + 3) 109 ans_product.append(answer_str) 110 if ans_position == 3: 111 answer_str = 'A.' 112 answer_str += str(eval(ans_str1) + 1) 113 ans_product.append(answer_str) 114 answer_str = 'B.' 115 answer_str += str(eval(ans_str1) + 2) 116 ans_product.append(answer_str) 117 answer_str = 'C.' 118 answer_str += str(eval(ans_str1) + 3) 119 ans_product.append(answer_str) 120 answer_str = 'D.' 121 answer_str += str(eval(ans_str1)) 122 ans_product.append(answer_str) 123 return ans_product
answer_get函数是用于生成答案的函数,输入题目,返回其答案逻辑与生成选项的函数的生成答案部分完全相同,只是返回的是一个正确答案的字符串。
1 def answer_get(s): 2 l = len(s) 3 z = 0 4 i = 0 5 ans_str1 = "" 6 while i < l: 7 r = 0 8 if s[i] == '√': 9 z = i + 1 10 i = i + 1 11 while i < l: 12 if s[i] >= '0' and s[i] <= '9': 13 i = i + 1 14 else: 15 break 16 num = int(s[z:i]) 17 one_ans = str(num ** 0.5) 18 ans_str1 += one_ans 19 elif (s[i] >= '0' and s[i] <= '9') and ( 20 (i + 1 < l and s[i + 1] == '^') or (i + 2 < l and s[i + 2] == '^') or 21 (i + 3 < l and s[i + 3] == '^')): 22 z = i 23 while i < l: 24 if s[i] >= '0' and s[i] <= '9': 25 i = i + 1 26 else: 27 break 28 num = int(s[z:i]) 29 one_ans = str(num ** 2) 30 ans_str1 += one_ans 31 i = i + 1 32 elif s[i:i + 3] == "sin" or s[i:i + 3] == "cos" or s[i:i + 3] == "tan": 33 r = i 34 z = i + 3 35 i = i + 3 36 while i < l: 37 if s[i] >= '0' and s[i] <= '9': 38 i = i + 1 39 else: 40 break 41 angle_str = s[r:z] 42 one_ans = "" 43 num_str = int(s[z:i]) 44 if angle_str == "sin": 45 one_ans = sy.sin(num_str / 180 * np.pi) 46 elif angle_str == "cos": 47 one_ans = sy.cos(num_str / 180 * np.pi) 48 elif angle_str == "tan": 49 one_ans = sy.tan(num_str / 180 * np.pi) 50 one_ans_str = str(one_ans) 51 ans_str1 += one_ans_str 52 else: 53 ans_str1 += s[i] 54 i = i + 1 55 return str(eval(ans_str1))
关于其他功能,验证码采用了榛子云服务,在网上找到对应的实现代码和下载了相应的jdk后实现。对于用户信息的保存,则是采用python的pickle模块来进行保存,将用户名和密码,电话号码和密码作为组合保存在两个文件中,需要修改密码时,找到该匹配,修改键值即用户名即可。其余入总体分析所述,总体框架采用的是tkinter模块。
# 获取验证码 global code code = '' # 判断电话号码的格式 if tel == '' or len_tel != 11 or is_number(tel) == False: tkinter.messagebox.showerror('Error', '请输入正确的电话号码') # 判断该电话号码是否注册过 elif tel in exist_usr_info2: tkinter.messagebox.showerror('Error', '改电话已被注册') else: tkinter.messagebox.showinfo('tip', '验证码已发送') for num in range(1, 5): code = code + str(random.randint(0, 9)) print(code) # 将AppId和AppSecret复制粘贴过来 client = smsclient.ZhenziSmsClient('https://sms_developer.zhenzikj.com', '参数1', '参数2') # 第一个参数为发送号码,第二个参数为发送的验证码内容 params = {} params['number'] = tel params['templateId'] = '6944' params['templateParams'] = [code, '15分钟'] print(client.send(params))
给出榛子云Python的JDK下载地址:http://smsow.zhenzikj.com/doc/sdk.html
总结:
本次项目笔者主要是负责出题外功能的设计,在一开始单单通过调用函数去实现,发现实现困难后采用类来进行实现,设定一个界面写在一个类中,登录界面,出题界面,修改密码界面等等。切换界是通过摧毁当前的界面的画布,并调用其它类形成一个新的画布来实现,而关于用户信息的储存,上述分析中已经提到是采用pickle模块来实现的,当pickle文件被删除其中的信息也一并被删除,并且创建该文件时必须预先写入一条信息,否则当文件是空时,读取会报错。一开始考虑过采用数据库实现,但是当时对于数据库的使用还并不熟悉。本次项目花费时间最多的还是Bug的查找与改进,这个过程是痛苦的,每次编写完部分功能的代码都会出现一堆bug,试错,修改,试错,修改,中后期基本就是这两步的循环,然后就是寻找他人来使用,再一步步修改界面,做到简洁实用的样子,能让人一目了然。本次项目不仅学会了许多新鲜的内容,同时也初步体会到了软件开发的过程。

浙公网安备 33010602011771号