分类

课内:
不限
类型:
不限 毕业设计 课程设计 小学期 大作业
汇编语言 C语言 C++ JAVA C# JSP PYTHON PHP
数据结构与算法 操作系统 编译原理 数据库 计算机网络 软件工程 VC++程序设计
游戏 PC程序 APP 网站 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019 2020

资源列表

  • 基于QT实现的图的遍历演示

    1 问题分析和任务定义1.1 问题描述很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示在连通的无向图上访问全部结点的操作。
    1.2 基本要求以邻接多重表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
    1.3 测试数据任选国内城市,起点为合肥,暂时忽略里程。
    1.4 问题分析及任务定义此程序需要完成以下操作:使用文件读取数据,以邻接多重表为存储结构,构成一个连通的图。用户输入一个起始点,从起始点开始对图分别进行深度优先和广度优先遍历。分别输出每种遍历下的结点访问序列和相应的生产树的边集。
    程序的执行流程如下图所示。

    1.5 实现本程序需要解决的问题
    如何利用从文件读取的数据构成无向图
    如何实现从用户指定的点开始遍历
    遍历完后如何输出遍历过得点的序列和边集
    如何利用图形化动态的演示遍历过程
    如何提高用户体验

    本程序的难点在于如何创建邻接多重表,和如何遍历所有城市。
    设图的结点20-30个,每个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,…,n)。通过输入图的全部边(存于数据文件中,从文件读写)输入一个图,每个边为一个数对,可以对边的输入顺序作出某种限制。注意,生成树的边是有向边,端点顺序不能颠倒。
    2 数据结构的选择和概要设计城市与城市之间是没有方向的,构成的无向图采用邻接多重表来实现,主要表示无向图的的各个边和结点,并通过对邻接多重表的操作实现对无向图的深度优先和广度优先遍历。创建AMLGraph类,其包含的属性和方法如下图所示。

    其中adjMulList是顶点数组指针,所用的顶点结点以顺序方式存储在一维数组中;vexnum和edgenum是无向图的结点个数和边个数;cityMap类是用来存储所有从文件中读取的城市名和坐标信息的类,points、dfs_str、bfs_str是用来存储遍历的城市的坐标和名称的变量。
    GreatAMLGraph函数用来构建邻接多重表,LocationVex函数通过城市名找到该城市在表结点的位置。AMLGraph、~AMLGraph函数分别是类的构造函数和析构函数,BFS_Traverse、DFS_Traverse分别是广度优先和深度优先遍历的函数。
    VexNode结构体是点结点(城市结点),name是城市的名称,firstEdge是EdgeNode类型的指针,指向该顶点的第一条边的结点。
    EdgeNode结构体是边结点,isVisited为标志域,用来标记该条边是否已被访问过;ivex为顶点域,指示依附于这条边的一个顶点的位置(序号);jvex指示另一个顶点的位置;ilink为链域,指向依附于第一条边的结点。
    3 详细设计和编码3.1 邻接多重表的建立
    从文件中读取所有城市名、坐标和线路信息。利用头插法建立邻接多重表。
    for (int i = 0; i < edgenum; ++i){ EdgeNode * e = new EdgeNode; e->isVisited = false; string v1, v2; inFile >> v1 >> v2; int a = LocationVex(v1); int b = LocationVex(v2); e->ivex = a; e->jvex = b; e->ilink = adjMulList[a].firstEdge; e->jlink = adjMulList[b].firstEdge; adjMulList[a].firstEdge = e; adjMulList[b].firstEdge = e;}
    3.2 深度优先遍历先调用DFS_Traverse函数,初始化visited[i]数组,再调用DFS(i),其中i为用户指定城市的索引。在DFS函数中,先访问表结点中第i个点,在判断该顶点的另一端是否被访问,如果没有访问,就在调用DFS(i->jvex),形成递归,直到访问到该顶点的另一端被访问过后,在访问与i相连的其它边,当与i相连的所有边都被访问完时,结束循环。
    while (e){ if (e->ivex == i) { if (!visited[e->jvex]) DFS(e->jvex); e = e->ilink; } else { if (!visited[e->ivex]) DFS(e->ivex); e = e->jlink; }}
    e为EdgeNode类型的指针变量。时间复杂度为O(n+edgeNum)。
    3.3 广度优先遍历先调用BFS_Traverse函数,初始化visited[i]数组,再调用BFS(i),其中i为用户指定城市的索引。BFS函数中,先访问表结点中第i个点,并将该点放入队列q中,在循环中不断取出队列的第一个元素,直到队列为空时,所以点遍历完成,结束该循环。取出队列第一个元素后,再判断该顶点的另一端是否被访问,如果没有访问,就遍历该点,并将该点放入队列,当队列第一个元素的所有相连的点都被访问过后,循环结束,再从队列中取出第一个元素,进行循环遍历。
    while (!q.empty()){ p = q.front(); q.pop(); EdgeNode * e = adjMulList[p].firstEdge; while(e) { if (e->ivex == p) { if (!visited[e->jvex]) { visited[e->jvex] = 1; //std::cout << adjMulList[e->jvex].name + " "; bfs_str.append(adjMulList[e->jvex].name.substr(0, 2) + "->"); q.push(e->jvex); } e = e->ilink; } else if (e->jvex == p) { if (!visited[e->ivex]) { visited[e->ivex] = 1; bfs_str.append(adjMulList[e->ivex].name.substr(0, 2) + "->"); q.push(e->ivex); } e = e->jlink; } }}
    分析上面算法,每个顶点至多进一次队列,遍历过程实质上仍是寻找每个顶点的邻接点的过程,因此时间复杂度和深度优先搜素一样。
    4 上机调试过程调试过程中,一方面是解决产生的bug,另一个面是调整文件中的数据,保证数据的合理性。
    点的名称及坐标

    无向图的边

    5 测试结果及其分析程序运行界面

    遍历演示


    本程序以图形化的形式演示,用户能够任意指定起始点开始遍历,遍历的结果也清晰可见。其中显示了两种遍历的城市编号的序列和遍历的边集。
    6 用户使用说明本程序是在windows7下的Qt creator5.4.1环境下编写的,因为Qt本身就是跨平台图形界面软件,所以本程序支持跨平台运行,支持的平台包括Windows、Mac OS、Linux等桌面操作系统。
    执行本程序后,窗口上回现实所有城市的名称、编号和路线,输入框中默认填写的合肥的编号,该输入框支持模糊查询,可以输入城市名称、城市编号或编号加名称。点击按钮“演示”后,会以文本形式显示遍历城市的序列,以图形化的方式,显示遍历的城市之间的连线。并且可以反复搜索遍历,具有良好的用户体验。在该可执行文件目录下的map.txt是本程序的所依赖的数据来源,文件中的数据可供用户或开发者调整修改。
    参考文献[1] 王昆仑, 李红. 数据结构与算法. 北京: 中国铁道出版社, 2006年5月
    [2] Kay_Sprint. 邻接多重表存储无向图以及有关操作[EB/OL]. http://www.2cto.com/database/201111/110964.html, 2011-11-13/2017-2-16
    3 评论 59 下载 2018-10-31 11:48:00 下载需要13点积分
  • 基于Java和Access数据库的飞机票订票系统

    一、课程设计目的
    通过实际的编程与学习,了解自己的学习情况和实际代码编辑的动手能力
    通过编程发现问题并解决问题,提高自己对程序代码的了解,解决问题的能力,以及动手操作能力
    通过编译代码,设计程序。熟悉 java 与SQL的编程过程与运用以及两种语言的链接。了解自己专业知识的运用
    通过这课程设计,知道自己专业知识的学习框架。对以后的工作更好的运用,更好的掌握专业技能

    二、 设计内容
    登陆界面:对于普通用户和管理员进行权限登陆,进行不同的权限授予
    普通用户界面:主要是进行航班查询和下订单等处理

    查询航班:用户可以对有需要的进行航班查询预订:用户可以在查询航班的界面直接预订,或者知道需要的航班号预订机票查询订单:用户可以通过已知订单号查询已下订订单退订:用户可以通过已知订单号退订已下订订单
    管理员界面:主要对用户已下订单的处理和处理航班等信息

    订单处理:查询出用户已下订单(包括所有状态的订单),然后对订单进行改签或删除处理查询航班:查询出现在正有效的航班,选择航班可以修改航班动态创建航班:创建新的航班删除航班:删除已处在的航班修改航班:修改一个已存在航班的信息

    三、需求分析3.1 系统的安全性系统运行安全是决定软件质量的重要因素,虽然此机票预订系统不需要接入Internet,但系统中涉及多方信息,如果有人蓄意破坏任意信息,都可能会导致系统的停用,会带来损失,因此仍然需要保证它的安全。为了保证系统的安全性,必须采取一定的安全措施,防止用户越权使用,防止工作数据被非法篡改、破坏和泄露等。既要考虑操作系统的安全性,还要考虑到本系统本身的安全性。所以首先运用数据库对于管理员、普通用户授予不同权限,各自进入自己相应的界面。
    3.2 系统的合理性在设计系统时要考虑实际的系统性能和硬件要求,不能忽视所处环境,也不能一味地追求新技术,要保证系统的合理性,使本系统能够在现流行的Windows XP和Window 7等系统中流畅运行。
    3.3 系统的简单性、易用性要考虑到不同用户的计算机操作水平,一般而言,大部分用户的计算机操作水平偏低,因此开发时要本着“简单易用”的原则,方便各层次人员的使用,使人员的培训降到最低。通过鼠标以及图标等按钮进行“傻瓜式”操作,简单明了,符合大众操作。
    3.4 系统的稳定性、可靠性、安全性订票人员来源广泛,分布性强,往往不具备专业的计算机知识,因此,本系统必须达到稳定、可靠、安全等要求。
    四、概要设计4.1 功能设计《网上机票预订系统》本是在Internet环境下运行的,但根据课程需求在此我们先将它做为一个c/s程序。该项软件开发的意图是为了方便航空公司进行乘客预定票的管理,减少管理中出现的麻烦,它主要在某一航空公司内部进行使用,再加之这是一项独立的软件,全部内容自含,所以不会涉及到与其它系统、产品的联系和接口问题。
    根据分析,机票预订系统系统模块如下图所示。

    4.2 数据字典数据字典是系统中各类数据描述的集合,是进行详细的数据的收集和数据分析所获得的主要成果。数据字典在数据库设计中占有很重要的地位。
    此系统数据字典部分如下:

    名字:旅客信息描述:旅客的个人信息,用于对旅客的确认定义:旅客信息=姓名+性别+工作单位+身份证号码+旅行时间+旅行目的地等等
    名字:航班信息描述:航班的起飞时间及上机时间等,让旅客能够查看和确认定义:航班信息=编号+终点+起飞始点等等
    名字:机票订单描述:旅客交付费用的凭证定义:机票账单=旅客姓名+交付费+时间等等
    名字:取票通知描述:旅客领取机票的凭证定义:取票通知=旅客姓名+领票时间
    名字:航空公司数据库信息描述:已订票的旅客在航空公司的记录和航班信息等等定义:航空公司数据库信息=航班机票信息+也订票旅客的信息等

    4.3 系统数据表下面将主要介绍关键数据表的数据库设计的详细说明。
    旅客信息系统的结构



    字段名
    数据类型
    长度
    约束
    描述




    passager name
    varchar
    50
    主键
    旅客姓名


    id
    decimal

    不为空
    证件号码


    telephone number
    decimal

    不为空
    联系方式


    sex
    varchar
    50
    不为空
    性别


    password
    Varchar
    20
    不为空
    密码


    power
    number

    不为空
    权限



    航班信息系统的结构



    字段名
    数据类型
    长度
    约束
    描述




    flight number
    decimal

    主键
    航班号


    take off place
    varchar
    50
    不为空
    起飞地


    destination
    varchar
    50
    不为空
    目的地


    flight time
    time

    不为空
    起飞时间


    BusyNumber
    decimal

    不为空
    商务舱数量


    CheapNumber
    decimal

    不为空
    经济舱数量


    ArriveTime
    time

    不为空
    到达时间


    Cprice
    int

    不为空
    经济舱价格


    Bpric
    int

    不为空
    商务舱价格


    FlightMove
    varchar
    100
    不为空
    航班动态


    FlightTime
    time

    不为空
    起飞日期



    取票通知单系统的结构



    字段名
    数据类型
    长度
    约束
    描述




    passager name
    varchar
    50
    主键
    旅客姓名


    get ticket time
    time

    不为空
    取票时间


    flight number
    decimal

    不为空
    航班号


    seat number
    decimal

    不为空
    座位号


    flight ticket type
    varchar
    50
    不为空
    机票类型


    OrderForm
    decimal

    自动编号
    订单号


    IfPay
    varchar
    100

    状态


    price
    decimal

    不为空
    价格



    银行用户表



    字段名
    数据类型
    长度
    约束
    描述




    Account
    varchar
    50
    不为空
    旅客姓名


    code
    time

    不为空
    取票时间


    passager name
    varchar
    50
    不为空
    旅客姓名



    4.4 E-R图介绍概念设计是一种面向对象的数据模型,是管理者观点使用自上向下方式来对数据和信息建模。它描述了从管理者角度看到的数据,它反映了用户的实现环境,这种方法用ER图描述现实世界中的实体,而不涉及这些实体在系统中的方法。概念设计中最著名的方法就是实体联系方法(ER方法),建立ER模型,用ER图表示概念结构,得到数据库的概念模型 [2]。
    ER模型中包含“实体”,“联系”和“属性”。在本系统中,多对多的关系用(m-n)表示。其各个实体ER图如下所示:
    旅客E-R图

    航班信息E-R图

    取票通知E-R图

    总体E-R图
    通过逻辑设计的目的是把概念设计好的概念模型转换成与数据模型相符合的逻辑结构。这些模型在功能上、完整性和一致性约束及数据库的可扩展性等方面均应满足用户的各种要求。因而,根据上节的实体E—R图,汇总成总体E—R图如下:

    4.5 系统流程图
    五、运行与调试普通用户注册


    登陆窗口


    航班查询界面

    查询到用户理想的航班后,点击航班,然后直接点击预订窗口

    订单查询


    用户可以在查询到的订单,然后取消订单

    在已知订单的情况下取消、增加订单


    用户输入银行卡账号密码支付


    管理员的界面

    查询用户提交的订单

    选择用户订单进行改签、删除处理


    查询航班、修改航班动态


    创建新的航班



    取消一个航班


    航班查询

    修改航班

    因为已存在用户订单,不能修改该航班信息

    数据库中不存在该航班,不能修改



    关于数据库的定义图片





    六、总结在课程设计的过程中,我学到了很多,也找到了自己身上的不足。感受良多,获益匪浅。我们分工合作、齐心协力,一起完成了课程设计前的准备工作(阅读课程设计相关文档)、小组讨论分工、完成系统开发的各个文档、课程设计总结报告、,个人小结的任务。在课程设计中我们便对这次任务进行了规划和分工。在设计中我们基本能按照规范的方法和步骤进行,首先对现有的系统进行调查,并查阅有关资料,最后确定设计方案,然后设计并制作,实施过程中我们深刻的认识到认真执行管理系统软件标准的重要性,由于我们对管理系统软件相关的标准和规范不太了解,缺少行为操作准则,所以在设计中手法比较生硬,主与次也没能很好把握住,这些方面通过这次的设计过程我们都要加强了解。我们组的成员一起努力,查阅资料、小组讨论、对资料进行分析,并在这段时间里完成了整个设计,并最后撰写课程设计报告及个人总结。
    2 评论 21 下载 2019-09-18 09:00:25 下载需要12点积分
  • 基于Java Web和Mysql的汽车租赁系统设计与实现

    摘 要今天,现如今的中国的经济正处于稳定的上升阶段,现在每个家庭基本上都有一部汽车作为代步工具,根据中汽协11月份发表的10月份汽车产销数据可知,中国车市已经连续四个月下滑,这意味着中国的家庭对于汽车这个商品的购买欲望已暂时趋于缓和,这个时候从一个渐趋于饱和的市场去开拓市场,事倍功半。
    这个汽车租赁管理系统针对手中有可出租车辆资源的客户或公司设计,主要实现了用户的注册、登录功能,租赁人在实体店选好车型后,可在该系统的租赁界面,填写订单,实现了添加订单功能,编辑订单,删除订单的操作。这个项目是基于JavaWeb开发的一个汽车租赁系统,使用了eclipse,MySQL,Tomact8.0,xampp,Navicat for MySQL工具进行项目开发及功能测试。
    这个产品的想法,出自于每天日常的出行,尤其是走入社会后,住房可能与公司的上班地点相间太远,又或者周六日和朋友短途出去游玩,拥有一辆汽车就很有必要性了,刚进入社会的大部分人,都是没有经济能力去支付购买一辆车的费用,更何况提车后的油费,保养费,停车费等等,而共享汽车刚好可以弥补这一块空白期。
    关键词:汽车;租赁;互联网
    1 绪论1.1 系统开发背景与意义今天,现如今的中国的经济正处于稳定的上升阶段,现在每个家庭基本上都有一部汽车作为代步工具,根据中汽协11月份发表的10月份汽车产销数据可知,中国车市已经连续四个月下滑,这意味着中国的家庭对于汽车这个商品的购买欲望已暂时趋于缓和,这个时候从一个渐趋于饱和的市场去开拓市场,事倍功半。
    这个汽车租赁管理系统针对手中有可出租车辆资源的客户或公司设计,主要实现了用户的注册、登录功能,租赁人在实体店选好车型后,可在该系统的租赁界面,填写订单,实现了添加订单功能,编辑订单,删除订单的操作.
    这个项目是基于JavaWeb开发的一个汽车租赁系统,使用了eclipse,MySQL,Tomact8.0,xampp,Navicat for MySQL工具进行项目开发及功能测试.
    1.2 文章的研究内容
    HTML:整个项目的界面,是用户对这个系统的第一印象,界面需满足用户的基本审美要求,简单易懂
    功能源码:包括jsp、sevlet,dao类(实现功能代码)、对应数据库表单的实体类,封装好的一个打通数据库通道的类
    数据库(MySQL):储存整个项目所所长生的信息,比如用户登录账号的注册信息、租赁人的订单信息
    Tomact的版本运行

    这个项目实现了用户的通过浏览器,进行注册或登录项目的操作,用户登录成功后可以通过订单展示查看所有的历史订单,可以在订单展示页面进行编辑订单和删除订单的操作;鼠标左键单击添加订单按钮,会跳转进入添加订单页面,进行添加新的订单操作。
    开发环境:Eclipse、MySQL、Tomact8.0
    整个系统采用了jsp,servlet,session,jdbc等技术。
    1.3 文章的组织结构这个章节的内容:

    第一章大致对开发的这套程序做一个讲解,让大家了解这个系统开发的意义
    第二章主要讨论了项目与管理人员的需求以及实现项目功能的一些核心技术
    第三章详细介绍了系统的业务建设与设计。对系统的管理人员与及系统功能需求进行了讲解

    第四章主要对这个系统的实现进行了详细地解释,最后对所有工作进行说明总结

    2 系统需求分析与关键技术2.1 可行性分析2.1.1 经济可行性这个项目的开发,主要是为了管理员可以方便的对客户基本信息、车辆信息进行登记和储存,方便公司后期做数据分析。
    2.1.2 技术可行性这个产品的运行,需要一台装有Eclipse ,配置好java开发环境和Tmocat8.0服务器的电脑,测试环境是360极速浏览器的兼容模式,可以实现所有开发好的功能,界面简洁,功能很容易上手操作。
    2.1.3 运营可行性互联网时代,办公基本上离不开电脑,大数据的也是基于大量的网上的数据进行分析,所有的信息最后基本都汇总到了各个云服务器,此次开发的这个汽车租赁系统界面简洁明了,管理人员可以轻松的掌握所有功能,做这个项目时充分考虑了管理人员用电脑和手机的习惯,运营可行性高。
    2.2 功能分析2.2.1 登录注册功能这个功能主要是对管理员的信息进行收集,实行可以登录。

    注册:用户进入系统的起始页,有两个按钮,分别是登录和注册,点击注册按钮可以进入注册界面,进行登录人员的信息的收集,并且把信息注入数据库中,实现信息的收集
    登录:注册的人进入系统的起始页,分别在账号和password输入框内输入曾经注册过的信息,按下登录图标,即可进入产品的功能界面

    2.2.2 信息的增删改查功能管理员进入系统后可以操作增加订单,添加用户,修改订单信息,删除订单,查询指定租赁人的功能。

    添加用户:管理员可以在界面添加新的人员登录此系统,进行相关功能的操作
    添加订单:管理员订单左侧菜单栏中的添加订单,即可进图添加订单页,进行租赁客户的相关信息登记
    查询:管理员添加数据后,点击左侧的菜单栏中的订单展示,即可查看所有订单的信息,订单展示页上方添加了一个身份证号的查询框,实现了对数据库中的订单实施精准查询
    修改:订单展示页面的每条订单后面都有一个编辑按钮,这个图标可以对该条信息进行修改,单击图标,就能进入编辑页面,对该条订单进行edit和保存
    删除;订单展示页面的每条订单后面都有一个删除按钮,此按钮可以对该条订单实施删除操作,若改订单已完结,即可进行订单的删除


    2.2.3 分页功能分页功能主要面对显示客户信息更优化管理。基于显示客户信息的基础上考虑到客户的数量进行合理化的分页进行管理,每6条客户信息为一个页面,可以进行比较好的可视化管理。
    2.3 关键技术2.3.1 jQuery框架jQuery里面涵盖了html,css,js的基本元素,并且将这些我们使用频繁的元素进行了更好的优化,让程序员使用提供的比较舒服的体验,节省了许多的时间进行代码的书写。此项目中系统主页,与栏目的子页使用了此项技术。
    2.3.2 分页技术分页这个技术是使用mysql进行查询我所需要的数据,然后每个页面设置所能展示的数据条数,设置当前数据条数然后根据对数据的总数进行分页,可以点击页面数与上下页的链接进行页面之间的浏览。
    2.3.3 精准查询精准查询功能,因为身份证号码的唯一性,这个项目是根据订单表里面的身份证号来进行进准查询,管理员可以在订单展示界面,输入租赁人的身份证号,即可在大量数据中准确的找到该订单的详细信息。
    3 业务建模与设计3.1 系统的整体设计先要把这个程序的大概想出来,下面就是对系统代码的解说图3-1进行设计:

    文件夹介绍
    src文件夹下有bean包有Lease实体类(订单)和User实体类(登录用户);dao包下有两个的LeaseDao类和UserDao类,实现用户注册登录,订单的增删改查功能接口的方法; servlet包下有servlet接口及实现类, util包有打通数据库的Dbutil类。
    WebCt文件夹

    WebCt文件夹有系统需要用到的的jsp页面
    css文件夹涵盖项目网页需要用的的样式
    img文件夹涵盖项目网页需要用的图片
    js文件夹包含网页的动态代码
    META-INF文件夹:这个包用来配置控制程序的正常运行,使得功能可以运转。文件中的manifest.mf文件,是用jar打包时自动产生的
    WEB-INF文件夹:有一个web.xml配置文件

    3.2 业务用例建模3.2.1 角色分析此系统中有管理员一个角色,管理员可以可添加新的订单,对历史订单进行编辑,删除操作,可以使用身份证精准查询出租赁人的订单。
    3.2.2 用例分析如下是程序的关系,就是更好能看清系统的功能,这图为3-2(管理模块):

    从上面的用例图可以知道,这系统只有一个角色,就是管路员,管路员登录系统,能查询单子的信息,生成一个单子,把他给摧毁,edit某个单子的信息。
    3.3 业务流程建模由流程图图3-3 说明管理人员先进入网站,进行系统登入,如果没有注册信息就先注册信息再进入系统,进入系统后可以进行添加订单信息,编辑订单信息,删除订单信息,查询订单信息,退出系统等一系列操作。

    3.4 系统数据库的设计下面进行数据库的打样。要完成客户需求构建出E-R图。然后根据E-R图设计表单。
    3.4.1 E-R图设计订单信息的实体的E-R图,如图3-4所示:

    用户信息的实体的E-R图,如图3-5所示:

    3.4.2 物理表设计管理人员注册表
    (注:id是自增长的不需要输入,用户注册需要输入账号,密码,输入邮箱)

    客户信息表

    上面有两个数据库的表格,分别是客户信息表,用户信息表。
    客户信息表的成员有:id(主键,自增长的,标识为一,是实现删除功能,编辑功能的重要参数),Name(是租赁客户的名字),Phone(是客户的手机号码),IDcard(是客户的身份证号码,这个参数是用来辅助完成精准查询的重要参数),Cartype(是客户选择租赁的车型),Create date(是该笔订单生成的时间,用于订单列表展示,默认按创建日期降序排列)。
    4 系统的实现与应用4.1 登录注册功能模块4.1.1 登录注册功能第一次使用这款产品,需要先注册一个账号,方可进入项目进行后续操作。
    注册展示图

    登录展示图

    MySQL数据库管理人员注册表

    注册页面的前端代码

    登录的前端代码

    注册页面的dao层代码(DAO)

    注册的Servlet层

    登陆的dao层代码

    4.2 系统的管理功能模块4.2.1 添加订单信息管理人员进入系统添加客户信息

    MySQL数据库订单信息表

    添加订单的DAO层功能代码

    添加订单的Servlet层

    4.2.2 修改订单信息管理人员点击显示管理信息后点击操作下的编辑按钮跳转到如图:


    修改订单的DAO层功能代码

    修改订单的servlet代码,抓取要修改的订单数据,传入修改界面

    抓取修改后要保存的订单信息,保存到数据库

    4.2.3 删除订单信息管理员点击展示管理信息后操作栏目下有产出的按钮如图:

    删除功能的DAO层代码

    4.3 分页功能模块管理人员面对大量的数据需要进行合理的分页来管理,查看这些数据如图:


    分页功能Dao层代码

    分页功能servlet代码

    分页功能的JSP代码


    4.4 精准查询管理员可以根据订单租赁人的身份证号实施精准查询:


    精准查询的DAO层代码

    精准查询的servlet代码

    精准查询的JSP代码

    精准查询的dao层代码


    5 结论这三个月的独立开发周期,让自己成长了很多,对于Java面对象编程理解的更加深刻,像jsp如何把值传到后台,后台如何接受前端传过来的值,以前总是模棱两可,现在对这些小知识点掌握的程度更深,整个项目做下来,虽然功能都是一些简单的功能,但是真的实实在在的感受到了Java基础加强了一遍理解,也更有信心去投新的简历。
    2 评论 12 下载 2020-08-24 12:30:42 下载需要12点积分
  • 基于VC++的MFC框架实现的数字图像处理算法

    一、课程背景数字图像处理技术的发展涉及信息科学、计算机科学、数学、物理学以及生物学等学科,因此数理及相关的边缘学科对图像处理科学的发展有越来越大的影响。近年来,数字图像处理技术日趋成熟,它广泛应用于空间探测、遥感、生物医学、人工智能以及工业检测等许多领域,并促使这些学科产生了新的发展。
    图像是人类获取和交换信息的主要来源,因此,图像处理的应用领域必然涉及到人类生活和工作的方方面面。随着科学技术的发展,数字图像处理技术的应用领域也将随之不断扩大。
    正因为图像处理在社会生活中有着越来越重要的作用,这也就体现了我们学习这门课程的必要性。而通过课程的学习可以让我们掌握一些图像处理的初步技术,从而为今后在图像处理的学习方面打下了基础。
    二、课程选题由于老师并没有做出规定去做哪一道题目,这给了我们很大程度上的自主性。我根据自身的情况和能力,选择了经典的几种边缘检测算子的特点的比较这个题目,和图象的平移和镜像,又因为在计算机图形学这门课中有讲到图片相加的具体程序,在这里图简单做一下颜色取反。通过对这些算法的编写,更加深入地了解了图像处理的过程,并且也在一定程度上提高了自己的编程能力。
    边缘检测技术是所有基于边界分割的图像分析方法的第一步,首先检测出图像局部特性的不连续性,再将它们连成边界,这些边界把图像分成不同的区域,检测出边缘的图像就可以进行特征提取和形状分析。为了得到较好的边缘效果,现在已经有了很多的边缘检测算法以及一些边缘检测算子的改进算法。但各算子有自己的优缺点和适用领域。基于以上理由,在此对学到的一些一些经典边缘检测算子进行实际编程验证,以便实际应用中更好地发挥其长处。
    三、设计思想拉普拉斯算子
    拉普拉斯算子是二阶微分算子,其差分的表示形式如下:

    拉普拉斯边缘检测算子的模板如下图所示,模板的基本特征是中心位置的系数为正,其余位置的系数为负,且模板的系数之和为零。它的使用方法是用图中的两个点阵之一作为卷积核,与原图像进行卷积运算即可。



    0
    -1
    0




    -1
    4
    -1


    0
    -1
    0






    -1
    -1
    -1




    -1
    8
    -1


    -1
    -1
    -1



    索贝尔算子
    该算子是由两个卷积核g1(x, y)与g2(x, y)对原图像f(x, y)进行卷积运算而得到的。用差分代替一阶偏导,算子的计算方法如下:

    Sobel算子垂直方向和水平方向的模板如下图所示,前者可以检测出图像中的水平方向的边缘,后者则可以检测图像中垂直方向的边缘。实际应用中,图像中的每一个像素点都用这两个卷积核进行卷积运算,取其最大值作为输出。运算结果是一幅体现边缘幅度的图像。



    -1
    -2
    -1




    0
    0
    0


    1
    2
    1






    -1
    0
    1




    -2
    0
    2


    -3
    0
    1



    罗伯特(Robert)算子
    Robert梯度算子所采用的是对角方向相邻两像素值之差,所以用差分代替一阶偏导,算子形式可表示如下:

    上述算子对应的两个2x2模板如下图所示。实际应用中,图像中的每个像素点都用这两个模板进行卷积运算,为避免出现负值,在边缘检测时常提取其绝对值。



    1
    0




    0
    -1






    0
    1




    -1
    0



    prewwit(普瑞维特)算子
    用差分代替一阶偏导可得算子形式如下:

    Prewitt边缘检测算子的两个模板如下图所示,它的使用方法同Sobel算子一样,图像中的每个点都用这两个核进行卷积,取得最大值作为输出。Prewitt算子也产生一幅边缘图像。



    -1
    -1
    -1




    0
    0
    0


    1
    1
    1






    1
    0
    -1




    1
    0
    -1


    1
    0
    -1



    镜像,平移,颜色取反
    都是对原图像(矩阵)进行遍历。
    四、数据结构与算法设计拉普拉斯算子
    很明显,算子用矩阵表示,在c++里面是二维数组,通过对话框确定使用哪个运算模板,通过调用模板运算函数实现。

    具体的伪代码如下:
    获得doc类的指针对象,因为指向位图数据的句柄这个成员变量在doc类里面;得到指向图像数据开始(不包含文件头)的指针,并且锁住图像所占用的那块内存;判断是否是8位的bmp图片,如果不是,报错返回,如果是则继续弹出选择具体模板的对话框,if(选择了模板,并且按下了确定按钮){ 调用doc类里面dib类对象的模板运算函数; if(函数模板运算成功) { 图像设置脏标记; 迫使程序重绘 } else 报错,返回 }
    因为下面几个算子的情况大同小异,下面不再详述!
    模板运算函数
    模板卷积在空域实现的主要步骤如下:

    将模板在图中漫游,并将模板中心与图中某个像素位置重合
    将模板上的各个系数与模板下各对应像 素的灰度值相乘
    将所有乘积相加(为保持灰度范围,常将结果再除以模板的系数个数)
    将上述运算结果(模板的输出响应)赋 给图中对应模板中心位置的像素

    具体的伪码如下:
    得到打开的图像的信息,计算每行的字节数;通过已经打开的图像的信息,新申请一块内存,并将打开的图像数据复制进来, 得到指向新申请的内存的指针,锁定指针所指向的内存;for (i = iTempMY; i < lHeight - 2*iTempMY; i++) // 行(除去边缘几行){ for (j = iTempMX; j < lWidth - 2*iTempMX; j++)// 列(除去边缘几列) { // 指向新DIB第i行,第j个象素的指针 pTemp = (unsigned char*)lpNewDIBBits + byteofLine * (lHeight - 1 - i) + j;// 保存当前像素点的灰度值 result = 0; for (int k = 0; k < iTempH; k++) { for (int l = 0; l < iTempW; l++) { // 指向DIB第i - iTempMY + k行,第j - iTempMX + l个象素的指针 lpSrc = (unsigned char*)lpDIBBits + byteofLine * (lHeight - 1 - (i - iTempMY + k)) + (j - iTempMX + l); // 保存象素值 result += (*lpSrc) * fpArray[k * iTempW + l]; } } result *= fCoef; // 乘上系数 result = (FLOAT)fabs(result); // 取绝对值 if (result > 255) //因为是256色图,超过直接赋值为255 { *pTemp = 255; } else { *pTemp = (unsigned char)(result + 0.5);//四舍五入取整 } } } 将新开辟的内存里面的数据复制给以前的打开的图像所占的内存; 为新开辟的内存解锁; 释放掉新开辟的内存; return TRUE;}
    索贝尔(sobel)算子

    对原图像进行两次模板运算,然后取两次模板运算的值中最大值作为新的图像的灰度值
    罗伯特(Robert)算子

    在此没有调用模板运算的函数,直接进行对角元素相减
    Prewwit算子

    除了模板和sobel算子不一样,其余的全都一样
    平移、取反、镜像

    平移
    新申请一块内存,初始化为已经打开的图像平移是用两个for循环,遍历临时图像的每个点如果临时图像的点平移回去没有出界,则保留,否则,直接赋为255(出界的点直接剪切)将新申请的内存的内容复制到打开的图像所在的内存重绘


    取反
    新申请一块内存,初始化为已经打开的图像;平移是用两个for循环,遍历临时图像的每个点,每个点的值=255-点的值;将新申请的内存的内容复制到打开的图像所在的内存;重绘


    镜像
    垂直镜像的话,交换第一行的像素和倒数第一行的像素,交换第二行的像素和倒数第二行的像素;水平镜像同样的道理

    五、运行结果与分析5.1 程序运行结果


    原始图片
    有噪声的原始图片









    拉普拉斯算子



    中心为4的拉普拉斯
    中心为8








    中心为4的
    中心为8的







    罗伯特(Robert)算子



    效果1
    效果2









    Prewwit算子



    效果1
    效果2









    平移、取反、镜像



    平移
    取反








    垂直镜像
    水平镜像







    5.2 分析
    图三四,和图六、八、十相比,很明显的得出结论,拉普拉斯算子对噪声敏感,在应用过程中应注意先进行平滑预处理
    图三、四、六、八、十纵向对比,可见Robert算子对噪声的抵抗能力最好,sobel和prewwit算子次之
    比较图五六和图九十可见sobel算子和Prewwit算子几乎一样

    Sobel算子模板



    -1
    -2
    -1




    0
    0
    0


    1
    2
    1






    -1
    0
    1




    -2
    0
    2


    -3
    0
    1



    Prewitt算子模板



    -1
    -1
    -1




    0
    0
    0


    1
    1
    1






    1
    0
    -1




    1
    0
    -1


    1
    0
    -1




    这几个算子中只有拉普拉斯算子是二阶算子,其余的均是一阶算子。但是也数拉普拉斯算子丢失边的信息最为严重。纵向对比图九七五二一,图一、二,也就是拉普拉斯算子的那两幅图,丢失lena身后的柱子信息(图最左面)最为严重,图七(Robert算子处理的那幅图)次之,剩余的保留信息最好。观察帽子顶的信息,图一和图七最差,图二次之,图五和图九最好
    很容易看出,sobel算子和prewwit算子检测出的边最为厚,尤其是sobel算子,Robert算子检测出的边最细

    目前,边缘检测技术在很多领域中都得到了广泛的应用。如对医学图像的边缘特征提取、车牌识别、人脸检测技术等。本文比较了几种常用的经典边缘检测算子。可以看出它们都不是具有绝对优势的方法,都各自存在优缺点。所以对于图像的边缘检测,要根据不同的系统,针对不同的环境条件和要求,选择合适的算子。
    以上是我从自己处理的图像中所看出来的,并没有严谨的理论分析,实际测试的样本数量也不是很多,仅仅作为验证,结果也基本符合网上的别人的总结,但还是有出入的。通过对这几种算子的比较,加深了对这几种算子的印象,在以后的学习和工作中能比较准确的判断使用哪种算子,为将来的学习工作打下了基础。


    注:红线的地方是结论基本一致的地方,画框是出入很大的地方,没划线的是我这里是没有结论的。
    六、困难及解决方法6.1 自己的结果产生错误自己历尽千辛万苦好不容易出来一幅图片,没有产生中断而崩掉,激动之后,自己做的结果到底对不对啊?和同学做的结果对照一下,很容易发现不一样:



    效果1
    效果2









    (这两幅图片都是用prewitt算子处理的,sobel也有这样的问题)
    后来发现是有符号的char和无符号的char的问题。

    百度之后,问题解决了。
    6.2 MFC编程过程中的问题很明显,在做课程设计的时候要用到内存的开辟,复制,释放等等。
    在做这个的过程中,学到了一个新的函数memcpy。

    但是在用的过程中很容易出现崩掉的情况,碰到这种情况之后只能自己检查数组在哪个地方越界。
    还有一些“灵异事件”,程序崩了,怎么看怎么对,调试也没调出来,新建一个工程,重新拷贝一份,又好了。
    6.3 选题由于是自己选题,所以想找个简单点的,所以在空域里面做,选了上面的几个题目,课程里面涉及到的边缘检测算子里面还有两个,一个是马尔算子,用到了平滑,另一个是Canny算子,要用到高斯滤波器,所以这两个算子没做。其实我也想在频域里面做一下,感受一下高通滤波的效果,但是让傅里叶变换就给治住了,所以就放弃了。
    6.4 对这几个算子的理解很明显从上面可以看出,我对这几个算子的理解还是很肤浅的,直接把它们的离散化的形式拿过来用,直接把卷积模板套用一下。我还是希望有机会,能推导一个算子的公式,加深一下理解。但是,通过自己在编码过程中的思考以及在代码中调试,也同样加深了自己对于该方法的理解,并且在程序中得到了很好地实现,收获还是很多的。
    6.5 查资料很明显,去网上可以搜到很多的关于这方面的资料,但是有的是论文,看起来很难,有的呢是比较垃圾的。关键就靠我们自己,擦亮眼睛,去找难度适合的能满足我们需求的资料。
    七、总结心得通过一学期的课程学习我们虽说还没有完全掌握数字图像处理技术,但也收获了不少,对于数字图像方面的知识有了深入的了解,更加理解了数字图像的本质,即是一些数字矩阵,但灰度图像和彩色图像的矩阵形式是不同的。对于一些耳熟能详的数字图像相关术语有了明确的认识,比如常见的:像素(衡量图像的大小)、分辨率(衡量图像的清晰程度)、位图(放大后会失真)、矢量图(经过放大不会失真)等大家都能叫上口却知识模糊的名词。也了解图像处理技术中一些常用处理技术的实质,比如锐化处理是使模糊的图像变清晰,增强图像的边缘等细节。而平滑处理是的目的是消除噪声,模糊图像,在提取大目标之前去除小的细节或弥合目标间的缝隙。对常提的RGB图像和灰度图像有了明确的理解,这对大家以后应用PHOTOSHOP等图像处理软件对图像进行处理打下了坚实的基础,就像武侠小说里面的“内功”。更重要的是了解到了数字图像处理的思想。通过学习也是对C++编程应用的很好的实践与复习。
    当然通过几周的的课程学习还是远远不够的,也有许多同学收获甚微,我总结了下大家后期的学习态度与前期的学习热情相差很大的原因。刚开始大家是有很高的热情学习这门课的,可是随着课程的逐渐深入学习,大家渐渐发现课程讲授内容与自己起初想学的实用图像处理技术是有很大的差别的,大家更着眼于如何利用一些软件、技术去处理图像而得到满意的效果,或者进行一些图像的创意设计,可是课程的内容更偏重于如何通过编程实现实现如何对图像进行一些类似于锐化、边缘提取、模糊、去除噪声等基础功能的实现,这其中涉及很多算法、函数,需要扎实的数学基础和编程基础,并且需要利用大量时间在课下编写代码,并用VISUAL C++软件实现并进行调试,然而大部分人的C++实践能力以及编程能力还有待提高,尤其是对于矩阵进行操作的编程尤为是个考验,并且后半学期课程任务较重,时间不是很充裕,这对于需要大量实践的数字图像处理课程就是个很大的问题。
    在老师授课方面建议可以在课上多进行具体操作,这样可以提起大家学习的兴趣,也可以让大家在课下积极准备,然后在上课由学员进行演示,还可以加入一些数字图像处理的经典范例,加深同学们的学习热情。
    八、程序操作说明及主要代码8.1 程序操作的简要说明书打开一幅图片


    从菜单选取操作



    如果要放弃所做的操作


    如果要保存所做的操作

    8.2 主要代码//拉普拉斯算子菜单的响应函数void CmyDibView::OnLplsSharp(){ CmyDibDoc* pDoc = GetDocument(); LPSTR lpDIB; // 指向DIB的指针 LPSTR lpDIBBits; // 指向DIB象素指针 int iTempH=3; // 模板高度 int iTempW=3; // 模板宽度 float fTempC=1; // 模板系数 int iTempMX=1; // 模板中心元素X坐标 int iTempMY=1; // 模板中心元素Y坐标 float aValue[9]; // 模板元素数组 lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)pDoc->GetHDIB()); // 找到DIB图像象素起始位置 lpDIBBits = pDoc->GetDibImage()->FindDIBData(lpDIB); // 判断是否是8-bpp位图 if (pDoc->GetDibImage()->DIBNumColors(lpDIB) != 256) { MessageBox(_T("只支持256色位图的锐化!"), _T("系统提示"), MB_ICONINFORMATION | MB_OK); ::GlobalUnlock((HGLOBAL)pDoc->GetHDIB()); return; } CLPLSDlg dlg; if (IDOK == dlg.DoModal()) { aValue[0] = dlg.m_n1; aValue[1] = dlg.m_n2; aValue[2] = dlg.m_n3; aValue[3] = dlg.m_n4; aValue[4] = dlg.m_n5; aValue[5] = dlg.m_n6; aValue[6] = dlg.m_n7; aValue[7] = dlg.m_n8; aValue[8] = dlg.m_n9; // 调用Template()函数用拉普拉斯模板锐化DIB if (pDoc->GetDibImage()->Template(lpDIBBits, pDoc->GetDibImage()->DIBWidth(lpDIB), pDoc->GetDibImage()->DIBHeight(lpDIB), iTempH, iTempW, iTempMX, iTempMY, aValue, fTempC)) { pDoc->SetModifiedFlag(TRUE); // 设置脏标记 pDoc->UpdateAllViews(NULL); // 更新视图 } else { MessageBox(_T("分配内存失败!"), _T("系统提示"), MB_ICONINFORMATION | MB_OK); } ::GlobalUnlock((HGLOBAL)pDoc->GetHDIB()); }}
    //功能:空域上的模板运算//参数:// lpDIBBits 指向源DIB图像指针 lWidth 源图像宽度(象素数)// lHeight 源图像高度(象素数) iTempH 模板的高度// iTempW 模板的宽度 iTempMX 模板的中心元素X坐标 ( < iTempW - 1)// iTempMY 模板的中心元素Y坐标 ( < iTempH - 1) fpArray 指向模板数组的指针// FLOAT fCoef 模板系数//返回值:成功返回TRUE,否则返回FALSE。bool CMyDib::Template(LPSTR lpDIBBits, long lWidth, long lHeight, int iTempH, int iTempW, int iTempMX, int iTempMY, float * fpArray, float fCoef){ LPSTR lpNewDIBBits; // 指向复制图像的指针 HLOCAL hNewDIBBits; //指向一块内存区域的指针 unsigned char* lpSrc; // 指向源图像的指针 unsigned char* pTemp; // 指向要重新赋值区域的像素点指针 float result; // 计算结果 long byteofLine; // 图像每行的字节数 byteofLine = LineBytes(lWidth * 8); // 暂时分配内存,以保存新图像 hNewDIBBits = LocalAlloc(LHND, byteofLine * lHeight); if (hNewDIBBits == NULL) { return FALSE; } lpNewDIBBits = (char *)LocalLock(hNewDIBBits); // 初始化图像为原始图像 memcpy(lpNewDIBBits, lpDIBBits, byteofLine * lHeight); for (int i = iTempMY; i < lHeight - 2*iTempMY; i++) // 行(除去边缘几行) { for (int j = iTempMX; j < lWidth - 2*iTempMX; j++)// 列(除去边缘几列) { // 指向新DIB第i行,第j个象素的指针 //lpDst = (unsigned char*)lpNewDIBBits + lLineBytes * i + j; //这样的话图像会倒置 pTemp = (unsigned char*)lpNewDIBBits + byteofLine * (lHeight - 1 - i) + j; result = 0; for (int k = 0; k < iTempH; k++) { for (int l = 0; l < iTempW; l++) { // 指向DIB第i - iTempMY + k行,第j - iTempMX + l个象素的指针 lpSrc = (unsigned char*)lpDIBBits + byteofLine * (lHeight - 1 - (i - iTempMY + k)) + (j - iTempMX + l); // 保存象素值 result += (*lpSrc) * fpArray[k * iTempW + l]; } } result *= fCoef; // 乘上系数 result = (FLOAT)fabs(result); // 取绝对值 if (result > 255) { *pTemp = 255; } else { *pTemp = (unsigned char)(result + 0.5); } } } // 复制变换后的图像 memcpy(lpDIBBits, lpNewDIBBits, byteofLine * lHeight); LocalUnlock(hNewDIBBits); LocalFree(hNewDIBBits); return TRUE;}
    //功能:该函数用来镜像DIB图像。可以指定镜像的方式是水平还是垂直。//参数:// LPSTR lpDIBBits 指向源DIB图像指针// LONG lWidth 源图像宽度(象素数)// LONG lHeight 源图像高度(象素数)// BOOL bDirection 镜像的方向,TRUE表示水平镜像,FALSE表示垂直镜像//返回值:镜像成功返回TRUE,否则返回FALSE。bool CMyDib::MirrorDIB(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, BOOL bDirection){ LPSTR lpSrc; // 指向源图像的指针 LPSTR lpDst; // 指向要复制区域的指针 LPSTR lpBits; // 指向复制图像的指针 HLOCAL hBits; LONG i; // 循环变量 LONG j; LONG lLineBytes; // 图像每行的字节数 lLineBytes = LineBytes(lWidth * 8);// 计算图像每行的字节数 hBits = LocalAlloc(LHND, lLineBytes); // 暂时分配内存,以保存一行图像 if (hBits == NULL) { return FALSE; } lpBits = (char *)LocalLock(hBits); // 判断镜像方式 if (bDirection) // 水平镜像 { // 针对图像每行进行操作 for (i = 0; i < lHeight; i++) { // 针对每行图像左半部分进行操作 for (j = 0; j < lWidth / 2; j++) { // 指向倒数第i行,第j个象素的指针 lpSrc = (char *)lpDIBBits + lLineBytes * i + j; // 指向倒数第i行,倒数第j个象素的指针 lpDst = (char *)lpDIBBits + lLineBytes * (i + 1) - j; // 备份一个象素 *lpBits = *lpDst; // 将倒数第i行,第j个象素复制到倒数第i行,倒数第j个象素 *lpDst = *lpSrc; // 将倒数第i行,倒数第j个象素复制到倒数第i行,第j个象素 *lpSrc = *lpBits; } } } else // 垂直镜像 { // 针对上半图像进行操作 for (i = 0; i < lHeight / 2; i++) { // 指向倒数第i行象素起点的指针 lpSrc = (char *)lpDIBBits + lLineBytes * i; // 指向第i行象素起点的指针 lpDst = (char *)lpDIBBits + lLineBytes * (lHeight - i - 1); // 备份一行,宽度为lWidth memcpy(lpBits, lpDst, lLineBytes); // 将倒数第i行象素复制到第i行 memcpy(lpDst, lpSrc, lLineBytes); // 将第i行象素复制到倒数第i行 memcpy(lpSrc, lpBits, lLineBytes); } } LocalUnlock(hBits); LocalFree(hBits); return TRUE;}
    //功能:水平移动DIB图像。函数不会改变图像的大小,移出的部分图像// 将截去,空白部分用白色填充。//参数:lpDIBBits 指向源DIB图像指针// lWidth 源图像宽度(象素数)// lHeight - 源图像高度(象素数)// lXOffset - X轴平移量(象素数)// lYOffset - Y轴平移量(象素数)//返回值:平移成功返回TRUE,否则返回FALSE。bool CMyDib::TranslationDIB1(LPSTR lpDIBBits, long lWidth, long lHeight, long lXOffset, long lYOffset){ LPSTR lpSrc; // 指向源图像的指针 LPSTR lpDst; // 指向要复制区域的指针 LPSTR lpNewDIBBits; // 指向复制图像的指针 HLOCAL hNewDIBBits; LONG i; // 象素在新DIB中的坐标 LONG j; LONG i0; // 象素在源DIB中的坐标 LONG j0; LONG lLineBytes; // 图像每行的字节数 lLineBytes = LineBytes(lWidth * 8);// 计算图像每行的字节数 // 暂时分配内存,以保存新图像 hNewDIBBits = LocalAlloc(LHND, lLineBytes * lHeight); if (hNewDIBBits == NULL) { return FALSE; } lpNewDIBBits = (char *)LocalLock(hNewDIBBits); for (i = 0; i < lHeight; i++) // 每行 { for (j = 0; j < lWidth; j++) // 每列 { // 指向新DIB第i行,第j个象素的指针 // 注意由于DIB中图像第一行其实保存在最后一行的位置,因此lpDst // 值不是(char *)lpNewDIBBits + lLineBytes * i + j,而是 // (char *)lpNewDIBBits + lLineBytes * (lHeight - 1 - i) + j lpDst = (char *)lpNewDIBBits + lLineBytes * (lHeight - 1 - i) + j; // 计算该象素在源DIB中的坐标 i0 = i - lXOffset; j0 = j - lYOffset; /*i0 = i; j0 = j;*/ // 判断是否在源图范围内 if ((j0 >= 0) && (j0 < lWidth) && (i0 >= 0) && (i0 < lHeight)) { // 指向源DIB第i0行,第j0个象素的指针 // 同样要注意DIB上下倒置的问题 lpSrc = (char *)lpDIBBits + lLineBytes * (lHeight - 1 - i0) + j0; *lpDst = *lpSrc; // 复制象素 } else { //对于源图中没有的象素,直接赋值为255 *((unsigned char*)lpDst) = 255; } } } // 复制平移后的图像 memcpy(lpDIBBits, lpNewDIBBits, lLineBytes * lHeight); LocalUnlock(hNewDIBBits); LocalFree(hNewDIBBits); return TRUE;} //取反菜单的响应函数void CmyDibView::OnAddcolor(){ CmyDibDoc* pDoc = GetDocument(); LPSTR lpDIB; // 指向DIB的指针 LPSTR lpDIBBits; // 指向DIB象素指针 lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)pDoc->GetHDIB()); // 找到DIB图像象素起始位置 lpDIBBits = pDoc->GetDibImage()->FindDIBData(lpDIB); int imgWidth = pDoc->GetDibImage()->DIBWidth(lpDIB); int ImgHeight = pDoc->GetDibImage()->DIBHeight(lpDIB); int byteofLine = LineBytes(imgWidth * 8); LPSTR lpNewDIBBits; // 指向复制图像的指针 HLOCAL hNewDIBBits; //指向一块内存区域的指针 // 暂时分配内存,以保存新图像 hNewDIBBits = LocalAlloc(LHND, byteofLine * ImgHeight); if (hNewDIBBits == NULL) { return ; } lpNewDIBBits = (char *)LocalLock(hNewDIBBits); // 初始化图像为原始图像 memcpy(lpNewDIBBits, lpDIBBits, byteofLine * ImgHeight); for (int y = 0; y<ImgHeight; y++) { for (int x = 0; x<imgWidth; x++) { lpNewDIBBits[y*imgWidth + x] = 255 - lpNewDIBBits[y*imgWidth + x]; } } memcpy(lpDIBBits, lpNewDIBBits, byteofLine * ImgHeight); LocalUnlock(hNewDIBBits); LocalFree(hNewDIBBits); Invalidate();}
    3 评论 24 下载 2018-10-31 15:04:10 下载需要10点积分
  • 基于Qt实现的图形系统

    一、概述
    本系统拟完成一个图形系统,对多种常见图形进行基本操作
    系统功能

    二维图形的输入:可输入或全部清除不同颜色的直线、矩形、圆、椭圆、多边形、曲线、铅笔工具二维图形的编辑:对于直线、矩形、圆、椭圆、多边形、曲线,可以通过鼠标拖拽标出的控制顶点来进行编辑二维图形的剪裁:可通过拖拽编辑矩形的剪裁窗口对当前直线进行剪裁二维图形的变换:在直线、矩形、圆、椭圆、多边形、曲线内部可通过鼠标拖拽进行平移,通过按钮进行左右旋转和翻转,以及缩放二维图形的存储:可将图形存储在选择的路径下三维模型的显示:可选择加载OFF文件,显示对应的三维模型,并通过鼠标拖动转换视角
    环境说明

    IDE:Qt Creator 4.8.0Qt版本:Qt 5.4.0 (mingw491_32)开发语言:C/C++Debuggers:GNU gdb 7.8 for MinGW 4.9.1 32bit
    编译运行说明


    二、算法介绍系统包括的算法主要是二维图形的操作、三维模型的显示和用户操作的响应。
    2.1 二维图形2.1.1 基本图形对于各种图形,虽然其具体的实现细节有所不同(主要体现在图形的绘制),但它们都有一些公共的属性和操作,比如计算图形的范围和中心点的位置,以及图形平移、旋转、缩放操作。这些属性和方法的算法是相同的。
    1. 图形范围对于直线、矩形、圆、椭圆、多边形、曲线,遍历其控制点的值,获得minX、maxX、minY、maxY。上述图形绘制后形成的所有点,不会有点的x值不在[minX, maxX]或y值不在[minY, maxY]中。四个值可以确定图形的范围,同时用于边框的绘制。
    2. 中心点中心点位置为(centerX, centerY),其中centerX = (minX + maxX) / 2,centerY = (minY + maxY) / 2。中心点可用做旋转、缩放的基准点。
    3. 平移图形的绘制依赖于控制点,因此只要更新每个控制点的信息即可。在图形平移方法中,传入的参数为x轴方向移动距离的值tx和y轴方向移动距离的值ty。对于(x, y)经平移(tx, ty)向量后的坐标为(x + tx, y + ty)。
    4. 旋转图形旋转传入的参数为角度数θ。控制点(x, y)顺时针旋转θ后,是以中心点(centerX, centerY)为基准的,坐标变为(centerX + (x - centerX) cosθ - (y - centerY) sinθ, centerY + (x - centerX) sinθ + (y - centerY) cosθ )。
    5. 缩放图形缩放传入的参数为在x方向上缩放比例为sx、在y方向上缩放比例为sy。控制点(x, y) 在x方向上缩放比例为sx、在y方向上缩放比例为sy后,坐标变为(x sx + centerX (1 - sx), y sy + centerY (1 - sy))。
    2.1.2 直线对于直线,只需要确定两个顶点的坐标作为控制点。直线生成有DDA算法、中点画线算法和Bresenham算法。
    1. 线生成DDA算法线生成的DDA算法,主要是在x和y中选择“变化得快”的方向,在这个方向上选取等距的一个个值,计算出每个值对应的另一方向的值。介于x和y的值都是整数,因此每次的“增量”至少为1,计算得到的另一个值往往不是整数,是需要四舍五入的,同时后者也可能是不精确的。如果选择“变化得慢”的方向作为基准,则每次迭代,另一方向上的变化幅度就会大于1,可以想象这种算法下点会比较“稀疏”,不如“稠密”的点组成的线精密。
    具体来说,当直线的斜率(m)的绝对值大于1时,就是所谓的y轴“变化得快”,选取y轴进行取样,当斜率绝对值小于1时则要选取x轴。或者在实现上,可以直接比较两个顶点x轴上和y轴上的差值的绝对值,在差值绝对值较大的方向上取样,计算差值较小的方向上的取值。确定方向后可以得到两个方向上每次取样的增量,结果应该是取样方向上增量为1,计算方向上增量为[-1, 1]之间的值(绝对值为min( |m|, |1/m|))。
    迭代计算出直线上每个点的位置。由于c++语言的特性,取值可以是加0.5再取整。部分伪代码如下:
    dx=x2-x1; dy=y2-y1; e=(fabs(dx)>fabs(dy))?fabs(dx):fabs(dy); dx/=e; dy/=e;for(int i=1;i<=e;i++) { SetPixel((int)(x+0.5), (int)(y+0.5)); x+=dx; y+=dy; }
    2. 中点画线算法根据直线的两个点,得到直线的方程f(x, y) = a*x + b*y +c。取样方向的选择同DDA算法,下面讨论在x轴上取样、斜率为正的情况,y轴同理。
    对于第i个选择的像素(xi ,yi),其下一位置的候选像素点为(xi+1, yi)和(xi+1, yi+1),取其中点(xi+1, y+1/2)代入直线方程,计算出来的决策参数为d = a * (xi+1) + b * (yi+1/2) +c。当d < 0时,中点是在直线的下方的,则靠上位置的(xi+1, yi+1)更靠近直线,如果d <= 0则选取(xi+1, yi)作为下个像素点。
    根据上述方法进行迭代,至到达顶点。
    3. 直线生成的Bresenham算法Bresenham算法中对于两个候选的“下一个”像素点的选取是与中点画线算法相同的。△y和△x分别为线段端点的垂直和水平偏离量(整数)在第k步,决策参数为pk = 2△y • xk - 2△x • yk +c,第k+1步的决策参数为pk+1 = 2△y • xk+1 - 2△x • yk+1 +c,根据决策参数的符号来决定像素的选取。
    4. 直线的裁剪 - Cohen-Sutherland算法根据如图所示的方法对直线段的两个端点进行编码,得到两个编码结果进行比较。完全在窗口边界内的线段,两端点区域码均为0000;完全在裁剪矩形外的线段,对两个端点区域码进行逻辑与操作,结果不为0000;不能确定完全在窗口内外的线段,进行求交运算,求出线段与裁剪框的交点坐标,作为线段的新端点坐标。

    2.1.3 矩形矩形由四个点首尾相连而成。事实上,只需要对角线的两个控制点,就可以组合出四个顶点的信息。矩形的绘制利用2.1.2中实现的直线生成算法,绘制出四条直线。
    2.1.4 圆圆可以根据两个控制点形成的矩形来框定,选取了两个控制点x轴、y轴上差值较大的一个作为圆的直径。圆的函数为f(x, y) = x^2 + y2^2 - r^2。其画法可以看作是下述椭圆的特例,不再具体描述。只不过因为对称性,只需要画出1/8的部分,即只需要画出区域1,且区域1和区域2的分界点就是x = y的时候。
    2.1.5 椭圆椭圆可以依靠两个控制点形成的一个矩形来规定范围,生成的椭圆内切在其中。椭圆的圆心就是中心位置(centerX, centerY),x轴上的直径ra和y轴上的直径rb分别是两点x轴、y轴上差值的。
    根据图形平移的理论,可以以原点为圆心计算出椭圆上的点(x, y),再变换成(x + centerX, y + centerY)。因为椭圆的对称性,可以在以原点为圆心的基础上,只进行第一象限的计算。椭圆的绘制采用中点椭圆生成算法。
    中点椭圆生成算法如图所示,定义椭圆函数f(x, y) = ry^2 * x^2 + rx^2 * y^2 - rx^2 * ry^2。以圆切线斜率绝对值为1作为划分,第一象限可以分为区域1和区域2。实际上,第一象限的切线斜率为负数,斜率dy/dx = -2ry^2 * x / (rx^2 * y),因此区域1进入区域2的条件是2ry^2 * x >= rx^2 * y。区域1中x轴“变化得快”,在x轴上取样,区域2则在y轴上取样。从(0, ry)或(rx, 0)开始,进行迭代,计算出椭圆上每个点的位置。下面指出区域1中的计算方法,区域2中同理。

    椭圆上的点的决策参数的初始值为p0 = ry^2 - rx^2 * ry + rx^2 / 4。假设第k次选择的像素为(xk, yk),为确定下一次选择的像素,计算x(k+1)位置两个候选像素点的中点,即(xk+1, yk)和(xk+1, yk-1)的中点(xk+1, yk-1/2),将其代入椭圆函数求出决策参数。

    若p1k < 0,则这个中点在圆内,(xk+1, yk)更接近真实的圆;若p1k >= 0,中点在不在圆内,选取(xk+1, yk-1)作为下一个像素点。迭代至循环结束,最后将第一象限的每个像素点映射到其余三个象限,再从原点将圆心移动到原本的中心位置。
    2.1.6 多边形多边形的生成也以直线生成为基础,利用直线的绘制将各个多边形的顶点作为控制点首尾相连起来。在实现上,多边形还需要一个额外的变量来记录控制点是否输入完全(首尾共点)。
    2.1.7 曲线曲线的绘制需要若干控制点,通过计算各个像素点的位置,将所有控制点按照顺序平滑地连接起来。曲线分为Bezier曲线和B样条曲线。
    1. Bezier曲线给定Pk=(xk,yk,zk),(k=0,1,2,…,n)共n+1个控制点,这些点混合产生位置向量BEZk,n(u)是Bernstein多项式,BEZi,n(u)=C(n,i)ui(1-u)n-i,其中C(n,i)=n!/[i!(n-i)!] (i=0,1,…,n) 。利用Bernstein基函数的降(升)阶公式,可使用递归计算得出Bézier曲线上点的坐标位置:BEZk,n(u)=(1-u)BEZk,n-1(u)+uBEZk-1,n-1(u),其中BEZk,k(u)=uk, BEZ0,k(u)=(1-u)k。
    2. B样条曲线给定n+1个控制顶点{Pi}(i=0,1,…,n),P0P1…Pn为控制多边形 n+k+2个参数节点向量:Un,k={ui|i=0,1,…,n+k+1,ui≤ui+1}。称如下形式的参数曲线P(u)为k+1阶(k次)B样条曲线:其中,Bi,k+1(u)为k+1阶(k次)B样条基函数。Bi,k+1(u)双下标中下标k+1表示k+1阶(k次)数、下标i表示序号。
    2.1.8 填充区域填充区域需要先有一个像素包围的封闭图形框架,然后计算出其内的所有像素点的位置。算法有扫描填充图元生成和区域填充图元生成。
    1. 扫描填充图元生成扫描填充的核心是“扫描”,一般从多边形的顶点出发,移动一条横越图形的扫描线,每次求交点,从左至右配对存储这些交点。扫描线每与两条线相交,这条线的交点表上就会有添加两个交点。需要处理共享顶点两条边位于扫描线同侧和异侧的情况。交点表的存储可以利用活化边表、排除不必要的求交测试的有序边表。
    2. 区域填充图元生成区域填充需要从一个连通的图形内的一个种子点开始,扩展到整个区域,直至遇到了边界。该算法比较适合实现“油漆桶”类似的功能。
    由于区域填充有着“递归”的思想,递归的结束条件至关重要,即对于“遇到边界”的判断。在此用到了图形4连通和8连通的内容,对于像素4连通的区域,其边界像素只需要是8连通的,也可以是过约束的4连通边界;而8连通的区域,边界像素只能是4连通的,如果边界是8连通,则欠约束,内部的点会通过对角线位置上没有填充像素的边界的空缺,泛滥到边界之外,造成全局的填充。
    此外,像素4连通区域定义的图形相对简单,8连通区域可以定义更复杂的图形,比如两个像素间只通过对角线形成连通的图形。
    2.1.9 铅笔工具铅笔工具的控制点是鼠标移动的轨迹,每个点都是控制点需要记录。为了保持轨迹的连贯性,可以在每两个距离很小的相邻点间用直线连接。
    2.2 三维模型取读OFF文件并显示其表示的三维模型,重点是在了解OFF文件存储的格式以及其表示的内容。
    OFF文件第一行为“off”字符串,第二行是三个整数,分别表示了这个模型的点数量numV、面数量numF、边数量numE。接下来的numV行是点的信息,每行都是每个点的x、y、z值,值的定义域为[-1,1]的浮点数,同时也确定了点的编号(从0开始)。然后是numF行的面信息,每行第一个整数表示这个面拥有的点的数量,接下来的几个整数是点的编号。
    在三维模型中,需要将面打印出来,也就是将每个面的顶点按顺序输出。
    2.3 主界面主界面是实现用户各种操作的接口,主要对于不同的鼠标事件进行响应。
    三、实现本系统采用面向对象的设计方法,将类分为“二维图形”、“三维模型窗口”、“主界面”三种。
    3.1 二维图形3.1.1 基本图形 Shape
    基本图形记录的信息有:

    图形类型:ShapeType typeenum ShapeType { BLANK, _LINE, _CURVE, _RECT, _CIRCLE, _OVAL, _POLY, _PEN, _FILL};图形控制点:QVector\<QPoint\*> points图形中点信息:int centerX, centerY图形范围:int minX, maxX, minY, maxY图形的颜色和绘制画笔风格:QRgb rgb, QPen pen
    基本图形共有的相同实现的方法有:

    构造函数:Shape(QVector\<QPoint\*> points, ShapeType type, QRgb rgb, QPen pen);改变某一控制点point为某值a:void setPoint(QPoint* point, QPoint a)判断a是否在图形某一控制点point上,允许有1个单位的误差:bool isAroundPoint(QPoint* point, QPoint a);着重打印出控制点(x, y)以便鼠标拖拽:void paintVertex(QPainter& p, int x, int y);移动某一控制点point改变(dx, dy),算法见2.1.1:void movePoint(QPoint* point, int dx, int dy);旋转某一控制点point弧长为arg,算法见2.1.1:void rotatePoint(QPoint* point, double arg);缩放某一控制点point,x轴上比例为sx,y轴上比例为sy,算法见2.1.1:void zoomPoint(QPoint* point, double sx, double sy);翻转某一控制点point,isV为true代表是竖直方向,否则为水平翻转,只需要让point的值以(centerX, centerY)作对称求值即可:void flipPoint(QPoint* point, bool isV);
    图形中还有一些共有的操作,具体实现有所不同,可以设置为虚函数:

    设置当前最后一个控制点为a,只有两个控制点的图形只需要更新points.last(),多边形和曲线另外实现:void setPoint(QPoint* point, QPoint a);判断当前的位置a是否是在某一控制点上并返回,基本思想是遍历控制点,调用isAroundPoint():QPoint* isAround(QPoint a);当前的位置a是否在图形内,主要根据范围的四个值来判断:bool isInside(QPoint a);更新图形的信息,如中心点、范围:void refreshData();绘制图形:void paintShape(QPainter& p, QImage* image, bool isSave);绘制当前图形的框架,主要绘制图形范围形成的矩形,加重控制点的绘制:void paintFrame(QPainter& p);移动图形增量为(dx, dy),基本思想是对每个控制点调用movePoint():void move(int dx, int dy);旋转图形弧长为arg,基本思想是对每个控制点调用rotatePoint():void rotate(double arg);缩放图形x和y轴上的比例分别为sx和sy,基本思想是对每个控制点调用zoomPoint():void zoom(double sx, double sy);翻转图形,对控制点进行flipPoint(),对称的矩形、圆、椭圆不用翻转:void flip(bool isV);裁剪图形(直线),保留在两点形成的矩形内的部分:void cutShape(QPoint* cutStartPos, QPoint* cutEndPos);

    3.1.2 直线 Line直线的特征体现在只有(x1, y1)和(x2, y2)两个控制点,属性的更新也是在这两个点的基础上进行。
    直线的剪裁首先确定裁剪框的大小,再对两个控制点进行编码,即调用int getCode(int x, int y, int minXW, int maxXW, int minYW, int maxYW)。获得的编码的四位从高到低依次表示是否在裁剪框的上下左右位置。如果编码不为0,则表示控制点在裁剪框外,需要根据直线的方程确定出交点。例如,如果第一个控制点在裁剪框的左侧,则可以计算出新的x = minXW时的y值。
    int newY = (int) points.first()->y() + m * (minXW - points.first()->x()) + 0.5;points.first()->setY(newY);points.first()->setX(minXW);
    对于直线来说,可以改变它的两个端点,“在直线内”的定义是在它的外接矩形内。
    3.1.3 矩形 Rect矩形主要注意四个顶点的顺序,只有两个控制点,这两个控制点间不是相连的。在更新控制点时,需要改变的是第二个控制点的值,同时需要重新计算其余一条对角线上的两个顶点的位置。绘制时需要新建直线对象,调用直线的绘制函数。
    3.1.4 圆Circle圆也是由两个控制点形成的,需要注意的是半径的设置,同时也是外接正方形四个顶点统一的问题。计算出两个控制点间的差值,选择较大的一个作为直径,为了方便后续的计算,在refreshData()中也更新控制点的值。下面给出了如果x轴方向差值较大的更新情况。
    int len = fabs(points[0]->x() - points[2]->x());int width = fabs(points[0]->y() - points[2]->y());if (len > width){ if (points[0]->y() < points[2]->y()) points[2]->setY(points[0]->y() + len); else points[2]->setY(points[0]->y() - len);}
    因为并非所有圆上的像素信息都保存在图形信息内,为减少判断的计算量,在外接正方形内,都算是“在圆内”,可以进行拖动等操作。此外,圆不需要实现旋转和翻转。
    3.1.5 椭圆 Oval椭圆也是根据外接矩形来确定各个参数的,绘制的具体算法在2.15中已经指出。对于平移和对称特性,在实现时,使用了void ovalPoints(QPainter& p,QImage* image, bool isSave, int centerX, int centerY, int x, int y)函数,对于计算出的第一象限的(x, y),绘制(x + centerX, y + centerY)、(-x + centerX, y + centerY)、(x + centerX, -y + centerY)、(-x + centerX, -y + centerY)。
    在外接矩形内就算做“在椭圆内”。此外,椭圆是不需要进行翻转的。
    3.1.6 多边形 Poly多边形的一组控制点作为多边形的顶点,每一个都是用户自定义的,需要按照顺序用直线连接起来。较之于矩形,多边形会有“封闭”与否的状态,实时绘制时,在未封闭的时候,最后一个控制点是不会与第一个控制点相连的,只有当用户最后点击起始点时才会封闭。在实现上,为多边形设置一个bool变量isEnd来记录这个图形的绘制是否完成。在每次setPoint时,是在新增控制点,判断工作交由refreshData()处理。在更新信息时,判断最后一个点是否是在起始点附近,如果是,则这个多边形绘制完成,并且将最后一个顶点的指针指向第一个顶点。同时要求多边形的顶点个数不能少于3。
    多边形只有在绘制完成后才会描绘出边框。在多边形各个控制点进行平移、旋转、缩放时,需要注意,最后一个控制点其实就是第一个控制点,不要再进行第二次的变换。
    3.1.7 曲线 Curve曲线对于控制点输入的处理与多边形类似,只不过再此设定曲线输入结束的标志为当输入的顶点与最后一个控制点重合(在附近)时,更新后的最后一个控制点指针其实是与倒数第二个相同的。在对各个控制点进行平移、旋转、缩放时,也需要忽略最后一个控制点。
    3.1.8 填充区域 Fill对于“油漆桶”功能,用户只需要输入一个点的位置,采取区域填充的方式,递归不断加入需要填充的像素的位置。为了避免填充区域颜色和边界颜色相同导致的过早停止填充问题,只要像素是白色qRgb(255, 255, 255)就不是边界,不是边界则继续递归。
    对于“实心”图形,在绘制边框的时候已知边界,在绘制同时填充内部区域。
    3.1.9 铅笔工具 Pen为了确保连续,在点之间用直线进行相连。铅笔工具与多边形类似,只是不需要对于封闭性有所判断,也没有编辑、平移、旋转、缩放的功能。

    3.2 三维模型 Window三维模型的点信息用MyVertex结构描述。面信息定义为MyFace,其中numV表示面上的点数,indexV记录各个点的编号。
    struct MyVertex{ GLfloat vx; GLfloat vy; GLfloat vz; MyVertex() {} MyVertex(GLfloat x, GLfloat y, GLfloat z) : vx(x), vy(y), vz(z) {}};struct MyFace{ int numV; QVector<int> indexV;};
    在主界面上可以再打开一个用于显示三维模型的窗口。这个窗口定义为Window类,包含了的信息有:

    点、面、边的个数:int numVertices, numFaces, numEdges
    所有点信息:QVector\<MyVertex> vertices
    所有面信息:QVector\<MyFace> faces
    用于鼠标控制模型转动的记录信息:QPoint* startPos、QPoint* endPos、double rotateX、double rotateY

    类中实现的方法主要是:

    初始化:void initializeGL()
    绘制模型:void paintGL()
    鼠标事件:void mousePressEvent(QMouseEvent *ev)、void mouseMoveEvent(QMouseEvent *ev)、void mouseReleaseEvent(QMouseEvent *ev)

    3.2.1 读取文件QString fileName = QFileDialog::getOpenFileName(this, tr(“Open Config”), “”, tr(“OFF Files (*.off)”));可以获取到选择的off文件的文件名,ifstream inFile(fileNameChar, ios::in);获得文件流,进行对象的初始化。
    3.2.2 绘制模型 paintGL()glBegin(GL_POLYGON)与glEnd()间绘制模型,对每个点vij进行glVertex3f(vij.vx, vij.vy, vij.vz)的绘制。为了显示出模型内部构造,利用glCollor3f()给顶点设置几种不同颜色。
    在绘制的最后,可能需要对模型进行旋转,旋转的角度保存在rotateX和rotateY中,根据鼠标事件实时更新。
    glRotated(rotateX, 0.0, -1.0, 0.0);glRotated(rotateY, -1.0, 0.0, 0.0);
    3.2.3 鼠标事件记录鼠标位置的改变,映射成旋转的角度。
    double x = (double)(endPos->x()-startPos->x())/this->width()*60;double y = (double)(endPos->y()-startPos->y())/this->height()*60;
    3.3 主界面 MainWindow主界面是实现用户各种操作的接口,主要对于不同的鼠标事件进行响应。
    主界面中包含的信息有:

    所有图形的存储:QVector\<Shape\*> shapes当前表示的图形类型:ShapeType curShapeType当前绘制中或绘制完成可编辑的图形:Shape* curShape当前画笔的类型:QPen curPen用于存储图像的信息:QImage* image、QRgb rgb用于鼠标移动的记录信息:QPoint* startPos、QPoint* endPos用于记录裁剪信息:QPoint* cutStartPos、QPoint* cutEndPos
    可以进行的操作:

    界面的绘制:void paintEvent(QPaintEvent *ev)鼠标事件:void mousePressEvent(QMouseEvent *ev)、void mouseMoveEvent(QMouseEvent *ev)、void mouseReleaseEvent(QMouseEvent *ev)各种按钮的响应,包括按钮和菜单
    3.3.1 图形绘制图形的绘制首先需要建立画笔,设置画笔的风格,将这个画笔作为参数传到各个图形中去。
    QPainter p(this);p.setPen(curPen);
    绘制需要遍历shapes,调用每个shape的paintShape()。如果curShape是完成绘制可编辑的,则可以绘制出其框架,调用curShape->paintFrame()。
    3.3.2 鼠标事件在鼠标按下事件中,分为几类操作:

    新建一个curShapeType类型的图形,需要更新curShape,设置当前图形为未完成的继续绘制当前图形,需要对当前图形的类型作出判断,只有多边形和曲线可以继续绘制,更新当前图形的信息编辑顶点,需要使得ev->pos()在图形控制点周围,开始记录这个控制点的变更移动当前图形,需要判断ev->pos()是否在图形内,只有在图形内才可以更新startPos开始记录移动的位置,并把图像状态标记为isMove = true进行裁剪,需要是在当前可编辑图形为直线的情况下
    鼠标移动事件是在鼠标按下事件之后、鼠标释放之前进行的,也可以不进行这个阶段:

    继续绘制图形,更新图形的控制点改变图形某一个控制点更新移动信息中的endPos编辑裁剪窗口
    鼠标释放事件对当前鼠标行为做收尾工作:

    完成绘制,将当前图形更新为可编辑状态完成编辑完成移动,将startPos、endPos清空,isMove设置为false完成剪裁,调用直线的剪裁方法,再清空剪裁信息
    在鼠标事件的所有操作进行之后,使用update()来调用paintEvent()。
    3.3.3 设置更改
    通过点击图形来更改curShapeType的类型
    通过点击调色板更改QPen的颜色

    3.3.4 图形存储
    通过点击按钮,保存当前图形image
    saveFileName = QFileDialog::getSaveFileName(this, tr("Save Image"), "", tr("JPG Files (*.jpg)")); //选择路径image->save(saveFileName);

    四、界面展示主界面

    二维图形(直线、矩形、圆、椭圆、多边形、曲线、填充图形)的输入

    二维图形的编辑

    二维图形的平移、旋转和缩放

    二维图形的保存结果

    三维图形的显示


    五、参考文献
    《计算机图形学教程》
    Qt画图小项目 https://blog.csdn.net/qianqin_2014/article/details/51236712
    Docs/.NET/.NET API浏览器/System.Drawing/Graphicshttps://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics?view=netframework-4.7.2
    圆和椭圆算法https://blog.csdn.net/qq_32307691/article/details/73437967
    B样条曲线 https://blog.csdn.net/Jurbo/article/details/75125663
    Bezier曲线 https://blog.csdn.net/jurbo/article/details/75069054
    Qt5.7.1中使用QImage保存成JPG文件到本地 https://blog.csdn.net/ZefinNg/article/details/81150683
    OFF文件格式 https://www.cnblogs.com/youthlion/archive/2012/02/04/2337790.html
    易百教程 3D图形 https://www.yiibai.com/jogl/jogl_3d_graphics.html
    2 评论 8 下载 2020-09-25 09:54:16 下载需要13点积分
  • 基于javaEE实现的在线音乐系统

    1、概述
    开发环境

    Windows10EclipseTomcat 9.0Mysql 8JDK 10
    运行环境

    Tomcat 9.0Mysql 8Chrome 71

    2、系统的需求分析2.1 系统可行性分析本系统是采用Java Web技术的B-S架构的网站,用到的技术有Java EE 、CSS、JavaScript、Ajax。为了使用的效果以及开发的简便,在前端使用了CSS的Bootstrap及其UI组件框架,JS的Jquery库以及Ajax技术。在线音乐是获取的外部站点的API提供查询服务,并且每一次播放在线音乐会将其数据保存到本地数据库。本地音乐是从本地数据库中获取数据展现到用户界面。
    2.2 系统功能描述2.2.1 用户管理
    用户注册:用户可以点击注册按钮进入注册界面,注册属于自己的账号
    用户登录:用户使用自己的账号登录系统
    用户设置:可点击个人设置,修改个人信息,例如昵称,头像等

    2.2.2 播放界面
    音乐播放[播放与暂停]:对在播放器中的音乐进行播放和暂停
    音量调节:对正在播放器中的音乐的音量大小进行调节

    2.2.3 歌单与音乐管理
    歌单管理:对歌单的增删查改
    音乐管理:将音乐添加进歌单,将音乐从歌单中删除

    2.2.4 音乐搜索
    在线搜索:在网络上搜索获取音乐资源
    系统乐库:获取系统所在服务器上的音乐资源

    2.3 系统的数据流图
    2.4 系统UML建模设计
    2.5 系统的状态图登陆模块状态图

    登陆模块状态图

    2.6 系统的UML类图本系统后台总共使用了四个包,其功能与关系如下。

    utils:封装了连接数据库和关闭数据库的操作
    dto:包含封装了某个对象的所有信息的类
    dao**:封装了对数据库的操作
    servlets:包含处理前端发送的各种信息的servlet

    以上四个包都位于cn.edu.whpu.music包下,它们的关系如下图所示。

    3、系统总体设计3.1 系统结构方框图
    3.2 各模块功能用户信息模块
    包含功能有用户的注册,用户的登录,用户个人信息的修改和用户个人信息的展示。
    搜索音乐模块
    包含功能有在线音乐搜索,本地乐库搜索。
    歌单管理模块
    包含功能有添加新的歌单,修改歌单信息,删除歌单。
    收藏歌曲模块
    包括添加音乐到指定歌单,从歌单中删除音乐。
    3.3 详细的UML类图用户信息模块


    DBManager:封装了连接数据库和关闭数据库的操作
    UserDTO:封装了用户个人信息的类
    UserDAO:封装了操作用户个人信息的类
    UserRegistServlet:用户注册的servlet
    UserLoginServlet:用户登录的servlet
    EditUserInfoServlet:修改用户个人信息的servlet

    搜索音乐模块(本地乐库)


    DBManager:封装了连接数据库和关闭数据库的操作
    MusicDTO:封装了音乐信息的类
    MusicDAO:封装了操作音乐信息的类
    LocalMusicServlet:处理点击本地乐库按钮后的请求并返回数据的servlet

    注:在线音乐搜索是用ajax请求的网络上的API接口获取数据并渲染到主页面,与该处的servlet无关。
    歌单管理模块


    DBManager:封装了连接数据库和关闭数据库的操作
    MusicListDTO:封装了歌单信息的类
    MusicListDAO:封装了操作歌单信息的类
    AddMusicListServlet:添加歌单信息的servlet
    InitMusicListServlet:通过用户ID查询并返回其对应的歌单的servlet
    DeleteMusicListServlet:删除歌单的servlet
    UpdateMusicListServlet:更新歌单的servlet
    ShowMusicsFromListServlet:通过用户ID和歌单ID查询并返回其对应的歌单的servlet

    收藏歌曲模块


    DBManager:封装了连接数据库和关闭数据库的操作
    MusicDTO:封装了音乐信息的类
    MusicDAO:封装了操作音乐信息的类
    ListMusicRelationDTO:封装了音乐与歌单对应关系信息的类
    ListMusicRelationDAO:封装了操作音乐与歌单对应关系信息的类
    CollectionMusicServlet:收藏音乐进入指定歌单的servlet
    CancelCollectMusicServlet:从歌单取消某音乐收藏的servlet

    3.4 设计数据管理子系统因为使用java编写的并发量不是特别大的后台程序,所以使用了更为轻量级的mysql 8。其好处有如下几点:

    普及性:MySQL在过去两年已经获得了25%的市场份额。相比其他的开源数据库和闭源数据库,越来越多的开发者将继续选择MySQL。MySQL在业界的流行所带来的另一个好处是,人们总可以很轻松地发现本行业的解决方案
    简单性:对于MySQL数据库,无论是在开发方面,还是支持方面,现在有大量强大的工具可以选择。每一个新手开发者可以轻松地使用MySQL数据库进行开发。甚至一个有经验的Windows管理者也可以轻松部署并开始学习它,不需投入一分钱来了解这个数据库
    低成本:MySQL数据库归MySQL AB公司所有,但是这个软件是开源的,有一个社区版可以免费下载。稍俱常识的新入门者都可以轻松实现在一个常见硬件上安装和配置MySQL。MySQL对硬件的较低要求是其最大的优势之一,不过需要注意的是:内存越多越好,因为所有的重要数据存储都在内存中完成。一个免费的数据库意味着,更多珍贵的资金可以用于其他业务的启动,诸如市场、广告或调研和开发等

    3.5 系统E-R图
    系统E-R图如上图所示。主要逻辑有:

    一个用户在某一时刻只能播放一首歌曲
    一个用户可以创建n个歌单
    一个音乐可以被n个歌单收藏,一个歌单也能收藏n首音乐

    3.6 数据库表
    本系统该次设计了四张表:

    tb_users表记录用户信息
    tb_musiclists表
    tb_list_music表
    tb_musics表

    3.7 数据库表之间的关系关系1
    tb_users表和tb_musiclists表是一对多的关系。其中tb_musiclists中表的list_uid字段必须遵照tb_users表中的user_id。即一个用户可以有多个歌单,而一个歌单只能由一个用户。

    关系2
    tb_musiclists表和tb_musics表是多对多的关系,并且tb_list_music表记录了它们之间的对应关系。tb_list_music表中lid参照tb_musiclists中的list_id字段,tb_list_music表中mid参照tb_musics中的music_id字段。并且lid与mid作为该表的联合主键。即一个歌单可以收藏多首音乐,一个音乐也能被多个歌单收藏。

    3.8 数据库表结构tb_users表

    tb_musics表

    tb_musiclists表

    tb_list_music表

    3.9 设计人机交互子系统3.9.1 登陆界面登录界面引用了jquery.video库,将登录的背景设置为一个动态的循环播放的视频,极具科技感和新鲜感。
    3.9.2 注册界面注册界面沿袭登陆界面的大体设计,并在注册信息上使用了jquery.validate库对于用户输入注册所需要的信息进行了初步的判断,避免录入冗余、错误的信息。
    规则有:

    用户名:必须输入,长度为6-20个字符
    密码:必须输入,长度为8-16个字符
    重复密码:必须输入,必须与密码一致
    用户昵称:必须输入,长度为20字符以内
    性别:必须选择
    头像:必须上传

    3.9.3 主界面整体布局
    使用一个界面避免用户进行跳转。采用bootstrap框架的栅栏式布局。大致布局如下图所示。

    顶部导航栏
    整体采用bootstrap的导航栏样式,如下图所示。左侧依次为Logo区,在线搜索区和本地乐库区。当在在线搜索的输入框中输入内容,并点击搜索按钮后,会在主界面呈现出搜索内容。当点击本地乐库按钮后,会在主界面呈现出系统自带的音乐列表。

    右侧为用户栏,点击歌单按钮会弹出“添加歌单”、“管理歌单”功能按钮。点击“添加歌单”按钮会弹出模态框。点击管理歌单会在歌单栏出现编辑和删除按钮。点击消息按钮会弹出“查看消息”功能按钮。点击用户按钮会弹出“修改信息”和“退出”功能按钮。点击修改信息会弹出修改用户信息的模态框。详情在模态框中介绍。
    主区域

    主界面大致分为左、中、右三个区域。按照1:2:1的分配区域。在点击导航栏中的“管理歌单按钮后”,歌单区域进入编辑模式。点击主区域的播放按钮和收藏按钮,歌单区的编辑和删除,用户区的头像均会弹出模态框。详情在模态框中介绍。
    唱片CD会在歌曲播放的时候旋转如下图所示。

    播放器区域
    播放器方面我们开发小组认为原生的AUDIO标签还有市面上找到的播放器插件不契合我们的样式,于是从5sing音乐网站的播放器部分借鉴了样式,并为静态的样式添加了拖动歌曲进度,实时显示已经播放时间,和调节音量大小的动态效果。其主要布局和功能如下图所示。

    模态框
    为了使界面不发生跳转,我们将主界面中所有的操作运用了bootstrap的模态框UI组件。在点击“歌单➡添加歌单”,“用户➡修改信息”,“歌曲➡收藏按钮”,“用户➡头像”,“歌单列表➡修改”,“歌单列表➡删除”时均会弹出相应的模态框组件。


    整体效果

    4、详细设计4.1 登陆注册模块流程图
    4.2 在线搜索模块流程图
    4.3 本地乐库模块流程图
    4.4 歌单管理模块流程图
    5、总结通过为期五天的课设,我收获颇多。最直观的感受就是在这次在线音乐网站的开发中能够系统性的运用从大一到现在所学到的知识。新学习到的Servlet的知识更是让我明白了为何之前写过的html只能叫做静态页面,还有一个网站的前端是如何与后台进行数据交互。同时这次也让我从平时的文件级别的代码编写上升到了工程级别的项目开发。在其中更好了理解了这个学期学过的关于软件开发的步骤。认识到了文档编写在软件项目开发中不可替代的重要性。我觉得收获最深的地方就是以前只觉得写代码才是最重要的部分,现在明白了项目管理、需求分析、文档编写才是项目开发中更重要的部分。让我把眼界从代码拓宽到了更高的层次。同时还深刻认识到自己在项目开发领域还存在着诸多的不足,明白了以后要学习的地方还有许多。
    当然,在项目开发期间也遇到过许多问题:
    老师教授的是用后台修改动态数据,而我们想将前端和后台完全分离,这就存在了一个如何将后台数据打包成JSON发送到前端的问题。
    解决方案:在老师的指导下使用了阿里巴巴公司的fastjson这个jar包来打包数据。同时在前端使用了axios来实现通信和局部页面刷新。
    因为一开始没有编写规范的文档,导致在开发途中想要增加功能的时候会发现不知道更改项目的哪个地方。他人接手编写的时候还要重新看一遍代码,效率低下。
    解决方案:停下开发工作,转头编写规范的文档,统一了数据库和测试数据。并且集中到一起开发,使得遇到什么问题小组成员之间可以很快的交流并解决。
    每个人有自己的代码编写风格,而为了赶时间没有在文件中书写规范的注释。结果在互相测试小组成员单元时发现看不懂对方的代码,导致浪费极多时间。
    解决方案:就算再赶时间也要书写规范的注释,写明类的用途,写明方法的用途以及接受参数和返回参数的类型、作用,这样在测试或者修改的时候会节省大量时间。
    在开发过程中想要对功能进行增加、删除、修改或者是想要复用一段代码时发现代码编写冗余没有规范。导致代码难以维护、复用程度低,重构成本极高。
    解决方案:在将来的项目开发中一定要灵活运用设计模式,设计模式是前辈留给我们的解耦、复用代码的成功经验,虽然使用设计模式在编写的时候可能会造成困难,但是在维护、更新的时候会体现出设计模式的优越性。
    13 评论 245 下载 2019-04-16 15:00:42 下载需要14点积分
  • 基于JSP实现的学生会管理系统

    摘 要目前高校学生会已经成为学生组织中的翘楚,在各高校内,学生会已经起到了作为学生和学校之间的桥梁作用,然而学生会在学校内作用的发展已经遇到了瓶颈。随着信息技术和计算机技术的不断发展,继企业之后,高校也在进行着信息化的改革,比如学生学籍管理系统,图书馆系统、学生选课系统等,但是相比于高校这些机构的信息化建设,学生会的信息化发展在各高校内基本为零。学生会的信息化建设是信息时代发展的必然趋势,也是高校提高竞争力的必然条件。
    本系统具有运行速度快、安全性高,跨平台,很强的可移植性。学生会管理系统基于WEB的java技术,采用MyEclipse为开发工具,利用MySQL作为数据库服务器进行数据管理。通过介绍了多层应用软件体系结构并采 B/S 模式结构的综合考虑,系统包括学生会干部、学生会成员两个角色,学生会干部实现了在考勤管理、活动记录、个人考评、活动管理、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能;学生会成员登录系统实现了查看个人考勤、活动记录、个人考评、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能子模块。
    关键词:JSP;学生会管理;B/S架构
    AbstractCurrently college students has become a leader in student organizations within the university, students will have to play as a bridge between the students and the school’s role, however, in the role of students in the school’s development has encountered a bottleneck. With the continuous development of information technology and computer technology, the following enterprises, universities are engaged in information technology reforms, such as the Student Management System, a library system, student elective system, but compared to the information these organizations universities construction, development of information technology students in colleges and universities essentially zero. Student of information technology is the inevitable trend of development of the information age, but also a necessary condition to improve the university’s competitiveness.
    The system has run fast, safe, cross-platform, highly portable. WEB-based student management system java technology, using MyEclipse development tools, the use of MySQL as a database server for data management. Through the introduction of multi-application software architecture and adopt comprehensive consideration of B / S architecture, the system includes a student union, student members of the two roles, student cadres to achieve the attendance management, activity records, personal evaluation, event management, work plan Information viewing, document management, modify personal information, modify the login password and other functions; student members login system to achieve personal view attendance, activity records, personal evaluation, work plans, information viewing, document management, modify personal information, modify the login password, etc. functional sub-modules.
    Keywords: JSP; student management; B / S architecture
    1 前言1.1 课题的背景及意义随着计算机技术的发展,特别是计算机网络技术与数据库技术的发展,人们的生活与工作方式发生了很大的变化。在利用网络技术允许计算机与可以实现的,使用的数据库技术提供了数据存储,检索和信息等功能的分析,提高了工作效率和质量的信息共享通信。
    数据库始于1960年,经过40多年的发展,已经较为成熟的理论体系已经成为计算机软件的一个重要分支。数据库技术展示先进的数据管理方法,因此使用电脑,真正渗透到经济和社会发展的各个领域,尤其是在数据处理领域中扮演着越来越重要的作用。
    随着改革开放和符合中国国情和独特的,教育的发展市场经济的发展一直受到国家和社会的大力支持,使对规范快速发展和现代化,但与发达国家相比,一些技术的使用的教育部门在教育部门,只有在旧的,大型机构的大城市信息,仅使用电脑进行操作,提高工作效率,实现社会效益和经济效益都不错,和一些新的,更小的机构并不都已经此功能。随着中国教育的快速发展,在教育管理信息技术将更加广泛和深入。
    在计算机技术和管理教育的快速发展并没有改变在一些地区和学校手动操作被广泛应用,特别是在贫困地区职业学院的西部。传统的管理模式,大量的成本,而且容易出错的低效率。随着计算机网络技术和数据库技术,教育和信息技术管理发展的发展是一个必然的趋势。
    目前的事态发展高等教育的一个重要步骤,增加科学和技术的力量,让高等教育面临着更大的发展机遇。教学的学院和大学的质量提高,同时管理高等教育的一些机构仍停留在手工,学生的注册,组织,统计等大型工作,往往是错误的水平。
    而在一些高职院校建立校园网,办公自动化,但是,也有大量的学校并不具备这样的条件。虽然一些学校使用电脑,也管理体系,但仍然存在不少问题,一些系统的功能还不够完善,制度不够稳定得到进一步的发展。因此开发学生学生会管理系统,实现部分办公自动化就具有很大的实际意义。
    1.2 国内外研究现状MIS(Management Information System)管理信息系统系统,是由人、计算机及其他外围设备组成的能够进行信息收集、传递、存贮、加工、维护和使用的系统。这是上个世纪的新的科学的兴起,是利用计算机和网络通信技术,加强信息管理,有资源做研究,并建立准确的数据。经过再加工成各种汇编资料,并提供给管理人员,使他们能够做出正确的决策,提高管理水平和工作效率。
    高校,我们大多是信息管理大规模始于20世纪90年代,晚于西方发达国家10-20年。在沿海地区和城市都比较先进,近年来高校信息化建设一直发展非常迅速,已被广泛应用于各个领域,如教学,科研和管理大学,并取得了良好的效果。如清华大学和北京大学已经开发出一种高速网络的主校区,包括学术研究,办公管理,社会服务和数字化管理系统等功能。
    在学校信息化建设之初,一般根据自己的需要,各部门和学校制定运行自己的业务系统,并配置一个独立的IT环境(包括机房,UPS,服务器等)。这会导致过量的设备,部署服务器收入的物理位置。这使得该系统的灵活性,完整性和可用性的数据差,而且还增加了学校的整个IT基础设施的总成本,而且还增加了系统的复杂性,增加了较低的操作的级别为每个系统不能达到预期的效果,需要。这碎片也是一个发展模式拥挤的学校信息。和因特网的发展和增长正好解决了这一问题。每所学校开始将各职能部门的小型数据中心和数据中心为基础的学校建设。从物理上,将各部门的系统集中起来,并运行在统一的IT设施上。
    1.3 课题研究方法与技术路线1.3.1 研究方法根据所给资料得出系统的基本的框图,划分功能模块,应用语言编程,应用Myeclipse与MySQL作为数据库服务器来开发本系统。系统包括学生会干部、学生会成员两个角色,学生会干部实现了在考勤管理、活动记录、个人考评、活动管理、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能;学生会成员登录系统实现了查看个人考勤、活动记录、个人考评、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能子模块。
    1.3.2 技术路线本系统采用B/S(Browser/server,浏览器/服务器)三层体系结构,这种模式采用多种标准的协议和技术,适合于任何硬件平台和软件环境。本系统采用当今社会比较先进的三层框架技术开发一个性能优越、可扩展性强和安全可靠稳定的学生会管理系统。
    2 相关技术介绍2.1 JSP技术简介JSP(Java服务器页面服务器Pages)是一种新技术自1998年开始出现。由Sun Microsystems公司倡导,参与了动态网页技术标准,许多公司一起建立了一个基于Web的Java开发技术JavaServlet以及整个系统。 (*.HTM,*.HTML)加入脚本片段和JSP标记(tag)在传统的网页HTML文件,构成了JSP网页(*.JSP)。 JSP技术来创建动态生成内容的Web页面的显示提供了一种简单的方法。 Java的JSP作为家族的一员,继承了Java的,即跨平台特性的特点,那一次,随处运行。
    在其他国家,已被广泛用作JSP的Web应用程序开发工具,并在国内,尽管JSP技术的发展不是主流,但由于JSP的强大优势,很多网站都已经准备转向JSP,使用JSP开发动态网站。
    2.2 JSP工作原理JSP是一个面向服务器的,因此支持任何浏览器。当Web服务器和JSP引擎遇到请求访问JSP页面时,JSP引擎将请求发送到相关的对象的服务器端组件,如JavaBean组件,Servlet或者EJB等,然后由服务器端组件处理这些转介,您可能需要从数据库或检索存储在数据信息,那么服务器端响应对象的部分,然后返回给JSP引擎。 JSP引擎响应对象传递给JSP页面,根据HTML格式的JSP页面组织完整的内容,JSP页面的最后Web服务器和格式化客户端浏览器的HTML格式后,JSP引擎将返回。这被广泛用于构建当前Web浏览器 - Web服务器 - 一个三层的后端数据库。由于JSP所有程序操作都在服务器端执行,网络上传到客户端是唯一得到的结果。
    2.3 JSP体系结构JSP的Web开发标准给出了使用JSP技术两种方式可以概括为一个模型,模型二。
    模式1:JSP + JavaBean技术在这种模式下,响应由个人请求JSP页面,并将结果返回给客户端。处理所有的数据访问bean,JSP页面来实现的性能,生成并显示内容,以实现相分离。当使用一个大型的,复杂的应用处理,嵌入了大量的页面脚本或Java代码段,当需要处理复杂的业务逻辑,这种情况会很糟糕,很多内联代码编写一个复杂的页面未来的界面设计师,这是不可想象的。因此,一款型号为他们的小应用程序不能满足大规模应用的需求。
    模式二: JSP + Servlet的+ JavaBean技术使用Java Servlet技术是一种技术,实现了技术的CGI功能, Servlet技术非常适合于服务器端的处理和编程,并且Servlet的将是一个长期驻留在内存中。
    从发展的角度来看,更清晰的图案两页的性能,开发人员分工明确的角色,在一个大型项目开发,模式两个时,模型中的两个也更符合目前流行的MVC架构行(模型/视图/控制器) ,其中Servlet的相应的控制器,位置控制器在处理HTTP请求,负责生成豆类JSP组件或使用的对象,并确定哪个JSP请求应该发送给对方, JSP对应的视图,最终负责用于生成动态网页,并返回到浏览器。对应的JavaBean模型,每一个具体的应用程序逻辑实现的功能。
    2.4 JSP的特点2.4.1 简化页面生成技术使用标准的HTML或XML JSP页面指令来处理格式和页面布局,而类似的HTML , XML标记语言和Java脚本来生成页面内容。这使得页面的表单和页面的内容是独立的,分工是非常有利于大型项目。
    2.4.2 与Java平台的有机整合JSP技术是Java 2平台, JSP中的Java语言作为其脚本语言的重要组成部分。 JSP页面可以在几乎所有的Java组件和Java API,它可以充分发挥Java语言的强大功能可以使用。 JSP技术来创建高度可扩展和可靠的Web应用程序。
    2.4.3 硬件平台和服务器的独立性Java的JSP作为一个家庭,秉承了Java技术的“一次编写,随处可用(一次写入,朗姆酒任何地方) ”功能,可以在最流行的操作系统平台和Web服务器上运行,服务器硬件和平台无关的操作系统是一个JSP的动态网页对其他技术的最大优势。
    2.4.4 个功能的可扩展性像微软的JSP技术可以通过ActiveX / COM组件一样,采用jsp + javabean和EJB ( Enterprise JavaBean组件)和自定义标记扩展来扩展功能。
    JSP通过JDBC ,以及诸如Oracle,MySQL等这样的大型关系数据库进行连接。
    JSP提供了一些隐藏的对象。这些隐藏的对象可以在JSP页面中直接引用,而不必首先声明。使用这些隐式对象JSP提供,可以使脚本更强大,更容易编程,使用方便。例如,使用request对象,你可以很容易地得到一个HTML表单提交的用户信息。
    2.5 系统数据库连接JDBC Java数据库连接技术的缩写,它是一种常见的功能支持由SUN (应用程序编程接口)提供了基本的SQL应用程序编程接口。它由一组类在Java语言和接口。通过这些类和接口,应用程序开发人员可以使用Java语言和数据库链接很容易地建立员工,通过执行相应的SQL语句来完成访问不同的数据库。因此,开发人员可以使用JDBC API ,而无需编写应用程序访问Sybase数据库,另一个编写一个应用程序访问Oracle数据库,编写一个应用程序来访问MySQL 。此外,使用Java语言的应用程序可以在任何支持Java的平台上运行,而不需要开发不同平台上的不同应用。
    简单地说, JDBC可以完成以下三件事情:

    建立与数据库的连接
    建立与数据库的连接
    由数据库返回的结果

    JDBC是一个普遍的低级应用程序编程接口,它提供了在不同的数据库级别的功能模块一个统一的用户界面。说JDBC是一种低级的API ,是指它直接调用SQL命令,它更容易使用一些API,比其他一些数据库连接,但它也可以用来作为一种更先进,更人性化的API做的人脸或发展工具的基础。
    许多可视化Java开发工具如Visual年龄对于Java ,视觉咖啡厅, J + +等提供了一个基于JDBC的多面向用户的类和包直接到表或视图的映射关系型数据库中的Java类,通过可视化编程工具直接在Java对象进行操作,并且SQL调用真正需要的是自动生成的基于对象的各种属性的操作发射的程序员,方法。另一种方法是使用JDBC API ,用户程序可以提供一个接口(如菜单等),以允许用户选择数据库的操作,选择一个任务,系统会提示用户输入必要的信息,然后生成基于用户输入的SQL命令和Java程序的合适的。通过这一点,用户可以完成对数据库的操作,即使他不明白的SQL语法和JDBC编程。
    数据库访问层结构,浏览器程序来访问数据库,首先通过中间件,中间件和数据库操作通过对数据库操作的权限进行认证。

    用户访问数据库的中间件认证完成后,数据库查询,插入,更新和删除操作都封装在中间件,中间件服务器端,对数据库进行操作,然后将结果返回给浏览器最终用户的中间件通过Web服务器。以这种方式,客户端输入用户名和密码,加密算法可以通过网络进行传输进行加密,解密的认证中间件,然后访问操作,数据库访问用户名和密码在位于服务器端中间件,从而更安全。该系统采用了这样一个三层的数据库访问模式的结构。
    在三层模式时,命令将被发送到“中间层”服务和“中间层”将SQL语句发送到数据库中。数据库处理SQL语句并将结果返回“中间层”,他们返回给用户“中间层”。其模型如图2-2所示。

    因为“中间层”可以控制和协调对数据库的访问进行更新,你可以使用一个易于高层次的API,它可以通过“中间层”进行转换,转换成底层调用。因此,在许多情况下,三层模型可以提供更好的性能,在系统中使用该模型。
    JDBC是一个桥梁JAVA应用软件和数据库。它提供三种服务:一是建立与数据库的连接。其次,通过SQL语句到数据库。三,结果从SQL语句的数据库中获得。当前者建立一个JDBC连接到数据库,首先,它必须首先获得该数据库的JDBC驱动程序,调用Class.forName (连接)执行此工作。建立数据库连接的第一步是将JDBC驱动程序类装载到JVM ( Java的VirtualL机) ,按照指定的类名, JDBC驱动程序的使用本系统内的forName java.lang.Class中的类( )静态函数加载英寸完成这些步骤来加载驱动程序必须使用规定的getConnection ( )静态函数java.sal.DriverManager下课后,所做的对象和数据库之间的连接。这种连接对象类类型是java.sal.Connection ,它必须被传递到SQL命令到数据库,并把结果还需要打通的连接对象。当对象获得连接,你必须获得一个Statement对象来执行对数据库的SQL命令。语句实现两个主要功能:执行SQL语句,并取得了成果。执行SQL查询java.sql.Statement中的对象或修改命令功能后返回ResultSet对象,提供对SQL的执行管线的结果,为了通过数据表从数据库中删除它。每个Statement对象只能产生一个ResultSet对象。
    在图2-3所示的数据库连接:

    有了JDBC ,发送SQL语句就是一件很容易的事,以各种关系数据。换言之,有了JDBC API,就不必访问专门写一个程序访问Oracle数据库和专门写一个程序或访问Informix数据库和写另一个程序,等等Sybase数据库,只需使用JDBC API的程序员写一个程序就够了,它可以将呼叫发送到适当的SQL数据库。同时,结合Java语言和JDBC使程序员不必编写不同的应用为不同的平台,您可以简单地写一遍程序将运行在任何平台上,这是Java语言“一次编写,到处运行”的优势。
    Java数据库连接体系结构是一种标准的方式为Java应用程序连接到数据库。在JDBC API的实现,数据库连接模式界面条款的Java程序员和服务提供商。作为一个API , JDBC提供程序开发的标准接口,并提供了一个标准方法,数据库厂商和第三方中间件厂商实现与数据库的连接。 JDBC使用现有的SQL标准,并支持如数据库之间的ODBC桥连接标准。 JDBC来执行所有这些标准,并具有面向对象的接口,便于实现确定的输入和严格的性能。
    Java有一个强大的,安全的,易于使用,易于理解,可以从互联网和其他功能自动下载,是编写数据库应用程序的优秀语言。这种方法只有一个具有多种不同的Java应用程序需要对话数据库。 JDBC是一种机制,用于上述用途。
    JDBC的Java扩展功能。例如,使用Java和JDBC API可以发布含有applet的网页,并且这些信息可能被使用的小程序从远程数据库企业可以通过内联网使用JDBC向全体员工将被连接到一个或多个内部数据库(即使这些员工使用的电脑有多种不同的操作系统Windows,Macintosh和UNIX等) 。随着越来越多的程序员开始使用Java编程语言,为方便从Java要求的数据库也越来越高。
    2.6 BS模式与C/S模式的比较分析C / S模式,主要是由客户端应用程序(客户端) ,服务器管理程序(服务器)和中间件(中间件)三部分组成。客户端应用程序是用户与系统的数据组件交互。服务器程序负责系统资源,如管理信息数据库的有效管理,其主要工作是当多个客户端同时请求同一个服务器上的资源,以优化这些资源的管理。中间件是负责连接客户端应用程序和服务器管理器,一个合作作业完成,以满足用户的查询的管理数据的要求。
    B / S模式是一种基于Web的技术平台的新MIS系统模式。传统的C / S模式,服务器部分被分成一个数据服务器和一个或多个应用服务器(Web服务器) ,从而构成一个三层客户机 - 服务器体系结构。
    第一层是在用户和客户机在整个系统之间的接口。简化客户端应用程序到一个通用的浏览器软件,如Netscape Navigator ,微软的IE浏览器等。浏览器的HTML代码到图文并茂的网页。现场也有一些互动功能,使用户输入提交给后台提供的网络申请表上,并处理请求的信息。第二层是背景的Web服务器。
    Web服务器的第二层将启动进程来响应此请求,并动态生成一串HTML代码,其结果是嵌入式处理,返回给客户端的浏览器。如果客户端提交的请求包括数据访问,Web服务器和数据库服务器需要完成协同处理。
    第三层是数据库服务器的任务类似于C / S模式,负责协调SQ不同的Web服务器发送请求,管理数据库。
    B / S模式的优势
    首先,它简化了客户端。它并不需要安装像C / S模式不同的客户在不同的客户端应用程序,并简单地安装一个通用的浏览器软件。这不仅节省了客户端的硬盘空间和内存,使安装过程更简单,更灵活的网络架构。假设一个企业决策者开座谈会库存问题,他们只是直接从电脑查询数据通过浏览器的会议室,然后显示给大家看的。与会者还可以把连一台笔记本电脑相连的网络接口上的会议室,自己的查询相关的数据。其次,它简化了系统的开发和维护。系统开发人员不再需要为不同级别的用户设计和不同的客户端应用程序的开发,而只是把所有的功能都在Web服务器上实现,不同的功能,为用户在其上设置权限为每个组。每个用户通过调用权限范围内的HTTP请求一个不同的Web服务器上的处理程序,从而完成数据的查询或修改。现代企业面临着不断变化的竞争环境中的企业运作机制内的更新和调整已经变得越来越频繁。相对于C / S,维持B / S拥有更大的灵活性。当情况发生变化,它不再需要为每个应用程序升级现有客户,但只是为了对Web服务器进程的服务进行修改。这不仅会提高公司的运作效率,而且还省去了麻烦的维护协调很多。如果一家公司有数千家客户,并分布在不同的位置,因此便于维护将变得更加重要。
    再次,它使用户的操作更容易。对于C / S模式,客户端应用程序都有其特定的规格,使用者需要接受专门的培训。而采用B / S模式,客户端只是一个简单易用的浏览器软件。无论是决策或操作层面的员工有没有培训,可直接使用。这种特性的B / S模式还允许限制因素MIS系统维护工作量少。
    最后, B / S特别适用于网上信息传播,传统的MIS系统的功能不断扩大。这是一个C / S无法实现。而这个新的在线信息发布仅仅是一个现代企业的需求。这使得大部分的书面文件可以通过电子文件取代,从而提高企业的工作效率,简化办事程序,使企业,节省了人力物力。
    鉴于B / S相对于C / S的性质, B / S已经成为一种流行的MIS系统平台。软件公司纷纷推出自己的互联网项目,基于Web的财务系统,基于Web的ERP 。一些公司已经开始使用它,领先一步,并收到了一定的效果。
    新颖和流行的B / S模式,并优先在某些方面相对于显著高于C / S ,使B / S成了MIS系统平台的首要选择。

    3 系统分析3.1 系统可行性分析可行性研究的目的是用最小的代价判断在问题定义阶段所确定的系统的目标和规模是否能实现,所确定的问题是否可以解决,系统方案在经济上、技术上和操作上是否可以接受。
    3.1.1 经济可行性JSP(Java Server Pages)是“基于Java的服务器段动态页面设计”,软件方面只需要Java虚拟机。通过文本编辑器就可以实现系统开发,并且Java虚拟机可以在Sun公司的官方网站上免费下载。从资金投入上看,建立较完备的学生会管理信息化平台,投入从几万到几十万不等,大多数学校管理部门是可以承受起的。通过至顶向下分析逐步求精的方法对系统进行设计,并通过维护使系统逐步完善,从而达到经济上的节约。
    3.1.2 软件和硬件条件从硬件环境来看,校园网硬件已相当完备,学生会管理信息化平台可依靠学校自身的网络环境和计算机设备。从软件环境来看,目前已经开发的类似系统存在着单一,功能过于简单大部分功能不能实现。基于这些原因,我们着手设计和开发更适合当今学校学生会管理的软件系统。随着计算机得到广泛应用,高校学生学生会管理者和大部分员工习惯使用计算机办公和通过上网查找信息。
    3.1.3 技术可行性JSP(Java Server Pages)是基于JavaServlet及整个Java体系的Web开发技术,它由Sun Microsystems公司在1996年6月推出。JSP在HTML代码中嵌入Java程序片断,并使用各种JSP指令,构成了JSP页面。这种页面可以完成操作数据库、上传文件等复杂的逻辑处理功能。另外,还通过支持JavaBean实现了功能扩展。JSP技术让动态网页的编写更加容易、功能更强,可移植性、可扩展更好。总结起来它具有:

    将内容的生成与显示分离
    支持可重用组件
    采用标签简化页面开发
    一次编写,处处运行的特点

    从技术上看,该系统可以运行于 windows 系列操作系统当中,可以为系统提供一个稳定的运行环境,在数据库方面也显得比较有利。因此更加节省了该系统开发经费,该系统应该说有开发的必要性。
    3.1.4 操作可行性从操作角度来分析,本系统操作简单方便,只要懂得上网都能方便操作本系统。所以在操作上也是可行的。
    3.2 系统功能需求概述系统需求文档是为了能在总体上对该软件有个整体认识,能从整体上把握方向,不至于盲目而使得浪费人力物力,从而达到节省时间、经费的目的。该项目的名称是“学生会管理系统”,开发此软件是由于市场上的同类软件不能全面的满足当前学校学生会管理的需要。因此该软件的开发自然而然提上了日程。该软件的开发主要用了 Java JDK1.5 开发工具和Sqlserver2005数据库软件,其中涉及了很多有关数据库知识如:数据库的插入,删除,更新以及数据库的安全性和参照完整性等将在设计过程中体现出来。
    本课题要实现的是学生学生会管理系统,在设计该系统时,应尽可能的贴近学生管理人员,便于用户操作。系统应提供简单、层次关系明了、清晰的操作界面,使用户一目了然。尽可能的为用户的录入、查询等功能操作提供方便。系统包括学生会干部、学生会成员两个角色,学生会干部实现了在考勤管理、活动记录、个人考评、活动管理、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能;学生会成员登录系统实现了查看个人考勤、活动记录、个人考评、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能子模块。
    3.3 系统设计规则与运行环境3.3.1 设计规则无论哪个网站都要有它自己的设计规则。该系统也一样,它的主要设计规则有:

    简单性:在实现系统的功能的同时,尽量让系统操作简单易懂,这对于一个系统来说是非常重要的
    针对性:该系统设计是学生学生会管理系统及后台管理的定向开发设计,所以具有专业突出和很强的针对性
    实用性:该系统能完成活动信息管理和文档管理的基本信息,具有良好的实用性

    3.3.2 运行环境本系统是一个WEb版的应用程序,需要在服务器上部署中间件Tomcat、MySQL数据库,其他的客户端通过网络进行访问该服务器即可。

    硬件平台:

    CPU:酷睿i3 -3.0GHZ内存:2G以上硬盘:320GB
    软件平台:

    操作系统:Window XP 或Win 7均可数据库:MySQL数据库

    第四章 总体设计4.1 架构设计软件架构设计,以达到以下目标:

    可行性,体系结构是可行的是建筑设计的基石
    可靠性,软件系统是为用户的业务经营及管理,软件系统至关重要,因此,必须是非常可靠的
    安全线,通过软件系统进行的交易的商业价值高,安全系统是非常重要的
    可定制,同样的一套软件,可以根据客户和市场的不同需求而有所不同进行调整
    可扩展性,在新的技术出现,一个软件系统应当允许引进新技术,从现有系统和扩展的性能的功能
    可维护性,维护软件系统包括两个方面,一个是排除现有的错误,二是新的软件需要反映在现有的系统中。一个易于维护的系统可以有效地降低技术支持成本
    可扩展性,软件在用户的数量,以便能够使用情况下,用户迅速增加,保持合理的性能

    4.2 接口设计4.2.1 外部接口用户界面
    用户界面即为Windows窗口。用户通过web页面上的菜单,按钮,文本框等元素与系统进行沟通。
    软件接口
    开发工具采用MyEclipse开发平台开发。
    硬件接口
    包括传统的与键盘,鼠标,显示器的接口,用来输入和输出。另外,还有与打印机的接口,以便打印报表和统计结果。也是用于输出。
    4.2.2 内部接口内部接口,使用不同的功能模块,参数传递的调用,返回按值传递的信息。具体参数结构将在结构设计数据的上下文中进行说明。接口的信息传输是基于一个数据结构,其包括数据,以形成一个参数或模块之间传递的返回值。
    4.3 代码设计4.3.1 代码设计的原则代码设计遵循以下原则:

    确定性,实体和每个代码的属性仅代表;相反,信息系统,每个实体或属性与特定的代码来表示
    标准化,考虑到企业信息系统部门,负责通信和网络的需求,尽可能与国际标准代码,国家,部门
    多样化,尝试使用原始代码和有效的业务流程已经在使用,使用方便
    扩展性,考虑到业务的发展和变化,增加实体和新的,直接用原代码的属性被添加时,保持系统不改变原代码
    识别性,使用需要,方便管理代码,和电脑应该很容易识别,分类
    简明性,在不影响编码和可扩展编码场所尽可能简洁统一的能力

    4.3.2 系统所需代码设计代码设计原则

    系统性:宏观把握,全局共享
    灵活性:易于修改,扩充能力强
    可靠性:安全,检错纠错能力,抗病毒能力
    经济性;使用第一,切记华而不实

    代码的功能
    便于数据的存储和检索,使信息系统在数据输入时更简单;通过代码提高了计算机处理的效率和精度;提高了数据的全局一致性;代码是人和计算机交流的一种工具或共同的语言。
    代码设计应注意的问题
    代码设计在逻辑上应满足用户需求。代码唯一性。预留发展空间,以适应变化的需要。尽量使代码结构有实际意义。避免引起误解,不要使用易混淆的字符。采用不易出错得代码结构。代码过长时,要注意分成小段。
    代码的种类

    顺序码
    区间码
    层次码

    4.4 系统功能图椐据对系统需求分析中的总体目标和具体目标部分的分析,系统包括学生会干部、学生会成员两个角色,学生会干部实现了在考勤管理、活动记录、个人考评、活动管理、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能;学生会成员登录系统实现了查看个人考勤、活动记录、个人考评、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能子模块。得到系统的体系结构如图4-1所示:


    第五章 数据库设计数据库设计,系统设计是一个非常重要的方面。数据是所有系统的设计,通俗地说,数据库设计为高层建筑的基础,因为如果设计不合理,不完善,将在系统开发过程中,甚至到了系统的后期维护的基础上,功能的变化和功能扩展时,造成更多的问题,甚至是严重的重新设计时,重做了很多工作已经完成。
    5.1 数据库的选定目前主流数据库主要有Oracle、SQL Server、Mysql、Access等。本系统运行在windows服务器上故而选择了MySQL。MySQL是一个关系型数据库,它的数据库引擎为关系型数据和结构化数据提供了更为安全而且可靠的存储功能,而且完全开放源代码。MySQL与 Microsoft Visual Studio、Microsoft Office System 以及新的开发工具包(包括 Business Intelligence Development Studio)紧密集成。
    5.2 安全性设置MySQL中存放了所有用户的数据,保障数据安全放置用户的隐私泄露至关重要。数据库安全性设置包括:

    安装数据库时使用混合模式,数据库密码不能为空,在安装完成数据库后将超级用户名修改或者是删除。同事使用安全的账户策略。对用户实施最强的保护,我们要使用一个强壮的密码
    禁止管理员接触数据库,禁用多余的账户
    管理扩展存储过程。我们要将不必要的存储过程删除掉,以免被别有用心的人利用从而破坏我们的数据库
    对远程的网络连接进行IP限制,使用系统提供的IPSec策略。拒绝非法IP所做的端口连接,将威胁降到最低
    设置服务器安全,打开服务器安全=》属性安全=》启用登录审核中的失败与成功登录,启用C2审核跟踪,监视所有数据库试题的所有访问

    5.3 数据库设计规范在概念设计中,通常用四种方法:
    自顶向下:先定义,然后逐步细化的全球结构的概念框架;从底向上:先定义每个局部应用的概念结构,然后将它们集成在一起以得到一个全局概念结构;逐步扩大:首先定义的核心概念结构,然后向外扩展,直至整体概念结构;混合策略:自上而下和自下而上结合自上而下的策略来设计一个全球性的概念。其结构,每一个本地集成底向上策略设计的骨架结构的概念在物理结构设计阶段首先分两步走。确定数据库的物理结构,在关系数据库中主要是指存取方法和存储结构。 对物理结构的评价是时间和空间效率.
    选取正确的关系模式存取方法,常用的有:存取索引方法、聚簇存取、HASH存取方法等。
    5.4 数据库建模根据以上分析,能够提取以下几个实体。系统整体E-R图如下5-2所示。

    系统实体及实体属性分析:
    用户实体及其属性图

    活动实体及其属性图

    考勤实体及其属性图

    5.5 关系描述的设计根据以上各实体图和学生会管理系统的E-R图,经过转换后,可以得到如下各表。
    用户信息表,用于存储用户相关信息。



    中文名称
    数据类型
    主外键
    字段约束




    ID
    Int(4)
    Y
    NOT NULL


    用户名
    Vachar(8)
    N
    NOT NULL


    权限
    Vachar(20)
    N
    NOT NULL


    姓名
    Vachar(20)
    N
    NOT NULL


    性别
    Vachar(20)
    N
    NOT NULL


    生日
    Vachar(20)
    N
    NOT NULL


    联系电话
    Vachar(20)
    N
    NOT NULL


    部门
    Vachar(20)
    N
    NOT NULL



    活动信息表,用于存储活动信息。



    中文名称
    数据类型
    主外键
    字段约束




    ID
    Vachar(20)
    Y
    NOT NULL


    活动名称
    Vachar(20)
    N
    NOT NULL


    活动日期
    Vachar(20)
    N
    NOT NULL


    地点
    Vachar(20)
    N
    NOT NULL


    备注
    Vachar(20)
    N
    NOT NULL


    相关文档
    Vachar(20)
    N
    NOT NULL



    考勤信息表,用于存储考勤信息。



    中文名称
    数据类型
    主外键
    字段约束




    ID
    Vachar(20)
    Y
    NOT NULL


    姓名
    Vachar(20)
    N
    NOT NULL


    考勤日
    Vachar(20)
    N
    NOT NULL


    事项
    Vachar(20)
    N
    NOT NULL


    类别
    Vachar(20)
    N
    NOT NULL



    得到以上关系后,根据数据库相关理论的要求,对所有关系进行关系规范化,要求各个关系至少要达到第三范式的要求,按照第三范式的要求对比以上各个关系在所有关系中都不存在非主属对关键字的部分依赖,即满足第三范式。
    第六章 系统实现系统包括学生会干部、学生会成员两个角色,学生会干部实现了在考勤管理、活动记录、个人考评、活动管理、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能;学生会成员登录系统实现了查看个人考勤、活动记录、个人考评、工作计划、信息查看、文档管理、修改个人信息、修改登录密码等功能子模块。以下对这些功能模块的具体实现过程进行介绍。
    6.1 登录模块实现用户登录模块主要实现用户登录系统的功能,用户需要输入用户名、口令等信息,并点击登录按钮实现登录系统的功能。用户登录模块界面如下图6-1所示。

    用户登录模块流程图如图6-2所示。


    第七章 测试7.1 测试目的软件测试在整个软件设计中占有重要地位,软件测试之前,只能在运行和维护阶段,传统的瀑布模型,是确保交付给软件质量之前,用户的软件产品的一个重要途径。最近,新的软件工程往往会看到,认为软件生命周期的各个阶段应包括测试,以检查是否该阶段的结果预计将接近目标,找出错误并尽可能早地,如果不解决这些问题在早期阶段的测试,难度的误差传播延迟往往导致测试最终产品。
    事实上,有问题的软件,不管什么技术和什么方法,软件将仍然是错误的。采用一种新的语言,先进的开发方法,改进开发过程,可以减少引起的误差,但不能在软件中消除错误,它引入了需要测试发现错误,软件中的错误还需要密度估计进行测试。测试是所有工程学科的基本单元,是软件开发的一个重要组成部分。自天的测试程序设计参加。统计显示,在一个典型的软件开发项目,软件测试工作往往是软件开发工作的总负担的40%以上。和软件开发的总成本,其成本为这个测试是30%至50%。如果维护阶段也考虑在内,讨论软件寿命试验,整体成本的比例可以降低,但二次开发实际上等同于维修,以及一些发展,这也必须包含大量的测试。
    软件测试是最低价格发现系统的分析,设计,编码存在于各种各样的问题,从而提高系统的质量。附带收获试验可以确认是否按照规范的要求,系统的功能和性能;同时,在测试后所收集的数据的结果,提供了一系列可靠的软件。
    软件测试方法和技术各不相同,可以从不同的角度进行分类。测试系统是否与特定算法的角度实施的内部结构,可分为黑盒测试和白盒测试。
    黑盒测试主要是进行以测试系统的功能,这两个程序可为每个操作可以有效地履行其角色,并且不会有任何异常发生。对于每一个点的作业系统已经过测试,每一个环节和按钮操作可以准确地显示正确的JSP页面,对数据库中的每个操作可更新的,准确的数据到数据库在数据库中不作例外。该系统允许操作的每一个步骤,以避免滥用。软件测试在软件生命周期中占据重要的地位,在传统的瀑布模型中,软件测试学仅处于运行维护阶段之前,是软件产品交付用户使用之前保证软件质量的重要手段。近来,软件工程界趋向于一种新的观点,即认为软件生命周期每一阶段中都应包含测试,从而检验本阶段的成果是否接近预期的目标,尽可能早的发现错误并加以修正,如果不在早期阶段进行测试,错误的延时扩散常常会导致最后成品测试的巨大困难。
    事实上,对于软件来讲,不论采用什么技术和什么方法,软件中仍然会有错。采用新的语言、先进的开发方式、完善的开发过程,可以减少错误的引入,但是不可能完全杜绝软件中的错误,这些引入的错误需要测试来找出,软件中的错误密度也需要测试来进行估计。测试是所有工程学科的基本组成单元,是软件开发的重要部分。自有程序设计的那天起测试就一直伴随着。统计表明,在典型的软件开发项目中,软件测试工作量往往占软件开发总工作量的40%以上。而在软件开发的总成本中,用在测试上的开销要占30%到50%。如果把维护阶段也考虑在内,讨论整个软件生存期时,测试的成本比例也许会有所降低,但实际上维护工作相当于二次开发,乃至多次开发,其中必定还包含有许多测试工作。
    软件测试是以最少的代价发现系统分析,设计,编码中存在的不同类型的问题,从而提高系统的质量。测试附带的收获是能证实系统的功能和性能是否与需求说明相符;同时,测试后收集的结果数据提供了软件可靠行。
    软件测试的方法和技术是多种多样的,可以从不同的角度加以分类。从测试是否针对系统的内部结构和具体实现算法的角度来看,可分为黒盒测试和白盒测试。
    黑盒测试主要是针对系统的功能进行测试,既程序中每一个可用操作是否可以有效的行使其作用,并且不会有异常发生。针对这一点对该系统的每一个操作都进行了测试,即每一个链接和按纽操作都可以准确的显示出正确的JSP页面,每一项针对数据库的操作都可以将数据准确的更新到数据库中,不使数据库产生异常。系统会对用户的每一步操作进行提示,以免误操作。
    7.2 测试实例登录模块测试表



    测试用例
    操作过程及数据
    预期结果
    实际结果
    偏差




    输入已存在的用户名和密码
    用户名:admin 密码:admin
    登录成功
    登录成功



    输入不存在的用户名和密码
    用户名:hello 密码:hello
    系统提示用户名和密码不正确
    系统提示出现错误
    提示不同



    7.3 测试结果系统经过测试实现了基本功能,可以正常使用。但系统的功能还不够完善,在软件投入使用后,必然会有一些隐藏的错误被暴露出来。在以后的学习中,要加强这些方面的学习,多查阅相关资料,多实践,积累经验,避免上述的缺憾和限制。
    结论经过三个月的努力,我的毕业设计:学生会管理系统终于圆满设计完成了,其功能基本符合用户需求。
    这次设计,使我学到了很多书本上没有的知识,提高了我对问题的分析能力。在这些天不分昼夜实践和摸索中,我受益匪浅,感触良多。此外,通过这次毕业设计,使我感到我的所学知识还是不够的,还要继续多加努力。由于毕业设计时间较短,所以该游戏还有许多不尽如人意的地方,有待进一步改善。
    毕业设计是要将四年来所学到知识的贯穿,并创制出新的成果。通过这次的毕业设计也让我学会了如何去查找各种相关资料,以及遇到问题后该如何去找到解决问题的方法。经过这三个月的毕业设计,我发现自己遇到问题后不再那么慌张,可以沉着的去分析、解决问题。这对我以后的工作会有很大的帮助的。
    参考文献[1]田翔川,田忠和,谢志宇.JSP数据库连接池的设计[J].计算机应用研究,2009.
    [2]明日科技. Java从入门到精通(第3版)[M].北京:清华大学出版社,2012.
    [3]蒋宗礼,马涛,唐好魁,闫明霞等.数据库技术及应用(第2版)[M].电子工业出版社,2010:43-65.
    [4]刘瑞新,张兵义.大学计算机规划教材:SQL Server数据库技术及应用教程[M].电子工业出版社,2012,8.
    [5]潘利群,李耿.JavaBean在JSP中的应用研究[J]..武汉理工大学学报2010,25(5):65-67.
    [6]李盛恩,王珊.数据库基础与应用(第二版)[M].北京:人民邮电出版社,2009:14-78.
    [7]吴以欣,陈小宁.JavaScript脚本程序设计[M].北京:北京人民邮电出版社[M]2005.
    [8]郝玉龙.J2EE编程技术[M].北京:清华大学出版社,2005.
    [9]埃史尔.Java编程思想(第4版)[M].机械工业出版社,2007:768-855.
    [10]张大治,王欢.JSP实用教程[M].北京:清华大学出版社,2006.
    [11]张永.Java环境下JavaBean的分析与应用[J].计算机与现代化,2005,10(6):73-76.
    [12]张德详.J2EE架构下校园网用户管理系统的分析与部分实现[J].青岛大学学报,2010,19(4):86-89.
    [13]郑凯,刘爱芳.基于JSP的分页和页面保存技术的实现[J].计算机工程,2009,30(1):179-181.
    [14]李明江.动态网页技术JSP与ASP、PHP的比较浅析[J].黔南民族师范学院学报,2010.
    [15]Vivek Chopra,Jpn Eaves,Rupert Jpmdes. Beginning Java Server Pages[M],wrox,2005.
    1 评论 57 下载 2019-05-10 10:05:51 下载需要15点积分
  • 基于JAVA FX实现的酒店预订系统网站

    1 产品概述参考酒店预订系统用例文档和酒店预订系统软件需求规格说明文档中队产品的概括描述。酒店预订系统主要是应用于网上预定远程酒店订单的在线系统,主要功能见用例图如下。

    2 用户界面层的分解根据需求,系统存在30个用户界面:客户界面,酒店工作人员界面,网站管理人员界面,网站营销人员界面,注册会员界面,搜索酒店信息界面,查看酒店详细信息界面,查看预订过的酒店信息界面,浏览订单界面,生成订单界面,评价订单界面,查看信用记录界面,查看基本信息界面,修改基本信息界面,查看信用记录界面,执行正常订单界面,补登记异常订单界面,维护酒店基本信息界面,录入可用客房信息界面,管理客房状态界面,制定酒店促销策略界面,制定网站促销策略界面,管理异常订单执行情况界面,查看未执行订单界面,信用充值界面,酒店管理界面,用户管理界面,管理客户信息界面,管理酒店工作人员信息界面,管理网站营销人员界面。

    3 数据库表


    表名
    内容




    account
    帐号信息


    Client
    用户信息


    Credithistory
    信用修改记录


    Evaluation
    评价信息


    Hotel
    酒店信息


    Hotelmanager
    酒店管理人员信息


    Locations
    地址及商圈


    Orders
    订单信息


    Promotion
    促销策略


    Room
    酒店房间信息


    vipLevel
    Vip等级对应的信用值


    Webbusiness
    网站营销人员信息



    4 依赖视角客户端开发包

    服务端开发包

    5 界面原型图登录界面

    管理酒店信息

    房间信息

    订单列表

    酒店信息

    预定信息
    2 评论 105 下载 2018-11-05 15:32:19 下载需要13点积分
  • 基于QT实现的基础图形绘图系统

    摘 要PaintWorks实现了图形数据的输入、编辑、填充、裁剪、变换、显示与保存功能。可绘制图形包括直线、圆、椭圆、多边形、曲线和3D六面体。交互界面简洁美观,操作实用方便。
    本绘图系统采用了面向对象设计,以C++11为基础,交互使用了Qt,基础图形的绘制使用了OpenGL,直线绘制使用了Bresenham算法,圆的绘制使用了中点圆生成算法,椭圆绘制用了中点椭圆生成算法,曲线绘制使用了3次Bézier曲线,多边形填充使用了多边形扫描填充算法,线段裁剪使用了梁友栋-Barsky算法,多边形裁剪使用了逐边裁剪算法。
    1 软件概述1.1 软件用途本软件主要用于基础图形绘制与保存,绘制完成之后可以对图形进行进行编辑、填充、裁剪等操作。
    1.2 特点本软件操作与显示简洁自然,简单方便。
    图形越新,所在图层越高;当用户选中某一图形时会自动切换到该图形对应的绘制模式并将该图形移动到最高图层。
    本软件的所有操作均可通过点击鼠标完成,其中,PaintWorks为使用频繁的操作与较为精细的操作提供了快捷键。
    2 软件安装为了免去用户安装的烦恼,PaintWorks给出了绿色免安装版,解压之后直接点击安装包中的PaintWorks.exe便可运行。(注:经过测试,不解压就运行可能会导致未知错误)
    3 使用指南3.1 图形绘制3.1.1 画笔颜色选取点击上方工具栏中的颜色选取工具(或按快捷键Ctrl+P),即可打开颜色选取对话框选取画笔颜色,之后线条颜色和填充色均为所选取的颜色。

    若不选取颜色,则默认线条颜色为黑色,填充色为绿色。
    3.1.2 2D图形PaintWorks可以绘制直线、圆、椭圆、多边形、曲线等二维图形,并可以对圆和多边形进行填充。绘制前首先点击菜单栏中的“新建画布”(或按快捷键Ctrl+N)新建画布。

    直线绘制:点击工具栏中的 按钮(或按快键键Alt+L),即可切换到直线绘制模式(软件启动默认绘制模式为直线)。点下鼠标左键并拖动,松开鼠标左键便可完成绘制,点下和松开的位置分别为直线的两端点。

    圆绘制:点击工具栏中的 按钮(或按快捷键Alt+C),即可切换到圆绘制模式。点下鼠标左键并拖动,松开鼠标左键便可完成绘制,点下鼠标左键的点为圆心,拖动的距离为圆的半径。点击工具栏中的 工具(或按快捷键Ctrl+F),即可完成对圆的填充。


    椭圆绘制:点击工具栏中的 按钮(或按快捷键Alt+E),即可切换到椭圆绘制模式。点下鼠标左键并拖动,松开鼠标左键便可完成绘制,拖动的横向偏移量为椭圆的横轴长度,纵向偏移量为椭圆的纵轴长度,拖动位移的中点为椭圆中心。

    多边形绘制:点击工具栏中的 按钮(或按快捷键Alt+P),即可切换到多边形绘制模式。单击鼠标左键绘制多边形第一个顶点,之后多次单击绘制多边形其他顶点,若要完成绘制只需单击第一个顶点,如图7. 选择填充色为绿色然后点击工具栏中的 工具(或按快捷键Ctrl+F)。


    曲线绘制:点击工具栏中的 按钮(或按快捷键Alt+V),即可切换到曲线绘制模式。先后单击4次鼠标左键绘制曲线的4个控制点,便可完成曲线的绘制。

    3.1.3 3D图形PaintWorks可以绘制3D六面体。点击点击菜单栏中的“新建3D画布”(或按快捷键Ctrl+T)新建3D画布标签页。

    此时新3D标签页中已经绘制完成一个3D六面体,如图11. 按键盘上的左右方向键可以控制这个六面体旋转。


    3.2 图形选中与编辑3.2.1 图形选中刚绘制完成的图形默认已经被选中,其关键点用蓝色圆点标记(如3.1.2中各图所示)。若要选中其他图形,只需点击想要选中的图形,该图形就会被选中且浮动到最高图层。

    3.2.2 图形编辑点击并拖动已选中图形的蓝色标记点,即可对图形形状进行编辑,操作与图形绘制时类似。

    直线标记点:两端点
    圆标记点:矩形四个端点
    椭圆标记点:矩形四个端点
    多边形标记点:各个顶点
    曲线标记点:四个控制点

    3.3 图形变换3.3.1 平移在选中情况下,图形中心有一个带十字的中心标记点。点击并拖动这个中心标记点,即可拖动这个图形。

    3.3.2 旋转在选中情况下,图形中心标记点有一条蓝色的直线延长出来的一个蓝色标记点,称为旋转标记点,旋转标记点与中心标记点的连线称为handle,如图14,中心标记点上方的蓝色圆点即为旋转标记点。点击并拖动这个旋转标记点,即可旋转该图形。


    直线旋转:以直线中点旋转
    圆的旋转:以圆心旋转,但由于圆是中心对称图形,所以旋转中视觉上没有明显效果
    椭圆旋转:以中心旋转,但由于椭圆旋转之后可以会出现变形,所以椭圆只能进行90度旋转,即只有在handle水平或垂直时做90度的旋转
    多边形旋转、曲线旋转:均绕中心标记点旋转

    3.3.3 缩放用户对当前选中的图形进行放大或缩小,放缩的中心为该图形的中心标记点。点击工具栏的放大与缩小按钮分别可以进行放大与缩小(快捷键为Ctrl + + 和Ctrl + -),如下图左右两侧图为曲线缩小和放大的效果。



    缩小
    原图
    放大










    3.4 图形裁剪用户可以使用工具栏中的 工具对当前选中的图形进行裁剪,裁剪仅限直线或多边形。
    首先点击这个工具(或按快捷键Ctrl+C),这个工具会显示被按下,此时可以在画布中绘制出一个红色的虚线框作为裁剪窗口。

    此时可以拖动红色虚线框的四个顶点对裁剪窗口进行调整,也可以直接重新绘制一个裁剪窗口。
    确定裁剪窗口之后,再次点击 工具(或按快捷键Ctrl+C),便可完成裁剪。其中需要说明的是,裁剪之后图形会被重绘,因为当前选择的画笔颜色为绿色,所以轮廓为绿色,与填充色融为了一体(而不是轮廓消失了)。

    3.5 图形删除与保存3.5.1 删除与清空
    用户可以对当前选中的图形、画布进行删除或清空操作
    要删除当前图形,只需点击工具栏中的 (或按快捷键Delete)即可将该图形删去
    要清空当前画布,只需点击工具栏中的 (或按快捷键Ctrl+Delete)即可清空画布

    3.5.2 保存图形用户可以保存画布上的图形为BMP文件(普通画布与3D画布均可保存),点击菜单栏中的“文件”>“另存为…”,即可打开一个文件保存对话框。


    输入文件名,选择路径,便可把当前画布的图像保存成BMP格式的文件。
    3.6 出错处理经过测试,本软件鲁棒性较好,但仍难以避免一些意外发生,如果出现了错误,重启即可。
    3.7 注意事项
    在新图形绘制过程中不要点到原有图形上,否则会选中原有图形,中断新图形的绘制。即便如此,原先的绘制信息也不会丢失,将绘制模式切换回来就可以继续绘制了
    由于裁剪算法所限,在裁剪凹多边形的一些特定情况下可能会出现一些不完全正确的裁剪结果

    4 系统框架设计4.1 开发环境


    操作系统
    Windows 10家庭中文版




    编程语言
    C++ 11


    外部库
    Qt 5.4,OpenGL 2.7


    编译器
    MinGW 4.9.1 32bit


    IDE
    Qt Creator 3.3.0



    4.2 系统架构概览用Qt向用户提供交互,用OpenGL提供底层绘制,考虑到Qt与OpenGL的兼容性,使用了继承自QWidget的子类QGLWidget实现Qt环境下的OpenGL绘制:

    QMainWindow负责与用户交互,并以标签页的形式集成了QGLWidget
    QGLWidget负责绘制本标签页的内容,并集成了各类FigureControl,用于将本标签页的交互信号送给不同的图形控制类
    FigureControl负责该类型图形的交互,有LineControl、CircleControl、EllipseControl、PolygonControl等,用于分别提供不同图形的不同交互方式
    Figure负责保存该图形的数据信息,包括顶点、圆心、半径以及轮廓点、填充点等,集成了Point类,图形绘制通过调用Point的draw函数完成
    Point负责实现绘制单个普通点或标记点,并向上提供其他常用功能函数支持

    系统核心架构图

    其中GLWidget派生出一个GL3DWidget,作为3D标签页,用于3D图形的绘制;在FigureControl层有一个CutWindow类,用于绘制裁剪窗口。系统架构中的聚集关系如下图:

    Point类作为轮廓和填充的构成部分聚集在Figure类中;Figure类的数组放在FigureControl中,由FigureControl处理本Figure的相关交互;CutWindow用于管理裁剪窗口;GLWidget为2D画布的标签页,GL3DWidget继承自GLWidget,作为3D画布标签页;所有标签页聚集在QMainWindow中,由QMainWindow与用户交互并向下发送消息。
    4.3 图形管理类:FigureControl图形管理类之间的关系较为简单,主要用于存储本图形的数据,并进行与本图形相关的交互,如鼠标点击、键盘按下、图形绘制、设置聚焦、裁剪当前图形等,类继承关系如下图:

    4.4 图形类:FigureFigure为所有图形的虚基类,继承关系如下图:

    Figure类提供所有图形都应有的共同接口:

    基础绘制:绘制图形、绘制图形标记点、清空图形
    基础变换:平移、旋转、缩放
    判断某点是否在该图形上


    SimpleFigure是对所有简单图形的抽象,SimpleFigure的轮廓均直接由点构成。而Polygon的边为Line,所以不属于SimpleFigure。SimpleFigure中提供了通用的draw()、clear()、isOn()。
    Area是对所有可填充图形的抽象,用于填充点管理,提供了与填充相关的函数。
    Line、Circle、Ellipse、Curve、Polygon分别为直线、圆、椭圆、曲线、多边形。其中由于Ellipse和Polygon与OpenGL库中命名冲突,所以在实现中命名为MyEllipse和MyPolygon,但相应代码文件名仍为Ellipse和Polygon。
    8 评论 183 下载 2018-12-10 20:51:38 下载需要12点积分
  • 基于java的作业管理系统

    一、项目背景随着互联网技术的迅猛发展,网络给人们带来了很多便利,比如人们借助于网络进行相互交流、相互通信、共享信息、文件的上传下载等。同时互联网的发展也推动了教育的发展和变革,现在不少高校都有自己的作业提交系统,由于各种原因我们川大还没有统一的使用作业提交系统。为了更好的利用校园网的软硬件资源,实现学生作业的无纸化管理,让学生提交作业、查询作业信息更为方便,让老师审核学生作业更加的快捷,我们决定基于B/S结构开发一个简单又实用的在线提交作业系统。
    二、理论基础2.1 数据库技术PostgreSQL 是一种对象-关系型数据库管理系统(ORDBMS),也是目前功能最强大、特性最丰富和最复杂的自由软件数据库系统.它起源于伯克利(BSD)的数据库研究计划,目前是最重要的开源数据库产品开发项目之一, 有着非常广泛的用户。
    PostgreSQL 是唯一支持事务、子查询、多版本并行控制系统、数据完整性检查等特性的唯一的一种自由软件的数据库管理系统.能在多平台下—-包括Linux、FreeBSD和Windows等—-运行,并且支持多语言的开发。
    我们采用PostgreSQL8.4作为我们的数据库系统。
    2.2 2B/S架构的WEB程序设计技术B/S结构(Browser/Server结构)即浏览器和服务器结构。它是随着Internet技术的发展,对C/S结构的一种变化或者改进的结构。在这种结构下,用户工作界面是通过网页浏览器来实现,极少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现,形成所谓三层3-tier结构。这样就大大简化了客户端电脑载荷,减轻了系统维护与升级的成本和工作量,降低了用户的总体成本(TCO)。以目前的技术看,局域网建立B/S结构的网络应用,并通过Internet/Intranet模式下数据库应用,相对易于把握、成本也是较低的。它是一次性到位的开发,能实现不同的人员,从不同的地点,以不同的接入方式(比如LAN, WAN, Internet/Intranet等)访问和操作共同的数据库;它能有效地保护数据平台和管理访问权限,服务器数据库也很安全 。B/S结构最大的优点就是可以在任何地方进行操作而不用安装任何专门的软件。只要有一台能上网的电脑就能使用,客户端零维护。系统的扩展非常容易,只要能上网,再由系统管理员分配一个用户名和密码,就可以使用了。甚至可以在线申请,通过公司内部的安全认证(如CA证书)后,不需要人的参与,系统可以自动分配给用户一个账号进入系统。
    三层客户/服务器(B/S)体系结构

    客户端仅仅运行用户界面,浏览器(Web browser)用于数据的呈现 瘦客户;应用服务器运行业务逻辑和数据处理,应用服务器 + Web server ;数据库服务器提供企业数据的集成和管理。
    三、需求分析3.1 系统用例图
    3.2 系统功能体系图
    3.3 目标完成一套网上提交作业系统,练习体验Web数据库应用开发,最终希望可以把源代码提供给学校使用,能够完成简单的作业管理任务,讲源代码公开,希望有更多的同学参与进来将这个系统修改和完善。
    3.4 用户的特点用户对象是川大老师和同学,基于同学和老师对计算机技术有一定了解,可以很快熟悉系统的使用方法。也正是这个原因,我们将尽可能简单的设计我们的系统,把更多的主动权交给用户,以此来满足用户的需求。管理员对计算机很了解,不过我们希望管理员只做很少一部分,即对部分老师和学生的不规范行为矫正,如老师的作业没有在一定时间内删除,学生毕业后没有注销等。
    3.5 基本功能
    不同用户登陆进入不同的用户界面
    学生、教师的信息修改和密码修改
    学生、教师的注册注销
    学生、教师可以多次上传文件覆盖上一次作业
    学生注册课程
    学生添加小组和加入小组
    组长管理组员
    学生查看作业和下载作业
    学生、小组上传提交作业
    学生查看教师通知
    教师添加注册课程
    教师布置作业
    教师发布关于作业通告
    教师查看选课情况和学生和小组信息
    教师查看和统计提交作业情况(人数、日期等)
    教师下载学生和小组作业

    3.6 对性能的规定要求系统支持PostGreSQL数据库
    3.7 限制条件
    学生不可以进行教师页面
    教师也不可以进入学生页面
    每个学生登录进入之后只能看到自己注册课程的作业,不要恶意注册其他没有选的课程
    每个教师登录只对自己发布的作业进行管理,不可以对别的教师发布的作业进行管理
    不同的用户使用修改密码都只能修改自己的密码,无法修改他人的
    不同的用户使用个人数据功能时,也是只可以看到自己的信息不可以看到他们的信息
    学生上传作业的类型,大小受系统约束,

    3.8 系统难点分析数据库的设计,推广本系统的话就要减少防止数据库的三类异常,同时兼顾效率,要在两者之间取舍。
    四、系统总体软件结构图4.1 MVC架构图
    控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。
    视图层能够实现数据有目的的显示(理论上,这不是必需的)。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。
    “数据模型”(Model)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模型”有对数据直接访问的权力,例如对数据库的访问。“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。
    4.2 系统包图
    Controller包控制系统程序的流程,并对事物作出响应,其中的servlet接受JSP中提交的信息后,调用Module包中的Action类,然后Action再调用DaoImpl中的方法,DaoImpl完成了和数据库的连接,具有访问数据库的权利。这种结构,使得调用结构更加清晰,容易调试错误和以后对程序功能的扩展。JavaBean包定义了系统中的各种自定义数据结构。
    五、详细设计5.1 E-R图
    5.2 关系模式Student(student_id,student_name,student_gender,grade,classes,student_sdept,mail,password);



    Student Table








    属性
    类型
    长度
    约束条件



    student_id
    varchar
    20
    主码



    student_name
    varchar
    20
    不为空



    student_gender
    varchar
    8
    ‘男’或‘女’



    grade
    varchar
    8




    classes
    varchar
    8




    student_sdept
    varchar
    20




    mail
    varchar
    30




    Password
    varchar
    20
    不为空



    Teacher(teacher_id,teacher_name,teacher_gender,mail,domain,password);



    Teacher Table








    属性
    类型
    长度
    约束条件



    teacher_id
    varchar
    20
    主码



    teacher_name
    varchar
    20
    不为空



    teacher_gender
    varchar
    8
    ‘男’或‘女’



    domain
    varchar
    20




    mail
    varchar
    30




    Password
    varchar
    20
    不为空



    Homework(homework_id,homework_filename,message);



    Homework Table








    属性
    类型
    长度
    约束条件



    homework_id
    varchar
    20
    主码



    homework_filename
    varchar
    20
    不为空



    message
    varchar
    250




    score
    float





    Course(course_id,course_name,course_sdept);



    Course Table








    属性
    类型
    长度
    约束条件



    course_id
    varchar
    20
    主码



    course_name
    varchar
    20
    不为空



    course_sdept
    varchar
    20




    Team(team_id,team_name,team_monitorid);



    Team Table








    属性
    类型
    长度
    约束条件



    team_id
    varchar
    20
    主码



    team_name
    varchar
    20
    不为空



    team_monitorid
    varchar
    20




    Student_team(student_id,team_id);(不完全遵守转换规则)



    Student_team Table








    属性
    类型
    长度
    约束条件



    team_id
    varchar
    20
    外码



    student_id
    varchar
    20
    外码



    Team_monitorid
    varchar
    20




    Homework_student(student_id,homework_id);(不完全遵守转换规则)



    Student_team Table








    属性
    类型
    长度
    约束条件



    homework_id
    varchar
    20
    外码



    student_id
    varchar
    20
    外码



    Course_id
    varchar
    20




    submit_date
    Date

    不为空



    Teacher_id
    varchar
    20




    Homework_team(student_id,homework_id);(不完全遵守转换规则)



    Student_team Table








    属性
    类型
    长度
    约束条件



    homework_id
    varchar
    20
    外码



    team_id
    varchar
    20
    外码



    Course_id
    varchar
    20




    submit_date
    Date

    不为空



    Teacher_id
    varchar
    20




    Teacher_course(teacher_id,course_id);



    Student_team Table








    属性
    类型
    长度
    约束条件



    teacher_id
    varchar
    20
    外码



    course_id
    varchar
    20
    外码



    Teacher_course_homeowrk(teacher_id,course_id);



    Student_team Table








    属性
    类型
    长度
    约束条件



    teacher_id
    varchar
    20
    外码



    course_id
    varchar
    20
    外码



    homework_id
    varchar
    20
    外码



    homework_date
    Date

    不为空



    5.3 软件包类图5.3.1 Action包图本包属于Module,Action包接受Controller包中servlet的调用,对其隐藏细节而完成系统的各个功能,其中包括了上传作业(下载作业不作为一个类)、添加删除学生、查找课程和作业等类。

    5.3.2 Bean包图本包属于Module,对Action包和Dao包中的数据结构进行自定义,其中包括了学生信息、教师信息和课程信息等类。

    5.3.3 Controller包图本包属于Controller,对用户事件作出反应,用于处理浏览器中的表单和返回查询结果,通过本包调用Action包完成查询。

    5.3.4 Dao包图接收Action的调用,访问数据库完成一次数据库操作。

    5.4 部分功能算法和代码5.4.1 文件上传的流程图
    效果截图:


    5.4.2 分页技术部分代码:
    String sql = "select course.course_id,course.course_name,course.teacher_id,course.teacher_name from course left outer join student_course on student_course.student_id=? and course.course_id=student_course.course_idand course.teacher_id=student_course.teacher_id where student_course.student_id !=? or student_course.student_id is null order by course.course_id limit 1 offset ?";
    效果图:(为展示效果我们将limit值设为 1,每页显示一项)

    5.4.3 小组删除识别效果图小组组长进行此操作将会删除这个小组,而小组成员操作知识退出小组。


    5.4.4 老师部分功能老师注册课程

    老师端的主界面:包含了主要功能,后期可以扩展

    查看作业提交情况,分别以个人和小组来分类

    查看学生个人作业,并且下载学生作业

    以小组为单位查看作业并下载

    布置课程作业,可以覆盖前次上传

    六、讨论和展望6.1 系统优点这是我们小组成员第一次合作,也是第一次做关于Jsp和数据库的应用系统,系统的基本功能已经实现,并且我们为以后的进一步开发业预留了接口和余地。在保证基本功能的同时我们也尽力为用户提供良好界面交互,也尽可能的接近可以应用的系统,为此我们添加了分页、重复上传和删除时识别权限等功能。
    6.2 系统缺点同样由于第一做此类系统,并且时间有点紧,所以系统还有很大缺点。功能不完善,我们在当初考虑要设计管理员,但是由于时间紧迫我们没有实现这方面的功能。教师和学生端的一些功能也有待进一步补充,教师端的发送群邮件没有完成。
    还有就是系统界面,这是我们最后提交系统给助教老师检查前的最大担心,也是我们最大的遗憾,没有在最后做出一个交互良好的界面。
    我们小组对于自己系统距离可以投入使用的差距很清楚,并且经过前面开发的经验和教训,如果有机会有时间的的话,可以根据上诉缺点一一改正,并且可以做到真正使用。
    6.3 系统设计和开发过程中的收获通过这次课程实践,我们对数据库的设计方法有了清晰的认识,即需求分析、概念设计、逻辑设计,对设计ER图和ER向关系模式的转换,这个过程中我们接受了书上的观点,为了提高代码效率和代码重用率我们允许了一些冗余。
    通过系统开发,我们对SQL语句的应用更加熟练,在进行分页过程中,总是遇到想不到的问题,然后我们将Servlet之间的逻辑关系画出来,一边编写代码,一边解释代码意思,这样总算做出了“健壮”的分页。在学习查询优化之后,我们又试图将一些SQL语句进行优化,不过不能直观的对比两种方式的效果。此外还获得了网页开发和软件工程方面的一些知识和经验,比如CSS。
    这次是我们小组成员的第一次合作,讨论决定这个项目后,我们在网上找资料、找视频,在图书馆看书学习,但效果很不好,写过一些代码,但随着后来任务的增多和小组成员的分工,开发进度越来越慢,后来由决定放弃,原来的开发模式,参考老师提供的项目代码和学习PPT后,我们决定按照MVC的模式从新开始工作。之后开发进度逐渐顺利起来,小组分工慢慢协调起来。整个项目坐下来,也从中体会了很多技术之外的东西,小组之间的交流和协调对于软件开发也是举足轻重。
    最后,要感谢数据库老师对我们不耐其烦的指导,如果没有老师帮助我们不可能顺利开发。还要感谢助教对我们系统的检查以及对我们提出的建议和批评,让我们认识到我们的缺点。我们将尽力推出更好的版本。
    2 评论 159 下载 2019-03-16 20:34:36 下载需要11点积分
  • 基于SSM和MySQL的企业人事管理系统的设计与实现

    摘 要随着现代计算机网络技术的不断完善,和经济的不断发展,传统的管 理技术已经不再满足整个企业的需要,更多的企业则注重计算机信息管理, 而人事管理系统是典型的计算机信息管理之一,企业可以借助于它进行人 事管理,达到事半功倍。随着企业的雇佣人数的增加,从而有效地管理人 员信息成为必然。人事管理系统的开发主要包括后台数据库的管理和维护,以及前台程序开发两个方面。本篇文章分析了人事管理系统的组成情况以 及一些基本的功能模块,包括系统功能的框架分析,系统开发的软硬件环 境分析,和系统的数据库逻辑设计分析,以及系统的数据流程分析。该系 统主要利用 Java 语言和 IntelliJ IDEA 软件进行设计开发,在文中进行了详细的介绍。该系统是小型企业的人事管理系统,它简单实用,安全性高, 能够基本上满足企业人事管理的需要,实现了对企业员工信息的整体化,系统化和自动化。
    关键词:人事管理;IntelliJ IDEA;系统化
    1 绪论随着科学技术的的不断提高,计算机科学日渐成熟,其强大的功能已为人们深刻人事,它已进入人类社会的各个领域并发挥着越来越重要的作用。作为计算机应用的一部分,使用计算机对人事信息进行管理,具有着传统系统无法比拟的优点。因此,开发这样的系统,对单位人事管理工作进行有效电子化管理,化简为繁的手工操作,提高工作效率是很有意义的。
    现代的社会中,办公自动化进入社会的每一个角落已经势不可挡,而人事管理系统时办公自动化的一个小小体现,它为人事管理大量又复杂的员工数据工作提供了方便,提高了人事管理工作的效率,为办公自动化的普及奠定了基础。人个现代化企业事业单位不可或缺的部分,它的内容对于企事业的管理者来说至关重要。人事管理系统应该能够为管理者提供充足的信息和快捷的查询手段。但很多单位并没有对人事工作进行电子化管理,还是那种管理人员多,管理效率低,决策依据少的尴尬局面。
    人事管理系统是典型的计算机信息管理系统之一,它开发主要包括后台数据库的建立和维护以及前端应用程序的开发。对于前者要求建立起数据一致性和完整性强、数据安全性好的库。而对于后者则要求应用程序功能完备,易使用,界面美观等特点。人事管理系统可以提供丰富的功能,降低人事管理工作的强度,提高其公司人事管理工作的效率,并依靠系统的强大的功能为人事管理的使用创造良好的条件,从而能够适应其公司人事管理的要求,推动其公司人事管理的信息化建设。
    1.1 设计背景随着经济体制的快速发展,对于企业、事业单位的人事管理部门来说,非常需要的一 个操作方便、功能实用、能满足本企业对员工信息管理及需求的系统。在企业选择人事管理系统时,主要存在以下几个方面的要求:

    可以真正的实现对企业的人事管理
    系统的功能要符合本企业的实际情况
    系统的功能操作要方便、易懂,不要有多余或复杂的操作
    可以方便地对人事信息进行输出打印

    1.2 设计意义本系统就是基于本公司的人事管理而设计的,是对公司的人事资料进行管理,为人事管理人员提供了一套操作简单、使用可靠、界面友好、易于管理和使用的处理工具。本系统对人事各种数据进行统一处理,避免数据存取、数据处理的重复,提高工作效率,减少了系统数据处理的复杂性。本系统不仅使公司人事管理人员从繁重的工作中解脱出来,而且提高了人事管理的效率,提高了人事管理的科学性,方便了用户查询、管理人员进行管理。
    人事管理是现代企业管理工作不可缺少的一部分,是适应现代企业管理尺度的要求、推动企业人事管理走向科学化、规范化的必要条件。只有人事管理规范了,才能在其他方面更好的发展。为了适应现代企业或公司经营发展的需要,人事管理也从以前的手工管理逐渐被规范化的管理信息系统所代替。众所周知,当今社会为信息社会,世界已进入在计算机信息管理领域中激烈竞争的年代。因此,为提高企业工作效率‘保证企业人事管理质量,人事管理便成当今企事业单位中不可缺少的一部分。它适应时代潮流,是现代企业制度发展的要求,推动企业人事管理科学化,规范化的必要条件“科学技术是第一生产力,只有人事管理规范化,其他事情才能办好。为适应现代企事业单位管理规范化,经营发展战略的需要,人事管理也应由原来的手工管理逐渐被规范化的信息管理系统所 代替。因此,要想加强人事管理,单纯依靠以前的手工管理,不尽浪费大量的人力,物力,财力,而且效率不高。所以人事管理信息系统能够为高层领导者提供准确的人员信息,以便领导者了解企业各个部门的人员构成,安排好工作计划使企业变得更高效,更具有生命力。因此,开发人事管理系统更具有一定的社会现实意义。
    1.3 设计思路能够录入人事的基本资料,在操作上能够完成诸如添加、修改、删除、查询、新用户的设置及密码修改等方面的工作,基本满足人事日常业务的需要。
    人事管理信息系统主要包含员工基本信息,工作情况,学历,职位情况等各方面信息,内容比较简单。同时还综合了其他系统的功能,总结归纳出所需现有的功能。主要是为人事信息进行服务,对职位的变动、员工资料查询,公告信息查看等功能。总体上说具有编辑,查询,员工管理等功能。
    在如今的社会中如果再用手工管理这样一个庞大的业务,那不仅对人的脑、身体都带来很多的不便,所以我们现在是很需要这样的一个系统来管理,我的这个设计是最基本的管理。提高系统开发水平和应用的目的。
    本系统采用模块化程序设计方法,既便于系统功能的各种组合和修改,又便于未参与开发的技术维护人员补充、维护。
    本系统应具备数据库维护功能,及时根据用户需求进行数据的添加、删除、修改、备份等操作。
    总之,在人事管理系统的开发过程中,我们应该充分考虑这些基本原则,这样,我们做的系统才能够具有一定的生命周期。
    2 需求分析基于其他企业人事管理软件的不足,要求能够制作一个可以方便、快捷的对员工信息进行添加、修改、删除的操作,为了能够更好的存储职工的信息,可以将职工的信息添加到 Word 文档,这样,不但便于保存,还可以通过 Word 文档进行打印。 根据企业对人事管理的要求,指定企业人事管理系统目标如下:

    操作简单方便、界面简洁美观
    在查看员工信息时,可以对当前员工的家庭情况等进行添加、修改、删除操作
    方便快捷的全方位查询所有数据
    可以将员工信息以表格的形式插入到 Word 文档中
    实现数据库的备份、还原及清空操作
    由于该系统使用对象较多,要有较好的权限管理
    能够在当前运行的系统中重新进行登录
    系统能够运行稳定、安全可靠

    2.1 需求描述管理员进入主界面,软件开始运行,提供用户登录功能,不同的用户登录操作的功能不同,非管理员用户登录只能查看一些公告信息等,而管理员登录后,可以进行用户管理丶部门管理、职位管理、员工管理、公告管理等功能。管理员可以通过点击部门管理进入里面,可以添加部门、查看部门信息、删除部门等操作。在主界面,管理员可以随意选择自己的操作达到最终的目的。
    人事管理系统的主要作用就是为用户管理不同部门的员工,能够有效的完成人事管理的日常工作;将零散的、杂乱无章的人员信息进行一些整理,方便用户的查询、汇总以及分析。一个完整的人事管理系统应实现以下基本功能。

    部门管理:能够维护体现部门间的组织关系,反映部门的基本信息,能够进行部门的增加撤消,以及部门信息(如部门名称、负责人名称等)的修改
    职位管理:用户管理:能够对系统用户进行编辑、增加或删除,并指定用户的权限
    员工信息的管理:维护员工的基本信息,用户可以进行员工档案信息的录入及更改,其中包括员工的基本信息,要求这些员上档案信息可以进行新增、删除、修改操作,同时可以进行浏览和查询的操作
    公告管理:可以查询所有公告或根据公告名称,公告内容进行模糊查询

    2.2 用例建模用户进行登录后,可以进行部门管理丶岗位管理丶员工管理丶公告管理和用户管理的增、删、改查操作,不同的用户具有不同的角色,分配的功能也不相同。如图:
    员工用例模型

    管理员用例模型

    2.3 用例描述2.3.1 用户管理用例描述
    用户管理用例描述

    用例编号:CASE01用例名称:用户管理用例描述:系统管理员对用户的信息进行 CRUD 操作前置条件:系统管理员成功登录系统后置条件:系统管理员在系统中新增了用户或修改了用户信息或删除了系统中已经存在的用户或查看某个用户的信息活动步骤
    系统管理员登录系统用户点击【新增】按钮,弹出新建用户界面,系统管理员输入用户信息,点击【保存】按钮,系统对输入的信息进行验证,将合法的信息保存,然后显示新增后的用户列表系统管理员在用户列表中选择某个用户,点击【修改】,弹出用户修改界面,显示用户的当前信息,管理员更改用户信息,点击【保存】,系统对输入的信息进行验证,将合法的信息保存,然后显示修改后的用户列表系统管理员在用户列表中选择某个用户,点击【删除】,弹出删除提示框,当用户确定删除后,系统将当前用户信息删除,并返回到用户列表,显示删除后的用户列表数据格式:用户信息(用户 ID、用户名,密码,登录名,状态)

    部门管理用例描述

    用例编号:CASE02用例名称:部门管理用例描述:系统管理员对部门的信息进行 CRUD 操作前置条件:系统管理员成功登录系统后置条件:系统管理员在系统中新增了部门或修改了部门信息或删除了系统中已经存在的部门或查看某个部门的信息活动步骤
    系统管理员登录系统管理员点击【新增】按钮,弹出新建部门界面,系统管理员输入部门信息,点击【保存】按钮,系统对输入的信息进行验证,将合法的 信息保存,然后显示新增后的部门列表系统管理员在部门列表中选择某个部门,点击【修改】,弹出部门修改界面,显示部门的当前信息,管理员更改部门信息,点击【保存】, 系统对输入的信息进行验证,将合法的信息保存,然后显示修改后的部门列表系统管理员在部门列表中选择某个部门,点击【删除】,弹出删除提示框,当部门确定删除后,系统将当前部门信息删除,并返回到部门列表,显示删除后的部门列表
    数据格式:部门信息(部门 ID 丶部门名称丶详细信息丶部门编号丶部门地址)
    职位管理用例描述

    用例编号:CASE03用例名称:职位管理用例描述:系统管理员对职位的信息进行 CRUD 操作前置条件:系统管理员成功登录系统
    后置条件:系统管理员在系统中新增了职位或修改了职位信息或删除了系统中已经存在的职位或查看某个职位的信息
    活动步骤
    系统管理员登录系统管理员点击【新增】按钮,弹出新建职位界面,系统管理员输入职位信息,点击【保存】按钮,系统对输入的信息进行验证,将合法的 信息保存,然后显示新增后的职位列表系统管理员在职位列表中选择某个职位,点击【修改】,弹出职位修改界面,显示职位的当前信息,管理员更改职位信息,点击【保存】, 系统对输入的信息进行验证,将合法的信息保存,然后显示修改后的 职位列表系统管理员在职位列表中选择某个职位,点击【删除】,弹出删除提示框,当职位确定删除后,系统将当前职位信息删除,并返回到职 位列表,显示删除后的职位列表数据格式:职位信息(职位 ID 丶职位编号丶职位描述)

    员工管理用例描述

    用例编号:CASE04用例名称:员工管理用例描述:系统管理员对员工的信息进行 CRUD 操作前置条件:系统管理员成功登录系统后置条件:系统管理员在系统中新增了员工或修改了员工信息或删除了系统中已经存在的员工或查看某个员工的信息活动步骤
    系统管理员登录系统管理员点击【新增】按钮,弹出新建员工界面,系统管理员输入员工信息,点击【保存】按钮,系统对输入的信息进行验证,将合法的 信息保存,然后显示新增后的员工列表系统管理员在员工列表中选择某个员工,点击【修改】,弹出员工修改界面,显示员工的当前信息,管理员更改员工信息,点击【保存】,系统对输入的信息进行验证,将合法的信息保存,然后显示修改后的员工列表系统管理员在员工列表中选择某个员工,点击【删除】,弹出删除提示框,当员工确定删除后,系统将当前员工信息删除,并返回到员 工列表,显示删除后的员工列表 数据格式:员工信息(员工 ID,姓名,性别,手机号码,邮箱,地址,学历等)

    公告管理用例描述

    用例编号:CASE05用例名称:公告管理用例描述:系统管理员对公告的信息进行 CRUD 操作前置条件:系统管理员成功登录系统后置条件:系统管理员在系统中新增了公告或修改了公告信息或删除了系统中已经存在的公告或查看某个公告的信息活动步骤
    系统管理员登录系统管理员点击【新增】按钮,弹出新建公告界面,系统管理员输入公告信息,点击【保存】按钮,系统对输入的信息进行验证,将合法的 信息保存,然后显示新增后的公告列表系统管理员在公告列表中选择某个公告,点击【修改】,弹出公告修改界面,显示公告的当前信息,管理员更改公告信息,点击【保存】, 系统对输入的信息进行验证,将合法的信息保存,然后显示修改后的公告列表系统管理员在公告列表中选择某个公告,点击【删除】,弹出删除提示框,当公告确定删除后,系统将当前公告信息删除,并返回到公告列表,显示删除后的公告列表
    数据格式:公告信息(公告 ID 丶公告标题,内容,发布日期)

    3 系统设计3.1 数据库设计本系统采用的是 mysql 数据库,Mysql 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的关系数据库管理系统应用软件之一。由瑞典 MySQL AB 公司开 发,目前属于 Oracle 公司。MySQL 是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
    3.1.1 概念模型
    3.1.2 逻辑模型
    3.1.3 数据库脚本



    3.2 系统架构设计本系统采用 Spring+SpringMVC+MyBatis 三大框架搭建,全过程使用注解式开发, 使用 spring 来管理所有的 bean 对象,不再需要我们去显式地new一个对象,而是让 Spring 框架帮你来完成这一切。使用 SpringMVC 在项目中拦截用户请求,它的核心 Servlet 即 DispatcherServlet 承担中介或是前台这样的职责,将用户请求通过 HandlerMapping 去匹配 Controller,Controller 就是具体对应请求所执行的操作。而 mybatis 则是对 jdbc 的封装,它让数据库底层操作变的透明。mybatis 的操作都是围绕 一个 sqlSessionFactory 实例展开的。mybatis 通过配置文件关联到各实体类的 Mapper 文件,Mapper 文件中配置了每个类对数据库所需进行的 sql 语句映射。在每次与数据库 交互时,通过 sqlSessionFactory 拿到一个 sqlSession,再执行 sql 命令。
    3.3 模块设计3.3.1 项目执行流程页面发送请求给控制器,控制器调用业务层处理逻辑,逻辑层向持久层发送请求,持久层与数据库交互,后将结果返回给业务层,业务层将处理逻辑发送给控制器,控制器再调用视图展现数据。
    4 产品实现4.1 部门管理模块实现4.1.1 界面设计部门管理列表界面

    部门管理添加界面

    部门管理修改界面

    4.1.2 功能实现部门管理 Controller 层代码实现:


    部门管理 service 层实现:


    部门管理 dao 层实现:

    4.2 职位管理模块实现4.2.1 界面设计职位管理列表界面

    职位管理添加界面

    职位管理修改界面

    4.2.2 功能实现职位管理 Controller 层代码实现:


    职位管理 Service 层代码实现:


    职位管理 Dao 层代码实现:

    4.3 员工管理模块实现4.3.1 界面设计员工管理列表界面

    员工管理添加界面

    员工管理修改界面

    4.3.2 功能实现员工管理 Controller 层代码实现:



    员工管理 Service 层代码实现:


    员工管理 Dao 层代码实现:


    4.4 公告管理模块实现4.4.1 界面设计角色管理列表界面

    角色管理添加界面

    角色管理修改界面

    4.4.2 功能实现公告管理 Controller 层代码实现:


    公告管理 Service 层代码实现:


    公告管理 Dao 层代码实现:

    4.5 用户管理模块实现4.5.1 界面设计 角色管理列表界面

    角色管理添加界面

    角色管理修改界面

    4.5.2 功能实现用户管理 Controller 层代码实现:


    用户管理 Service 层代码实现:


    用户管理 Dao 层代码实现:

    5 结论5.1 毕业设计成果特点本系统主要是面向管理者群体。方便用户信息的管理、查询和更改,减少人员的重复劳动,节约大量人力和物力。企业人事管理系统的出现,解决了老式的纸质管理的弊端,使企业的人事管理工作系统化、规范化、自动化,降低了企业人事管理工作的强度,从而提高企业人事管理的效率。
    5.2 不足之处或遗留未予解决的问题因为本系统是基于个人的理解来进行开发的,所以考虑到还有很多不足之处和一些还遗留未解决的问题:

    对于框架的底层实现原理不清楚,只能做到运用却不理解,这是之后需要学习的
    对于建用例图,E-R 图等等运用还不是很熟练,这是后面我需要进行学习和提升的地方
    对于 AdminTEL 前端框架里模态框实现下拉列表回写数据的功能没有得到解决,还有 jquery 的一些事件处理和属性选择运用的不是很熟练,这是我后面需要进行学习的地方

    参考文献[1] 刘浩. Java 从入门到精通[M]. 北京:人民邮电出版社,2010:7-10.
    [2] 印旻. Java 语言与面向对象程序设计[M]. 北京:清华大学出版社,2000:88-102.
    [3] 萨师煊 ,王珊. 数据库系统概论[M]. 北京:高等教育出版社,2005:23-57.
    [4] 王玉英. 基于 JSP 的 MySQL 数据库访问技术[J]. 现代计算机:专业版
    [5] 赵钢. JSP Servlet+EJB 的 Web 模式应用研究[J]. 电子设计工程
    [6] 肖英. 解决 JSP/Servlet 开发中的中文乱码问题[J]. 科技传播
    [7] 戴维尔,JavaScript 程序员教程,电子工业出版社
    [8]安训国.数据结构(第四版).大连:大连理工大学出版社,2009.6
    1 评论 5 下载 2020-08-25 11:28:07 下载需要13点积分
  • 基于JAVA和MySQL的办公自动化系统

    摘 要该办公自动化系统专门用于企业内部员工信息交流的软件,其开发过程主要包括前端应用程序的开发和后端数据库的建立两个方面。对于前者要求应用程序功能完备操作简单,对于后者要求建立起数据一致性、完整性和安全性好的数据库。
    系统用网络数据库将企业内员工的基本信息统一管理起来,形成集成的信息源,系统会根据员工所属部门对员工进行分类,这样有利于发送者快速找到发送目标,也能方便的进行信息的群发,在该系统中总经理具有最高的权限,负责对系统的管理。
    该系统可使企业内员工之间信息的交流更高效,更便捷,也使企业内各部门之间员工的频繁往来减到最少,从而提高企业整体的办公效率,为企业节约成本,使企业在激烈的市场竞争中赢得优势,实现企业整体战略目标。
    关键词:办公自动化 客户机/服务器 系统管理
    1 需求分析办公自动化系统的建立,需要进行用户需求调查与分析,以确定系统目标,这是系统建设的重要环节。中小企业需求规模相对较小,在功能上对系统的需求也相对较少。大体可分为:一方面是通过收发消息为主的方式,与其他员工交流各种信息,基于工作流的方式实现诸如请假申请、物品申领等日常办公功能。另一方面则是面向公司办公业务方面的业务管理功能,即完全采用计算机技术处理办公业务,使企业内部人员能够方便快捷地共享信息、交流信息,高效地协同工作,既兼顾个人办公效率的提高,又可以实现群体协同工作。
    传统的办公方式极大的束缚了人的创造和想象力,埋没了人的智慧和潜能,使人们耗费了大量的时间和精力去手工处理那些繁杂重复的工作,手工处理的延时和差错,正是现代化管理中应去除的弊端。用先进的、现代化的工具代替手工作业,无疑是生产力发展的方向。办公自动化对传统办公方式的变革,正是适应了人们的普遍需求,也顺应了技术发展的潮流。
    针对上述问题,我开发了办公自动化系统,它具有如下特点:

    办公自动化系统可以将人们从繁重的重复性劳动中解放出来,大大节省工作时间,提高工作效率、减少办公费用
    办公自动化系统将是企业竞争取胜的法宝,它所收集、处理、分析的对象是“信息”。这些准确、及时、可靠的信息将大大有助于提高领导者决策的正确性和科学性
    通过办公自动化系统,能给企业的管理者在行为方式和思维模式上带来革命性进步。企业在管理手段与管理思想方面已经落后于发达国家,所以迫切需要通过推行办公自动化来改善管理手段,增强竞争力

    2 系统数据流程2.1 数据流程图概念数据流程图(DFD——Data Flow Diagram)是描述系统逻辑模型的主要工具,它可以用少数几种符号综合的反映出信息在系统中的流动、传递、存储、和处理的总情况。数据流程图具有抽象性和综合性两个特点,其中抽象性表现在已经完全舍去了具体的物质,如组织机构、工作场所、物质流、货币流等,只保留了数据的流动、存储、使用及加工的情况。综合性表现在它可以把系统中的各种业务处理过程联系起来,形成一个整体。
    2.2 系统数据流程图系统顶层DFD如下:


    F1:修改、查询、发送信息等F2:接受信息F3:管理信息F4:修改职员资料F5:返回职员资料
    系统第一层DFD如下:


    F1:取得服务器IPF2:写入服务器IPF3:职员录入注册资料F4:职员录入登陆信息F5:职员资F6:更新职员资料表F7:取得登陆职员资料F8:写入职员资料F9:其他职员资料查询结果F10:职员基本资料修改F11:保存修改资料F12:取得职员资料F13:取得部门F14:修改部门表F15:取的项目组F16:修改项目组表F17:查询职员资料F18:修改职员特殊资料F19:所需信息F20:连接请求与交流信息输入F21:交流信息输出F22:信息记录更新
    办公自动化系统第一层DFD说明:
    首先职员输入注册信息,注册管理会在服务器IP表里找到服务器IP以便进行数据库服务器的连接,连接到数据库以后,就会把职员资料插入职员资料表中。注册管理会把职员资料传入登陆管理中,如果以前已经注册就可以把资料直接输入登陆管理,登陆管理会在职员资料表中取得职员密码进行核对,如果输入正确就可以登陆自己的账户,并且把用户的状态设为在线。在资料管理中职员可以查询其他职员资料,也可以修改自己的基本资料,总经理可以修改职员的特殊资料,
    也有输入部门,与组的权限。信息传输管理会根据用户的连接请求,对指定的职员进行连接,并向其发送信息。也可以向公司全体职员、某个部门的职员或部门中的某个组发送信息。接收信息的职员可以保存信息记录。
    系统第二层DFD的注册管理如下:

    首先它会连接本地数据库,在本地数据库中的服务器IP表里找到服务器IP地址,如果表中存在服务器IP地址那么准备进行数据库服务器的连接,如果不存在那么会要求用户输入服务器IP地址,然后将服务器IP地址保存进表中,以便以后取用。连接好数据库服务器后,职员就可以输入注册信息,经过核查确认没有用户名相同的情况那么就可以对职员资料表进行更新,插入职员注册的资料。
    以下是各数据流代表的意义:

    F1:修改服务器IPF2:查询服务器IPF3:服务器IPF4:输入注册信息F5:表信息取得F6:插入注册信息
    系统第二层DFD的登陆管理如下:

    首先在服务器IP表里查到服务器IP地址进行数据服务器的连接,然后在职员资料表中取得用户信息对用户输入的信息进行核查,如果输入正确,就会取得本地信息,如端口号、IP地址等。最后把职员资料表中的用户状态设为在线并把IP地址,端口号等进行更新。

    F1:取得服务器IPF2:登陆信息输入F3:更新职员表F4:该职员信息取得
    第二层DFD的资料管理如下:

    职员可以输入修改信息对职员注册信息进行修改,职员也可以输入查询信息,查询有关企业与用户资料的所有信息。总经理有最高的权限可以修改职员的信息,和企业的组织机构包括:组的增加与删除,部门的增加与删除,组与部门名称的修改,信息资料的修改等。
    以下是各数据流代表的意义:

    F1:修改信息输入F2:查询信息输入F3:返回查询结果F4:修改职员表F5:职员信息取得F6:部门信息取得F7:项目组信息取得F8:修改职员特殊信息F9:修改、添加部门F10: 修改、添加项目组F11:取得公告内容F12: 取得规章制度F13:输入公告内容F14:输入规章制度
    第二层DFD的信息传输管理如下:

    首先职员选择想要连接的对象,输入发送信息,然后系统根据职员选择的连接对象在职员资料表中找到所需的连接信息,包括IP地址与端口号,然后根据选择的连接对象进行相应的连接,最后更新信息记录表,将职员的信息记录保存到本地数据库。
    以下是各数据流代表的意义:

    F1:输入连接信息与交流信息F2:连接信息F3:其他职员资料输入F4:其他职员资料取得F5:连接信息与交流信息F6:交流信息输出F7:更新信息记录
    3 系统设计3.1 系统设计原则作为一套应用系统,除了要能够完成预定的各种功能外,在设计时还必须遵循实用性、完备性、可靠性、安全性、兼容性、可扩充性等原则。具体要求原则如下:

    安全性 主要是指系统运行的安全性、数据的安全性和保密性等。同时,应根据用户的工作和业务流程为用户提供合理可靠的安全策略
    实用性 是系统建设的主要目标。主要是指系统功能应能够满足当前和今后一段时间内企业办公的实际需要
    可靠性 是指系统的数据结构合理、模块功能正确等,以保证数据处理和信息传输的正确可靠
    友好性 是指界面的美观和使用的方便程度,即界面友好,有良好的引导功能、容错和查错功能
    可维护性 是指系统应具有较好的可维护性,以方便系统管理员对系统进行维护
    可扩充性 本系统目前还不是很完善以后需要改进的地方还很多。随着计算机在企业各部门应用的进一步深入,也后需要增加的功能还很多。另外,系统还应该留有一定的接口,以便将来扩充的方便等
    可移植性和适应性 针对目前我国企业的实际情况,系统应具有较强的适应性和较好的可移植性,同时尽量避免对硬件的依赖

    3.2 系统功能模块设计极光办公自动化系统有五个子系统

    个人工作子系统
    信息中心子系统
    日常工作子系统
    流转中心子系统
    维护中心子系统

    各子系统包含的功能模块如下:

    个人工作子系统:电话簿、总经理工作计划
    信息中心子系统:信息交流、电子公告、规章制度、新闻信息
    日常工作子系统:资料管理、办公用品申领
    流转中心子系统:公文管理
    维护中心子系统:权限管理、注册管理

    下图为本系统的功能模块图:

    3.2.1 个人工作提供员工个人工作中的基本功能,电话簿(允许当前用户创建属于自己的名片信息,分类、管理个人名片,查阅组织内用户的名片)、总经理工作计划(主要是记录了总经理近期的工作计划)等。**
    3.2.2 信息中心是本单位发布信息与交流的平台,包括电子公告(用来向公告板上发布通知、制度、活动和会议等消息)、规章制度(数据库可以管理规章制度,用户可以看到已发布的规章制度)、新闻信息(企事业单位为让员工了解一些信息,而发布本单位的新闻信息,和录入一些国际国内新闻)等。
    3.2.3 日常工作根据各部门及其工作流程定制相关内容,资料管理(辅助管理公司内部的各种资料)、办公用品申领(主要用来对企业中办公用品的使用情况进行统计,办公用品领用申请,办公用品领用申请进行批阅,以及办公用品的领用办理)等。
    3.2.4 流转中心根据系统管理中设置的相应流程进行公文流转,最终实现无纸化办公,提供公文流转(实现了收、发文的管理、流转、批约、转出和归档)等功能。
    3.2.5 系统管理包括组织机构的设置、用户权限角色的定义等功能。把系统管理员从烦琐的管理工作中解脱出来。
    3.3 数据库设计数据库的设计分为逻辑设计和物理设计两部分。在数据库逻辑设计方面,可以遵循自顶向下的结构化设计方法,确定系统目标后,采用E—R图设计数据库的概念模型,然后根据此模型导出数据库的关系模型。
    用户的需求集中体现在各种信息的提供、保存、更新和查询等方面,这要求数据库结构要充分满足各种信息的输出和输入。收集基本数据、数据结构以及数据处理的流程,组成一份详尽的数据字典,为后面的具体设计打下基础。
    以下为本系统所用到的数据表:

    ServerIP表位于本地数据库中用来存放数据库服务器的服务器名或服务器IP地址,它由系统自动生成,下图为该表的具体构成


    BuMen表位于数据库服务器中用来存放公司的所有部门情况,包括部门名称、部门简介。下图为该表的具体构成


    XinXi表位于数据库服务器中用来存放用户不在线时,其他用户给其发送的信息。下图为该表的具体构成。其中userName为发送者用户名,XinXiDuiXing为发送的目的地


    YongHu表位于数据库服务器中用来存放企业内部员工的信息,以及用户的状态等。下图为该表的具体构成。其中UserName为用户名,Name为用户真实姓名,IPAddress为用户的IP地址,State为用户的状态(是否在线),BuMen为用户所在部门,Zu为用户所在组,Port为用户使用的端口号


    信息记录表位于本地数据库中,用来存放用户的信息记录如下图。其中XinXiDuiXiang为用户交流的对象,XinXiJiaoHu为用户是发送数据还是接收数据

    参考文献[1] Louis Davidson 著,《SQL Server 2000 数据库设计权威指南》中国电力出版社
    [2] 刘韬 楼兴华 著,《SQLServer 2000数据库系统开发实例导航》,人民邮电出版社
    [3] 丁宝康 主编,《数据库原理》,经济科学出版社
    [4] 罗晓沛 主编,《数据库技术》,华中科技大学出版社
    [5] 陈宗兴 著, 《SQL Server程序设计超级管理篇》, 中国铁道出版社
    [6] 吴其庆 著,《JBuilder9编程思想与实践》,冶金工业出版社
    [7] 陆正中 著,《JBuilder 9软件开发项目实践》,清华大学出版
    [8] 埃克尔侯捷 著,《JAVA编程思想》, 机械工业出版社
    3 评论 49 下载 2018-12-25 13:37:55 下载需要11点积分
  • 基于java和sql server实现的日历系统

    1.日历事件程序设计要求1.1 程序说明该系统实现对日历事件的录入、显示、修改、排序、保存等操作的管理。
    1.2 程序设计要求
    一个日期有N个事件,每个事件有对应的类别,对应的标题,M个关联参与者等
    设置系统登陆密码,只有正确输入密码方可进入管理系统
    日历事件显示时,如果点击,输入日期,则显示该日期对应的所有时间,标题,优先级,日期(起始日期,结束日期),分类(工作,学习,运动等),关联参与人(系统下的其他用户名),提示时间
    根据时间,并选择特定事件修改内容时,要求先输入密码,如果密码正确方可修改时间信息,否则不予操作;在最终修改之前提示用户确认后,再进行修改操作,无需提供数据备份复原
    系统主菜单

    密码重置日历事件录入日历事件显示日历事件修改定时提醒根据优先级分色彩显示,并通过调整显示次序,体现优先级退出系统
    执行一个具体的功能之后,程序将重新显示菜单

    2.程序功能描述及要求该系统实现对日历事件的录入、显示、修改、排序、保存等操作的管理。要设置系统初始登陆密码,只有正确输入密码方可进入管理系统主菜单页面。并且,每执行一个具体的功能之后,程序需要重新显示主菜单。

    添加日历事件 :输入信息
    显示日历事件 :选择日期,显示该日期对应的事件列表,选择事件显示详情。并在该页面上修改按钮
    以日期时间排列日历事件 :系统需要根据日期时间显示该日期对应的事件列表(按时间从现在到将来显示)
    修改日历事件信息 :日历事件修改:可以根据选择的条目,点击修改按钮后,修改事件信息;也可通过检索关键字的形式定位条目
    修改系统的密码 :必须先输入原始密码,当原始密码输入正确时,才能修改密码
    修改日历事件信息:在最终修改之前提示用户确认后,再进行修改操作,无需提供数据备份复原

    3.设计思想
    密码匹配限制能否进入系统主菜单页面
    设定范围查询数据库条目
    运用数据库数据
    利用SQL Server数据库,对数据进行管理
    利用UI界面获得更加友好的用户接口

    4.结构框图、各模块的功能描述4.1 主函数(H1)定义了主函数H1,然后new一个4.2中的界面的对象,执行里面的构造函数。
    4.2 登录界面(Loading)定义了JFrame类,该类用于显示登录界面。包含两个JButton,一个JTextField,一个JPassWordField,三个Jlabel。
    显示“确定”的JButtton是获取输入的信息和通过数据库验证密码是否正确和账号是否存在;当确认密码,账号正确后会关闭此页面并且进入4.4的页面,密码如果和账号不匹配则在JTextField中显示:密码或者账号错误,累计三次密码错误后,登录页面会自动关闭。
    显示“注册,修改密码”的JButton是进入4.3中的界面进行密码的修改或者注册账号,点击后new一个4.3中的界面对象,执行构造函数进入。
    三个Jlable分别显示了:日历登录、账号、密码。
    4.3 注册和修改密码(Exc)定义了JFrame类,该类用于显示修改,注册界面。包含两个JBtton,4个JLabel,两个JPasswordField,,一个JTextField。
    JTextField用于输入用户的账号;两个JPasswordField用于输入密码;
    显示“注册”的JBtton,点击后获取账号和两次输入的密码,然后通过数据库判断账号是否存在,存在则在JTextField显示 “账号已存在”,不存在则继续判断两次密码是否是一样的,一样 则关闭界面并把账号和密码存到数据库里面,否则JTextField显示两次输入的密码不一致。
    显示“修改”的按钮,点击后获取账号和密码,通过数据库判断账号和第一场输入的密码在数据库中是否存在,存在且匹配则把数据库中的密码修改为第二次输入的密码,保存并且退出此界面。
    4.4 日历显示类(C1)定义了JFrame类,该类用于显示日历界面。包括了一个构造方法,显示日期的37个显示日期的JBtton,一个显示当前年份的JTextField,一个显示和选择月份的JCombox,七个显示星期的JLabel,一个显示“年份”和一个显示“月份”的JLabel,还有一个大小为整个界面的JLabel,内容为空。
    定义了一个jisuan()的方法。构造方法:显示界面,设置点击事件。jisuan()用于获取选择的日期并计算选的这个月的第一天为星期几,打开时候默认获取系统时间件并计算本月第一天是星期几,并且从数据库中查询当天有没有事件,有的话会跳出4.4中的页面进行提醒。本页面中new了一个Daysxiqi类。Daysxiqi用于计算从1900年一月一日到所选月份第一天的天数,从而计算出第一天是星期几。Daysxiqi也new了一个Getrunnian的类。Getrunnian中传入年份,用于计算是不是闰年。
    占满全屏的JLabel:设置点击事件,当点击的时候运行jisuan()的方法,然后刷新日历到选择的时间。显示日期的JBtton:点击后获取JButton显示的日期,如果不为空,把背景颜色设置为蓝色,并且new4.4中的Even类,把时间传入Even类中。
    4.5 日历事件类(Even)这是一个选项卡面板,包括了四类事件,学习、生活、紧急和加密,前三类包含一个Textarea、一个删除按钮和一个添加按钮。加密事件则添加了一个JPasswordField,用来加密事件,每一个加密事件可以设置一个密码,只有密码和时间相匹配才可以删除事件。
    首先会根据传进来的时间在数据库中查询有没有事件存在,有则显示在Textarea中,输入事件后点击添加就会存入数据库中,然后TextArea会显示添加成功,也可以删除事件,会提示删除失败。
    4.6 程序框图
    4.7 程序截图登录界面

    注册界面

    修改密码界面

    日历界面

    紧急事件

    学习事件

    生活事件

    加密事件
    1 评论 5 下载 2020-09-23 11:10:53 下载需要11点积分
  • Java飞机大战游戏设计与实现

    1 概述1.1 项目简介本次Java课程设计是做一个飞机大战的游戏,应用Swing编程,完成一个界面简洁流畅、游戏方式简单,玩起来易于上手的桌面游戏。该飞机大战项目运用的主要技术即是Swing编程中的一些窗口类库、事件监听以及贴图技术。
    1.2 实训功能说明1.2.1 基本功能
    通过键盘,方向键和ASWD键可控制战机的位置,空格键和鼠标左键发射子弹
    界面中敌机出现的位置,以及敌机和Boss炸弹的发射均为随机的,敌机与敌机炸弹、Boss炸弹均具有一定的速度,且随着关卡难度的增大,数量和速度均随着关卡数增加而增加
    对于随机产生的敌机和敌机炸弹,若超过矩形区域,则释放该对象
    添加碰撞效果,包括战机子弹打中敌机爆炸、敌机炸弹打中战机爆炸、战机与敌机相撞爆炸、战机子弹与敌机炸弹相撞爆炸、战机子弹打中Boss、战机与Boss碰撞以及战机吃到血包七种碰撞效果。且碰撞发生后子弹、炸弹、血包均消失,战机生命值减一,敌机和Boss生命值减少当前战机炮弹威力的生命值,若敌机或Boss生命值归零,则删除敌机或Boss
    血包:随着关卡游戏进程的进行,会出现一定量的血包供战机补给生命值,血包会在客户区矩形框内运动,10秒后消失;若战机在10秒内吃到血包,则会增加5点生命值知道生命值上限
    每关中战机有三条命,每条命10点生命值,生命使用完后,会进入GameOver界面显示得分数,并提供重新开始游戏和退出功能
    游戏提供10个关卡,每个关卡需要打死相应关卡的敌机数量才能进入Boss模式,打败Boss之后将会进入下一关。10关通关后,显示通关界面,并提供重新开始游戏和退出游戏的功能选项
    暂停功能:游戏进行过程中按下Z键可进入暂停模式,再按Z则返回游戏
    无敌模式:游戏进行过程中按下Y键可进入无敌模式,再按Y则返回正常游戏。该模式下战机生命值不会减少,可供测试使用
    魔法值:游戏进行过程中,战机魔法值会随着时间递增到上限10,魔法值供战机道具功能的使用,过一个关卡魔法值不清零
    战机大招:当战机魔法值为10满状态时,按下X键消耗所有魔法值可发动大招,对屏幕中的敌机进行清屏,Boss扣50点血量
    防护罩:当魔法值不为0时,按下C键可打开防护罩道具,该状态下战机处于无敌状态,不会损失生命值,但魔法值会随着防护罩开启慢慢降低
    战机升级功能:战机子弹单个威力为1,在魔法值不为0时,按下V键开启升级战机模式,战机图标变为动画,子弹威力变成两倍。(若同时开启防护罩和战机升级,则魔法值递减速度翻倍)

    1.2.2 附加功能
    为游戏界面每个关卡添加了滚动背景图片和背景音乐,并在敌机发送炮弹、战机发射子弹、战机击中敌机、敌机击中战机、战机敌机相撞、敌机战机子弹相撞、战机吃到血包、战机大招、战机升级、战机防护罩、游戏结束时均添加了音效
    为美化游戏界面,采用了一部分全民飞机大战图标,并添加了爆炸动画和升级战机动画特效,背景音乐采用微信飞机大战背景音乐和相关特效音效
    为游戏设置了不同的关卡,每个关卡难度不同,敌机与敌机炸弹的速度随着关卡增大而加快,进入第五关以后敌机从上下方均会随机出现,且随机发射炸弹
    前五关卡敌机从上方飞出,速度一定,战机每打掉一架敌机则增加一分,当战机得分超过该关卡所需分数(和关卡数相关)则可进入Boss模式,打败Boss进入下一关;进入第六关以后,敌机分别从上下两方飞出。随着关卡数增加,敌机数量增加,速度增快,敌机炮弹数量和速度也相应增加,进入Boss所需分数增加,Boss生命值和火力也随着关卡数的增加而增加,游戏难度陡然直升
    游戏界面中显示当前状态下的关卡数、当前命数、当前得分、战机血条、战机魔法条、无敌模式提醒和战机道具提醒,Boss模式下还有Boss血条
    增加了鼠标控制战机位置这一效果,战绩的位置随着鼠标的移动而移动,并且点击鼠标左键可使得战机发射子弹
    进入游戏先进入欢迎界面,欢迎界面中显示游戏使用说明,点击鼠标左键和空格键开始游戏。游戏过程中战机命数使用完、通关均有相应界面进行提醒,用户可选择重新开始游戏或退出游戏

    2 相关技术2.1 Timer定时器技术本次项目采用了Java的Timer定时器和TimerTask任务,Timer周期性地在每经过一个指定的时间间隔后就通知TimerTask一次,让TimerTask按照Timer设定的周期循环地执行任务。本程序中使用多个定时器,分别控制不同的功能,分别是屏幕刷新Timer,敌机产生Timer,魔法值变化Timer,血包生命周期Timer。
    2.2 透明贴图实现技术绘制透明位图的关键就是创建一个“掩码”位图(mask bitmap),这个“掩码”位图是一个单色位图,它是位图中图像的一个单色剪影。
    整个绘制过程需要使用到ImageUtil类的createImageByMaskColorEx静态方法,把传入的原图BufferedImage中的指定颜色的背景去掉,返回去掉背景的BufferedImage对象,传送到窗口进行展示。
    2.3 游戏对象列表Java类库中提供了丰富的List接口的实现方法,本项目采用ArrayList存放游戏运行过程中的游戏对象。
    滚动背景模块
    // 欢迎界面图像列表private static BufferedImage titleImage;// 游戏背景对象public static Scene scene;
    各游戏对象
    public static MyPlane myplane = null;Enemy enemy = null;public static Boss boss = null;Bomb bomb = null;Ball ball = null;Explosion explosion = null;Blood blood = null;
    存储游戏对象的对象列表
    public static List<Enemy> enemyList = new ArrayList<Enemy>();public static List<MyPlane> meList = new ArrayList<MyPlane>();public static List<Bomb> bombList = new ArrayList<Bomb>();public static List<Ball> ballList = new ArrayList<Ball>();public static List<Explosion> explosionList = new ArrayList<Explosion>();public static List<Blood> bloodList = new ArrayList<Blood>();
    游戏运行相关参数
    int speed; // 战机的速度,方向键控制public static int myLife; // 为战机设置生命值public static int lifeNum; // 战机命条数public static int myScore; // 战机的得分public static int passScore; // 当前关卡得分数public static int lifeCount; // 血包产生控制参数public static boolean bloodExist; // 标记屏幕中是否存在血包public static int magicCount; // 魔法值,控制能否发大招public static int bossBlood; // Boss血量public static int passNum; // 记录当前关卡
    游戏运行相关标志位
    public static boolean isPass; // 是否通关的标志public static boolean isPause; // 是否暂停public static boolean isBoss; // 标记是否进入Bosspublic static boolean bossLoaded; // 标记Boss出场完成public static boolean isProtect; // 标记是否开启防护罩public static boolean isUpdate; // 标记战机是否升级public static boolean test; // 无敌模式参数public static int isStop; // 标记游戏停止public static boolean isStarted; // 标记欢迎界面是否加载完成
    2.4获取矩形区域使用 Rectangle的intersects方法来判断两个源矩形是否有重合的部分。如果有重合,返回true,没有从何返回false。
    2.5 List<BufferedImage>处理爆炸效果爆炸效果是连续的显示一系列的图片。如果把每一张图片都在要显示的时候进行加载,占用的时间是非常多的,必然后导致程序的可行性下降。List<BufferedImage>是一个“图象列表”是相同大小图象的集合,每个图象都可由其基于零的索引来参考。可以用来存放爆炸效果的一组图片,通过Timer消息连续循环绘制出List<BufferedImage>中的多张图片做成的爆炸效果。
    3 总体设计与详细设计3.1 系统模块划分该飞机大战游戏程序分为游戏滚动背景绘制模块、各游戏对象绘制模块、游戏对象之间的碰撞模块、爆炸效果产生模块、游戏界面输出玩家得分关卡信息模块、战机道具技能维护模块、消息处理模块、视图生命周期维护模块。
    其中在游戏对象绘制模块中,战机是唯一对象,在游戏开始时产生该对象,赋予其固定的生命值,当其与敌机对象、敌机炸弹碰撞时使其生命值减一,直至生命值为零,便删除战机对象。敌机对象与敌机炸弹对象的绘制中采用定时器技术,定时产生。爆炸对象初始化为空,当游戏过程中即时发生碰撞时,在碰撞位置产生爆炸对象,添加到爆炸链表中,并根据爆炸素材图像分八帧进行输出,达到动画特效。
    3.2 主要功能模块3.2.1 系统对象类图
    GameObject是各个游戏对象的抽象父类,继承自Object类,其他的类:战机类、敌机类、爆炸类、子弹类、炸弹类、血包类、文字类都继承了此类,Boss类继承敌机类。每个游戏对象类中既继承了来自父类GameObject的属性,又有自己的特有属性和方法。
    GameObject类介绍:
    // 该游戏对象在窗口中的坐标位置protected Point point;// 在窗口中绘制该游戏对象图标public boolean draw(Graphics g, JPanel panel, boolean pause);// 获取该游戏对象图标在窗口中的矩形框,进行碰撞检测时使用public Rectangle getRect();// 加载该游戏对象对应的图像到内存,方便之后的显示调用public static boolean loadImage(BufferedImage image, String source);
    继承GameObject的其他游戏对象类也都重载了相关方法,以便于各个游戏对象在程序的调用过程中调用方式的一致性。
    3.2.2 项目包和类层次结构图

    MyPanel:JPanel的继承类,是飞机大战窗口的总显示面板,其中包含了游戏运行过程中的大量全局参数和全局标记位
    SpaceWar:程序的入口,窗口对象启动的位置,并在此对相关事件进行了监听
    Ball:敌机炮弹类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Blood:血包类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Bomb:战机炮弹类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Boss:Boss类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Enemy:敌机类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Explosion:爆炸效果类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制图片等游戏对象通用操作方法
    GameObject:游戏对象基类,有加载图片,获取矩形框,获取对象位置,绘制图片的游戏对象统一的方法,统一游戏对象的操作方式
    MyPlane:战机类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制图片等游戏对象通用操作方法
    Scene:场景类,实现了背景滚动,并根据关卡显示不同的背景图片
    EnemyTask:实现了TimerTask接口,随着Timer计时器的调用而随时间产生敌机对象和敌机炮弹对象,实现自动产生敌机的效果
    MagicTask:实现了TimerTask接口,随着Timer计时器的调用而随时间改变魔法值
    RefreshTask:实现了TimerTask接口,随着Timer计时器的调用而随时间刷新窗口界面
    AudioUtil:音频操作工具类,提供播放背景音乐和操作音效
    ImageUtil:图片加工工具类,实现了透明贴图方法和背景图片拼接

    3.2.3 系统主程序活动图
    3.2.4 系统部分流程图飞机大战游戏执行流程图

    定时器产生敌机和炸弹流程图

    血包执行流程图

    4 编码实现4.1 滚动背景在滚动背景的初始化方法和释放方法添加背景音乐播放和释放
    //场景类public class Scene { private int beginY;// 背景的Y坐标 private List<BufferedImage> images; public Scene() { this.images = new ArrayList<BufferedImage>(); } // 初始化场景 public boolean initScene() { // 加载开始图片 BufferedImage buffer; try { buffer = ImageUtil.copyImage(ImageIO.read(new File( "images/start.bmp"))); this.images.add(buffer); // 如果加载失败, 返回false for (int i = 1; i <= 6; i++) { buffer = ImageUtil.copyImage(ImageIO.read(new File( "images/background" + i + ".bmp"))); this.images.add(buffer); } } catch (IOException e) { e.printStackTrace(); return false; } // 背景起始坐标为0 this.beginY = 0; // 播放背景音乐 AudioUtil.playBackground(); return true; } // 绘制场景 public void stickScene(Graphics graphics, int index, ImageObserver observer) { if (index == -1) index = 0; else index = index % 6 + 1; BufferedImage image = images.get(index); // 窗口滑在图片中间 if (beginY >= 0 && beginY + SpaceWar.WINDOWS_HEIGHT <= image.getHeight()) { BufferedImage buffer = image.getSubimage(0, beginY, image.getWidth(), SpaceWar.WINDOWS_HEIGHT); graphics.drawImage(buffer, 0, 0, SpaceWar.WINDOWS_WIDTH, SpaceWar.WINDOWS_HEIGHT, observer); } else if (beginY < 0) { // 超出图片上界 BufferedImage imageUp = image.getSubimage(0, image.getHeight() + beginY, image.getWidth(), -beginY); graphics.drawImage(imageUp, 0, 0, SpaceWar.WINDOWS_WIDTH, -beginY, observer); graphics.drawImage(image, 0, -beginY, SpaceWar.WINDOWS_WIDTH, SpaceWar.WINDOWS_HEIGHT, observer); if (-beginY > SpaceWar.WINDOWS_HEIGHT) { beginY = image.getHeight() + beginY; } } } // 移动背景 public void moveBg() { // 移动背景 beginY -= 1; } // 释放内存资源 public void releaseScene() { for (int i = 0; i < 7; i++) if (images.get(i) != null) images.get(i).flush(); // 关闭背景音乐 AudioUtil.stopBackground(); } public int getBeginY() { return beginY; } public void setBeginY(int beginY) { this.beginY = beginY; }}//在MyPanel中刷新滚动// 滚动背景scene.stickScene(g, -1, this);scene.moveBg();
    4.2 显示战机if (myplane != null) { myplane.draw(g, this, isPause, isProtect);}
    4.3 随机产生敌机和敌机炮弹、Boss炮弹 //随机添加敌机,敌机随机发射炸弹,此时敌机速度与数量和关卡有关 // 根据关卡数产生敌机 if (MyPanel.passNum <= 5) { // 前五关只有一个方向的敌机 Enemy enemy = new Enemy(Enemy.ENEMY_SPEED, 1);// 设置敌机的方向,从上方飞出 enemyList.add(enemy);// 随机产生敌机 if (new Random().nextInt(2) == 0) {// 控制敌机炮弹发出频率 Ball ball = new Ball( enemy.getPoint().x + Enemy.ENEMY_WIDTH / 2, enemy.getPoint().y + Enemy.ENEMY_HEIGHT, enemy.getDirection()); ball.setBallSpeed(enemy.getSpeed()+2); MyPanel.ballList.add(ball); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); } } else if (MyPanel.passNum > 5) {// 第五关之后,两个方向的敌机 Enemy enemy1 = new Enemy(Enemy.ENEMY_SPEED, 1);// 设置敌机的方向,从上方飞出 enemy1.setSpeed(Enemy.ENEMY_SPEED + (new Random().nextInt(2) + MyPanel.passNum - 1)); enemyList.add(enemy1); Enemy enemy2 = new Enemy(Enemy.ENEMY_SPEED, -1);// 设置敌机的方向,从下方飞出 enemy2.setSpeed(Enemy.ENEMY_SPEED + (new Random().nextInt(2) + MyPanel.passNum - 1)); enemyList.add(enemy2); int rand = new Random().nextInt(3); if (rand == 0) {// 控制敌机炮弹发出频率 Ball ball = new Ball(enemy1.getPoint().x + Enemy.ENEMY_WIDTH / 2, enemy1.getPoint().y + Enemy.ENEMY_HEIGHT, enemy1.getDirection()); ball.setBallSpeed(enemy1.getSpeed()+2); MyPanel.ballList.add(ball); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); } if (rand == 1) {// 控制敌机炮弹发出频率 Ball ball = new Ball(enemy2.getPoint().x + Enemy.ENEMY_WIDTH / 2, enemy2.getPoint().y, enemy2.getDirection()); ball.setBallSpeed(enemy2.getSpeed()+2); MyPanel.ballList.add(ball); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); } } if (MyPanel.isBoss) { // Boss发射子弹 // 敌机炸弹产生定时器触发 // 设置定时器产生敌机炸弹 Ball ball1 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball1.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball1); Ball ball2 = new Ball(MyPanel.boss.getPoint().x + 5, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball2.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball2); Ball ball3 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH - 5, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball3.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball3); Ball ball4 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2 + 85, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball4.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball4); Ball ball5 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2 - 85, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball5.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball5); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); }
    4.4 显示战机发射子弹for (int i = 0; i < bombList.size(); i++) { bomb = bombList.get(i); if (bomb == null) continue; bomb.setCurrentIndex(i); bomb.isUpdate = isUpdate; if (!bomb.draw(g, this, isPause)) i--; }
    4.5 碰撞检测,以战机子弹集中敌机为例if (MyPanel.myplane != null && !MyPanel.isPause) { // 子弹打中敌机 boolean flag = false; for (int i = 0; i < MyPanel.bombList.size(); i++) { Bomb bomb = MyPanel.bombList.get(i); if (bomb == null) continue; Rectangle bombRectangle = bomb.getRect(); for (int j = 0; j < MyPanel.enemyList.size(); j++) { Enemy enemy = MyPanel.enemyList.get(j); if (enemy == null) continue; Rectangle enemyRectangle = enemy.getRect(); if (enemyRectangle.intersects(bombRectangle)) { Explosion explosion = new Explosion( (bomb.getPoint().x + Bomb.BOMB_WIDTH / 2 - Explosion.EXPLOSION_WIDTH / 2), (bomb.getPoint().y + Bomb.BOMB_HEIGHT / 2 - Explosion.EXPLOSION_WIDTH / 2)); MyPanel.explosionList.add(explosion); // 音效 AudioUtil.play(AudioUtil.AUDIO_EXPLOSION); // 爆炸后删除子弹 MyPanel.bombList.remove(i); i--; // 敌机生命值减少 enemy.life -= MyPanel.isUpdate ? 2 : 1; if (enemy.life <= 0) { // 增加得分 MyPanel.passScore++; // 删除敌机 MyPanel.enemyList.remove(j); j--; } // 炮弹已删除,直接跳出本循环 flag = true; break; } } if (flag) continue; if (MyPanel.isBoss && bomb != null) { // 获得战机子弹的矩形区域 Rectangle bombRect = bomb.getRect(); // 获得Boss的矩形区域 Rectangle bossRect = MyPanel.boss.getRect(); // 判断两个矩形区域是否有交接 if (bombRect.intersects(bossRect)) { // 将爆炸对象添加到爆炸链表中 Explosion explosion = new Explosion( (bomb.getPoint().x + Bomb.BOMB_WIDTH / 2 - Explosion.EXPLOSION_WIDTH / 2), (bomb.getPoint().y + Bomb.BOMB_HEIGHT / 2 - Explosion.EXPLOSION_WIDTH / 2)); MyPanel.explosionList.add(explosion); // 音效 AudioUtil.play(AudioUtil.AUDIO_EXPLOSION); // 爆炸后删除子弹 MyPanel.bombList.remove(i); i--; bomb = null; // 是Boss,不删除敌机,只扣血 MyPanel.bossBlood -= MyPanel.isUpdate ? 2 : 1; if (MyPanel.bossBlood <= 0) { Explosion explosion1 = new Explosion( MyPanel.boss.getPoint().x, MyPanel.boss.getPoint().y); MyPanel.explosionList.add(explosion1); Explosion explosion2 = new Explosion( (MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH), (MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT)); MyPanel.explosionList.add(explosion2); Explosion explosion3 = new Explosion( (MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH), (MyPanel.boss.getPoint().y)); MyPanel.explosionList.add(explosion3); Explosion explosion4 = new Explosion( (MyPanel.boss.getPoint().x), (MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT)); MyPanel.explosionList.add(explosion4); Explosion explosion5 = new Explosion( (MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2 - Explosion.EXPLOSION_WIDTH / 2), (MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT / 2 - Explosion.EXPLOSION_WIDTH / 2)); explosion5.setBossDie(true);// 标记最后一个炸弹,炸完之后跳入下一关 MyPanel.explosionList.add(explosion5); MyPanel.boss = null; // 过关的标志变量 // isPause = TRUE; // CMyPlane* temp = myplane; // myplane = new CMyPlane(FALSE); MyPanel.myplane = null; MyPanel.isPass = true; MyPanel.isBoss = false; } } } } }
    4.6 显示爆炸效果for (int i = 0; i < explosionList.size(); i++) { explosion = explosionList.get(i); if (explosion == null) continue; boolean b = explosion.draw(g, this, isPause); if (!b) { explosionList.remove(i); i--; } }
    4.7 血包功能游戏三分之一和三分之二进程时刻出现血包
    //开启血包 if (MyPanel.myplane != null && MyPanel.myLife > 0 && !MyPanel.isPause) { // 关卡打了三分之一三分之二处出现血包 if (MyPanel.passScore > (MyPanel.PASS_SCORE + MyPanel.passNum * 5) * MyPanel.lifeCount / 3) { // 若屏幕中有未吃掉的血包,这次不产生血包 if (!MyPanel.bloodExist) { MyPanel.lifeCount++; // 产生血包 Blood blood = new Blood(); MyPanel.bloodList.add(blood); MyPanel.bloodExist = true; bloodTimer = new Timer(); bloodTimer.schedule(new TimerTask() { @Override public void run() { bloodTimer.cancel(); bloodTimer = null; MyPanel.bloodExist = false; // 声明血包位置 for (int i = 0; i < MyPanel.bloodList.size(); i++) { MyPanel.bloodList.remove(i); i--; } } }, 10000); } else MyPanel.lifeCount++; } } //血包定时器,10秒后血包消失 bloodTimer = new Timer(); bloodTimer.schedule(new TimerTask() { @Override public void run() { bloodTimer.cancel(); bloodTimer = null; MyPanel.bloodExist = false; // 声明血包位置 for (int i = 0; i < MyPanel.bloodList.size(); i++) { MyPanel.bloodList.remove(i); i--; } } }, 10000); //显示血包 if (myplane != null && !isPause) { // 检索血包链表,非空时在所在位置显示 int i = 0; while (i < bloodList.size()) { blood = bloodList.get(i); if (blood == null) continue; blood.draw(g, this, false); i++; }// while }//血包碰撞检测 if (MyPanel.myplane != null && !MyPanel.isPause) { // 吃到血包 // 声明血包位置 for (int i = 0; i < MyPanel.bloodList.size(); i++) { Blood blood = MyPanel.bloodList.get(i); // 获得血包矩形 Rectangle bloodbRect = blood.getRect(); // 获得战机矩形 Rectangle planeRect = MyPanel.myplane.getRect(); // 判断两个矩形区域是否有交接 if (bloodbRect.intersects(planeRect)) {// 音效 AudioUtil.play(AudioUtil.AUDIO_BLOOD); // 加血效果 MyPanel.myLife += 5; if (MyPanel.myLife > MyPanel.DEFAULT_LIFE) MyPanel.myLife = MyPanel.DEFAULT_LIFE; // TODO 声音 // 加血后血包删除 MyPanel.bloodList.remove(i); i--; break; }// if }// for }
    4.8 通关和死亡消息页面if (isStop == FLAG_RESTART) { Font textFont = new Font("宋体", Font.BOLD, 20); g.setFont(textFont); // 设置透明背景 // cdc.SetBkMode(TRANSPARENT); g.setColor(Color.red); g.drawString("哇,恭喜你已通关!", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 30); g.drawString("您的得分为:" + myScore, SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 10); g.drawString("COME ON !重新开始?Y/N", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 + 10); return; } else if (isStop == FLAG_STOP) { Font textFont = new Font("宋体", Font.BOLD, 20); g.setFont(textFont); // 设置透明背景 // cdc.SetBkMode(TRANSPARENT); g.setColor(Color.red); // 显示最后结果 g.drawString("GAME OVER!", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 30); g.drawString("您的得分为:" + myScore, SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 10); g.drawString("COME ON !重新开始?Y/N", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 + 10); return; }
    4.9 魔法值控制维护public class MagicTask extends TimerTask { @Override public void run() { if (MyPanel.myplane != null && !MyPanel.isPause && MyPanel.isStarted) { // 防护罩和战机升级没打开,魔法值递增 if (!MyPanel.isProtect && !MyPanel.isUpdate) { MyPanel.magicCount++; if (MyPanel.magicCount > 10) MyPanel.magicCount = 10; } // 判断是否打开防护罩 if (MyPanel.isProtect) { // 开启防护罩魔法值递减 MyPanel.magicCount--; if (MyPanel.magicCount <= 0) { MyPanel.magicCount = 0; MyPanel.isProtect = false; } } // 判断是否升级战机 if (MyPanel.isUpdate) { // 战机升级,魔法值递减 MyPanel.magicCount--; if (MyPanel.magicCount <= 0) { MyPanel.magicCount = 0; MyPanel.isUpdate = false; MyPanel.myplane.isUpdate = MyPanel.isUpdate; } } } }}
    4.10 得分到达关卡需求,进入Boss // 进入Boss int pScore = MyPanel.PASS_SCORE + MyPanel.passNum * 5; // TODO调试条件 // if (MyPanel.myplane != null && MyPanel.passScore >= 3 && // !MyPanel.isPause&&!MyPanel.isBoss) if (MyPanel.myplane != null && MyPanel.passScore >= pScore && !MyPanel.isPause && !MyPanel.isBoss) { // 进入Boss MyPanel.isBoss = true; MyPanel.boss = new Boss(1); MyPanel.boss.setSpeed(Boss.BOSS_SPEED + MyPanel.passNum - 1); MyPanel.boss.life = Boss.BOSS_LIFE + MyPanel.passNum * 50;// Boss总血量 MyPanel.bossBlood = Boss.BOSS_LIFE + MyPanel.passNum * 50;// 当前Boss血量 // Boss出场,暂停游戏 MyPanel.bossLoaded = false; // 重新设置Boss的子弹产生频率,增强Boss子弹发射频率 MyPanel.enemyTimer.cancel(); MyPanel.enemyTimer = null; MyPanel.enemyTimer = new Timer(); MyPanel.enemyTimer.schedule(new EnemyTask(MyPanel.enemyList), 0, 2000 - MyPanel.passNum * 120); }//显示Boss if (myplane != null && boss != null && !isPause && isBoss) { boolean status = boss.draw(g, this, passNum, isPause); if (status) bossLoaded = true; }
    4.11 检测标记位isPass,判断打赢Boss,进入下一关if (MyPanel.isPass) { MyPanel.isPass = false; if (MyPanel.passNum == 10)// 10关 { // 重新初始化数据 MyPanel.killTimer(); MyPanel.myplane = new MyPlane(false); MyPanel.isPause = true; MyPanel.isStop = MyPanel.FLAG_RESTART; // 清屏 }// if else { MyPanel.killTimer(); MyPanel.isPause = true; // 保存所需数据 int tScore = MyPanel.myScore + MyPanel.passScore; int tPassNum = MyPanel.passNum + 1; boolean tTest = MyPanel.test; int magic = MyPanel.magicCount; // 重新开始游戏 MyPanel.Restart(); MyPanel.myplane = new MyPlane(false); MyPanel.myScore = tScore; MyPanel.passNum = tPassNum; MyPanel.magicCount = magic; MyPanel.test = tTest; }// else }// if
    4.12 消息监听4.12.1 按键监听 // 按键监听 frame.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent event) { if (MyPanel.myplane != null && !MyPanel.isPause) { switch (event.getKeyCode()) { case KeyEvent.VK_LEFT: int x = MyPanel.myplane.getPoint().x - MyPanel.DEFAULT_SPEED; if (x < 0) x = 0; MyPanel.myplane.setPoint(new Point(x, MyPanel.myplane .getPoint().y)); break; case KeyEvent.VK_RIGHT: int x1 = MyPanel.myplane.getPoint().x + MyPanel.DEFAULT_SPEED; if (x1 > SpaceWar.WINDOWS_WIDTH - Bomb.BOMB_WIDTH) x1 = SpaceWar.WINDOWS_WIDTH - Bomb.BOMB_WIDTH; MyPanel.myplane.setPoint(new Point(x1, MyPanel.myplane .getPoint().y)); break; case KeyEvent.VK_UP: int y = MyPanel.myplane.getPoint().y - MyPanel.DEFAULT_SPEED; if (y < 0) y = 0; MyPanel.myplane.setPoint(new Point(MyPanel.myplane .getPoint().x, y)); break; case KeyEvent.VK_DOWN: int y1 = MyPanel.myplane.getPoint().y + MyPanel.DEFAULT_SPEED; if (y1 > SpaceWar.WINDOWS_HEIGHT) y1 = SpaceWar.WINDOWS_HEIGHT; MyPanel.myplane.setPoint(new Point(MyPanel.myplane .getPoint().x, y1)); break; case KeyEvent.VK_SPACE: Bomb bomb1 = new Bomb( MyPanel.myplane.getPoint().x + 10, MyPanel.myplane.getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb1); Bomb bomb2 = new Bomb(MyPanel.myplane.getPoint().x + MyPlane.PLANE_WIDTH - 40, MyPanel.myplane .getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb2); // 音效 AudioUtil.play(AudioUtil.AUDIO_BOMB); break; case KeyEvent.VK_C: // 开启防护罩 MyPanel.isProtect = true; // 音效 AudioUtil.play(AudioUtil.AUDIO_PROTECT); break; case KeyEvent.VK_V: // 战机升级 MyPanel.isUpdate = true; MyPanel.myplane.isUpdate = true; // 音效 AudioUtil.play(AudioUtil.AUDIO_UPDATE); break; case KeyEvent.VK_Y: if (MyPanel.isStop == 0) { // 无敌模式开关 if (MyPanel.test == false) MyPanel.test = true; else MyPanel.test = false; } break; case KeyEvent.VK_X:// 大招 if (MyPanel.bossLoaded) { // 战机发大招 if (MyPanel.magicCount >= 10) { MyPanel.magicCount -= 10; // 清空敌机 for (int i = 0; i < MyPanel.enemyList.size(); i++) { Enemy enemy = MyPanel.enemyList.get(i); // 将爆炸对象添加到爆炸链表中 Explosion explosion = new Explosion( (enemy.getPoint().x + Enemy.ENEMY_WIDTH / 2), (enemy.getPoint().y + Enemy.ENEMY_HEIGHT / 2)); MyPanel.explosionList.add(explosion); // 删除敌机 MyPanel.enemyList.remove(i); // 增加得分 MyPanel.passScore++; }// for if (MyPanel.isBoss) { // 将爆炸对象添加到爆炸链表中 Explosion explosion = new Explosion( MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT / 2); MyPanel.explosionList.add(explosion); MyPanel.bossBlood -= 50; if (MyPanel.bossBlood <= 0) { // boss死,过关 // 过关的标志变量 MyPanel.boss = null; // 过关的标志变量 MyPanel.isPause = true; MyPanel.myplane = new MyPlane(false); MyPanel.isPass = true; MyPanel.isBoss = false; } } // 清空敌机炮弹 for (int i = 0; i < MyPanel.ballList.size(); i++) { MyPanel.ballList.remove(i); } // 音效 AudioUtil.play(AudioUtil.AUDIO_DAZHAO); } } break; default: break; } } // 暂停 if (event.getKeyCode() == KeyEvent.VK_Z) { if (MyPanel.isPause) MyPanel.isPause = false; else MyPanel.isPause = true; } // 取消键N else if (event.getKeyCode() == KeyEvent.VK_N) { if (MyPanel.isStop != 0) { JDialog dialog = new JDialog(frame); dialog.setTitle("关于"); dialog.setSize(400, 200); dialog.setLocation(450, 200); JButton button = new JButton(); button.setText("确定"); button.setSize(100, 50); button.setLocation(200, 120); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); dialog.add(button); dialog.setVisible(true); } } // 确认键Y if (MyPanel.isStop != 0 && event.getKeyCode() == KeyEvent.VK_Y) { MyPanel.isStop = 0; MyPanel.Restart(); } // 按空格进入游戏 if (!MyPanel.isStarted && event.getKeyCode() == KeyEvent.VK_SPACE) { MyPanel.isStarted = true; MyPanel.scene.setBeginY(0); } } });
    4.12.2 鼠标移动监听frame.addMouseMotionListener(new MouseMotionListener() { @Override public void mouseMoved(MouseEvent arg0) { if (MyPanel.myplane != null && !MyPanel.isPause) MyPanel.myplane.setPoint(arg0.getPoint()); } @Override public void mouseDragged(MouseEvent arg0) { } });
    4.12.3 鼠标左键发射子弹和开始界面进入游戏frame.addMouseListener(new MouseListener() { @Override public void mouseReleased(MouseEvent arg0) { } @Override public void mousePressed(MouseEvent event) { if (MyPanel.myplane != null && !MyPanel.isPause) { if (event.getButton() == MouseEvent.BUTTON1) { // 左键 if (MyPanel.myplane != null && !MyPanel.isPause) { Bomb bomb1 = new Bomb( MyPanel.myplane.getPoint().x + 10, MyPanel.myplane.getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb1); Bomb bomb2 = new Bomb(MyPanel.myplane.getPoint().x + MyPlane.PLANE_WIDTH - 40, MyPanel.myplane .getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb2); // 音效 AudioUtil.play(AudioUtil.AUDIO_BOMB); } if (!MyPanel.isStarted) { MyPanel.isStarted = true; MyPanel.scene.setBeginY(0); } } } } @Override public void mouseExited(MouseEvent arg0) { } @Override public void mouseEntered(MouseEvent arg0) { } @Override public void mouseClicked(MouseEvent arg0) { } });
    4.13 生命周期4.13.1游戏重新开始public static void Restart() { // TODO: 在此处添加游戏重新开始初始化参数 // 战机重新加载 MyPanel.myplane = new MyPlane(false); scene.setBeginY(0); // 清空敌机链表 if (MyPanel.enemyList.size() > 0) MyPanel.enemyList.removeAll(MyPanel.enemyList); // 清空战机链表 if (MyPanel.meList.size() > 0) MyPanel.meList.removeAll(MyPanel.meList); // 清空战机子弹链表 if (MyPanel.bombList.size() > 0) MyPanel.bombList.removeAll(MyPanel.bombList); // 清空敌机炸弹链表 if (MyPanel.ballList.size() > 0) MyPanel.ballList.removeAll(MyPanel.ballList); // 清空爆炸链表 if (MyPanel.explosionList.size() > 0) MyPanel.explosionList.removeAll(MyPanel.explosionList); // 清空血包列表 if (MyPanel.bloodList.size() > 0) MyPanel.bloodList.removeAll(MyPanel.bloodList); // 参数重新初始化 MyPanel.myLife = DEFAULT_LIFE; MyPanel.lifeNum = DEFAULT_LIFE_COUNT; MyPanel.myScore = 0; MyPanel.passScore = 0; MyPanel.passNum = DEFAULT_PASS; MyPanel.isPass = false; MyPanel.isPause = false; MyPanel.lifeCount = 1; MyPanel.magicCount = 0; MyPanel.bloodExist = false; MyPanel.bossBlood = Boss.BOSS_LIFE; MyPanel.isBoss = false; MyPanel.bossLoaded = true; MyPanel.isProtect = false; MyPanel.isUpdate = false; MyPanel.test = false; MyPanel.boss = null; // isStarted = FALSE; initTimer(); }
    4.13.2 生命值归零,游戏结束public static void gameOver() { // 结束游戏界面 // 释放计时器 killTimer(); // 计算最后得分 myScore += passScore; // 播放游戏结束音乐 // 清屏 // 音效 AudioUtil.play(AudioUtil.AUDIO_GAMEOVER); isStop = FLAG_STOP; // TODO System.out.println("-----------gameOver"); }
    5 课程设计中遇到的主要问题及解决方法5.1 滚动背景实现问题要实现滚动背景,需要在背景图上取出客户区矩形大小的区域,并按照Timer进行递进,并且要在背景图边界处衔接好返回背景图开头位置,实现一张背景图的循环反复,达到滚动背景图的目的,刚开始由于不懂得如何实现开头结尾处的衔接问题,导致滚动背景图实现难度大,后来经由网上查阅的资料得知要把背景图加载两份,第二份背景图开头衔接在第一份结尾处,让客户区矩形在该两份背景图上进行滑动,当滑动到第二份背景图时,再把第一份接到第二份结尾处,从而实现循环反复滚动背景。其中实现的难点在于控制好客户区矩形坐标和背景图上要显示的图片块位于图片上的坐标的对应关系。
    5.2 背景音乐循环播放和游戏音效同时输出问题由于平时接触Swing太少,对Swing操作多媒体文件颇为陌生,对于Java的音频播放也是颇为陌生,通过网上查找了很多相关资料,才把AudioClip对音频的播放功能实现,而且刚开始使用,没注意到音频文件播放完的释放问题,导致程序运行一阶段之后出现了内存溢出而崩溃的情况。
    5.3 多帧位图素材的动画特效实现问题飞机大战中的爆炸显示是通过一张带有八个帧的位图进行连续输出实现爆炸特效,刚开始只是简单的在一个while循环中循环八次,结果不尽如人意,后来联想到帧和时间的对应关系,在每一次TimerTask调用时输出一帧,并在爆炸对象中用progress标记位记录播放位置,等到八个帧播放结束时,返回播放结束标记位,在TimerTask中检测并删除播放完成的该爆炸对象。
    5.4 游戏结束和游戏重新开始的游戏资源维护问题该游戏由于实现了多个额外功能,且都是带有全局意义的,因此放置了较多标记位来标记每个状态,通过这些标记位来控制游戏进程中的各个状态,因此在游戏结束和重新开始游戏时,各个标记位的正确重置将会决定重新开始游戏之后的游戏状态。还由于这些操作可能会在TimerTask类调用过程中进行中断,因此程序运行中途的中断时的游戏参数的维护对程序的正确执行至关重要。
    6 课程设计体会由于对Swing接触不多,因此在开发过程中走了很多弯路,但是也是这次的实训,让我了解了Swing的事件处理机制,了解了如何在屏幕中绘制需要向用户展示的图形信息,学会了使用基本的操作对屏幕中绘制的图形进行控制,也了解了Java的Timer定时器的机制,学习了Sun封装好的Swing类库,学会遇到问题到JDK文档中查询Api,并对Eclipse的使用和编程也进行了提高。
    在开发过程中遇到了大量的异常,通过此次开发,也学会了遇到错误如何慢慢通过IDE的错误信息进行错误查找和代码排错更正,通过添加断点调试程序一步步监视程序运行的具体过程,从而找到程序运行出错的原因。
    本次的课程设计的飞机大战代码是基于前一次的MFC飞机大战进行移植的,因此,在模块的分布和设计方面大多维持原来C++的布局,因此,本次项目开发的源码层次接口没有严格的按照Java的规范,这是这个项目的不足之处,如果时间允许,应该着手对其中的代码布局进行相应的修改,以符合Java的风格,增强代码的可读性。
    7 游戏演示游戏画面1

    游戏画面2
    6 评论 611 下载 2018-11-04 23:00:52 下载需要12点积分
  • 基于WPF实现的简单绘图工具

    1、系统功能设计
    开发、测试环境

    开发环境: Visual Studio 2012 Premium运行框架:.net framework 4.5测试环境:Windows 8.1、Windows 7
    开发语言

    C#XAML

    1.1 前言当初选择这么一个软件来编写纯粹是出于对玩弄文字游戏的xx管理系统的不喜,但我们没有料想到,经实践表明,涉及实时跟踪鼠标键盘事件和实时绘图的软件编写难度远大于主要通过文字实现信息交互的xx管理系统。仅仅实现一个屏幕上图形的框选功能就让我改了六七遍代码,我的队友更是间断地找出了五个bug。当终于能够把整体功能流畅地实现时,我们对软件开发者的了解与敬意又加深了一层。
    1.2 总体功能描述当今图像处理越发普及,人们对于图像处理的需求也各不相同。而一些绘图软件存在过于复杂(如PS)或是只具备基础功能(如windows自带画图)的问题,因此我们开发一个基于Windows Presentation Foundation(WPF)的简单绘图工具。
    以下为程序的工作界面

    1.3 功能点说明


    功能类型
    功能点名称
    按键
    实现方式
    功能点描述




    基本功能
    主流类型图片载入
    Ctrl+O
    自己编写C#代码
    支持.png.jpg.gif.bmp.eps等主流图片类型的读入和加载。使用C#标准OpenFileDialog对象获得文件路径并且使用Uri读入之后转为BitmapImage,用Image控件显示


    基本功能
    图片保存
    Ctrl+S
    自己编写C#代码
    支持bmp(位图)和eps(矢量图)两种格式。使用SaveFileDialog对象获得文件路径之后用filestream输出。


    基本功能
    鼠标手绘图形
    鼠标拖框
    自己编写C#代码
    包括矩形、圆角矩形、圆、椭圆、直线、贝塞尔曲线等


    基本功能
    单击选择图形
    鼠标单击
    自己编写C#代码
    调用类接口SelectPoint来实现


    复杂功能
    框选图形
    鼠标拖框
    自己编写C#代码
    递归调用类接口SelectRect来实现


    复杂功能
    全选图形
    Ctrl+A
    自己编写C#代码
    调用类接口SelectAll来实现


    复杂功能
    多次选中图形
    Shift+鼠标拖框
    自己编写C#代码
    调用类接口MergeComposite来实现


    复杂功能
    被选择图形的闪烁

    自己编写C#代码
    通过来回设置选择的CompositeGraphic的isVisible属性来达到闪烁的目的。


    复杂功能
    删除图形
    Delete
    自己编写C#代码
    调用类接口Clear来实现


    复杂功能
    图形状态变更

    自己编写C#代码
    设置类属性DrawMode来实现。可以设置图形的边框粗细、颜色,以及内部颜色


    复杂功能
    调色板

    使用第三方库
    支持通过ARGB属性或是在图形中直接选色的方式给图形的边框和填充分别选色


    复杂功能
    拖动图形
    鼠标按住拖动
    自己编写C#代码
    调用类接口Move来实现


    复杂功能
    键盘移动
    Up,Down, Left,Right
    自己编写C#代码
    通过长按可快速移动


    复杂功能
    剪切选中图形
    Ctrl+X
    自己编写C#代码
    将选中的图形加入一个List,并从画布上删除选中的图形


    复杂功能
    复制选中图形
    Ctrl+C
    自己编写C#代码
    将选中的图形加入一个List,但不从画布上删除选中的图形


    复杂功能
    粘贴图形
    Ctrl+V
    自己编写C#代码
    对上述List中的所有成员递归调用接口 ICloneable.Clone。长按Ctrl+V可以连续粘贴


    统计功能
    图形个数统计

    自己编写C#代码
    递归调用类属性Count并将它和Label绑定


    用户体验优化
    打开和新建图片、关闭程序时的友情提醒。

    自己编写C#代码
    防止误删未完成画布中的内容。


    用户体验优化
    显示系统时间

    自己编写C#代码
    使用DateTime.Now.ToString方法


    错误处理
    载入图像时对不支持图像及无法处理的图像的抛出。

    自己编写C#代码
    若捕获异常,会弹出对话框



    2、系统总体结构2.1 概要设计按照“面向接口编程,而不是面向实现编程”的面向对象基本原则,在建立解决方案的时候我就将解决方案分成了Ccao-big-homework(UI,WPF工程项目,由我的队友邵键准同学实现,调用者)和Ccao-big-homework-core(实现,C#类库,由我实现,被调用者)。双方互不干涉,独立调试,在整个大作业过程中不曾因为接口耦合问题出现bug。
    由于采用了多种科学合理的设计模式(见下文各模块介绍),本类库在维持良好的可复用性的同时,不曾进行过任何强制类型转换,不曾进行过任何运行时类型判定,所有的多态都靠重载函数来实现,充分体现了面向对象的思想。
    3、具体实现3.1 GUI的设计GUI的设计采用了较为简洁的风格,设计完成后曾请求周边同学进行体验并对细节进行改进,力求用户体验较好。主体采用XAML语言,实现设计和功能的分离,并配之以C#的事件处理函数。程序取消了不怎么美观的窗口边框,并采取点住程序窗口任何一个位置均可拖动的方法。
    3.1.1 程序启动和关闭动画设计程序的主要窗口在开始和结束时都是通过淡入和淡出来呈现和销毁窗口,实现此效果使用了一个计时器,并让窗口的透明度随计时器而改变。下图为启动过程截图,可看见窗体还是半透明状态(请无视背景的代码)。
    3.1.2 程序启动界面的设计界面包括版本号与开发人员,“新建绘图”按钮在鼠标移上后会有高亮,点击进入绘图页面,点击“离开”按钮则直接调用Application.Current.Shutdown()函数销毁窗口。
    3.1.3 程序主体绘图界面设计主体绘图界面如下。

    上部为菜单栏,具体按钮功能见使用手册,实现了鼠标移到某个按键上时该按钮闪烁一次并且放大,同时调整整个工作框的布局。效果如下。

    左部为绘图框,选择后可使用鼠标绘制不同的图形,包括直线、圆、椭圆、正方形、长方形、贝塞尔曲线。其中贝塞尔曲线限于WPF提供的贝塞尔曲线构造函数的局限性,其必定从画布的左上角开始绘制。
    左下角为一个图标,无实际作用。
    3.1.4 程序图标的添加我们的程序可是有图标的哦!

    下面是调色板的图标。

    3.2 逻辑层主要实现解决方案Ccao-big-homework。
    3.2.1 工作窗口WorkWindow部分3.2.1.1 概述主要实现人与程序的交互,包括鼠标事件和键盘事件等。由于绘图软件基本是在一个窗口进行操作,一般使用组合而非继承的方式,因此类的结构比较扁平。类图如下。


    由于没有继承,全部实现在一个窗口里代码显得非常臃肿,因此我根据功能的不同将同一个窗口类分成了如下几个文件。

    下面逐步介绍各个文件实现的方法。
    3.2.1.2 BtnEvent实现菜单栏所有按钮功能,包括新建、打开、保存、退出、全选、复制、剪切、粘贴、样式选择按钮。实现方法是调用其他文件里的私有函数。
    3.2.1.3 FileEvent实现图片读写的相关方法。我们的程序支持使用文件夹视图来把文件保存到计算机的任意位置或是从计算机任意位置载入图片。同时还在现有图片未保存时弹出对话框提示用户要保存,防止了图片的误删,优化用户体验。

    实现画布的新建与刷新,并实现程序的退出功能。
    3.2.1.4 FishEyePanel实现上部菜单栏中当鼠标移到某个按键上时该按钮闪烁一次并且放大,同时调整整个工作框的布局。此处采用了组合的形式,FishEyePanel是一个新的类,在WorkWindow类中创建该类的对象,并布局到主窗口上。
    3.2.1.5 GraphicsOperation实现图像整体操作,包括:

    全选:调用队友提供的SelectRect接口,并把范围设置成整个画布大小的矩形,从而得到一个类型为List<CompositeGraphic>的对象,添加到selectedGraphics里
    复制:调用队友提供的Clone()函数,往clonedGraphics这个对象里添加对象
    剪切:复制的同时清空selectedGraphics
    粘贴:将clonedGraphics里的所有成员添加到总画布compositeGraphic的Children()成员中,从而实现绘制,并向右下角移动(10,10),从而和复制的原图区分开,然后清空clonedGraphics(),并再次调用复制函数(),从而实现粘贴的连续性,即复制一次可连续粘贴。下图为按下Ctrl+C后连续按下Ctrl+V的效果


    3.2.1.6 KeyBoard实现键盘的按键监控,包括:

    Ctrl+A:全选图像
    Ctrl+C:复制选中图像
    Ctrl+V:粘贴选中图像
    Ctrl+X:剪切选中图像
    Ctrl+W:关闭程序
    Ctrl+O:打开图片
    Ctrl+N:新建画布
    Ctrl+S:保存图片
    Delete:删除选中图像
    Key Up、Key Down、Key Left、Key Right:选中图像的上下左右移动
    Shift:按住时可多次增加选择已选中的图形

    3.2.1.7 MouseEvent我的工作中最难的部分,而且肩负调试队友代码的使命。

    Window_MouseLeftButtonDown事件:实现鼠标不在画布上时窗口根据鼠标的移动而拖动
    OnMouseLeftButtonDown事件:鼠标左键按下处理事件。主要记录鼠标开始移动的点startPoint,让画布捕捉到鼠标,并标记左侧的ToolBar是否选中rbSelect选择按钮
    OnMouseMove事件:鼠标移动时的处理事件。当鼠标移动且画布捕捉到鼠标的时候,若是发现rbSelect选择按钮被选中且当前有图形被选中且Shift键未被按下,那么说明这个鼠标事件需要的是移动图形,因而用虚直线实时绘制鼠标指示的移动路径;若是其他绘图按钮被选中,则说明要绘制图形,则用虚长方形或是虚直线实时绘制图形
    OnMouseLeftButtonUp事件:鼠标左键抬起的处理事件。首先判断鼠标抬起时和按下时位置是否相同。若是相同,则说明用户只是按了一下,那么不管左侧ToolBar是选中的什么,说明用户都是想选中一个图像,因此将左侧的工具条调整到rbSelect按钮,并且调用总画布compositrGraphic的SelectPoint方法,得到这个点选中的图像,将其加入selectGraphics,在这个List里面的对象,每隔0.5秒更改一次可见性,从外观看来,选中的图形会闪烁。若是鼠标移动了,且选项选中的是图形选项卡,则绘制对应的图形,并且刷新画布,并把之前实时跟踪鼠标的虚线图形删去。接下来判断选项卡是否为贝塞尔曲线选项,若是则把该点增加到贝塞尔曲线的List里,当List里的成员个数增加到4时,绘制一条贝塞尔曲线并清空该List。最后是最为复杂的rbSelect按钮,如果当时选中的图形为0,即selectGraphics为空,则说明用户想要选中他框选的图形,则调用SelectRect得到选中的所有图形,并把其加入selectGraphics中使其闪烁。若selectGraphics不为空,则说明用户想要移动选中的图形,则调用move方法移动选中的图形,并把selectGraphics清空。同时,如果整个过程中Shift键被按下且rbSelect被选中,说明用户想要增加选择图形,于是将选中的图形增加到selectGraphics里面。

    以上方法还各自特判了贝塞尔曲线绘制时的点四个点的情况。
    3.2.1.8 Paint各种图形的绘制,包括:

    直线
    长方形
    正方形

    椭圆
    圆角矩形
    贝塞尔曲线

    其中(1)~(6)的绘制方式基本相同,都是新建一个该图形的对象,然后根据传来的两个点确定图形的长宽和位置,将这个对象添加至总画布compositeGraphic,然后刷新画布。
    对于(7),绘制方式是在画布上点四个点,则出现贝塞尔曲线。
    3.2.1.9 其他零碎功能主要包括一些动画效果。启动时工具条的移入运用了ThicknessAnimation控件。为了美观我特意写了一个函数隐藏了工具条尾部的小箭头。选中图形闪烁的功能则使用了一个DispatcherTimer计时器,每过500ms就把selectGraphics的成员的可见性改变一次。图形个数统计调用总画布compositeGraphic的count属性,每隔0.5秒刷新一次。系统时间标签则使用DateTime.Now.ToString方法获取。
    3.2.2 启动窗口MainWindow部分这个窗口很简单,只实现了弹出WorkWindow和关闭窗口退出的功能。并贴了一张图,写了版本号。
    3.2.3 颜色拾取窗口StyleSettingWindow部分颜色拾取窗口我并没有花太多时间自己写,本来以为WPF自带调色板控件,结果发现没有,于是在网上找了一个扩展控件,并组合到WorkWindow类中,在按下上部菜单栏中的“样式选择”按钮时弹出。

    3.3 类库Ccao-big-homework-core-wpf的实现3.3.1概述。本类库(Ccao-big-homework-core-wpf.csproj)基于WPF,文件统一注释为”Du 2015.9”。

    由于本类库被设计作为实现图形操作的类的类库,所以各类之间比起“is-implemented-in-term-of-a“或者”has-a“来说更符合”is-a”关系,所以本类库大量使用了继承、多态、递归调用这些OO的方法,和UI相比更好的体现了这次大作业的教育目的。

    3.3.2 MyGraphic类基本的图像类。之所以声明为类而不是接口是因为有几个函数要有默认实现。
    下图是Mygraphic类的全部类图。各函数基本都是函数名自解释的,如果一眼看不出来有什么用可以参见源代码的注释。

    乍看上去,Ienumerator和Ienumerable两个接口(包括count,getenumerator,current,add,clear,dispose,reset等,可以使得这个类被像list一样用foreach遍历)看上去都不应该是MyGraphic类作为一个基本的图像类应该有的方法或者属性。事实上这里采用了Composite设计模式。为了使得单个对象和组合对象的使用具有一致性,其他库函数(以及用户)能够统一的使用组合结构中的所有对象(具体来说,使得我们不用在override各种函数的时候来写出诸如MyGraphic g as CompositeGraphic这种效率很低的运行时类型判定代码来),所以在MyGraphic类中定义了Composite类的各种接口,并且给以了默认实现(当然了由于MyGraphic不是CompositeGraphic,默认实现理应是空实现)。
    另外,MyGraphic类还实现了接口ICloneable,这个接口允许MyGraphic被深复制,因而实现了UI的复制-粘贴功能。
    IsVisible指示本图像是否会被画到画布上。
    Father指示MyGraphic类的父图像。Father类的set方法被重写来调用父图像的DisposeChildren方法来删除父图像维护列表中自己的存在。这样就能保证整个MyGraphic类是一棵树(树的顶点是由UI保存的一个Composite,事实上充当了画布的功能),为递归遍历创造了条件。所以同时,当Father被置null的时候,本图形就会因为失去一切引用而被C#的GC机制自动回收,相当于是被Dispose了。因此本程序的效率大大提高,本人的电脑在有1500个图形的时候还能够正常工作。
    SelectError指示点击一个点的时候允许多大的误差以选中一个图形。
    还有不少函数在MyGraphic类中被声明仅仅是为了作为接口被递归调用,例如SelectRect、Count属性等就是这样,当然整个程序中最重要的Draw也不例外。
    3.3.3 SingleModeGraphic类顾名思义,是整个图形有同一个drawmode的类,也是所有基本图形(basic_graphics)的基类。显然composite类由于可以add进各种drawmode的图形所以不可能能维持有同一个drawmode。

    drawmode指示一个给定border的geometry的图形如何被绘制,将调用drawmode的draw方法(见3.3.6)。
    重载了基类的SelectRect方法,当选中时返回包括自己的一个CompositeGraphic,反之返回null。
    getGeometry方法返回这个对象的border的geometry。
    3.3.4 CompositeGraphic类可以包含其他graphic的graphic的实现,或者说用windows画图打比方的话,就是那个选框功能。重载了IEnumerable和IEnumerator接口。

    首先,作为Composite设计模式的一部分,override了MyGraphic中的Composite相关的函数(isComposite, Clear, Add, MoveNext, Reset, Current),进行了事实上的操作。
    CompositeGraphic类维护一个私有的辅助list,用来保存每个成员相对于本体的左上角的相对位置。当CompositeGraphic类要执行某个操作的时候(例如Draw或者SelectRect),它就递归调用每个成员的相应操作,好像这个类不曾存在过一样(如果设置了isCombined = false,当这个标记被置true的时候整个CompositeGraphic将被视为是一个整体,类似于PPT或者Flash提供的“组合”功能)
    另外CompositeGraphic也像SingleModeGraphic一样有drawmode类型的属性backgroundmode,这是为了画出它的边界和背景色。边界总是一个矩形。
    当调用SelectRect的时候,由于递归调用的过程会产生大量很多层的CompositeGraphic(因为selectRect的原理是将所有被选中的图形改为连接到同一个Composite中,然后将这个Composite的父图形设为this),所以设计了MergeComposite,将一个Composite中的全部内容合并到另一个中,减少了递归的层次(在这种设计之下,递归最多两层),有效提高了运行效率。
    3.3.5 基本的图像类(basic_graphics)这些类都继承自SingleModeGraphic并且是Sealed类。有几个属性来记录自己的形状,并且override了getGeometry接口来确定自己的外形。如类图所显示的那样,新建一个基本的图像类是非常简单的,只需要实现getGeometry方法和Clone方法两个方法,并且定义几个足够确定图形位置和形状的属性,就能够被立刻应用到类库之中,像其他图形一样被选择、被移动、被更改颜色、被剪切、被复制、被粘贴,这充分体现了本类库良好的可扩充性。

    对于Ellipse、Line、Rectangle,WPF都提供了相应的Geometry对象来绘画,然而对于Bezier,WPF并没有提供相应的Geometry,而是提供了一个PathSegment——BezierSegment。BezierSegment只有三个参数(也就是说默认从原点开始画),第四个点需要通过设置PathFigure的起始点来确定。
    3.3.6 DrawMode类
    抽象类。指示如何绘制一个Geometry形状的Mygraphic为drawing。这个类其实做成接口也没什么问题。如果深究起来的话把绘图方式单独封装起来似乎也是一种设计模式,学名叫做Strategy模式。Strategy模式的好处也是显而易见的——如果直接将各种绘图方式以硬编码的方式写在SingleModeGraphic中,虽然可以少几个类,但是这意味着你可以在完全不破坏类库的情况下添加新的DrawMode,使得它易于切换、易于理解、易于扩展,进一步体现了作为一个类库的重要理念——可扩充性。(嘛,不过这毕竟只是大作业,虽说类库要可扩充性良好,但是也就我自己扩充我自己的类库玩罢了…但是当我发现WPF的drawing系列图像的构架思想竟然和我一开始手画的类图一模一样的时候,我心中别提有多得意了)
    3.3.7 DrawMode类的派生类
    这些类都继承自Drawmode类并且是Sealed类。有几个属性来记录自己的绘图方式,且override了draw来绘制。
    3.3.8 defaultConstant类
    储存一些常量。在GDI+中有System.Drawing.Color枚举,然而在WPF中似乎没有对应字段,为了方便类库的设计所以作为static readonly变量定义在这里,效果类似于C++的全局const。另外准备了defaultbrush(透明色)和defaultpen(黑色,宽2.0f)来作为GeometryMode的默认构造值。
    由于是常量,所以被作为partial类分散定义在drawmode的各个派生类中。
    3.3.9 DrawingUIElement类
    本来作为一个普适的类库有上面的类就已经能够绘图了;但是我所写的这个类库毕竟是个wpf类库,然而wpf的基本控件Canvas的Children只接受某种System.Windows.UIElement作为它的子成员。所以为了避免推倒重来,此处采用了Adapter(或者叫做Wrapper)设计模式,将本类库的接口类型(System.Windows.Media.Drawing)转换为我的调用方,邵键准同学的UI能够接受的参数UIElement。
    重写了OnRender方法,只要对它调用UIElement所固有的InvalidateVisual方法就能迫使控件重画,从而将drawing指示的画面显示在wpf界面上。
    3.4 类库Ccao-big-homework-core-winform的实现本类库(Ccao-big-homework-core-winform.csproj,并没有被本次大作业引用)是一开始我的队友尚未开始施工的时候编写而成的(文件注释为”Du 2015.8”),当时前期调研做的不够好,没有意识到wpf和winform不兼容,所以手贱就先写了一个winform版。Winform版实现比wpf版要早,Class Diagram也和wpf版类似,几乎所有的类名都一样,只是winform版基于GDI+,命名空间为System.Drawing,而wpf版基于WPF,命名空间为System.Windows。在此对于两个类库之间雷同的部分不再赘述。
    在本大作业中并没有用到winform类库(因为winform的界面远没有wpf美观),但是winform类库在我们往届的测试中证明是可以使用的(见github上9.6晚上的commit,hash为87ce7da,当时有一个基于VB的样例test基于winform测试了这个类库的可行性)。如果有兴趣做winform开发,可以尝试使用这个类库。
    本类库和wpf版的唯一区别在于draw的实现。在wpf版中,draw函数是一个普通的返回System.Windows.Media.Drawing的函数。然而在winform中我们采取的并不是Adapter模式,而是另一种著名设计模式Bridge模式:我们有一个接口IWindow实现drawpath和fillpath两个接口,然后draw函数接受参数IWindow,并在函数体内调用这两个接口进行绘图,没有返回值。这样做的好处在于可以将抽象部分与它的实现分离,使得双方都可以有独立的变化。无论类库将要面对怎样的UI(即便这是个WPF UI甚至是个VB项目,例如我的测试用项目),只要这个UI实现了接口IWindow,类库就可以不作任何修改的应用到这个UI上。
    以下是项目文件列表,除了IWindow外的所有类都和wpf版一样。

    4、项目总结4.1 亮点首先,由于采用了Composite模式,所以几乎所有的Composite接口都是通过递归调用来完成的,简洁明了,体现了合理设计模式的优越性。假使不采用Composite模式,那么(任举一例),SelectRect方法将不得不返回一个list<MyGraphic>,且不说这样子将使得这个类库丧失组合/打散的固有功能,而且返回list将使得对于这个list的每个操作都必须由客户端通过类似foreach MyGraphic g…这样子的调用方式手动对每个list的成员执行。现在只需要调用Composite的对应函数,就能通过递归调用在对客户隐藏的情况下执行了,体现了良好的封装。
    其次,本类库的可扩充性堪称良好。向本类库中加入新的几何图形只需要继承SingleModeGraphic并且重写getGraphic和Clone;向本类库中加入新的绘图方式只需要继承DrawMode并且重写Draw。
    4.2 OOP模式的优越性接口和实现的分离极大提高了我们的开发效率。只需要知道给接口提供什么参数,能得到什么结果就可以了,不需要知道它是如何被实现的,这样的黑箱极大地提高了我们的开发效率。
    4.3 版本控制的重要性版本控制不仅让我们能够清晰地了解自己的开发进度,而且在开发出现重大bug的时候,能够迅速地回滚到以前的版本。这样不会出现自己写了什么挂了都不知道的,结果只好全部删除重来的情况。况且,每一次在Git上的Commit都意味着我实现了新的功能,内心总能感到满足。
    4.4 自主解决困难的能力为了写大作业我估计百度了不下300次,基本开发的状态就是:想实现某功能—>发现自己不会—>百度google大法好—>会了—>实现该功能—>又想实现某功能。
    开发过程中我遇到过很多困难,举个例子:关闭窗口时我写了个淡出动画,结果发现动画刚开始运行就程序就被退出,表现为动画无法播放。百度了一个小时,终于找到解决办法:在给程序的OnClosing事件中,把close命令取消,然后坐等0.6秒动画放完后再退出程序。
    真正到开发程序才发现以前学的都是纸上谈兵,经过一份大作业的洗礼我收获了极大提升的编程技能。
    7 评论 55 下载 2019-04-02 10:31:39 下载需要12点积分
显示 0 到 15 ,共 15 条
eject