0.小组成员

赵坤2017282110261
黄亦薇201728210260

1.项目Github地址

 https://github.com/zkself/homework3
PS:建议使用chrome浏览器

2.预估耗时

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20  
· Estimate · 估计这个任务需要多少时间   10   
Development 开发 500   
· Analysis · 需求分析 (包括学习新技术)   30   
· Design Spec · 生成设计文档   30   
· Design Review · 设计复审 (和同事审核设计文档)   0   
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)   10   
· Design · 具体设计   30   
· Coding · 具体编码   400  
· Code Review · 代码复审   60   
· Test · 测试(自我测试,修改代码,提交修改)   60   
Reporting 报告 120   
· Test Report · 测试报告   150   
· Size Measurement · 计算工作量   10   
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划   20  
合计   640  

3.解题思路

  拿到这个问题的时候,我们俩面临的 第一个问题 就是,她没接触过GUI编程,对于GUI编程思想缺乏认识,所以我们决定页面布局让她来写。选择GUI库的时候我们考虑过tkinter、pyqt、wxpython。最后选择了wxpython因为它文档全面并且demo较多。然后我们考虑的 第二个问题 就是,页面上会有很多重复的控件,作用是显示题目、以及让用户输入答案、判定结果。相当于每道题都有3个控件,所有题目都是这样的通过3个控件组成的。所以我就在考虑能不能动态生成控件呢?比如说5道题那么页面上就有1至少5个控件这样会造成代码不简洁,然后就找到了python提供的locals函数,这种动态生成控件(变量)的思想是我在js中学到的,python中正好也提供这种方法。我们面临的 第三个问题 就是计时器,计时器我发现wxpython没有现成的控件,所以必须得自己写,而我们用了一个笨办法就是用while循环的方式计时,时间间隔靠sleep函数,虽然比较笨但是实际上来说,pyhon执行每条代码的时间是很快很快的,基本上是us级别的所以循环一次的时间可以忽略不计。第四个问题 就是如何存储用户的记录,考虑到存储的信息很少且不是那么重要,如果用数据库来存储会增加代码的复杂性,我们认为没有这个必要,最后采取的方式是直接以txt方式存储在用户当前打开的目录下即可。第五个问题 多语言,这也是中期汇报把很多同学卡住的地方。说实话以前确实没接触过,最开始我们也是想实在不行就是用if-else来弄吧,但是我的伙伴说,肯定有聪明的办法,不然那么多大公司在开发多语言软件的时候都是这样if-else的?然后我们就搜了一下资料,发现python有一个模块是专门解决多语言(i18n)问题的,然后这个问题才解决掉。总的来说,这次开发我们遇到的主要是这五个问题。

4.设计实现过程

  针对上面遇到的五个重要问题来详细说明一下实现过程。

4.1 页面布局

  首先我们知道,一个GUI程序用户体验取决于布局是否合理、美观。而GUI编程本身不是python的强项,并且由于python跨平台的特性,wxpython这个库主要的优点就是能解决跨平台GUI编程问题,其本身无论是开发工具还是API设计都无法和windows平台下的GUI编程语言所媲美。换句话说就是原生界面不好看。它的编程逻辑是,首先在窗体上添加一个frame空间,然后再选择layout布局。且定位问题只能通过开发者自己去尝试,并不能做到c#那样在任何位置拖放控件。说实话这个布局问题确实让我们头疼了一段时间,因为layout的选择直接决定了布局的方式、最后我们选择了最灵活的layout————GirdBagSizer,这种布局可以随意在一行、一列放置任意控件。wxpython的布局逻辑是把frame分成行和列来看的,最后我们调出了比较简明的布局。

4.2 重复控件

  这个问题就是解决代码的简洁性。因为,试想一下我们的页面最重要的部件就是一个题目(Label)、提供用户输入(Text)、显示判断结果(Label)由这三种控件组成。如果页面上一次显示5个题目就是15个控件,如果10题目就是30个控件,且如果手写这么多不利于后期的版本升级,意思就是我要从显示5个题改成显示10个题。所以我们没有选择最笨的办法而是借鉴了Js里面的动态生成变量的方式,用for循环去动态生成变量(控件),这里就不得不说脚本语言有脚本语言的好处,如果换成java/c++这种语言这个问题无解。还有这种方式,解决了变量名可以用变量拼接,比如题目一的label就是lanbel1,题目二的label就是label2 。python提供了一个内置的方法locals来以以字典的方式存储这种动态生成的变量,然后我们访问这些变量的时候直接个根据键值对的形式就可以取到了。还有就是事件绑定的问题,由于我们设定的逻辑是用户输入完之后按回车直接出发textbox的回车事件,所以这些变量都是绑定的同一个事件,就是说多个事件源一个事件处理函数。

4.3 计时器

  这里又要感谢wxpython了,它里面没有计时的控件,所以没办法只能自己来写了。我们的逻辑很简单,一个while循环里面有个变量每次循环自加1,然后判断是否达到60,如果达到60,分钟变量加1,此变量归0,继续判断,如果分钟变量达到60则小时变量加1,分钟、秒变量归0。然后sleep1秒。虽然这个办法不好,但是我测了一下,每条语句的执行时间是us级别的,所以精度基本不怎么受影响。别的方法肯定有,但是我们没想到。只能用这个折中的办法来代替了。

4.4 用户记录存取

  前面分析了我们为什么用存取txt这种方式来解决这个问题。这里我讲一下我们的逻辑,首先进入程序后判断本目录是否有record.txt这个文件,如果有则读取里面的数据显示在页面上,如果没有则新建一个record.txt。当用户选择关闭程序的时候把本次的统计结果保存到文件中。这样如果用户不小心删掉了文件也不会出现bug问题,无非就是记录丢失了。

4.5 多语言

  利用Python提供的gettext模块可以解决这个问题。首先我们把需要显示不同语言的文字用_()这种形式做上记号,然后在python目录下的TOOls文件夹下找到i18n文件夹,cmd运行里面的pygettext.py文件会生成一个message.pot文件,用 非记事本 编辑器打开,使用非记事本的原因是win中文版系统默认编码是gb2312,记事本也是,并且改不了。如果你用记事本打开那么不管你最后保存成什么编码都是gb2312。我用的sublime text打开并编辑的。编辑的内容就是以msgid 和msgstr的这种形式来把你需要翻译的字符串,手动翻译成对应的语言,例如 msgid "hello world" ,msgstr "你好世界"。然后保存成.po文件,接着运行此目录下另外一个msgfmt.py文件用刚才保存的文件再生成一个.mo文件。然后把这两个文件移动到项目目录下即可。这样程序运行的时候就能按照你手动翻译的结果把_()标记的字符串转换成对应语言的翻译了。

4.6 程序操作流程图

5.代码说明

页面布局以及控件循环生成

wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition, size=wx.Size(600, 500), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize)gbSizer1 = wx.GridBagSizer(10, 10)
gbSizer1.SetFlexibleDirection(wx.BOTH)
gbSizer1.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)# 生成控件
self.btnStart = wx.Button(self, wx.ID_ANY, _(u"开始答题"))
gbSizer1.Add(self.btnStart, span=(1, 1), pos=(1, 6))self.labTime = wx.StaticText(self, wx.ID_ANY, _(u"时间:"))
gbSizer1.Add(self.labTime, span=(1, 1), pos=(2, 0), flag=wx.EXPAND, border=3)self.labTime1 = wx.StaticText(self, wx.ID_ANY, u"0:0:0")
gbSizer1.Add(self.labTime1, span=(1, 1),pos=(2, 1), flag=wx.EXPAND, border=3)
self.labRatio = wx.StaticText(self, wx.ID_ANY, u"")
gbSizer1.Add(self.labRatio, span=(1, 1),pos=(2, 3), flag=wx.EXPAND, border=3)
for i in range(1, 6):#循环生成控件self.creatvar['self.labQues' +str(i)] = wx.StaticText(self, wx.ID_ANY, u"")gbSizer1.Add(self.creatvar['self.labQues' + str(i)], span=(1, 5),pos=(i + 2, 0), flag=wx.ALIGN_CENTER_VERTICAL, border=3)self.creatvar['self.texAns' +str(i)] = wx.TextCtrl(self, i - 1, wx.EmptyString)gbSizer1.Add(self.creatvar['self.texAns' + str(i)],pos=(i + 2, 6), flag=wx.ALIGN_CENTER_VERTICAL)self.creatvar['self.texAns' + str(i)].Disable()self.creatvar['self.labCor' +str(i)] = wx.StaticText(self, i + 4, u"")gbSizer1.Add(self.creatvar['self.labCor' + str(i)],pos=(i + 2, 7), flag=wx.ALIGN_CENTER_VERTICAL)self.btnNext = wx.Button(self, wx.ID_ANY, _(u"再来五题"))
gbSizer1.Add(self.btnNext, span=(1, 1), pos=(9, 3))self.btnPause = wx.Button(self, wx.ID_ANY, _(u"暂停"))
gbSizer1.Add(self.btnPause, span=(1, 1), pos=(9, 6))self.btnEnd = wx.Button(self, wx.ID_ANY, _(u"结束答题"))
gbSizer1.Add(self.btnEnd, span=(1, 1), pos=(9, 8))
#################################################################
self.SetSizer(gbSizer1)
self.Layout()

事件绑定

self.btnStart.Bind(wx.EVT_BUTTON, self.btnStartOnButtonClick)
for i in range(1, 6):#动态控件绑定事件self.creatvar['self.texAns' +str(i)].Bind(wx.EVT_TEXT_ENTER, self.texAnsOnTextEnter)
self.btnNext.Bind(wx.EVT_BUTTON, self.btnNextOnButtonClick)
self.btnPause.Bind(wx.EVT_BUTTON, self.btnPauseOnButtonClick)
self.btnEnd.Bind(wx.EVT_BUTTON, self.btnEndOnButtonClick)

事件处理函数

def btnStartOnButtonClick(self, event):for i in range(1, 6):self.creatvar['self.texAns' + str(i)].Enable()tmp = initFix()self.creatvar['self.labQues' +str(i)].SetLabel("".join(printFix(tmp)))PostfixExp = changeToPostfix(tmp)self.res.append(PostfixExp)self.eve.set()t = threading.Thread(target=self.stopwatch, name='timer')t.start()self.btnStart.Disable()def texAnsOnTextEnter(self, event):tex = event.GetEventObject()value = tex.GetValue()index = tex.GetId()cor = CalculatePostfix(self.res[index])if value == str(cor):self.creatvar['self.labCor' + str(index + 1)].SetLabel(_(u'正确'))self.ratio[0] += 1self.ratio[1] += 1self.labRatio.SetLabel(_(u'正确|总数') + ':' + str(self.ratio[0]) + '|' + str(self.ratio[1]))else:self.creatvar['self.labCor' +str(index + 1)].SetLabel(_(u'错误,正确答案是') + str(cor))self.ratio[1] += 1self.labRatio.SetLabel(_(u'正确|总数') + ':' + str(self.ratio[0]) + '|' + str(self.ratio[1]))tex.Disable()def btnNextOnButtonClick(self, event):self.res = []for i in range(1, 6):tmp = initFix()self.creatvar['self.labQues' +str(i)].SetLabel("".join(printFix(tmp)))PostfixExp = changeToPostfix(tmp)self.res.append(PostfixExp)self.creatvar['self.texAns' + str(i)].Enable()self.creatvar['self.labCor' + str(i)].SetLabel('')self.creatvar['self.texAns' + str(i)].SetValue('')def btnPauseOnButtonClick(self, event):if self.btnPause.GetLabel() == _(u'暂停'):self.eve.clear()self.btnPause.SetLabel(_(u'开始'))else:self.eve.set()self.btnPause.SetLabel(_(u'暂停'))def btnEndOnButtonClick(self, event):self.eve.clear()writeFile(regularizeData(self.ratio))dlg = wx.MessageDialog(None, _(u"您的记录已保存"), _(u"感谢使用"),wx.OK | wx.ICON_QUESTION)if dlg.ShowModal() == wx.ID_OK:self.Close(True)dlg.Destroy()

计时器函数

def stopwatch(self):  # 计时器second = 0minute = 0hour = 0while 1:if not self.eve.isSet():#判断eve变量是否为假continuesecond += 1time.sleep(1)if second == 60:minute += 1second = 0if minute == 60:hour += 1minute = 0second = 0ntime = "%d:%d:%d" % (hour, minute, second)self.labTime1.SetLabel(ntime)

用户记录读写

# 初始化记录
content = readFile()
if content:self.labRatio.SetLabel(_(u'正确|总数') + ': ' + content)
else:self.labRatio.SetLabel(_(u'当前无记录'))
#写用户记录
def btnEndOnButtonClick(self, event):self.eve.clear()writeFile(regularizeData(self.ratio))dlg = wx.MessageDialog(None, _(u"您的记录已保存"), _(u"感谢使用"),wx.OK | wx.ICON_QUESTION)if dlg.ShowModal() == wx.ID_OK:self.Close(True)dlg.Destroy()
def regularizeData(ratio):#把数据变成想要的格式recoard = str(ratio[0]) + '|' + str(ratio[1])return recoarddef readFile():#读文件,如果没有文件则生成新的cupath = os.getcwd()fpath = cupath + '/recoard.txt'if os.path.exists(fpath):f = open(fpath)content = f.read()f.close()return contentelse:f = open(fpath, 'w')f.close()return 0def writeFile(content):#写文件cupath = os.getcwd()fpath = cupath + '/recoard.txt'f = open(fpath, 'w')f.write(content)f.close()

多语言

gettext.install('lang', './locale/', unicode=False)
choices = ['简体中文', '繁體中文', 'English']
dialog = wx.SingleChoiceDialog(None, "语言选择", "请选择语言", choices)
if dialog.ShowModal() == wx.ID_OK:langu = dialog.GetStringSelection()if langu == u'English':gettext.translation('lang', './locale/',languages=['en_US']).install(True)if langu == u'繁體中文':gettext.translation('lang', './locale/',languages=['zh_HK']).install(True)

6.运行结果

6.1 系统界面

首先选择语言

然后进入系统界面(简体中文)

繁体中文

英文

6.2 做题、计时、保存记录

做题

暂定做题

更换题目

结束答题,存储记录

7.合作情况

7.1赵坤

  完善了上次作业的随机运算符个数问题、解决了动态生成重复控件功能、编写了基础的文件读写模块、解决了多语言问题、编写了测试用例
  我在任务过程中比较急躁多亏了黄亦薇能帮我及时整理思路分析问题。女生做事往往比较仔细,有时候代码里的问题她能看出来而我却需要多次测试才能找到Bug在哪。

7.2黄亦薇

  构建了基本的界面布局、编写了计时器功能、编写了显示题目处理用户输入并判断结果功能、使用coverage功能测试代码分支覆盖率
  我的代码能力不强,赵坤也交给我了很多编码技巧,多语言问题本来他都要放弃了,然后我鼓励他一起继续找找资料然后才解决的了这个问题。

8.项目小结

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20  10
· Estimate · 估计这个任务需要多少时间   10  10 
Development 开发 500   600
· Analysis · 需求分析 (包括学习新技术)   30  30 
· Design Spec · 生成设计文档   30   30
· Design Review · 设计复审 (和同事审核设计文档)   0 
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)   10  10 
· Design · 具体设计   30  40 
· Coding · 具体编码   400  500
· Code Review · 代码复审   60  70 
· Test · 测试(自我测试,修改代码,提交修改)   60  60 
Reporting 报告 120  150 
· Test Report · 测试报告   150   180
· Size Measurement · 计算工作量   10   20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划   20 30 
合计   640 770 

8.1赵坤

  我觉得这次我主要掌握了多语言问题的处理方法,以及测试用例到底怎么编写。两个人结对编程最大的好处就是可以交换思路,有时候一个人容易钻牛角尖,并且找bug的时候别人看你的代码和自己看是不一样的,旁观者清当局者迷。但是我们还是没解决括号的问题……因为我们当时不想大改程序结构,因为后面的GUI也是挺需要时间来搞定的。

8.2黄亦薇

  这次是赵坤一直在督促我快点写,现在看来要不是他的督促时间根本不够。他对于时间把控方面还是需要我学习的。这次我学到了好多,GUI编程思想、多线程问题等。可以说大的方向都是他把控的我的工作主要是给他提出建议以及帮他分担任务。感谢赵坤对我的帮助,我会继续努力的。

9.结对照片


转载于:https://www.cnblogs.com/zkself/p/7650987.html

高级软件工程第三次作业 赵坤黄亦薇相关推荐

  1. 高级软件工程第三次作业:数独游戏界面功能

    此次作业基本引用了,第二次作业中使用的code.在保留第二次作业的功能基础上添加了两个文件,实现了界面游戏的功能. 完成数独游戏界面功能,代码能实现以下题目要求: 生成任意数量的数独题目并将数独棋局依 ...

  2. 2019软件工程第三次作业

    2019软件工程第三次作业 数独游戏 戳这里进入Github项目 第一眼看到要做数独的时候,脑海里的第一反应就是用深搜.现在好了,确定了算法,接下来就是要去实现它,可是对于将近半年没写过深搜的我要写一 ...

  3. 软件工程第三次作业——软件质量保证鄙见

    阅读教材第14章及课后参考文献  写一篇关于软件质量保障的博文 参考文献: 两种不同的声音(1)https://coolshell.cn/articles/6994.html (2)https://w ...

  4. 北航2022软件工程第三次作业——结对编程(最长英语单词链)

    软件工程第三次结对编程作业 项目 内容 这个作业属于哪个课程 北京航空航天大学2022春季软件工程(罗杰 任健) 这个作业的要求在哪里 结对编程项目-最长英语单词链 我在这个课程的目标是 学习软件工程 ...

  5. 高级软件工程第三次大作业(周帅)

    一如软工深似海,从此硬件是故人 课前疑问的自答 课前,我的主要疑问是通过这门课对我的课题有什么帮助?我了解软件开发流程对于我的课题有什么意义?伴随着这门课的学习,我有了一个契机去主动了解Pycharm ...

  6. 2017高级软件工程第1次作业

    第一部分:结缘计算机 1.你为什么选择计算机专业?你认为你的条件如何?和这些博主比呢?   说起来也是阴差阳错,高考填志愿的时候考虑的是当时最火的3个专业:机械.土木.电气.只知道哎呀这个专业好,有前 ...

  7. 高级软件工程第五次作业:东理三剑客团队作业-2

    团队的作业是:图书管理系统 队员学号: 金安安(队长) 201810812003 谢竟成      201810812002 彭丽君 201810812010 1.需求分析 用户调研 我们通过发放调查 ...

  8. 高级软件工程第六次作业:东理三剑客团队作业-3

    团队的作业:图书管理系统 队员学号: 金安安(队长)  201810812003 谢竟成                201810812002 彭丽君                20181081 ...

  9. test软件工程第三次作业

    零.前言 本次作业要求个人编写程序,截止日期2019年9月25日23:00. 请先阅读<构建之法>第一章至第三章的内容,并在下方作业里体现出阅读后的成果.特别是第2章中的效能分析及个人软件 ...

  10. 软件工程第三次作业(微软小娜案例分析)

    第一部分 调研, 评测 1.上手体验  我使用的是win10系统,所以自带Cortana,之后又在手机上下载了安卓版本.第一感受不管是在电脑上还是在手机上都比较流畅,优化的比较好.电脑上使用也比较方便 ...

最新文章

  1. 2022-2028年中国电熔镁行业市场研究及发展趋势分析报告
  2. 谷歌正在测试一个人工智能系统,帮助视障人士参加跑步比赛
  3. kubenerte启动_Kubenertes资源分配之Request和Limit解析
  4. GitHub 为什么有些时候进去特变慢
  5. C语言两班平均成绩,两班工人的平均日产量的标准差系数分别为()。A.甲班11.44%B.甲班8.41%C.乙班12.51%D.乙班9.89% - 作业在线问答...
  6. iOS 通知中心(NSNotificationCenter)
  7. javascript二维数组转置_精通Excel数组公式010:认识数组函数:TRANSPOSE、MODE.MULT和TREND...
  8. 遍历WinForm窗体 根据语言类型设置其控件Text显示
  9. LC-1186 连续子数组中可删除一个数的最大和
  10. 固态硬盘故障检测_有哪些好用的检测软件是用于判断固态硬盘故障的?
  11. 使用Cloudflare API动态解析域名IP
  12. Win10 ipv4无网络访问权限的解决办法
  13. phpwind不支持php7,PHP论坛程序PHPWind 7发布 新增四个功能
  14. 使用PreListener与InteractionListener的一个小发现
  15. opencv学习手册(三)(线条识别)(更新中
  16. arthas 热更新
  17. Vue中mixin的使用
  18. 创客筑梦,第二届机智云智能硬件36小时开发大赛圆满落幕
  19. 敷完面膜后要擦水乳吗_敷面膜后要用爽肤水还是乳液 你不得不知道的护肤小习惯问题...
  20. 21个小故事,21个大道理

热门文章

  1. 打印目录和更新时就会出现“错误!未定义书签!”的解决技巧
  2. PHP ThinkPHP 命名空间引入(use )
  3. python列表姓氏_Python 批量生成中文姓名(百家姓)
  4. 根据输入的姓氏,随机获取对应的男生和女生姓名
  5. Torrent 文件图文解析
  6. matlab 复权数据,用Pandas计算前复权数据
  7. win10系统找不到telnet服务器,win10系统找不到Telnet服务的技巧介绍
  8. 计算机处理器性能排名,2020最新电脑cpu性能天梯图_i5i7i9处理器性能排行榜介绍...
  9. 如何突破村镇银行业务发展的瓶颈
  10. 机器人资和迅_《机器人与智能系统》-工业机器人与智能机器人杂志-工业自动化领域时尚先锋传媒!...