湖大创新课程结对编程项目总结
1、简介
这次的博客针对的是在国庆期间我和索央同学的结对编程项目的总结,包括对结对项目的分析总结、结对项目对个人项目的代码复用情况和对结对项目的感想,并且分享自己在结对项目的过程中学到的东西。
2、项目分工
索央:对图形化界面进行设计和布局,负责主要的图形化界面代码编写
厉平安:对个人项目中的生成题目模块进行改写,并负责题目答案生成和注册模块、登录和修改密码的数据库模块
3、结对项目
结对项目是出一个图形化界面实现中小学生的数学学习系统,并且实现用户注册、登录以及修改密码等功能。
刚开始拿到题目还是很担心的,因为之前没有接触过类似手机短信验证码这类的编程,还有对我们所使用的语言python的图形化界面也并不是很了解,但是做着做着发现,问题一个个被解决,边做边学,边学边做,最后还是比较好地实现了这次的任务。
3.1、验证码系统
对于手机验证码的解决方法,我专门去申请了腾讯的短信服务,但是它太麻烦了,我就选择了榛子云短信服务,花了20购买了大约500条短信的短信包,总的来说,还是比较值当的,榛子云使用的SDK非常方便,我直接将其下载后放入文件夹后导入即可,其官网的SDK文档也非常清楚,发送短信的问题就解决了
def send_verification(phone_number, verification_code): """ 发送验证码 :param phone_number: 手机号 :param verification_code: 随机生成的六位数字验证码 :return:返回发送状态,发送成功返回0 """ client = zc.ZhenziSmsClient('https://sms_developer.zhenzikj.com', 'AppId', 'AppSecret') # AppId应用ID可在应用管理-我的应用下查看 # AppSecret秘钥可在应用管理-我的应用下查看 params = { 'number': phone_number, 'templateId': '6871', 'templateParams': [verification_code, '5']} return json.loads(client.send(params))['code']
这个就是我的发送验证码的函数,只要引入其的sdk包就能通过这样一段代码实现验证码的发送了,还是非常方便的。
3.2、用户管理系统
用户注册完成后,需要记录用户的id和密码,一开始我是用表格进行记录用户的,不过后来经同学启发,只要用轻量级的sqlite进行数据管理即可,sqlite3也是python自带的模块包,支持直接建库建表并且增删改查都比较容易实现
def user_login(user_id, user_pwd): """ 检查账号密码是否匹配 :param user_id: 用户id :param user_pwd: 用户密码 :return: 返回flag是否账号密码匹配 """ path = (os.getcwd() + os.path.sep + 'src' + os.path.sep + 'user_info\\users.db') conn = sqlite3.connect(path) cursor = conn.cursor() flag = True cursor.execute('''SELECT * FROM USER WHERE NAME = ? AND PASSWORD = ?''', (user_id, user_pwd)) if cursor.fetchone() is None: flag = False cursor.close() # 关闭cursor conn.commit() # 提交事务 conn.close() # 关闭连接 return flag
这是检查用户登录时账号或者密码是否匹配的代码
def user_new(user_id, user_pwd): """ 新建用户 :param user_id: 用户 ID :param user_pwd: 用户密码 :return:none """ path = (os.getcwd() + os.path.sep + 'src' + os.path.sep + 'user_info\\users.db') conn = sqlite3.connect(path) cursor = conn.cursor() # 不存在User新建User表 cursor.execute('''CREATE TABLE IF NOT EXISTS USER (NAME TEXT NOT NULL,PASSWORD TEXT NOT NULL);''') cursor.execute('SELECT * FROM USER WHERE NAME = ?', (user_id,)) if cursor.fetchone() is None: # 不存在记录就添加 cursor.execute('INSERT INTO USER VALUES (?,?);', (user_id, user_pwd)) else: # 存在则更新密码,针对已有用户但是重新注册的情况 cursor.execute('UPDATE USER SET PASSWORD = ? WHERE NAME = ?', (user_pwd, user_id)) cursor.close() # 关闭cursor conn.commit() # 提交事务 conn.close() # 关闭连接
这个是新建用户时所用到的代码
其实这些对于用户新建或者修改密码的代码,看起来重复度比较高,但还是有差别的,对于各种条件的判断还是不一样的
3.3、代码复用情况
对于这次的结对项目的题目模块来说,基本上是复用的我们在个人项目中的生成题目模块,为了简便运算,我做了一些适当的调整,比如限制括号数和特殊运算符的个数,这样对与运算来说也会变得比较容易,出题模块负责生成固定数量的题目,并且给出一个正确答案选项和4个备选项,其中有一个是正确答案,也就是返回三个元素的元组;对于答案生成模块来说,主要是利用了中缀表达式的思想,通过栈的数据结构实现对表达式列表的运算
def calculate(equation): """ 算式运算 :param equation: 算式的字符序列 :return: 返回最后的运算结果 """ number = Stack() sign = Stack() sign_dict = {'^2': 3, '√': 3, 'sin': 3, 'cos': 3, 'tan': 3, '*': 2, '/': 2, '+': 1, '-': 1, '(': 0} # 符号表按优先级从低到高安排 for item in equation: if item.isdigit(): # 数字直接入栈 number.push(float(item)) elif item == '(': # 左括号入栈 sign.push(item) elif item == ')': # 右括号则进行运算 top = sign.pop() while top != '(': if top in ['+', '-', '*', '/']: op1 = number.pop() op2 = number.pop() number.push(double_value(top, op2, op1)) top = sign.pop() elif top in ['^2', '√', 'sin', 'cos', 'tan']: op = number.pop() number.push(single_value(top, op)) top = sign.pop() else: # 运算符要把之前优先级大于其的都弹出来计算 while (not sign.is_empty() and sign_dict[sign.peek()] >= sign_dict[item]): top = sign.pop() if top in ['+', '-', '*', '/']: op1 = number.pop() op2 = number.pop() number.push(double_value(top, op2, op1)) elif top in ['^2', '√', 'sin', 'cos', 'tan']: if item in ['^2', '√', 'sin', 'cos', 'tan']: sign.push(top) break else: op = number.pop() number.push(single_value(top, op)) sign.push(item) while not sign.is_empty(): top = sign.pop() if top in ['+', '-', '*', '/']: op1 = number.pop() op2 = number.pop() number.push(double_value(top, op2, op1)) elif top in ['^2', '√', 'sin', 'cos', 'tan']: op = number.pop() number.push(single_value(top, op)) if int(number.peek()) == number.peek(): return int(number.pop()) return float('%.2f' % number.pop())
这一段是主要的运算代码,传入的是一个算式列表,返回的是答案,主要就是入栈出栈,优先级比较的思路来完成计算的,在初高中计算时还要考虑一些边界情况,加入判断语句即可。
3.4、图形化界面实现
图形化界面主要是由索央同学负责。我们主要用到的模块就是python自带的tkinter的图像化界面库,包括创建窗体,创建按钮、文本框、绑定响应等操作。我们实现每一个页面的方法就是为每一个页面创建自己的一个类,然后窗体不变,只在上面增改组件,为了能够实现比较好的交互性,我们设置了回车响应事件、显示密码以及在做题时可以自由选择题目的功能,加强了用户体验。生成的新窗口在桌面中心,可以在正中间看到窗体
self.window = parent_window
self.window.title('中小学数学学习系统') self.frame = tk.Frame(self.window) self.frame.place(x=0, y=0, width=800, height=500) self.frame.focus_set() self.bg_photo = tk.PhotoImage(file=PHOTO_PATH) self.canvas = tk.Canvas(self.frame, width=800, height=500, highlightthickness=0, borderwidth=0) self.canvas.place(x=0, y=0) self.canvas.create_image(0, 0, anchor='nw', image=self.bg_photo) self.canvas.create_text(400, 75, text='中小学数学学习系统', font=('微软雅黑', 40, 'bold'), fill='green') self.canvas.create_text(400, 150, text='By:厉平安 & 索央', font=('微软雅黑', 20)) self.tip_label = self.canvas.create_text( 400, 200, text='', font=('微软雅黑', 15, 'bold'), fill='red') self.canvas.create_text(300, 250, text='账号:', font=('微软雅黑', 20)) self.id_entry = tk.Entry(self.frame, font=('宋体', 20), relief='groove', width=15) self.id_entry.place(x=450, y=250, anchor=tk.CENTER) self.canvas.create_text(300, 300, text='密码:', font=('微软雅黑', 20)) self.pwd_entry = tk.Entry(self.frame, font=('宋体', 20), width=15, show='*') self.pwd_entry.place(x=450, y=300, anchor=tk.CENTER) self.show_button = tk.Button(self.frame, text="显示", font=('微软雅黑', 10), width=5, height=1, command=self.show_change, fg='black', bg='lightpink', activebackground='pink', activeforeground='black') self.show_button.place(x=585, y=300, anchor=tk.CENTER) sign_button = tk.Button(self.frame, text="注册", font=('微软雅黑', 20), width=10, height=1, command=self.sign_in, fg='black', bg='tan', activebackground='pink', activeforeground='black') sign_button.place(x=300, y=400, anchor=tk.CENTER) log_button = tk.Button(self.frame, text="登录", font=('微软雅黑', 20), width=10, height=1, command=self.log_in, fg='black', bg='springgreen', activebackground='pink', activeforeground='black') log_button.place(x=500, y=400, anchor=tk.CENTER) self.window.bind("<Return>", self.log_in) # 绑定响应 self.id_entry.focus_set() # 设置聚焦 self.window.mainloop()
这里就是登录界面的布局,在画布上,我们用了place布局,因为用pack和grid布局会导致不太可控制目标位置,会比较麻烦。
下面就是我们的设计界面:





4、结对编程项目感想与收获
这次的结对项目离个人项目过去没多久就开始了,总的来说,写代码的过程中遇到了不少问题,比如对还不是特别了解的图形化界面,需要边学习边做项目,同时还有对sqlite的一些语句以及对短信验证码模块的不了解,不过在慢慢地看教程和自己查资料中解决了,最后终于完成了项目。
两个人一起写项目是以前未有过的体验,因为平时都是一个人写代码,有些时候比如漏了括号等情况就发现不了,但是结对项目提供了让两人一起工作的环境,当一个人在写代码的时候,另一个人可以在旁边观察指出错误。对方的想法和意见也可以在一起工作的时候被自己捕获到,不断修改我们的方案和最后的呈现结果。
不过结对项目也不是没有缺点的,就是两个人在一起工作的时候会导致效率比较低,两个人的想法不能很好的统一起来,而且当换另一个人工作时,虽然代码结构是一起在看的,但还是会有不明白的地方,或者变量名等,都需要前一个人解释和提醒。
不过总得来说,结对编程项目给我们提供了一种新的写代码方式,也让我了解到真正做工程中,来自不同方的要求和想法是很不一样的,需要综合考虑,让步妥协,最后达成一个对各方来说都是可以接收的结果。
这次的两个编程项目虽然不难,但是挑战不小,希望我能把从这里吸收到的经验和教训运用在我的团队项目上。

浙公网安备 33010602011771号