最开始我们两个人分别写个人项目时,分别用的是Java和C++,但是在做这个带UI的升级版后,我坚定地摈弃了之前两个人所写的代码,选择用Python完全重写,原因有以下几点:

1. 之前两个人写的都不够好,在生成算式(尤其是括号匹配等方面)的过程中算法过于繁琐,而且有缺陷(我的只能最多生成一对括号,他的括号分布是固定搭配中的伪随机)。

2. 之前两人采用的算法导致生成算式后并不能很好的进行计算,而结对项目需要生成正确答案。

3. Java下的UI框架Swing过于陈旧,已不是一个相当受开发者欢迎的框架。C++下的Qt由于C++语法过于繁琐,会带来诸多不便。

4. Python有自带的eval函数,该函数能够进行简单的四则运算(但不能计算乘方、三角函数等),带来简便。

于是最后决定使用Python+Qt的搭配,且UI的设计在Qt Designer下进行。

① 核心代码部分

吸取了之前个人项目中因为采用面向过程的思想,结果导致整个算法过于繁琐冗杂,且牵涉到了相当复杂的字符串处理,无论是代码的简洁清晰程度还是程序的运行效率都不够好。

于是这次打算采用思路更加清晰、可塑性更高的面向对象的思想。

关于算式的生成与计算有两个类:

a. Item类,代表算式中的某一项。

主要包括三个成员:前缀,本体与后缀。

  前缀包含三角函数、左括号、平方根、负号

  后缀包括平方、右括号

  本体则是该一项的数值对应的字符串。

包含多个成员函数

  构造函数传入一个boolean值,默认为False。当且仅当其为True时会生成带有三角函数的项。

其余的例如插左括号、插右括号等操作就是直接对该个对象的前缀或后缀进行插入,较为简单,不一一介绍。

具体代码如下:

 1 class Item:
 2     def __init__(self, flag=False):  # when the flag is true, initialize an trigonometric item
 3         random = Random()
 4         if flag == True:
 5             func = ['sin', 'cos', 'tan']
 6             value = ['0°', '30°', '45°', '60°', '120°', '135°', '150°', '180°', '90°']
 7             choice = func[random.randint(0, 2)]
 8             if choice == 'tan':
 9                 self.curr = choice + value[random.randint(0, 7)]
10             else:
11                 self.curr = choice + value[random.randint(0, 8)]
12         else:
13             self.curr = str(random.randint(1, 100))
14
15     def __str__(self):
16         return self.prev + self.curr + self.next
17
18
19     def add_left_bracket(self):
20         self.prev = '(' + self.prev
21
22     def add_right_bracket(self):
23         self.next += ')'
24
25     def add_square(self):
26         random = Random()
27         length = len(self.next)
28         if length == 0:
29             self.next = '²'
30             return
31         pos = random.randint(0, length - 1)
32         self.next = self.next[0: pos + 1] + '²' + self.next[pos + 1:]
33
34
35     def add_sqrt(self):
36         random = Random()
37         length = len(self.prev)
38         if length == 0:
39             self.prev = '√'
40             return
41         pos = random.randint(0, length - 1)
42         self.prev = self.prev[0: pos + 1] + '√' + self.prev[pos + 1:]
43
44     curr = ''
45     prev = ''
46     next = ''

b. Exp类,代表一个算式表达式。

成员变量只有两个:

一个存有Item的列表,以及一个存储对应运算符的列表。

之所以将二者分开存储,也是为了进一步对象化整个过程,否则各项和运算符容易互相杂糅导致必须进行较为复杂的字符串处理操作。

成员函数包括以下几种:

为表达式添加Item的函数

添加括号的函数

添加乘方的函数

处理三角函数的函数

以字符串形式返回表达式的函数

返回运算结果的函数

随机返回一个运算符的函数(静态)

处理平方的函数(静态)

处理平方根的函数(静态)

具体代码如下(已省去较为复杂的函数的实现)

class Exp:def __init__(self):passdef append(self, item):self.items.append(item)if len(self.items) != 1: # the first item added
            self.op.append(Exp.get_op())def add_brackets(self):passdef add_power(self):  # add ² or √passdef __str__(self):  # return the str of the expressiontmp = ''for i in range(0, len(self.items)):if i == 0:tmp += self.items[i].__str__()else:tmp += self.op[i - 1] + self.items[i].__str__()return tmpdef handle_func(self):passdef get_answer(self):  # return the answerpassitems = []op = []@staticmethoddef get_op():ops = ['+', '-', '*', '/']random = Random()return ops[random.randint(0, 3)]@staticmethoddef handle_square(s):  # to compute the squarepass@staticmethoddef handle_sqrt(s):  # to compute the square root, similar to the function abovepass

整体的思路是

先判断难度:

若为高中,则生成带三角函数的各项加入表达式;否则不加。(用Item的构造函数是否传True来区分)

然后进行添加括号操作,再根据是否是小学题选择添加乘方运算或者不添加乘方运算。

获取运算结果时,先处理算式的三角函数(如果有),再处理算式中的乘方(如果有),最后将处理结果(没有任何三角函数以及乘方运算)的字符串传给eval函数计算结果。

② UI部分

用Qt Designer设计界面。然后设定好各个signal和slot,再编写各个slot的函数即可。

整体只需要两个界面,一个是主界面,一个是答题界面

该部分较为简单,并无特别的技术要求,故不细述。

③ 短信API接口部分

采用阿里云,注册后直接自己编写一个函数调用DEMO即可。

因为接口需要先安装阿里云的库才能用,所以我干脆设定成了每运行一次都安装一次库(用os.system函数)。

④ 中途遇见的问题

这样完全靠系统随机产生的算式,会存在无法运算的情况。例如负数开方,tan90°,或是0作了除数。

从而导致程序直接崩溃,因为无法运算。

解决方案:

1. 为避免生成tan90°,对随机范围进行限定:

    def __init__(self, flag=False):  # when the flag is true, initialize an trigonometric itemrandom = Random()if flag == True:func = ['sin', 'cos', 'tan']value = ['0°', '30°', '45°', '60°', '120°', '135°', '150°', '180°', '90°']choice = func[random.randint(0, 2)]if choice == 'tan':self.curr = choice + value[random.randint(0, 7)]else:self.curr = choice + value[random.randint(0, 8)]else:self.curr = str(random.randint(1, 100))

2. 为避免负数开根,在开根号前进行检查(捕捉异常):

  @staticmethoddef handle_sqrt(s):  # to compute the square root, similar to the function abovecnt = s.count('√')while cnt != 0:pos = s.find('√')i = pos + 1if s[i].isdigit():j = iwhile j < len(s) - 1 and (s[j + 1].isdigit() or s[j + 1] == '.'):j += 1tmp = ''try:tmp = str(round(math.pow(float(s[i: j + 1]), 0.5), 3))except Exception:print('\tException: negative square root')return ''s = s[: i - 1] + tmp + s[j + 1:]cnt -= 1else:j = iflag = 1while flag != 0 and j < len(s) - 1:j += 1if s[j] == ')':flag -= 1elif s[j] == '(':flag += 1tmp = ''try:tmp = str(round(math.pow(eval(s[i: j + 1]), 0.5), 3))except Exception:print('\tException: negative value or zero division in square root')return ''s = s[: i - 1] + tmp + s[j + 1:]cnt -= 1return s

3. 为避免除0,在最后一步计算时也捕捉异常。

    def get_answer(self):  # return the answer
        self.handle_func()print('\tafter handling the functions: ' + self.__str__())s = Exp.handle_sqrt(Exp.handle_square(self.__str__()))print('\tafter handling the powers: ' + s)if s != '':res = 0try:res = round(eval(s), 3)except ZeroDivisionError:res = 77777print('\tException: zero division')finally:return reselse:return 77777

对于无法计算的算式,请求返回其答案时会返回固定值77777。

当软件生成一个题目时,发现其答案为77777,直接跳过该题,并在控制台输出错误报告。

if res == 77777:self.curr -= 1print('-----ILLEGAL EXPRESSION, AVOIDED-----')self.set_problem()return

总结:

结对项目历时数天,因为之前自己就学过Qt,所以UI部分实现难度不大,主要难度还是在于生成算式并计算答案中的算法中,以及申请阿里云的API使用权也是一个较为费神的东西。

通过这次项目,也算是进一步体味到了OOP的重要性与优越性,可以让很复杂的一个程序结构变得非常清晰易懂,一有错误也能立刻找出来。

转载于:https://www.cnblogs.com/cadenza/p/9751591.html

结对编程小项目实现 Python+PyQt5+OOP相关推荐

  1. linux系统编程 小项目,linux系统编程小项目.doc

    linux系统编程小项目.doc 一.项目概述简单智能远程监控功能服务器端1.服务器端利用随机数模拟向串口读取传感数据,需要模拟的传感数据要求有温度.湿度.光照.室内噪音度等等.2.服务器要求在数据保 ...

  2. pyqt5 getsavefilename 默认文件名_经Jerry编程小课堂之python如何安装PyQt5和QT Designer...

    小伙伴们大家好,欢迎来到经Jerry编程小课堂,有没有很想我呢?嘿嘿嘿,我也很想你们啊!想死你们了,亲!嘤嘤嘤! 哈哈,话不多说,转入正题,今天我们聊一聊如何安装python的图形界面模块PyQt5以 ...

  3. python小项目案例-python简单项目实例

    语言多元化是PayPal编程文化中一个重要的组成部分.在C++和Java长期流行的同时,更多的团队选择了Jva和Scala.同时,Braintree的收购也引入了一个久经世故的Ruby社区.Pytho ...

  4. python开发web运维工具_【实战小项目】python开发自动化运维工具--批量操作主机...

    有很多开源自动化运维工具都很好用如ansible/salt stack等,完全不用重复造轮子.只不过,很多运维同学学习Python之后,苦于没小项目训练,本篇演示用Python写一个批量操作主机的工具 ...

  5. python小项目案例-Python小项目:快速开发出一个简单的学生管理系统

    本文根据实际项目中的一部分api 设计抽象出来,实例化成一个简单小例子,暂且叫作「学生管理系统」. 这个系统主要完成下面增删改查的功能: 包括: 学校信息的管理 教师信息的管理 学生信息的管理 根据A ...

  6. python switch_从邮箱验证小项目说python字符串判断与if判断那些事儿

    好了,接下来依旧是我不务正业的蹭课做的小项目.string.find这个函数真的好用啊!另外python没有switch结构真的难受.... IPO分析 代码 ad 首先要注意的是string.fin ...

  7. 一个用python做的完整项目_我从一个小项目学习Python编程的全过程(二)

    在(一)中的时候我们分析了如何获取所有人无忧币的统计情况,接下来开始学着写代码了: 首先第一步我们得把第一个页面的源代码:#coding:utf-8 import urllib url = 'http ...

  8. python编程小游戏-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  9. python编程小游戏代码-Python小游戏之300行代码实现俄罗斯方块

    前言 本文代码基于 python3.6 和 pygame1.9.4. 俄罗斯方块是儿时最经典的游戏之一,刚开始接触 pygame 的时候就想写一个俄罗斯方块.但是想到旋转,停靠,消除等操作,感觉好像很 ...

最新文章

  1. oracle数据库风险链接,数据库的风险主要来源
  2. 搭建LAMP下的ucenter家园博客
  3. python面试-python简单面试题
  4. DL之LSTM:基于tensorflow框架利用LSTM算法对气温数据集训练并回归预测
  5. golang管道channel的遍历和关闭:应该使用for...range来遍历
  6. jpa批量保存,事务没提交_在事务外自动保存托管JPA实体
  7. JQUERY解析XML IE8的兼容问题
  8. Mpi与Cuda混合编程(Makefile)
  9. 西湖区政府门户网站项目签约西部动力We7网站群系统
  10. 敏捷开发一千零一夜读书笔记之敏捷初探
  11. 从44.556677想到的
  12. Dev ChartControl 显示设置百分比
  13. 自学-Linux-老男孩Linux77期-day5
  14. java JDK安装及环境变量配置
  15. 网络基础:动态路由OSPF配置
  16. docker装LibreELEC_Linux和macOS系统安装LibreELEC的方法
  17. ps基本操作以及常用快捷键
  18. 笔记本电脑上insert scrlk 键和 insert键(插入和改写格式转换)
  19. 【MyBatis 面试题】
  20. 如何从零开始创建React项目

热门文章

  1. ADO.NET数据库操作------SqlDataReader和SqlDataAdapter 区别
  2. python magic文档
  3. BUUCTF-WEB:[极客大挑战 2019]Havefun 1
  4. 杂项题的基本解题思路——1、文件操作与隐写
  5. 打印所有低于平均分的分数(数组)
  6. 鸡兔同笼 n为总数,m为总腿数,a为鸡,b为兔
  7. SpringBoot解耦的扩展机制 Spring Factories介绍及使用
  8. Maven学习笔记(二)
  9. GAN——UNIT简单梳理
  10. Alibaba-AndFix Bug热修复框架原理及源码解析