分类

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

资源列表

  • 基于C++的哈夫曼编码

    1. 设计目的在计算机科学中,数据结构是一般程序设计的基础。通过综合设计,使学生学会分析研究数据结构的特征,以便为应用涉及的数据选择适当的逻辑结构、存储结构及相应的算法,掌握算法的时间复杂度分析技术。另一方面,综合设计也是复杂程序设计的训练过程,要求学生编写的程序结构符合软件工程规范,培养他们的数据抽象能力、建模能力和算法设计能力,提高复杂问题的解决能力,为后续课程的学习和应用奠定基础。
    2. 任务与要求要求学生以5人一组,自由结合,从给定的综合设计题目中进行选择。本次设计题目是设计内容不固定的题目,这就要求学生自己先确定本组设计题目,然后确定具体内容和设计目标,从而为数据结构设计、数据建模和算法设计奠定基础。提交的资料包含综合设计报告纸质版、电子版及源程序电子版。
    3. 哈夫曼编/译码器3.1 系统(问题)描述利用哈夫曼编码器进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码。于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。为这样的信息收发站写一个哈夫曼码的编/译码系统。
    3.2 系统需求一个完整的系统应具有以下功能

    I:初始化( Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件 hfmTree中
    E:编码( Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
    D:译码( Decoding)。利用已建好的哈夫曼树将文件Code file中的代码进行译码,结果存入文件TextFile中。
    P:印代码文件( Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码,同时将此字符形式的编码文件写入文件CodePrin中。
    T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。

    4. 实现
    5. 总结5.1 系统特点及创新
    初始化前加入删除原文件操作,可在建立新哈夫曼树之前删除原来保存在文件中的旧哈夫曼树,避免之后的编码解码冲突,也降低了操作的复杂性。
    代码片段:
    case 'I': { //初始化系统 system("CLS"); remove("E:\\CodeFile.txt"); remove("E:\\TextFile.txt"); remove("E:\hfmTree.txt"); //删除上次生成的文件}

    将建好的哈夫曼树,编码结果都直接存入文件,便于之后的查看与再次使用。
    在结构体HTNode中增加一个数据域char ch,用于储存该节点所表示的字符值,便于之后的编码解码操作。

    代码片段:
    typedef struct{ char ch; //结点的值 int weight; //结点的权值 int parent,lchild,rchild; //双亲结点和孩子结点}HTNode,*HuffmanTree;

    灵活使用fread(),fwrite(),fgetc(),fputc(),fgets(),fputs()等文件内容读写函数,对于不同的数据类型和不同的操作要求采用最合适的函数实现。
    代码片段:
    fread(&Ht[i],sizeof(HTNode),1,rfp); //从文件中读入数据并输出printf("%c %d %d %d %d\n",Ht[i].ch,Ht[i].weight,Ht[i].parent,Ht[i].lchild,Ht[i].rchild); //从文件中读入编码过后的文本并输出codefile=fopen("E:\\CodeFile.txt","r");fgets(code,codemax,codefile); printf("%s",code);if(HT[k].lchild==0){ fputc(HT[k].ch,textfp); k=2*n-1; //每次译码从头开始}

    在Select()函数中,即在HT[1…i-1] 选择parent为0且weight最小的两个结点,使用一轮循环,找到两个最小值。
    5.2 遇到的问题及解决思路
    解码算法设计:设接收二进制序列用字符串表示,从根结点出发,‘1’表示向左,‘0’表示向右深入,直到到达某个叶子结点时,输出叶子结点对应的符号;重复此过程,直到接收序列全部译出。
    代码片段:
    while(b[j]!='\0'){ if(b[j]=='0') k=HT[k].lchild; //分解字符串,从根出发,按字符0或1确定左右孩子直至叶子节点 else k=HT[k].rchild; if(HT[k].lchild==0){ fputc(HT[k].ch,textfp); k=2*n-1; //每次译码从头开始 }

    文件内容读写格式复杂:在读写哈夫曼树结点信息时,采用fread()和fwrite()函数,每次读写一个单位大小的结构体结点信息。在写入译码文件时,采用fputc()函数,每次将一个译好的字符写入文件,在写入编码文件时,采用fputs()函数,将每个字符对应的一串编码整个写入编码文件,在读出编码文件和译码文件时,采用fgets()函数,将编码结果和译码结果整体读出并打印。
    Select()函数使用一轮循环找到两个最小值:先保存数组前两个值,再将从第三个值开始直至最后与这两个值进行比较,如果大于这两个值,则继续下一值比较,若小于两个值,则将两个值中较大的一者替换为该值,并进行下一次比较,直至结束。

    代码片段:
    if(k>3){ w3=HT[j].weight; w1=HT[s1].weight; w2=HT[s2].weight; if((w3<w2)&&(w3<w1)){ //比较第三个结点与之前暂存的结点权值大小 if(w2<=w1) s1=j; else s2=j; } //如果第三个结点小于前两个,则将较大者替换 if((w3<w2)&&(w3>w1)) s2=j; if((w3<w1)&&(w3>w2)) s1=j;}
    1 评论 0 下载 2019-05-25 08:03:58 下载需要5点积分
  • 基于JAVA和MYSQL数据库的学生成绩管理系统

    一、需求分析本系统是学生成绩管理系统,所以应该做到可以录入学生成绩,修改学生成绩,删除学生成绩,查询学生成绩,以及最后的所有学生按照GPA排名。
    本系统的数据来源期末考试成绩,用来实现录入,查询,修改,删除,以及排名。
    1.1 增加学生成绩增加学生信息主要是把学生的成绩录入系统中,录入信息包括学生学号,姓名,C++成绩,电路成绩,英语听说成绩,英语读写成绩,大学物理成绩,概率论成绩,近代史成绩,形式与政策成绩,体育成绩,离散数学成绩。
    1.2 修改学生成绩修改学生成绩功能可以通过输入学生的学号,如果该学生在系统中,就弹出一个可以编辑的显示成绩的窗体,然后用户再编辑想要修改的成绩,如果该学生不在系统中,就弹出该学生不在系统中的提醒窗体。
    1.3 查询学生成绩通过查询功能输入学生的学号来查询学生的所有成绩以及GPA,总分。
    1.4 删除学生成绩通过删除学生成绩的功能输入想要删除学生的编号,然后在系统中删除该学生。
    1.5 按GPA排名通过该功能打印出所有学生的成绩,并按照GPA进行排名
    二、概要设计2.1 数据库设计
    2.2 功能模块结构图根据需求分析,为了满足用户的功能需求,将本系统主要划分为如下模块:添加、修改、查询,显示,各模块之间的关系如图所示。

    三、运行环境
    硬件环境:Macintosh电脑
    软件环境:操作系统:macOS Sierra

    四、开发工具和编程语言
    开发环境:IDEA
    编程语言:Java语言、MySQL语言

    五、详细设计在概要设计的基础上,对每个模块进行内部逻辑处理部分详细设计。下面分别列出各个模块具体实现流程图:
    5.1 增加学生信息输入学生的学号,姓名,一系列成绩。
    判断该学生的学号是否已经在系统中,如果在,重新输入,如果不在,就用链接mysql录入系统中

    5.2 修改学生信息首先输入想要修改的学生成绩的学号,然后先判断该学生是否在系统中,如果在,就弹出一个可以编辑成绩的窗体,直接在窗体上编辑就行。

    5.3 查询学生信息通过输入学号,来查找系统中是否有该学号的学生,如果有,则显示该学生的成绩,
    GPA,总分,如果不存在则重新输入。具体实现过程如图所示。

    5.4 删除学生信息通过输入学号,来查找系统中是否有该学号的学生,如果有,则删除该学生的成绩,如果不存在则重新输入。具体实现过程如图所示。

    5.5 按照GPA排名从数据库取出所有数据时候调用排序函数来把所有的学生成绩根据算出的GPA进行排序,
    然后用表格控件来接受所有数据,再输出。

    六、界面添加学生成绩

    修改成绩

    查找结果

    GPA排名
    2 评论 191 下载 2018-11-01 20:33:19 下载需要8点积分
  • 基于JAVA和MYSQL实现的清朝名人数据库系统

    摘 要清朝是中国历史最后一个大一统封建王朝,共传十帝,享国二百七十六年。为了能够将古代名人及各个朝代的皇帝的信息统一起来,使用数据库信息管理系统是很常见的 方法。数据库与 JAVA Swing 结合起来,既可以做出一个直观、简约的界面,也可以很容易实现添加、删除、查询和修改操作。同时也可以存储相当可观的资料。首先,利用 java 做出必要的界面,其中包括登录以及增删查改五个界面,其次,使用 JDBC 将已经存入名人、皇帝资料库的数据库连接起来,最后再加入指令代码,即可实现名人数据库 管理系统。在名人数据库管理系统设计完成之后,它必须具备界面中所对应的功能,即可以完成登录、注册、添加、删除、查询和修改操作,并且在数据库中会得到相应的反映。名人数据库管理系统相对于之前的管理方法及途径更方便管理,易于更新,最大化 的简化了管理员的管理工作,同时也让使用者更容易接受该系统。
    关键词:古代名人;管理系统;javaswing 开发;mysql 储存
    AbstractThe Qing Dynasty was the last unified feudal dynasty in Chinese history. It was handed down to ten emperors for 276 years. In order to unify the information of ancient celebrities and emperors of different dynasties, the use of database information management system is a common method. The combination of database and JAVA Swing can not only make an intuitive and concise interface, but also make it easy to add, delete, query and modify operations. At the same time, considerable data can be stored. Firstly, we use java to make the necessary interfaces, including login, add, delete and modify five interfaces. Secondly, we use JDBC to connect the databases already stored in celebrities and emperors’databases. Finally, we add instruction codes to realize the celebrity database management system. After the design of celebrity database management system is completed, it must have corresponding functions in the interface, that is, it can complete login, registration, addition, deletion, query and modification operations, and it will be reflected in the database accordingly. Celebrity database management system is easier to manage and update than the previous management methods and ways, which simplifies the management of administrators and makes users more receptive to the system.
    Keywords:Ancient celebrities; Management system; JavaSwing development; Mysql storage
    第一章 课题背景以前对古代名人管理的主要方式是基于文本、表格等纸介质的手工处理,对于数据信息处理工作量大,容易出错;随着网络的发展很多资料由纸质迁移到了网络上,但是 由于数据繁多,杂乱且良莠不齐,不易查找[1]。采用数据库技术生成的名人管理系统 将会极大地方便古代名人管理和使用人员,使得需要查找整理此类资料的人员从繁忙、复杂的工作进入到一个简单、高效的工作中。
    1.1 课题介绍进入 21 世纪,随着计算机和网络技术的飞速发展,数字资源越来越显现其重要作 用,依托计算机中的资源,建立具有文化特色的专题资源数据库,已成为许多机构的重要任务之一[1]。名人数据库就是其中的一种以收集、整理、开发、利用具有公众效应的 名人资料,并对名人资源进行重组、整合和分层次加工,实现多途径的管理和深层次地揭示名人资料的一种专题数据库[2]。
    名人数据库是一种以收集、整理、开发、利用具有公众效应的名人资料,并对名人 资源进行重组、整合和分层次加工,利用多个软件实现检索和增删查改的功能。建设名人数据库应具备资源、数据、技术和网络等方面的基础[2]。名人专题数据库的组织结构应以人为主线,并充分发挥 JAVA 的性能,集多功能为一体,具体结构框架可由 MYSQL 中建立的数据库、JAVA 的功能和 JDBC 连接构成。
    网络中虽然存贮大量古代名人资料,但是其中的内容也是鱼龙混杂。所以让当用户 们在网上查询名人资料时,虽然网页上囊括的知识比较多,但是事实上见效甚微[3]。为 了能够使得用户们可以更高效的查询到自己想要的知识内容,名人数据库管理系统应运而生。现代计算机可以帮助人们实现这些看似并不复杂的而实际操作起来非常不顺心的工作。试想一下,当用户想要查询某个人的资料时,只需要输入人物名字,就可以看到 该人的详细资料。设计名人数据库的目的便在于在计算机软件支持下,实现对名人资料信息采集、输入、输出,便于管理,便于检索的技术系统[4]。
    1.2 工具介绍1.2.1 java 语言Java 是一门面向对象编程语言,不仅吸收了 C++语言的各种优点,还摒弃了 C++ 里难以理解的多继承、指针等概念,因此 Java 语言具有功能强大和简单易用两个特征。Java 语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程[5]。
    Java 具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点。Java 可以编写桌面应用程序、Web 应用程序、分布式系统和嵌入 式系统应用程序等。
    1.2.2 Eclipse 介绍Eclipse 是著名的跨平台的自由集成开发环境(IDE)。最初主要用来 Java 语言开发, 通过安装不同的插件 Eclipse 可以支持不同的计算机语言,比如 C++和 Python 等开发工具。Eclipse 的本身只是一个框架平台,但是众多插件的支持使得 Eclipse 拥有其他功能相对固定的 IDE 软件很难具有的灵活性。许多软件开发商以 Eclipse 为框架开发自己的 IDE[6]。
    Eclipse 最初由OTI和IBM两家公司的IDE产品开发组创建,起始于1999年4月。IBM 提供了最初的 Eclipse 代码基础,包括 Platform、JDT 和 PDE。Eclipse 项目 IBM 发起,围绕着 Eclipse 项目已经发展成为了一个庞大的 Eclipse 联盟,有 150 多家软件公司参与到Eclipse项目中,其中包括Borland、Rational Software、Red Hat及Sybase 等。Eclipse 是一个开放源码项目,它其实是Visual Age for Java的替代品,其界面跟先前的Visual Age for Java 差不多,但由于其开放源码,任何人都可以免费得到,并可以在此基础上开发各自的插件,因此越来越受人们关注[7]。随后还有包括 Oracle 在内的许多大公司也纷纷加入了该项目,Eclipse 的目标是成为可进行任何语言开发的 IDE 集成者,使用者只需下载各种语言的插件即可。
    1.2.3 SQL Server 简介SQL Server 是由 Microsoft 开发和推广的关系数据库管理系统(DBMS),目前最新版本是2012年3月份推出的SQL SERVER 2012。SQL是英文(Structured Query Language) 的缩写,意思为结构化查询语言。SQL语言的主要功能就是同各种数据库建立联系,进行沟通。SQL被作为关系型数据库管理系统的标准语言[8]。SQL语句可以用来执行各种 各样的操作,例如更新数据库中的数据,从数据库中提取数据等。
    1.3 章节安排本报告总共分为四个章节:

    第一章:主要写的是名人管理系统的背景、设计目的、使用的工具介绍等
    第二章:主要介绍该系统的实现的主要功能以及相应的模块
    第三章:主要介绍 MySQL 中主要的数据格式,以及实现各个功能的伪码
    第四章:主要写的是该系统的测试以及结果分析

    第二章 设计简介及设计方案论述整个应用系统的设计严格按照数据库设计的方法来进行,包括数据库的设计和应用程序的设计,两部分相辅相成[9]。数据库设计过程包含以下步骤:需求分析:系统的目 的、用户需求、功能流程图;概念结构设计:用 E-R 图来描述实体及实体间的联系;逻辑结构设计:确定关系模式,各种约束的声明,同时给出系统的功能模块组成图,系统 各模块功能;物理结构设计。数据库的实施阶段:数据库用 SQL SERVER 等创建,前端开发使用 JAVA 实现。
    2.1 需求分析所谓”需求分析”,是指对要解决的问题进行详细的分析,弄清楚问题的要求,包括需要输入什么数据,要得到什么结果,最后应输出什么。可以说需求分析是做系统之前必做的。需求分析是软件工程中的一个关键过程[10]。在这个过程中,系统分析员和软件工程师确定顾客的需要。只有在确定了这些需要后,设计者才能够分析和寻求新系统的 解决方法。需求分析阶段的任务是确定软件系统功能。
    2.1.1 用户需求在构造系统时,首先从需求出发构造数据库表,然后再由数据库表结合需求划分系统功能模块。这样,就把一个大的系统分解成了几个小系统。这里把系统划分为了三个模块:登录模块,管理员模块,用户模块。模块分别能够实现以下功能:

    登录模块,实现登录功能,注册功能
    管理员模块,实现管理员对名人的增删改减功能
    读者模块,实现用户查询功能

    2.1.2 系统目标根据需求分析及用户的沟通,该系统应达到以下目标:

    界面设计友好,美观
    数据存储安全,可靠
    信息分类清晰,准确
    强大的查询功能,保证数据查询的灵活性
    操作简单易用,界面清晰大方
    系统安全稳定
    开发技术先进,功能完备,扩展性强
    占用资源少,对硬件要求低
    提供灵活,方便的权限设置功能,使整个系统的管理分工明确

    2.2 概要设计数据库系统主要使用面向对象的分析方法,采用 JAVA、SQL语句来详尽地描述清朝历代皇帝和名人的详细资料,名人数据库管理系统可以分为两个大型模块:用户登录和 管理员登录[11]。
    用户登录是名人数据库系统中的一个重要部分,它虽然不能像管理员一样对数据库进行更新操作,但是管理员的存在也正是服务于用户,管理员的操作能够让用户查询到 相对准确的信息,同时也提高了用户查询的效率。同时给予用户查询功能可以防止一些不良用户对系统的恶意使用,进而保证了系统的安全性[12]。
    Swing 程序表示 JAVA 的客户端窗体程序,除了通过手动编写代码的方式设计 Swing 程序之外,Eclipse中还提供了一种Window Builder工具,该工具是一种非常好用的 Swing 可视化开发工具,有了它,开发人员就可以通过拖放组件的方式编写 Swing 程序了。
    当名人、皇帝的资料表导入 MYSQL之后,需要在 Eclipse 中做出几个必要的界面: 登录界面和增加、删除、查询、修改界面,同时也需要必须的管理员选择界面。在界面中设计采用 Window Builder 插件,进而在后台自动生成代码。
    数据库管理系统是在管理员对系统进行更新操作之后,数据库内的数据随之发生改 变。登录操作已经完成,其中包括用户登录与管理员登录。两种身份登陆之后出现的界面是不同的,用户登录成功之后只能进入查询界面,而管理员登录之后进入一个选择界面,可以自主选择增加、删除、查询、修改操作。
    登录操作已经完成,其中包括用户登录与管理员登录。两种身份登陆之后出现的界面是不同的,用户登录成功之后只能进入查询界面,而管理员登录之后进入一个选择界面,可以自主选择增加、删除、查询、修改操作。
    在管理员登录成功之后,进入一个选择界面,在这个界面中包含增删查改四个功能。在添加界面中,必须先输入人物的名字,并在文字提示框后输入相应的内容,进而通过系统将输入的信息录入数据库。
    增加、删除、修改三个操作中,无论其中哪一种进行操作,数据库中的数据也需要发生相应改变,这一点可以在基本实现系统中进行操作,操作完成之后打开 MYSQL的 表进行验证。
    管理员登录的数据需求分析用户的需求具体体现在各种信息的提供、保存、更新和查询上,这就要求数据库结构能充分满足各种信息的输出和输入。其主要模块示意图如 图 2.1 所示。


    增加信息(管理员身份):在界面上输入相应的名人信息后,经过系统确认即可以在数据库中存储名人的信息,并且在查询界面可以查询到相应的信息
    查询信息(管理员身份):输入人物名字,选择该人物的身份(皇帝或者名人),经过系统查询之后即可在界面上显示相应内容
    删除名人(管理员身份):在相应界面中输入需要删除的名人姓名,确认之后系统将删除该名人信息,并在数据库中清除该名人信息
    修改信息(管理员身份):在修改界面中输入名人姓名及相应的信息,系统确认之后即可修改信息,并且在数据库中得以修改,在查询界面亦可查询到更新后的信息
    查询信息(用户身份):输入人物名字,选择该人物的身份(皇帝或者名人),经过系统查询之后即可在界面上显示相应内容
    登录验证(用户身份):输入用户名字和密码,点击登录,通过验证即可登录跳转用户查询界面,假如密码错误,则显示“该用户不存在”
    登录验证(管理员身份):输入管理员名字和密码,点击登录,通过验证即可登录跳转用户查询界面,假如密码错误,则显示“该管理员不存在”
    用户注册(用户身份):在用户登录界面点击用户注册,输入用户名和密码以及 Id,点击确定,即可注册成功

    第三章 详细设计概念设计是由分析用户需求到生成概念产品的一系列有序的、可组织的、有目标的设计活动,它表现为一个由粗到精、由模糊到清晰、由抽象到具体的不断进化的过程。概念设计即是利用设计概念并以其为主线贯穿全部设计过程的设计方法。概念设计是完整而全面的设计过程,它通过设计概念将设计者繁复的感性和瞬间思维上升到统一的理性思维从而完成整个设计。
    3.1 逻辑设计逻辑设计就是把一种计划、规划、设想通过视觉的形式通过概念、判断、推理、论证来理解和区分客观世界的思维传达出来的活动过程。逻辑设计比物理设计更理论化和抽象化,关注对象之间的逻辑关系,提供了更多系统和子系统的详细描述。
    3.1.1 关系模型概念结构设计所得的 E-R 模型是对用户需求的一种抽象的表达形式,它独立于任何一种具体的数据模型,因而也不能为任何一个具体的 DBMS 所支持。为了能够建立起最终的物理系统,还需要将概念结构进一步转化为某一 DBMS 所支持的数据模型,然后根据逻辑设计的准则、数据的语义约束、规范化理论等对数据模型进行适当的调整和优化,形成合理的全局逻辑结构,并设计出用户子模式。这就是数据库逻辑设计所要完成的任务。
    数据库逻辑结构的设计分为两个步骤:首先将概念设计所得的 E-R 图转换为关系模 型;然后对关系模型进行优化,如图 3.1 所示。

    关系模型是由一组关系(二维表)的结合,而 E-R 模型则是由实体、实体的属性、实体间的关系三个要素组成。所以要将 E-R 模型转换为关系模型,就是将实体、属性和联 系都要转换为相应的关系模型。
    本系统的关系模型转换如下:
    user (id,username,password)people(id,name,national,place,time,work,message)emperor(id,name,national,yearname,place,miaohao,overtime,message,shihao)3.1.2 系统功能总框图书馆管理系统功能总框图,如图 3.2 所示。

    3.2 物理设计数据库在物理上的存储结构与存储方法称为数据库的物理结构,它依赖于选定的数据库管理系统。为一个给定的逻辑数据模型选取一个最适合应用要求的物理结构的过程, 就是物理设计。
    3.2.1 基本表设计由于名人数量过于繁多,需要先从网络中收集名人信息,并按照一定的属性存放在 Excel 工作表中。清朝名人分为皇帝、名人两个表,并在收集工作完成之后导入 MYSQL 中,在 MYSQL 同样需要两个表进行存储[10]。
    皇帝表需要的属性有 Id、姓名、在位时间、民族、年号、庙号、成就,在 MYSQL 存储方式如表 3-1 所示。



    属性
    存储类型
    是否可为空




    Id
    Var char(10)



    姓名
    Var char(10)



    在位时间
    Var char(10)



    民族
    Var char(10)



    年号
    Var char(10)



    庙号
    Var char(10)



    成就
    Long text




    名人表需要的属性有 Id、姓名、时期、民族、官职、出生地、成就,在 MYSQL 存 储方式如表 3-2 所示。



    属性
    存储类型
    是否可为空




    民族
    Var char(10)



    Id
    Var char(10)



    时期
    Var char(10)



    姓名
    Var char(10)



    官职
    Var char(10)



    成就
    Long text



    出生地
    Var char(10)




    对于数据库中的皇帝表和名人表存在一定联系,其中的实体与属性的联系如图 3.1 所示。

    3.2.2 系统功能设计当名人、皇帝的资料表导入 MYSQL之后,需要在 Eclipse 中做出几个必要的界面: 用户和管理员登录界面、用户注册界面、增加、删除、查询、修改界面,同时也需要必须的管理员选择界面。在界面中设计采用 Window Builder 插件,进而在后台自动生成代码[10]。
    在用户和管理员登录界面中,在键盘上输入用户名和密码,如果为空或者用户名与 密码不匹配都会给出错误提示并无法登录,且在登陆之前需要选择登录身份,即选择“管理员”或者“用户”。如果登陆错误次数超过一定数目,同样会导致无法登陆。如果登 录成功则进入相应的界面。故可以写出登录伪码:
    If 用户名=空 Then 输出 "登录用户名不能为空,请重新填写!" ExitEnd IfIf 密码= 空 Then 输出"登录用户密码不能为空,请重新填写!"End If If 选择身份=空 Then 输出 "你没有选择用户身份,请选择!" Exit End If连接数据库之后,核对过账号密码; 输出"祝贺你!你已经成功登录!" "如果账号密码不匹配,输出"对不起!你输入的用户密码不正确,请重新输入!" 次数 + 1;重新输入; 如果连续三次不正确,则输出“对不起,你已经连续三次输入错误,不能继续登录 程序就退出!" End ELSE If 身份 = "管理员" Then 出现选择界面 Else If 身份= "用户" Then 出现查询界面 End If此时,登录操作已经完成,其中包括用户登录与管理员登录。两种身份登陆之后出 现的界面是不同的,用户登录成功之后只能进入查询界面,而管理员登录之后进入一个选择界面,可以自主选择增加、删除、查询、修改操作。
    在管理员登录成功之后,进入一个选择界面,在这个界面中包含增删查改四个功能。在添加界面中,必须先输入人物的名字,并在文字提示框后输入相应的内容,进而通过系统将输入的信息录入数据库。
    If 身份= "皇帝" Then 查找皇帝名字; 更新信息; End If If 身份 = "名人" Then 更新信息 If 信息错误 输出"添加失败",Else 输出"添加成功" End If对于增加操作,实际上是在系统中键入皇帝或者名人的资料,在 UTF-8 格式中的信 息会被系统识别,所以在文本框内添加信息会被系统录入。在选择了身份之后,系统会根据代码将信息录入对应的表中,进而将存入的信息放入数据库中,在添加之后,就可以在查询界面中查到相应结果。
    同样的,在实现删除功能之前,首先要通过 SQL 语句连接上数据库,在管理员输入了人物姓名之后,系统查找出相应的人物,进而删除掉该任务的所有信息,有删除界 面伪代码如下:
    连接数据库; If 身份= "皇帝" Then 查找皇帝名字; 删除; End If If 身份 = "名人" Then 删除; If 信息错误 输出"添加失败" Else 输出"添加成功" End If删除功能实际为系统在识别查询出管理员键入的人物名字之后,在数据库中将相应的信息更新为空,同时将姓名置空,所以在删除之后,数据库中该人物的资料已经被清除,无法再被查询出来。
    在修改功能中,管理员首先通过输入人物名字并选择相对应的身份(皇帝或者名人),在相对应的文本框内输入更改后的信息,在输入完成之后,点击修改,系统则会自动修改数据库内的人物信息。修改界面伪代码如下:
    连接数据库; If 身份= "皇帝" Then 查找皇帝名字; 修改信息; End If If 身份 = "名人" Then 修改信息; If 信息错误 输出"修改失败" Else 输出"修改成功" End If修改功能中同样是经过识别之后,找到相应的人物,将其中的信息更新为管理员键入的新内容,伪代码中的“修改信息”实际上是将管理员键入的内容代替原来的资料,从而做到信息更改。
    用户或者管理员都可以使用查询界面,在此界面中,需要在文本框内输入人物名字,再选择人物的身份(皇帝或者名人),点击查询,系统则会输出对应的人物信息,如果信息错误则无法输出。查询界面伪代码如下:
    连接数据库; If 身份= "皇帝" Then 查找皇帝名字; 输出信息; End If If 身份 = "名人" Then 输出信息; If 姓名错误 输出"查询失败"Else 输出"查询成功" End If查询信息的运作方式与其他操作类似,在用户或者管理员选择人物身份之后,系统在数据库的表中查询对应的人物名字,查询成功之后在界面上显示出内容。伪代码中的“输出信息”实为将表中的某一项内容特定输出,在不同的文本框内输出不同的内容。
    增加、删除、修改三个操作中,无论其中哪一种进行操作,数据库中的数据也需要发生相应改变,这一点可以在基本实现系统中进行操作,操作完成之后打开 MYSQL的表进行验证。
    四种操作已经基本完成,也就是 MYSQL 和 Eclipse 两个软件的工作已经完成,此时需要使用 JDBC 将二者进行连接,才能在 Eclipse 中的界面实现对应的功能。打开 NAVICAT FOR MYSQL 后,建立连接,新建数据库,在 JDBC 的连接 MYSQL。即完成了 Eclipse 与 MYSQL 的连接。
    第四章 设计结果及分析系统有 3 个模块:登录模块,管理员模块和用户模块。登录模块实现登录功能, 注册功能;管理员模块实现名人的增删改查;用户模块实现名人查找功能。
    4.1 功能测试在 Eclipse 打开工程,即可进入用户登录界面,登录界面显示如图 4.1 所示。

    选择用户身份进行登录操作时,点击注册用户,注册用户账号密码,即可完成注册,显示如图 4.2 所示。

    注册用户账号密码及序号填写完毕后,即可完成注册,具体结果显示如图 4.3 所示。

    使用数据库中已经存储的用户账号密码,进行登录,登录成功界面如图 4.4 所示。

    查询界面选项界面,在此界面中,需要选择人物的身份(皇帝或者名人),在文本框内输入人物名字,再点击查询,显示如图 4.5、4.6 所示。


    选择人物的身份(皇帝或者名人)之后,在文本框内输入人物名字,再点击查询,系统则会输出对应的人物信息。显示如图 4.7、4.8 所示。


    选择以管理员身份进入系统,显示管理员登录验证界面,输入管理员帐号和密码,点击登录按键,如图 4.9 所示。

    管理员帐号、密码和数据库管理员信息表比对后,验证成功,即可出现管理员主界面如图 4.10 所示。

    选择皇帝中的查询,即可看到添加窗口,名人和此界面一样,如图 4.11,图 4.12 所 示。

    点击添加皇帝,即可成功添加皇帝 选择皇帝中的查询,即可看到查询窗口,名人和此界面一样,如图 4.13 所示。

    搜索刚才的添加皇帝,即可进入皇帝的删,改界面,如图 4.14 所示。

    加入新的文字,或者修改旧信息,即可做到修改皇帝的功能,如图 4.15所示。

    直接点击删除按钮,即可做到删除当前页面皇帝的功能。
    4.2 结果分析一款好的名人数据库管理系统是在管理员对系统进行更新操作之后,数据库内的数据随之发生改变。在这一款清朝名人管理系统中,经过增加、删除、更改之后,再次查询人物信息都能得到正确的结果,说明在各种操作之后,数据库中的数据同样改变,可以评价为这一款名人管理系统是相对比较成功的。
    管理员增加、删除、修改三个操作中,无论其中哪一种进行操作,数据库中的数据也需要发生相应改变,这一点可以在基本实现系统中进行操作,操作完成之后打开 MYSQL 的表进行验证。四种操作已经基本完成,也就是 MYSQL和 Eclipse 两个软件的 工作已经完成,此时需要使用 JDBC 将二者进行连接,才能在 Eclipse 中的界面实现对应的功能[11]。
    总 结经过两周的努力,清朝名人管理系统终于完成,经过这次综合设计,自己总结了这个名人数据库管理系统的一些问题,不过收获还是颇为丰富的,再有理论知识上结合实践,使我学到了更多知识。
    首先,更进一步的了解了系统分析与设计、JAVA、JDBC 的基本操作,在这之前,系统分析与设计的学习仅仅刚开了个头,我们只是在了解一些概念性的东西。在做这个系统之前,我连基本的系统分析与设计,界面设计等这些东西都不熟练。现在对于系统中的增删改查操作比较熟练了。
    对于初学者来说,比较头疼的就是对于数据库连接的处理。我的建议是如果不理解先把按照课本上正确的语句敲,然后在多次进行数据库的链接,增删改查操作中不断总结规律。
    这次设计的名人数据库管理系统,全在自己所掌握的知识下,进行系统分析与设计,完全体现了自己在系统分析与设计中设计课程学习状况,充分地为自己以后更深入了数据库语言奠下深厚的基础。 纵观此名人数据库管理系统的整体概况,目前,自我认为设计良好,相关功能都能够实现,功能强大,条理清晰,界面可观性比较好。并且特色在于,所设计的表单都在一个表单系统桌面中运行,比较符合系统的观念。 在系统设计的过程中,我从中发现,学习系统分析与设计要细心和有耐性,并且要不断地从外界学习更多的技术才能设计出一套完美的系统。
    参考文献[1] 余丽君.关于建立沈阳名人数据库的设想[J].图书馆学刊,1999,21 (6):19-20.
    [2] 赵志刚,王伟.使用 SQL Server 管理应用程序服务数据[J].沈阳师范大学学报(自然 科学版),2010,28(2):233-235.
    [3] 黄欣欣.浅谈权限管理系统的需求分析[J].科技信息,2010(16):69-70.
    [4] 杨华.基于 B/S 模式的高校仓库管理系统的需求分析[J].无线互联科技,2014(8): 71-71.
    [5] 周龙.分布式数据库管理系统实现技术[M].科学出版社,1998.15-16.
    [6] 张慎明,卜凡强,姚建国.遵循 IEC61970 标准的实时数据库管理系统[J].电力系 统自动化,2002,26(24):26-30.
    [7] 廖卫东.陈梅. JAVA 程序设计[M].机械工业出版社,2008.268-346.
    [8] 李素若.JAVA 面向对象程序设计[M].北京化学工业出版社,2008.126-136.
    [9] 傅仕星.JAVA Swing 设计基础[M].清华大学出版社,2003.68-89.
    [10] 刘彦明.数据库基础设计与开发[M].西安电子科技大学出版社,2009.56-67.
    [11] 华轩逸.JAVA 面向对象程序设计(第二版)[M].北京:中国铁道出版社,2012.21-29.
    [12] 郑国果.JAVA 语言程序设计(第 4 版)[M].北京:清华大学出版社,2010.33-38.
    3 评论 5 下载 2019-02-25 17:35:27 下载需要16点积分
  • 基于JAVA和MYSQL数据库实现的小型公司工资管理系统

    第一章 需求分析1.1 功能要求1.1.1 功能概况本次设计要求运用面向对象设计知识,利用 JAVA 语言设计实现一个“小型公司工资管理系统”。
    1.1.2 存储功能能够存储雇员的编号、姓名、性别、所在部门,级别,并进行工资的计算。 其中,雇员级别分为经理、技术人员、销售人员和销售经理。四类人员的月薪计算方法如下:经理拿固定月薪;技术人员按小时领取月薪;销售人员按其当月销售额的提成领取工资;销售经理既拿固定月薪也领取销售提成。由此要求,该项目需要利用数据库的关系表存储数据。
    1.1.3 添加功能程序能够任意添加上述四类人员的记录,可提供选择界面供用户选择所要添加的人员类别,要求员工的编号要唯一,如果添加了重复编号的记录时,则提示数据添加重复并取消添加。需要利用 Java 语言连接数据库,使用数据库添加语句。
    1.1.4 查询功能可根据编号、姓名等信息对已添加的记录进行查询,如果未找到,给出相应的提示信息,如果找到,则显示相应的记录信息。利用 Java Swing 控件进行展示。
    1.1.5 编辑功能可根据查询结果对相应的记录进行修改,修改时注意编号的唯一性。
    1.1.6 删除功能主要实现对已添加的人员记录进行删除。如果当前系统中没有相应的人员记录, 则提示“记录为空!”并返回操作;否则,输入要删除的人员的编号或姓名,根据所输入的信息删除该人员记录,如果没有找到该人员信息,则提示相应的记录不存。
    1.1.7 统计功能能根据多种参数进行人员的统计。例如,统计四类人员数量以及总数,或者统计男、女员工的数量,或者统计平均工资、最高工资、最低工资等信息。利用数据库语句完成统计。
    1.1.8 界面功能利用 java 的 GUI 功能设计系统的图形用户界面,要求界面美观,易用。
    第二章 概要设计2.1 系统分析2.1.1 架构设计本系统采用架构如下图所示,主要分为三个模块:用户操作界面、身份认证中心和用户数据库。其中,用户操作界面为最顶层功能,便于用户选择以添加、修改、 删除、查询统计公司信息;身份认证中心便于鉴别不同用户,以实现映射到不同的数据库(表);用户数据库中保存用户个人存储的信息,同时实现最底层功能,即各项功能的基本操作。

    2.1.2 实体设计登陆实体作为程序的进入窗口,用户登陆时,系统会要求用户输入账号和密码,并在数据库中进行匹配分析,若用户名存在且密码正确则通过身份认证,同时映射到用户 个人的数据表中,不同用户对应不同数据表存储相关信息。
    主界面实体登录成功后的功能选择界面,包含添加、查询、显示、统计四个功能,其中,对于查询或显示的信息可直接选择并完成修改删除功能。
    添加功能实体从主界面中选择进入,负责接收用户输入的员工信息,判断员工编号是否重复、录入的员工信息格式是否正确,并将符合要求的数据项其上传至数据库,完成添加员工信息的操作。
    查询功能实体从主界面中选择进入,按照用户选择的查找方式和具体查找要求,从数据库中筛选符合条件的信息并以表格形式显示,如果未查询到相关信息会给以提示。
    修改功能实体从查询功能实体进入。查询结果会以表格的形式显示在用户面前,用户可以双击某项查询结果进入编辑界面完成修改功能,同添加功能类似,系统会判断用户修改的编号是否已经重复、修改的信息是否格式正确,并将符合要求的信息在数据 库中完成更新修改操作。
    删除功能实体从查询功能实体进入。查询结果会以表格的形式显示在用户面前,用户可以双击某项查询结果进入编辑界面,在编辑界面中可选择删除此信息,删除完成后将会予以反馈。
    统计功能实体自动统计以下内容:公司整体信息(职位分布、部门人数分布等)、各部门详细信息(男女比例、工资情况、年龄情况、职位分部等),并可由用户自行选取具体部门、具体级别并显示统计结果,所有统计结果均自动绘图显示。

    2.1.3 分层设计数据库驱动层(数据层)数据库驱动层为最底层,主要以各种 JDBC 数据库驱动语句以及各种算法组成,完成添加、修改、删除、查询、统计分析的基本功能,并完成添加修改过程中的编号查重、格式判断等异常判断。各功能成功或失败情况将传递给上层,同时,异常情况也将分类传递给上层。
    异常处理层(功能层)主要分类处理数据库反馈的结果,例如查询成功失败、添加的编号重复、修改成功等操作的反馈结果。并处理来自底层抛出的异常,捕获后生成提示信息传递给上一层。
    用户界面层(表示层)接受来自异常处理层的处理结果:对于异常,采用消息框提示的方式展示给用户;对于查询结果,以表格的形式显示在界面上;对于统计结果,自动绘制图表显示给用户;其他提示信息同样采用消息框给用户反馈。同时用户可以直接在界面上选择相应功能,底层会自动实现相应操作并反馈给用户界面层。

    2.2 程序流程2.2.1 系统流程图
    2.2.2 调用关系运行程序后,首先进入用户登陆界面,用户名与密码不匹配则登陆失败,成功则进入用户操作主界面。
    在用户操作主界面中有四个按钮供用户选择,分别是:添加记录、查询记录、显示信息、统计整理,点击不同按钮跳转至不同功能实现界面。
    添加信息模块允许用户添加新信息,系统会判断编号是否重复、各式是否正确。若通过审核则将此信息添加至数据库中,询问用户是否继续添加。如果退出则重新返回主界面。
    查询信息模块允许用户按个人需求查询相关信息,如:职员编号、姓名、性别、级别、部门等。查询得到的信息会显示在表格中,用户可以点击表格中具体某一项选择编辑信息,在编辑模块中可以修改或删除选中的信息。
    显示信息模块与查询功能类似,显示的内容会自动放入表格中同时允许编辑修改。 统计整理模块会将统计得到的信息以图表形式显示给用户,同时允许用户自行查询想要统计的信息并自动绘制图表。
    第三章 详细设计3.1 功能类定义与实现3.1.1 常量与参数的定义(Parameter 类)JDBC 数据库连接所用参数与端口JDBC(Java Data Base Connectivity,java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 为数据库开发人员提供了一个标准的 API,据此可以构建更高级的工具和接口,使数据库开发人员能够用纯 Java API 编写数据库应用程序,并且可跨平台运行,并且不受数据库供应商的限制。
    本系统采用的是MySQL数据库,编程语言为Java语言,编译器为Eclipse Oxygen,Java连接数据库时需要用到JDBC驱动器、数据库URL以及数据库管理员账号密码等。
    根据数据库的 URL、用户名和密码,创建一个 JDBC Connection 对象。如: Connection connection = DriverManager.geiConnection(“连接数据库的 URL”, “ 用户名”, “密码”)。其中,URL=协议名+IP 地址(域名)+端口+数据库名称;用户名 和密码是指登录数据库时所使用的用户名和密码。示例创建 MySQL 的数据库连接代码如下:
    Connection connectMySQL = DriverManager.geiConnection("jdbc:mysql://localhost:3306/myuser","root" ,"root"); public static final String JDBC_DRIVER = "com.mysql.jdbc.Driver"; public static final String DB_URL = "jdbc:mysql://localhost:3306/java?useSSL=true&characterEncoding=UTF8"; public static final String ACC = "root"; public static final String PWD = "zxczxc";
    职员级别对应代号本系统中将职员级别分为四种:经理、技术人员、销售人员、销售经理,其中经理拿固定月薪;技术人员按小时领取月薪;销售人员按其当月销售额的提成领取工资;销售经理既拿固定月薪也领取销售提成。若想方便统计需要将对应职位转换成相应的代号。此处经理对应 int 型数字 1,技术人员对应 2,销售人员对应 3,销售经理对应 4.具体如下:
    public static final int Manager = 1; //经理 public static final int Engineer = 2; //技术人员 public static final int Salesman = 3; //销售人员 public static final int SalesManager = 4; //销售经理
    工资自动计算所需常量由于经理拿固定月薪;技术人员按小时领取月薪;销售人员按其当月销售额的提成领取工资;销售经理既拿固定月薪也领取销售提成。若想方便计算工资,必须提前订好各种常量,如:固定的月薪、工作每小时的工资、销售额提成比例等,具体如下:
    public static final int BASE = 5000; //底薪 public static final int HOUR = 80; //每小时工资 public static final double Parameter = 0.015; //提成比例
    3.1.2 职工个人信息的定义(Person 类)职工个人信息职工个人信息包括:职工编号(唯一)、姓名、性别、年龄、电话号码、所属部 门、工作级别、月工作时间、月销售总额、月工资。其中月工资不可显式输入,自动由程序结算得到。通过构造函数直接创建对象,具体如下:
    public Person(String ID,String name,String sex,int age,String phone, String department,int kind,int hours,double sells) { this.ID = ID; this.name = name; this.sex = sex; this.age = age; this.phone = phone;this.department = department; this.kind = kind; this.hours = hours; this.sells = sells;}
    通过此方法得到私有成员变量:
    public String getID() { return ID; }
    职工工资计算方法经理拿固定月薪;技术人员按小时领取月薪;销售人员按其当月销售额的提成领取工资;销售经理既拿固定月薪也领取销售提成。具体计算方法如下:
    switch (kind) { case 1: this.salary = Parameter.BASE; break; case 2: this.salary = Parameter.HOUR*hours; break; case 3: this.salary = Parameter.Parameter*sells; break; case 4: this.salary = Parameter.BASE + Parameter.Parameter*sells; break; default: return; }
    3.1.3 数据库查询与底层算法(JDBC 类)JDBC 连接数据库在开发环境中加载指定数据库的驱动程序。使用的数据库是MySQL,所以需要下载MySQL支持JDBC的驱动程序(mysql-connector-java-5.1.18-bin.jar);而开发环境是Eclipse,将下载得到的驱动程序加载进开发环境中。
    在Java程序中加载驱动程序。在Java程序中,可以通过 “Class.forName(“指定数据库的驱动程序”)” 方式来加载添加到开发环境中的驱动程序,例如加载MySQL的数据驱动程序的代码为:
    Class.forName(“com.mysql.jdbc.Driver”);
    创建数据连接对象:通过DriverManager类创建数据库连接对象Connection。DriverManager类作用于Java程序和JDBC驱动程序之间,用于检查所加载的驱动程序是否可以建立连接,然后通过它的getConnection方法,根据数据库的URL、用户名和密码,创建一个JDBC Connection 对象。如:
    Connection connection = DriverManager.geiConnection(“连接数据库的URL", "用户名", "密码”)
    其中,URL=协议名+IP地址(域名)+端口+数据库名称;用户名和密码是指登录数据库时所使用的用户名和密码。
    创建Statement对象:Statement 类的主要是用于执行静态 SQL 语句并返 回它所生成结果的对象。通过Connection 对象的 createStatement()方法可以创建一个Statement对象。例如:
    Statement statament = connection.createStatement();
    具体示例创建Statement对象代码如下:
    Statement statamentMySQL =connectMySQL.createStatement();
    调用Statement对象的相关方法执行相对应的 SQL 语句:通过execuUpdate() 方法用来数据的更新,包括插入和删除等操作,例如向staff表中插入一条数据的代码:
    statement.excuteUpdate("INSERT INTO staff(name, age, sex,address, depart, worklen,wage)" + " VALUES ('Tom1', 321, 'M', 'china','Personnel','3','3000' ) ");
    通过调用Statement对象的executeQuery()方法进行数据的查询,而查询结果会得到 ResulSet对象,ResulSet表示执行查询数据库后返回的数据的集合,ResulSet对象 具有可以指向当前数据行的指针。通过该对象的next()方法,使得指针指向下一行,然后将数据以列号或者字段名取出。如果当next()方法返回null,则表示下一行中没有数据存在。使用示例代码如下:
    ResultSet resultSel = statement.executeQuery( "select * from staff" );
    关闭数据库连接:使用完数据库或者不需要访问数据库时,通过Connection的 close() 方法及时关闭数据连接。
    身份认证对于用户,需要先要求输入账号密码,并将账号密码传入数据库身份鉴别表进行匹配,若用户名密码均匹配成功,则映射到用户创建的表中。传入参数为账号和密码,返回判断情况:返回1为账号不存在、返回2为密码不正确、返回3为出现数据库连接异常、返回0则认证成功。
    public static int LoginCharge(String acc,String pwd) { try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); String query = "select * from `" + Parameter.COMPANY + "` where acc=\"" + acc + "\""; ResultSet rs = stat.executeQuery(query); if (!rs.next()) return 1;//账号不存在 String pwd2 = rs.getString("pwd"); if(!pwd2.equals(pwd)) return 2;//密码不正确 String table = rs.getString("table"); Parameter.TABLE = table; return 0; }catch (Exception e) { e.printStackTrace(); return 3; }
    插入信息模块中,需要判断插入的编号是否重复,如果重复则不允许插入,函数 rs.next()如果返回结果不为空,则说明已经存在此编号。输入信息为Person对象,返回判断结果:返回1为编号已存在,返回2为数据库连接异常,返回0为插入成功,具体如下:
    public static int insertIntoSql(Person p) { try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); // 判断是否已经存在编号 String query1 = "select * from `" + Parameter.TABLE + "` where no=\"" + p.getID() + "\""; ResultSet rs = stat.executeQuery(query1); if (rs.next()) { return 1; } String query = "INSERT INTO `" + Parameter.TABLE + "` VALUES (\"" + p.getID() + "\",\"" + p.getName() + "\",\"" + p.getSex() + "\",\"" + p.getAge() + "\",\"" + p.getPhone() + "\",\"" + p.getDep() + "\",\"" + p.getKind() + "\",\"" + p.getHours() + "\",\"" + p.getSells() + "\",\"" + p.getSalary() + "\")"; stat.executeUpdate(query); return 0; } catch (Exception expt) { return 2; } }
    从数据库中删除信息删除信息模块中,需要删除的编号是否存在,如果不存在则不允许删除,函数 rs.next()如果返回结果为空,则说明此编号对应的职员不存在。输入信息为职员的 编号ID,返回判断结果:返回1为编号不存在,返回2为数据库连接异常,返回0为插 入成功,具体如下:
    public static int deleteFromsql(String id) { try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); String query1 = "select * from `" + Parameter.TABLE + "` where no=\"" + id + "\""; ResultSet rs = stat.executeQuery(query1); if (!rs.next()) { return 1; } String query2 = "delete from `" + Parameter.TABLE + "` where no=\"" + id + "\""; stat.executeUpdate(query2); return 0; } catch (Exception expt) { return 2; } }
    从数据库中查询信息查询信息模块中,具体分为按照ID查询和按照其他条件查询,具体差别为ID唯一,至多只能查找到一条信息,而按照其他条件查询可能会返回多条信息,因此需要区 别处理,输入信息为职员的信息,返回值为查询结果,为空则查询失败具体如下:
    public static String searchByID(String id) {//按照ID查询 try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); String query1 = "select * from `" + Parameter.TABLE + "` where no=\"" + id + "\""; ResultSet rs = stat.executeQuery(query1); if (!rs.next()) { return null; } String name = rs.getString("name"); String sex = rs.getString("sex"); int age = rs.getInt("age"); String phone = rs.getString("phone"); String department = rs.getString("department"); int kind = rs.getInt("kind"); int hours = rs.getInt("hours"); double sells = rs.getDouble("sales"); double salary = rs.getDouble("salary"); String result = id + "@" + name + "@" + sex + "@" + age + "@" + phone + "@" + department + "@" + kind + "@" + hours + "@" + sells + "@" + salary; // 生成日志 File f = new File("log.txt"); if (!f.exists()) f.createNewFile(); PrintStream ps = new PrintStream(new FileOutputStream(f)); ps.println(result); ps.close(); return result; } catch (Exception expt) { return "Fail"; } } public static String searchByOthers(String chosen, String others) { //按照其他条件查询,chosen为条件,others为具体筛选内容 try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); String query; if (chosen.equals("age") || chosen.equals("kind")) query = "select * from `" + Parameter.TABLE + "` where " + chosen + "=\"" + Integer.parseInt(others) + "\""; else query = "select * from `" + Parameter.TABLE + "` where " + chosen + "=\"" + others + "\""; ResultSet rs = stat.executeQuery(query); if (!rs.next()) { return null; } String id = rs.getString("no"); String name = rs.getString("name"); String sex = rs.getString("sex"); int age = rs.getInt("age"); String phone = rs.getString("phone"); String department = rs.getString("department"); int kind = rs.getInt("kind"); int hours = rs.getInt("hours"); double sells = rs.getDouble("sales"); double salary = rs.getDouble("salary"); String result = id + "@" + name + "@" + sex + "@" + age + "@" + phone + "@" + department + "@" + kind + "@" + hours + "@" + sells + "@" + salary; while (rs.next()) { id = rs.getString("no"); sex = rs.getString("sex"); name = rs.getString("name"); age = rs.getInt("age"); phone = rs.getString("phone"); department = rs.getString("department"); kind = rs.getInt("kind"); hours = rs.getInt("hours"); sells = rs.getDouble("sales"); salary = rs.getDouble("salary"); result += "#" + id + "@" + name + "@" + sex + "@" + age + "@" + phone + "@" + department + "@" + kind + "@" + hours + "@" + sells + "@" + salary; } } catch (Exception expt) { expt.printStackTrace(); return "Fail"; } }
    从数据库中更新信息编辑信息模块中,如果需要修改信息,则需要启用更新操作:先判断是否修改了编号,如果修改编号则需要判断修改后的编号是否重复,如果重复则不允许修改为此编号。输入修改前的Person对象和修改后的Person对象,如果修改失败则回退操作,即把原来的Person对象恢复,具体实现如下:
    public static int UpdateFromsql(Person newP, Person oldP) { try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); if (deleteFromsql(oldP.getID()) != 0) { return 1; } String query1 = "select * from `" + Parameter.TABLE + "` where no=\"" + newP.getID() + "\""; ResultSet rs = stat.executeQuery(query1); if (rs.next()) { if (insertIntoSql(oldP) != 0) JOptionPane.showMessageDialog(null, "发生未知错误"); return 2; } if (insertIntoSql(newP) != 0) { if (insertIntoSql(oldP) != 0) JOptionPane.showMessageDialog(null, "发生未知错误"); return 3; } return 0; } catch (Exception expt) { if (insertIntoSql(oldP) != 0) JOptionPane.showMessageDialog(null, "发生未知错误"); return 4; } }
    从数据库中统计整理信息统计整理模块功能较为复杂,但实现方法大体类似,根据具体的查询条件和查询内容查询相关的信息,并返回结果,具体实现如下:
    统计数量
    public static int statisticCount(String option) { try { Class.forName(Parameter.JDBC_DRIVER); Connection conn = DriverManager.getConnection(Parameter.DB_URL, Parameter.ACC, Parameter.PWD); Statement stat = conn.createStatement(); String query = "select count(*) from `" + Parameter.TABLE + "` "+option; ResultSet rs = stat.executeQuery(query); if (!rs.next()||rs.getString(1)==null) { return 0; } int result = Integer.parseInt(rs.getString(1)); return result; } catch (Exception expt) { JOptionPane.showMessageDialog(null, "发生未知错误"); return 0; } }
    统计不同职位的人数
    public static int statisticKindCount(int kind, String option1, String option2)
    统计不同性别的人数
    public static int statisticSexCount(String sex, String option1, String option2)
    统计最高、最低、平均工资
    public static double statisticAvgSalary(String option1, String option2) public static double statisticMaxSalary(String option1, String option2) public static double statisticMinSalary(String option1, String option2)
    统计最大、最小、平均年龄
    public static double statisticAvgAge(String option1, String option2) public static int statisticMaxAge(String option1, String option2) public static int statisticMinAge(String option1, String option2)
    3.2 图形界面类定义与实现3.2.1 JFreeChart 制图(JFreeChart 类)柱形图制作传入图表的名称(标题)、Dataset 容器(存放图形参数)、以及所要绘制图形的颜色,返回绘制的 Chart 图形。
    public class CategoryChart { public static JFreeChart createChart(String name, CategoryDataset dataset,Color color) { JFreeChart chart = ChartFactory.createBarChart3D(name, null, null, dataset, PlotOrientation.VERTICAL, true, false, false); CategoryPlot plot = chart.getCategoryPlot();// 设置图的高级 属性 BarRenderer3D renderer = new BarRenderer3D();// 3D 属性修改 plot.setNoDataMessage("无 信 息"); renderer.setSeriesPaint(0,color); //设置柱的颜色 // 设置网格竖线颜色 plot.setDomainGridlinePaint(Color.gray); plot.setDomainGridlinesVisible(true); // 设置网格横线颜色 plot.setRangeGridlinePaint(Color.gray); plot.setRangeGridlinesVisible(true); // 图片背景色 plot.setBackgroundPaint(Color.LIGHT_GRAY); plot.setOutlineVisible(true); // 设置墙体颜色 renderer.setWallPaint(Color.gray); // 对 X 轴做操作 CategoryAxis domainAxis = plot.getDomainAxis(); // 对 Y 轴做操作 ValueAxis rAxis = plot.getRangeAxis(); renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); renderer.setBaseItemLabelsVisible(true); renderer.setBaseItemLabelFont(new Font("黑体", Font.CENTER_BASELINE, 12), true); renderer.setBasePositiveItemLabelPosition( new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.CENTER_LEFT)); renderer.setItemLabelAnchorOffset(15); //设置距离图片左端距离 domainAxis.setUpperMargin(0.2); //设置距离图片右端距离 domainAxis.setLowerMargin(0.2); //设置柱的透明度 plot.setForegroundAlpha(0.6f); // 最后还得将 renderer 放到 plot 中 plot.setRenderer(renderer);// 将修改后的属性值保存到图中 return chart; } }
    饼图制作传入图表的名称(标题)、Dataset 容器(存放图形参数),饼图颜色自动分配,返回绘制的 Chart 图形。
    public class PieChart { public static JFreeChart createChart(String name,PieDataset piedataset) { JFreeChart jfreechart = ChartFactory.createPieChart3D(name, piedataset, true, true, false); PiePlot3D pieplot3d = (PiePlot3D) jfreechart.getPlot(); pieplot3d.setNoDataMessage("无 信 息"); pieplot3d.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}:{1}({2})"));//设置标签显示的格式 pieplot3d.setSectionOutlinesVisible(false); pieplot3d.setLabelBackgroundPaint(new Color(220, 220, 220));//设置标签的背景颜色。 //设置旋转角度 pieplot3d.setStartAngle(180.0); //设置旋转方向,Rotation.CLOCKWISE)为顺时针 pieplot3d.setDirection(Rotation.CLOCKWISE); //设置图表透明图 0.0~1.0 范围。0.0 为完全透明,1.0 为完全不透明 pieplot3d.setForegroundAlpha(0.9F); return jfreechart; } }
    3.2.2 Dataset 容器(JFreeChart 制图所需容器)柱形图数据集主要包括年龄统计的数据集和工资统计的数据集,对于年龄和工资,本系统统计最大值、平均值和最小值,对于这三项数据,采用柱形图绘制比较合适。代码见附录。

    饼图数据集主要包括性别比例统计的数据集和不同部门、级别人数分布统计的数据集,对于年龄比例和人数比例,本系统绘制饼图直观的展现不同种类人数所占比例的大小。代码见附录

    3.2.3 界面设计与制作(JFrame 继承子类)主要包括登陆界面、主界面、插入信息界面、查询信息界面、编辑界面(修 改删除查询详情)、显示信息界面(表格显示)、统计整理界面(自动制作柱形图、 饼图),详情见第五章。 设计界面主要使用 Java Swing 和 Java AWT,采用框架 Java BeautyEye,同 时采用事件处理等接口完成图形界面设计,做到良好的美观性以及实用性、易用性。便于用户操作。
    3.3 函数调用关系图
    程序由 LoginFrame()进入,登陆成功进入 MainFrame(),Mainframe 可以选择进入 InsertFrame、QueryFrame 和 StatisticFrame,其中 QueryFrame 调用 Delete 和 Update 函数,Statistic 调用 HandleDate、JFreeChart 和 JDBC 函数完成统计并自动将数据可视化。
    第四章 测试结果4.1 登陆界面


    4.2 程序主界面如果登陆失败,如账号不存在或密码错误,则会给以相应提示,登陆成功则跳转至程序主界面。

    4.3 添加信息界面
    如果编号重复、添加内容格式不正确,则会有一定提示:


    4.4 查询信息界面查询界面打开后默认显示全部记录,以表格形式显示

    共有五种查询方式可选择:

    在此处我们选择查询部门为技术部的所有职员,其他类似不做重复测试。可以看到表格刷新,显示出符合条件的所有记录:

    4.5 编辑信息界面在查询界面点击每条查询结果的最右侧按钮可进入编辑界面,可以修改、删除、查询详细信息

    4.6 统计整理界面



    3 评论 63 下载 2018-11-05 11:25:51 下载需要8点积分
  • 基于JAVA和MYSQL数据库实现的银行信息管理系统

    1 概述1.1 背景和意义本软件主要模拟了银行ATM机的工作流程以及银行柜台的工作流程。包括用户的存取款、修改用户信息、注册新用户、计算利息等。修改了ATM机中的部分不安全的设计,比如在用户没拔卡的情况下其他人使用时将钱转到自己的卡上,所以本系统新增了转账时二次输入密码的设计。
    同时为了系统的安全性,本系统所有的密码都采用了双重MD5加密技术。防止了被其他人盗取数据库数据进行非法操作的情况。
    通过此次课程设计,来巩固所学Java语言基本知识,加深对Swing的熟练程度,以及对面向对象、继承的理解、动态数组等。掌握JDK、MyEclipse、MySql等开发工具的运用,拓宽常用类库的应用。并通过自己动手、组内讨论合作-,对Java这门课程有更深得了解与认识。
    1.2 任务概述本系统主要分三个模块:ATM机用户模块、银行柜台管理员模块、部门员工管理模块。

    在ATM机用户模块,包括八个主要的业务:

    转账业务取款充值缴费实时存款理财频道修改密码退出查询业务
    在银行柜台管理员模块,包括八个主要的业务:

    转账业务取款充值缴费实时存款新用户注册修改用户信息退出查询业务
    在部门员工管理模块里,包括八个主要的模块:

    查看部门增加部门修改部门信息删除部门查看员工增加员工修改员工信息删除员工

    在本软件的开发过程中,由我担任组长负责ATM机用户的那部分,两个组员负责两个其它的模块,在开发过程在中对她们进行了一定的指导。
    本软件主要目的是为了模拟银行管理系统的实现流程,凭借自己的一些认识修改了其中自认为的一些不合理的地方。提高了使用的方便程度。同时用到了十分先进的密码加密技术,大大提到了系统的安全性。
    1.3 设计环境硬件要求能运行Windows XP、Windows7、Windows 8或Windows 10操作系统的微机,JAVA程序设计语言、数据库管理系统及相应的集成开发环境,jre1.7、jdk1.7、MySQL、以及MyEclipse2015、Eclipse Mars2。
    2 需求分析与设计2.1 系统功能分析2.1.1 用户相关在ATM机用户模块,包括八个主要的业务:

    转账业务:主要是指不同用户之间的转账功能
    取款:主要是指取款,其中包括活期取款、定期取款和添加特约取款这三个子功能
    充值缴费:提供了一些常用推荐网站供用户使用
    实时存款:其中包括了定期存款和活期存款,不同类型的存款,利息的计算利率不同
    理财频道:提供了一个常用的理财网站供用户使用
    修改密码:用于修改用户的密码,这里必须输入原密码并且需两次输入新密码,同时用到了双重MD5加密技术对新密码进行了加密,提高了系统的安全性
    退出:主要用于退出系统
    查询业务:查询当前账户的余额以及一些常用信息,比如银行最新的利率信息等

    2.1.2 管理员相关
    转账业务:跟用户部分的转账业务类似
    取款:跟用户部分的取款业务类似
    充值缴费:跟用户部分的充值缴费业务类似
    实时存款:跟用户部分的实时存款业务类似
    新用户注册:这里主要是指新增用户,随机生成一串19位的卡号,之后用户可以用这个卡号
    修改用户信息:这部分主要包括忘记密码和修改用户信息。
    退出:用于退出系统
    查询业务:跟用户部分的查询业务类似

    如果该管理员的帐号叫做‘admin’,那么该用户拥有本系统的最高权限,包括部门员工的处理:

    查看部门:查看当前部门的信息,包括部门名、部门管理者姓名、部门编号等信息
    增加部门:用于新增部门
    修改部门信息:用于修改部门的信息
    删除部门:用于删除部门
    查看员工:用于查看当前员工信息
    增加员工:用于新增员工
    修改员工信息:用于修改员工信息,由于有主外键的约束,所以这里不允许将员工所在部门调到不存在的部门
    删除员工:用于删除掉已经离职或因特殊情况离开公司的员工

    2.2 数据流图
    2.3 数据字典2.3.1 建表信息用户基本信息表

    主键:卡号用户ID,密码,性别,姓名,邮箱,电话,是否为管理员
    部门基本信息表

    主键:部门编号部门名称,部门主管,电话
    员工基本信息表

    主键:员工编号卡号,出生日期,入职日期,部门编号
    活期存款信息表

    主键:卡号余额,利息,执行时间
    定期存款信息表

    主键:卡号余额,利息,类型,tip,执行时间
    特约取款信息表

    主键:SIDS账户,S密码
    定期类型信息表

    主键:类型号类型名
    币种类型信息表

    主键:币种类型卡号,余额,可用余额,币种类型
    2.3.2 基础数据
    用户基本信息表:卡号,用户ID,密码,性别,姓名,邮箱,电话
    部门基本信息表:部门编号,部门名称,部门主管,电话
    员工基本信息表:员工编号,出生日期,入职日期,部门编号,卡号
    活期存款信息表:卡号,余额,利息,执行时间
    定期存款信息表:卡号,余额,利息,类型,tip,执行时间
    特约取款信息表:SID,账户,密码
    定期类型信息表:类型号,类型名
    币种类型信息表:币种类型,余额,可用余额,币种类型,卡号

    2.3.3 数据详情用户表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    userAcount
    varchar
    20


    卡号


    userID
    varchar
    20


    身份证号


    userPassword
    varchar
    20


    密码


    userTel
    varchar
    20


    电话


    userName
    varchar
    20


    姓名


    userSex
    vachar
    20


    性别


    userMail
    vachar
    30


    邮箱


    ifAdmin
    tinyint
    4


    是否管理员



    部门表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    depnum
    int
    11


    部门编号


    depname
    varchar
    20


    部门名称


    depmanager
    varchar
    20


    部门经理


    deptel
    varchar
    20


    电话



    员工表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义
    数据项说明




    empnum
    int
    11


    员工编号
    自增


    depnum
    int
    11


    部门编号



    empborn
    varchar
    20


    出生日期



    hireday
    date



    入职日期



    userAccount
    varchar
    20


    卡号



    活期存款表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    userAcount
    varchar
    30


    卡号


    balance
    float
    9,3


    余额


    interest
    float
    9,3


    利息


    operateTime
    datetime



    执行时间



    定期存款表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    userAcount
    varchar
    20


    卡号


    balance
    float
    9,3


    余额


    interest
    int
    11


    利息


    type
    int
    11


    类型


    tip
    varchar
    20


    Tip


    time
    date



    执行时间



    特约取款表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    SID
    varchar
    20


    Sid


    Samount
    int
    11


    S账户


    sPassword
    varchar
    20


    S密码



    定期类型表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    typeID
    tinyint
    4


    类型号


    typename
    varchar
    20


    类型名



    币种类型表



    数据项名称
    数据项类型
    数据项长度
    是否关键字
    是否为空
    数据项含义




    userAcount
    varchar
    20


    卡号


    Amount
    int
    11





    aAmount
    int
    11





    moneyType
    tinyint
    4


    币种类型



    2.4 功能模块设计
    3 数据库设计3.1 数据库概念结构设计
    3.2 数据库逻辑结构设计3.2.1 关系模式
    用户(卡号,用户ID,密码,性别,姓名,邮箱,电话,是否管理员)
    部门(部门编号,部门名称,部门主管,电话)
    员工(员工编号,出生日期,入职日期,卡号,部门编号)
    活期存款(卡号,余额,利息,执行时间)
    定期存款(卡号,余额,利息,类型,tip,执行时间)
    特约取款(SID,S账户,S密码)
    定期类型(类型号,类型名)
    币种类型(币种类型,余额,可用余额,卡号)

    3.3 数据库物理设计与实施3.3.1 数据库选择本软件选择的数据库是当前主流数据库之一的MySQL数据库。
    3.3.2 数据库创建# SQL Manager 2005 Lite for MySQL 3.6.5.8# ---------------------------------------# Host : localhost# Port : 3306# Database : bank_management_system/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;/*!40101 SET NAMES gbk */;SET FOREIGN_KEY_CHECKS=0;CREATE DATABASE `bank_management_system` CHARACTER SET 'latin1' COLLATE 'latin1_swedish_ci';
    3.3.3 数据表创建# Structure for the `userinfo` table : #CREATE TABLE `userinfo` ( `userAccount` varchar(20) NOT NULL, `userPassword` varchar(50) NOT NULL, `userTel` varchar(20) default NULL, `userID` varchar(20) NOT NULL, `userMail` varchar(30) default NULL, `userName` varchar(20) character set utf8 default NULL, `userSex` varchar(20) default NULL, `ifadmin` tinyint(4) default NULL, PRIMARY KEY (`userAccount`), UNIQUE KEY `userID` (`userID`), UNIQUE KEY `userAccount` (`userAccount`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;## Structure for the `currentaccount` table : #CREATE TABLE `currentaccount` ( `userAccount` varchar(30) NOT NULL, `balance` float(9,3) default NULL, `interest` float(9,3) default NULL, `operateTime` datetime default NULL, PRIMARY KEY (`userAccount`), CONSTRAINT `currentaccount_fk` FOREIGN KEY (`userAccount`) REFERENCES `userinfo` (`userAccount`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;## Structure for the `dep` table : #CREATE TABLE `dep` ( `depnum` int(11) NOT NULL, `depname` varchar(20) character set utf8 default NULL, `depmanager` varchar(20) character set utf8 default NULL, `deptel` varchar(20) default NULL, PRIMARY KEY (`depnum`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;## Structure for the `desposittype` table : #CREATE TABLE `desposittype` ( `typename` varchar(20) character set utf8 default NULL, `typeid` tinyint(4) NOT NULL, PRIMARY KEY (`typeid`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;## Structure for the `emp` table : #CREATE TABLE `emp` ( `empnum` int(11) NOT NULL auto_increment, `depnum` int(11) default NULL, `empborn` date default NULL, `hireday` date default NULL, `userAccount` varchar(20) NOT NULL, PRIMARY KEY (`empnum`), UNIQUE KEY `userAccount` (`userAccount`), KEY `depnum` (`depnum`), CONSTRAINT `emp_fk` FOREIGN KEY (`depnum`) REFERENCES `dep` (`depnum`), CONSTRAINT `emp_fk1` FOREIGN KEY (`userAccount`) REFERENCES `userinfo` (`userAccount`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;## Structure for the `fixedaccount` table : #CREATE TABLE `fixedaccount` ( `userAccount` varchar(20) NOT NULL, `balance` float(9,3) default NULL, `interest` float(9,3) default NULL, `type` int(11) default NULL, `tip` varchar(20) default NULL, `time` date default NULL, PRIMARY KEY (`userAccount`), UNIQUE KEY `userAccount` (`userAccount`), CONSTRAINT `fixedaccount_fk` FOREIGN KEY (`userAccount`) REFERENCES `userinfo` (`userAccount`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;## Structure for the `moneytypes` table : #CREATE TABLE `moneytypes` ( `userAccount` varchar(20) character set latin1 NOT NULL, `Amount` int(11) default NULL, `aAmount` int(11) default NULL, `moneyType` tinyint(4) default NULL, `typeid` int(11) NOT NULL auto_increment, PRIMARY KEY (`typeid`), UNIQUE KEY `typeid` (`typeid`)) ENGINE=InnoDB DEFAULT CHARSET=gbk;## Structure for the `specialdraw` table : #CREATE TABLE `specialdraw` ( `sID` varchar(20) NOT NULL, `sAmount` int(11) default NULL, `sPassword` varchar(20) default NULL, PRIMARY KEY (`sID`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    4 系统详细设计与实现4.1 系统主窗体设计与实现4.1.1 用户部分:登录页面

    ATM机部分主页面

    管理员登录页面

    管理员主界面

    4.2系统公共模块设计与实现本系统的公共模块有两个表模型,分别是部门表表模型以及员工表表模型。里面用到了Vector动态数组,主要是用DefaultTableModel实现的。
    如部门表的表模型:
    package method;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Vector;import javax.swing.table.DefaultTableModel;public class DepTableModal { public static DefaultTableModel getDepTableModal() { // 给表格设置表模型 -->设置表头和表数据 Vector<String> thVector = new Vector<String>(); thVector.add("部门编号"); thVector.add("部门名称"); thVector.add("部门号码"); thVector.add("部门主管"); // 表数据 Vector<Vector<String>> dateVector = null; try { Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection( "jdbc:mysql://127.0.0.1/Bank_Management_System", "root", "admin"); String sql = "select depnum,depname,deptel,depmanager from dep"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); dateVector = new Vector<Vector<String>>(); while(rs.next()){ Vector<String> vector = new Vector<String>(); vector.add(rs.getInt(1) + ""); vector.add(rs.getString(2)); vector.add(rs.getString(3)); vector.add(rs.getString(4)); dateVector.add(vector); } }catch (Exception e) { e.printStackTrace(); } DefaultTableModel defaultTableModel = new DefaultTableModel(dateVector,thVector); return defaultTableModel; }}
    再如员工表模型:
    package method;import java.sql.Connection;import java.sql.Date;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.util.Calendar;import java.util.Vector;import javax.swing.table.DefaultTableModel;public class EmpTableModal { public static DefaultTableModel getEmpTableModal() { // 给表格设置表模型 -->设置表头和表数据 Vector<String> thVector = new Vector<String>(); thVector.add("员工编号"); thVector.add("员工姓名"); thVector.add("性别"); thVector.add("年龄"); thVector.add("员工号码"); thVector.add("入职日期"); thVector.add("部门名称"); // 表数据 Vector<Vector<String>> dateVector = null; try { Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection( "jdbc:mysql://127.0.0.1/Bank_Management_System", "root", "admin"); String sql = "select empnum,depnum,empborn,hireday,userAccount from emp"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); dateVector = new Vector<Vector<String>>(); while (rs.next()) { Vector<String> vector = new Vector<String>(); String hireday = rs.getString(4); //1员工编号 vector.add(rs.getInt(1) + ""); Date borndate = rs.getDate(3); Calendar calendar = Calendar.getInstance(); calendar.setTime(borndate); int n = calendar.get(Calendar.YEAR); int s2 = rs.getInt(2); java.util.Date now = new java.util.Date(); calendar.setTime(now); int n1 = calendar.get(Calendar.YEAR); int age; age = n1 - n; String userAccount = rs.getString(5); Class.forName("com.mysql.jdbc.Driver"); Connection connection5 = DriverManager.getConnection( "jdbc:mysql://127.0.0.1/Bank_Management_System", "root", "admin"); String sql5 = "select userName,userSex,userTel from userinfo where userAccount = ?"; PreparedStatement ps5 = connection5.prepareStatement(sql5); ps5.setObject(1, userAccount); ResultSet rs5 = ps5.executeQuery(); while (rs5.next()) { vector.add(rs5.getString(1)); if (rs5.getInt(2) == 1) vector.add("男"); else{ vector.add("女"); } vector.add(rs5.getString(3)); //2年龄 vector.add(age + ""); vector.add(hireday); } Connection connection2 = DriverManager.getConnection( "jdbc:mysql://127.0.0.1/Bank_Management_System", "root", "admin"); String sql2 = "select depname from dep where depnum = ?"; PreparedStatement ps2 = connection2.prepareStatement(sql2); ps2.setObject(1, s2); ResultSet rs2 = ps2.executeQuery(); rs2.next(); vector.add(rs2.getString(1)); dateVector.add(vector); } }catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } DefaultTableModel defaultTableModel = new DefaultTableModel(dateVector, thVector); return defaultTableModel; }}
    4.3 系统ATM机模块设计与实现 // 取款 qukuanButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String account_ = account; MainView_User.this.dispose(); Draw_main draw_main = new Draw_main(account); } }); // 存款 cunkuanButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { MainView_User.this.dispose(); Desposit_Main desposit_Main = new Desposit_Main(account); } }); zhuanzhangButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { MainView_User.this.dispose(); Transfer_accounts_Main transfer_accounts_Main = new Transfer_accounts_Main( account); } }); xiugaimimaButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String accountnum = account; MainView_User.this.dispose(); RevisePassword revisePassword = new RevisePassword(accountnum); } }); tuichuButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int n = JOptionPane.showConfirmDialog(null, "确定退出?", "提示", JOptionPane.YES_NO_OPTION); if (n == 0) { JOptionPane.showMessageDialog(null, "退出成功!祝您工作顺利,生活愉快!"); System.exit(0); } else { return; } } }); licaiButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { Runtime .getRuntime() .exec( "rundll32 url.dll,FileProtocolHandler http://localhost:8080/Bank_Management_System_"); } catch (IOException e1) { e1.printStackTrace(); } } }); chongzhiButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { MainView_User.this.dispose(); Recharge_pay recharge_pay = new Recharge_pay(account); } }); chaxunyewuButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { MainView_User.this.dispose(); Inquiry_main inquiry_main = new Inquiry_main(account); } }); this.setTitle("主菜单"); this.setResizable(false); // 不可拖动或最大化 this.setSize(850, 600); this.setLocationRelativeTo(null); // 居中 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); // 关闭,做什么都不关 this.setVisible(true); // 界面可视化 } public void run() { // 线程,显示当前时间 while (true) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = sdf.format(date); time1Label.setForeground(Color.red); time1Label.setText(time); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
    5 总结本次Java和数据库课程设计我们这组做的是银行管理系统,刚开始确定课题之后,就在组内进行了明确的分工。在程序编写过程中,组员遇到了许多问题,作为组长我帮助她们解决了绝大部分问题,但也有少部分问题是我无法解决的。因为有了一定的Java基础以及一定的Java项目经验,所以自认为本次课设不会遇到让我十分棘手的问题,但事实上并不然。这也让我收敛了很多谦虚了很多。那些不懂的地方最后通过问同学、讨论、百度谷歌等途径终于基本上得到了解决。这个过程也更增大了我对学习Java虚拟机核心技术的兴趣。
    感谢组员的付出,特别是两个组员讨论设计出来的数据库和数据字典。也感谢Java老师、数据库老师对我们的帮助。本次课程设计,我收获很大。希望在今后的学习中,我和我可爱的组员都能更进一层楼。
    参考文献[1] (美)Bruce Eckel,陈昊鹏(译).Java编程思想第四版. 机械工业出版社, 2007.6.
    [2] (美)Mark Allen Weiss,冯顺玺(译).数据结构与算法分析Java语言描述.:机械工业出版社,2008.3.
    [3] Cay S. Horstmann、Gary Cornell,陈昊鹏(译)、王浩(译)、姚建平(译). JAVAJava核心技术卷1:基础知识.:机械工业出版社,2008-11
    [4] Cay S. Horstmann、Gary Cornell,陈昊鹏(译)、王浩(译)、姚建平(译). JAVA核心技术卷2:高级特征.:机械工业出版社,2008-12
    2 评论 16 下载 2018-11-06 16:03:47 下载需要6点积分
  • 基于JAVA和TCP SOCKET实现的P2P的局域网即时通信系统

    一、设计要求
    掌握P2P原理
    实现一个图形用户界面局域网内的消息系统。
    功能:建立一个局域网内的简单的P2P消息系统,程序既是服务器又是客户,服务器端口(自拟服务器端口号并选定)

    用户注册及对等方列表的获取:对等方A启动后,用户设置自己的信息(用户名,所在组);扫描网段中在线的对等方(服务器端口打开),向所有在线对等方的服务端口发送消息,接收方接收到消息后,把对等方A加入到自己的用户列表中,并发应答消息;对等方A把回应消息的其它对等方加入用户列表。双方交换的消息格式自己根据需要定义,至少包括用户名、IP地址发送消息和文件:用户在列表中选择用户,与用户建立TCP连接,发送文件或消息
    用户界面:界面上包括对等方列表;消息显示列表;消息输入框;文件传输进程显示及操作按钮或菜单

    二、软件开发工具及运行环境
    软件开发工具

    编程语言:Java开发环境:EclipseJDK版本:1.8操作系统:windows 10
    运行环境

    操作系统无关性:Windows、Linux、Mac OS X下安装了Java的运行环境JRE即可运行

    三、程序开发的基础知识3.1 学习Socket和TCP的基本原理和通信机制3.1.1 TCP连接电脑能够使用联网功能是因为电脑底层实现了TCP/IP协议,可以使电脑终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。
    建立起一个TCP连接需要经过“三次握手”:

    第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
    握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”。(过程就不细写了,就是服务器和客户端交互,最终确定断开)
    3.2 SOCKET原理3.2.1 套接字(socket)概念套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
    应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
    3.2.2 建立socket连接建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
    套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

    服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求
    客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求
    连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求

    3.3 SOCKET连接与TCP连接创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
    3.4 主要运用知识
    Java socket编程Java GUI编程Java继承和事件绑定Java异常与捕获
    四、总体设计4.1 设计思路4.1.1 经典的TCP通信服务器客户端架构服务器有一个服务器等待用户连接的线程,该线程循环等待客户端的TCP连接请求。一旦用ServerSocket.accept()捕捉到了连接请求,就为该TCP连接分配一个客户服务线程,通过该消息传递线程服务器与客户端通信。服务器发送消息通过该客户服务线程的方法在主线程完成,而接收消息全部在客户服务线程中循环接收并处理。
    客户机能发起一个向服务器的socket连接请求,一旦收到服务器成功响应连接请求,客户机便为这个socket分配一个消息接收线程,否则关闭该socket。和服务器任务分配类似,发送消息作为非常用方法在主线程中完成,而接收消息在消息接收线程中不停刷新并作相应处理。
    4.2 统一ASCII码级文本传输协议为了实现客户机对服务器命令的响应、服务器对客户机需求的解读以及客户机与客户机之间的消息传递,我为服务器和客户端之间通信定义了一组文本传输协议。协议属于变长文本传输协议,用@作为各字段分隔符,所有消息的首节一定是消息类型,方便解析。协议定义了以下按发送方分类的消息格式:


    4.3 MVC分层模式Model-View-Controller是经典的应用程序开发设计模式,它讲究数据管理、界面显示和用户交互、程序维护管理分别封装在MVC三种类中,够成松耦合关系。本次课程设计中我也利用MVC的设计思路,独立了Model类User用于保存客户机用户信息,DefaultListModel模型类用于储存在线用户队列;将View单独放在一个包中,Controller监听用户操作事件,反映给Model类处理并在View中更新。
    MVC的思想即是M和V之间不要直接产生联系,业务逻辑均封装在MC中,而V仅仅负责显示。本实验为V类绑定了各自的Listener监听用户操作,在C中完成业务逻辑处理,保存并更新User和DefaultListModel,最后再显示到UI界面上。
    4.4 concurrentHashMap管理线程队列和用户列表concurrentHashMap是java.util.concurrent包中定义的多线程安全的哈希表,利用哈希表管理线程队列和用户列表可以快速索引,多线程安全也保证了多个用户服务线程之间共享资源的数据一致性。
    4.5 程序流程图
    4.6 关键数据结构4.6.1 User模型类
    构造方法
    有两个,一个是用独立的name和IP实例化一个User,另一个是用name%IP拼接而成的字符串实例化User
    只读字段
    name和ipAddr均是private的,给他们配置一个只读的getter
    description()用户描述
    返回name%IP拼接而成的字符串,用以代表一个独立的用户

    4.6.2 ServerView类
    UI相关的方法
    构造函数中的initUI()大部分是设置UI界面,其中用到了GridLayout和BorderLayout。用serviceUISetting(false)把所有连接状态才起作用的button和textField全部关闭了(false改为true开启他们,并关闭所有设置相关的button和textField)

    4.6.3 ServerMain类
    startServer()开启服务器方法———startButton绑定
    先检查maxClientNum和port的合法输入,如果不合法弹出出错窗口并退出。接着初始化管理客户服务线程队列的并发哈希表clientServiceThreads,初始化监听客户机连接请求的serverSocket,并且初始化和开启一个监听连接请求的线程。最后有一些差错处理以及服务器log日志
    请求监听线程ServerThread类
    isRunning作为线程运行标志位控制线程存活,线程start后会调用的函数run()里完成了监听逻辑。如果开启则一直循环,serverSocket.accept()是阻塞的,线程不会运行直到有其他线程/进程向其请求Socket连接。这也是我下面提到的一个bug的原因:accept()阻塞了线程它一直在等待,仅仅用标志位来结束线程并不能使之跳出阻塞状态(还没有循环到下一次while的判断),因此我在closeThread()中强行关闭serverSocket会报出一个异常!
    收到连接请求后accept()返回一个socket,这个socket用于和请求连接的客户机通信。至此时TCP建立连接3次握手已经完成,全部被serverSocket类封装起来了。获取了通信socket之后检查服务器在线人数是否已满,向客户机发送一个登陆成功或失败的消息。若在线人数未满连接成功,则为客户机分配一个clientServiceThread线程专门用于发送和接受客户机的TCP包。
    监听客户机消息的ClientServiceThread线程类
    该类比较庞大,我挑重点介绍。
    关键字段
    private Socket socket;private User user;private BufferedReader reader;private PrintWriter writer;private boolean isRunning;
    分别保存了通信socket、当前连接用户Model、绑定在socket输入流上的BufferedReader、绑定在socket输出流上的PrintWriter以及线程运行控制标志位isRunning。reader用来读取客户机消息输入,readLine方法也是阻塞的,直到客户机有消息发送过来。writer有一个写缓冲区,有flush()函数强制发送消息并刷新缓冲区,我把写消息封装在sendMessage(String)中。
    初始化
    初始化中先绑定reader和writer到socket响应流,在判断用户socket请求发送的消息格式是否正确(不正确线程将不能执行)。接着向所有已上线的用户通知一遍这个新用户上线了,发送通知需要遍历整个服务线程队列并发送文本传输协议中定义的各式的通知。注意到这时候该服务线程并没有加入到服务线程队列中,是在初始化完成之后加入的。
    通知了其他用户这个新客户机上线后,再告诉该客户机现在已经有哪些用户在线了,这也是用协议中的格式发送通知即可。这里用到了StringBuffer类,多字符串连接时该类比String的+的效率要高。
    线程run
    收到客户机消息后判断消息类型,若是LOGOUT通知客户机下线,则向所有其他客户端进程发送该用户下线的信息,并删除model类里的该用户对象和线程队列里的该线程。
    如果是消息则交与dispatchMessage(String)方法专门分发消息。
    分发消息方法dispatchMessage(String)
    该方法解析MSG类消息的to字段,根据to字段选择是将消息发给特定用户还是直接群发。发给特定用户的话根据to字段(userDescription)做索引,快速从服务线程队列找出服务该用户客户机的线程来发送信息。
    其他
    绑定时间如stopServer关闭服务器和sendAll群发消息都比较直白便省略介绍,主要需要注意一下其中的差错控制。关闭服务器还需要更新UI控制逻辑。
    说明
    ServerMain类虽然通过ClientServiceThread里的writer发送消息,并且也是调用封装在这个Thread内部类中的,但是调用writer来sendMessage并不是一定在该线程内完成的(该线程内指的是run()里的while循环内部),sendMessage是非阻塞的我们没有必要专门在线程中执行。ClientServiceThread主要工作是收听各个客户端向服务器发送的消息。

    4.6.4 ClientView类Client和Server稍微有点不一样,只有一个辅助线程MessageThread用于接收服务器消息。由于只需要绑定在一个socket上,所以writer和reader只有一个,是直接属于Client实例的字段。

    UI相关方法
    构造函数里的init和Server中几乎完全一样,这部分属于代码复用。注意需要多绑定一个监听器:
    javax.swing.event.ListSelectionListener类用来监听用户选择JList框里的条目,JList框里固定一个所有人的项(点击选中表示消息发送给所有人,默认发送给所有人,目标对象下线后也是自动把对象转变成所有人),其他则是在线用户。点击这些列表项时触发一个选择事件,通过判断index来判断用户的选择,并更新模型记录sendTarget和UI中messageToLabel显示的text。

    4.6.5 ClientMain类
    connect连接到服务器
    差错检测这里没有判断IP地址合法性,判断也不是很麻烦。用户输入合法时,根据服务器IP地址和端口实例化一个socket,这个socket用于将来和服务器通信。获取客户机本地IP地址并用这个IP地址实例化,通过socket给服务器发送一条自己用户信息(name和IP)的消息表示请求。发送完毕后立即开启MessageThread等待服务器的回应。
    MessageThread接受服务器消息线程
    reader.readLine()阻塞读取服务器消息。一直忘记介绍StringTokenizer类,这里说明一下。StringTokenizer类通过一个String和一个分割字符串实例或一个tokenizer,通过分割得到一系列记号流通过tokenizer.nextToken()获取这些记号字符串。不难发现其作用和String.split(String)一样也是做字符串分割,但是其效率显著优于split方法(百度搜索两者比较会有较详细的性能分析)。
    根据tokenizer返回的记号流我们来判断消息类型:
    服务器关闭:向服务器发送一个下线信息,关闭socket, write和read,清空记录Model,最后退出线程。
    服务器错误:log错误类型,啥也不干进入下一轮循环。
    登陆信息:
    成功:log成功,进入下一轮循环。
    失败:log失败,关闭socket, write和read,清空记录Model,最后退出线程。

    五、关键代码 private class ClientServiceThread extends Thread { private Socket socket; private User user; private BufferedReader reader; private PrintWriter writer; private boolean isRunning; private synchronized boolean init() { try { reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new PrintWriter(socket.getOutputStream()); String info = reader.readLine(); StringTokenizer tokenizer = new StringTokenizer(info, "@"); String type = tokenizer.nextToken(); if (!type.equals("LOGIN")) { sendMessage("ERROR@MESSAGE_TYPE"); return false; } user = new User(tokenizer.nextToken()); sendMessage("LOGIN@SUCCESS@" + user.description() + "与服务器连接成功!"); int clientNum = clientServiceThreads.size(); if (clientNum > 0) { //告诉该客户端还有谁在线 StringBuffer buffer = new StringBuffer(); buffer.append("@"); for (Map.Entry<String, ClientServiceThread> entry : clientServiceThreads.entrySet()) { ClientServiceThread serviceThread = entry.getValue(); buffer.append(serviceThread.getUser().description() + "@"); //告诉其他用户此用户在线 serviceThread.sendMessage("USER@ADD@" + user.description()); } sendMessage("USER@LIST@" + clientNum + buffer.toString()); } return true; } catch(Exception e) { e.printStackTrace(); return false; } } public ClientServiceThread(Socket socket) { this.socket = socket; this.isRunning = init(); if (!this.isRunning) { logMessage("服务线程开启失败!"); } } public void run() { while (isRunning) { try { String message = reader.readLine(); // System.out.println("recieve message: " + message); if (message.equals("LOGOUT")) { logMessage(user.description() + "下线..."); int clientNum = clientServiceThreads.size(); //告诉其他用户该用户已经下线 for (Map.Entry<String, ClientServiceThread> entry : clientServiceThreads.entrySet()) { entry.getValue().sendMessage("USER@DELETE@" + user.description()); } //移除该用户以及服务器线程 listModel.removeElement(user.getName()); clientServiceThreads.remove(user.description()); close(); return; } else { //发送消息 dispatchMessage(message); } } catch(Exception e) { e.printStackTrace(); } } } public void dispatchMessage(String message) { StringTokenizer tokenizer = new StringTokenizer(message, "@"); String type = tokenizer.nextToken(); if (!type.equals("MSG")) { sendMessage("ERROR@MESSAGE_TYPE"); return; } String to = tokenizer.nextToken(); String from = tokenizer.nextToken(); String content = tokenizer.nextToken(); logMessage(from + "->" + to + ": " + content); if (to.equals("ALL")) { //send to everyone for (Map.Entry<String, ClientServiceThread> entry : clientServiceThreads.entrySet()) { entry.getValue().sendMessage(message); } } else { //发送给某一个人 if (clientServiceThreads.containsKey(to)) { clientServiceThreads.get(to).sendMessage(message); } else { sendMessage("ERROR@INVALID_USER"); } } } public void close() throws IOException { this.isRunning = false; this.reader.close(); this.writer.close(); this.socket.close(); } public void sendMessage(String message) { writer.println(message); writer.flush(); } public User getUser() { return user; } }
    六、测试6.1 运行ServerMain和ClientMain本程序支持单服务器多客户端,我打开了1个Server和2个Client作为演示。进程开启后界面如下:


    服务器和客户端在未开启/连接的状态下无法发送消息只能修改配置,而在开启/连接状态下无法修改配置只能发送消息,UI逻辑均由开启/连接状态决定。
    服务器配置中可以修改监听端口和人数上限,开启后接收所有来自客户端的消息,模仿解析包动作进行转发,服务器可以群发消息。
    客户端可以设置自己的名字、服务器IP和服务器端口(默认均是192.168.1.154本地IP和端口6666)。客户端连接后可以在在线用户列表中选择所有人或其他在线用户发送消息。其他用户上下线信息在在线用户列表和聊天消息框中都有提示。
    6.2 开启服务器并登陆客户端下图是第二位登陆的用户杨劲登录时,从服务器接受其他用户,显示在左侧在线用户列表中。

    6.3 服务器群发消息
    6.4 客户端群发消息客户端 选择 所有人,并发送消息:

    客户端 收到了消息,并标记为群发:

    6.5 客户端发送私聊消息:客户端 和 客户端 私聊:


    6.6 客户端下线客户端下线时所有在线用户收到其下线消息,在线列表中不再出现此用户:
    客户端 下线,通知服务器,服务器转发其下线消息:

    客户端 收到 下线通知,左侧在线列表中已经没有下线的用户了:

    七、开发过程中遇到的问题及解决办法本次完成这个课程设计还是花了不少精力和时间,由于对于Java网络编程这一块并不是很擅长,开发过程中老是遇到各种大大小小的问题,例如一开始不太清楚tokenizer.nextToken()的意思以及用法, 在参考了一些书籍以及百度各种关于网络编程的文章,才得以顺利完成本次课设。
    八、程序中待解决的问题及改进方向程序可能还存在不少小bug,后期有时间再进行修复。目前发现的一个Bug是服务器关闭时,我关了服务器接收Socket请求的线程并close了该ServerSocket,但是该线程仍然继续执行了一次ServerSocket.accept()。我尝试用了synchronized方法并判断ServerSocket是否关闭,但这个异常还是会出现。我捕捉了该异常,仅仅printStackTrace而没有做其他错误处理,幸运的是这小Bug并不影响服务器关闭,所有客户端都能正确的接收服务器关闭的消息。希望以后有时间能把这个错误给改过来。
    3 评论 27 下载 2018-11-29 09:34:06 下载需要8点积分
  • 基于tensorflow和flask的本地图片库web图片搜索引擎

    1 Project Overview1.1 Project DescriptionBased on tensorflow and Flask, a web-based image search engine is realized, which can realize simple image search function based on server images database.
    1.2 Project function requirements
    It contains an input box to upload an image (Formulation)
    Users can preview the query image in the searching window (Formulation)
    It has a search button (Initiation)
    Provide an overview of the results (e.g. the total number of results) (Review)
    Allow changing search parameters (e.g. select certain category/tag) when reviewing results (Refinement)
    Users can take some actions, e.g. add selected images to a favorite list (Use)

    2 Design and implementation2.1 Design2.1.1 Code designThe program code is divided into two parts: front end and back end. The front and back end realize data transmission through Flask. The back end is python program, and the front end is web application.
    The file directory structure is as follows:
    ├── database│ ├── dataset│ └── tags├── imagenet├── static│ ├── favorite│ ├── images│ └── result├── uploads├── templates│ └── HTML└── .pyMain function designfront end
    //Show favorite function, bind with favorite buttonfunction favorite_fun()//Add a favorite function, bound to the Add to favorites buttonfunction addFavorite_fun()//Confirm the collection function, bind to the Confirm collection buttonfunction confirm_fun()//Delete favorite image function, bind with Delete buttonfunction delete_fun()//Confirm delete function, bind with Confirm delete buttonfunction confirmdelete_fun()//Tag selection function, binding to each tag button except favorite, passing arguments by strfunction tag_fun(str)//Search function, bound to the Search buttonfunction fun()
    back end
    # This function is used to do the image search/image retrieval@app.route('/imgUpload', methods=['GET', 'POST']) # /imgUpload matches the url in ajaxdef upload_img():# This function is tag@app.route('/tag', methods=['GET', 'POST'])def tag():# This function is addFavorite@app.route('/addFavorite', methods=['GET', 'POST'])def addFavorite():# This function is displaying favorites@app.route('/favorite', methods=['GET', 'POST'])def favorite():# This function is deleting favorites@app.route('/deleteFavorite', methods=['GET', 'POST'])def deleteFavorite():
    2.1.2 interface designStart interfaceInclude theme、upload file button、search button、preview image、tag bar and my favorites.

    Search results interfaceShow search results and description of search results:

    My favorites interfaceShow a collection of pictures, a description of the number of pictures.

    2.2 Implementation2.2.1 Main function implementationShow search resultsfront end
    function fun(){ $('#div2').hide();//Hide favorites content $('#load').show();//Display loading animation $("#confirm").hide();//Hide Confirm Favorites button $("#addFavorite").show();//Show Add to favorites button $("form").submit(function(evt){ evt.preventDefault(); var formData = new FormData($(this)[0]); $.ajax({ url: 'imgUpload', type: 'POST', data: formData, //async: false, cache: false, contentType: false, enctype: 'multipart/form-data', processData: false, success: function (response) {//Response returns the searched image path $('#load').hide();//Hidden loading animation $('#clearrow').show();//Show Clear button //Show searched images $("#img0").show(); $("#img1").show(); $("#img2").show(); $("#img3").show(); $("#img4").show(); $("#img5").show(); $("#img6").show(); $("#img7").show(); $("#img8").show(); //Assign values ​​to each image path document.getElementById("img0").src = response.image0; console.log(response.image0); console.log(document.getElementById("img0").src); document.getElementById("img1").src = response.image1; document.getElementById("img2").src = response.image2; document.getElementById("img3").src = response.image3; document.getElementById("img4").src = response.image4; document.getElementById("img5").src = response.image5; document.getElementById("img6").src = response.image6; document.getElementById("img7").src = response.image7; document.getElementById("img8").src = response.image8; $('#table').show();//Display picture table $('#resultStats').show();//Display result statement $('#clear').show();//Show Clear button } }); return false; }) }
    back end
    @app.route('/imgUpload', methods=['GET', 'POST']) # /imgUpload matches the url in ajaxdef upload_img(): print("image upload") result = 'static/result' if not gfile.Exists(result): # If there is no result folder, create it os.mkdir(result) shutil.rmtree(result) # Empty the result folder if request.method == 'POST' or request.method == 'GET': print(request.method) # check if the post request has the file part if 'file' not in request.files: print('No file part') return redirect(request.url) file = request.files['file'] print(file.filename) # if user does not select file, browser also # submit a empty part without filename if file.filename == '': print('No selected file') return redirect(request.url) if file: # and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) inputloc = os.path.join(app.config['UPLOAD_FOLDER'], filename) recommend(inputloc, extracted_features) os.remove(inputloc) image_path = "/result" image_list = [os.path.join(image_path, file) for file in os.listdir(result) if not file.startswith('.')] images = { 'image0': image_list[0], 'image1': image_list[1], 'image2': image_list[2], 'image3': image_list[3], 'image4': image_list[4], 'image5': image_list[5], 'image6': image_list[6], 'image7': image_list[7], 'image8': image_list[8] } return jsonify(images)
    Show favoritesfront end
    //Show favorite function, bind with favorite button function favorite_fun(){ $.ajax({ url: 'favorite', type: 'POST', cache: false, contentType: false, enctype: 'multipart/form-data', processData: false, success: function (response) {//Get the image file name in the favorites from the response $('#div2').show();//Showcase your favorites $('#table').hide();//Hide search results $('#resultStats').hide();//Hide search results description var str = response; console.log(str);//Print the image file name in your favorites var rows = str.length/3 + 1; var cols = 3; div1 = document.getElementById('div1'); //Dynamically add a favorite image table var tab='<div id="favoriteStats">“There are '+ str.length +' favorites” </div>'; tab+='<table id = "favoritetable" class="table" style="background: white; border: 1px ;display:none;" >'; var number = 0; for(var i=0;i<rows;i++){ tab+='<tr>'; for(var j=0;j<cols;j++){ if(number < str.length){ tab+='<td style= "box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); transition: 0.3s;width: 200px;height: 200px;padding-top: 0px;padding-bottom: 0px;padding-right: 0px;padding-left: 0px;border-left-width: 1px;border-bottom-width: 1px;border-right-width: 1px;background: white;">'+(i+j)+'</td>'; tab+='<img id= favoriteimg'+number+' src= favorite/'+str[number]+ ' alt = Noway style= "box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); transition: 0.3s; width: 290px;height: 200px;padding-top: 5px;padding-bottom: 5px;padding-right: 5px;padding-left: 5px;border-left-width: 0px;border-bottom-width: 0px;border-right-width: 0px;"></img>'; tab+='<input type= checkbox id = checkfavoriteimg'+number+' name="checkimg2" value= favoriteimg'+number+' style= "display:none; width:30px; height:30px;">'; } number ++; } tab+='</tr>'; } tab+='</table>'; div1.innerHTML=tab; } }); }
    back end
    @app.route('/favorite', methods=['GET', 'POST'])def favorite(): print('favorite') image_list = [] # image_list store all image names under the favorite folder for filename in os.listdir(r"static/favorite"): # listdir's parameter is the path of the folder image_list.append(filename) print(filename) # The filename is the name of the files in the favorite folder. images = {} for i in range(0, len(image_list)): images['image'+str(i)] = image_list[i] return jsonify(image_list)
    Delete favoritesfront end
    //Confirm delete function, bind with Confirm delete button function confirmdelete_fun(){ var checked = document.getElementsByName("checkimg2");//Read the contents of the picture selection box according to the name console.log(checked)//Print the read content var checkedData = [];//Store the file name of the selected picture picture var j = 0; for (var i = 0; i < checked.length; i++) { $("#checkfavoriteimg"+i).hide(); if (checked[i].checked){//When the selection box is selected console.log(checked[i].value);//Print the selected image corresponding to the value of the selection box, and print the id of the selected image console.log(document.getElementById(checked[i].value).src);//Print the address of the selected image in the format of http://127.0.0.1:5000/result/im1428.jpg checkedData.push(document.getElementById(checked[i].value).src);//Get the image address according to the picture id, the address format is http://127.0.0.1:5000/result/im1428.jpg if(checkedData[j].indexOf("/")>0)//If the "/" is included, the string is intercepted starting from the last "/" +1 {checkedData[j]=checkedData[j].substring(checkedData[j].lastIndexOf("/")+1,checkedData[j].length);} else {checkedData[j]=checkedData[j];} j++; checked[i].checked = false;//The status of the selection box is unchecked, otherwise the status of the selection box will not be refreshed after the final confirmation of the collection } } console.log(checkedData)//Print the file name of the selected picture console.log(JSON.stringify({checkedData}))//Print the result of checked data being jsonized $.ajax({ url: 'deleteFavorite', type: 'POST', data: JSON.stringify({checkedData}),//Transfer the "checkedData" json dataType: 'json', contentType: 'application/json; charset=UTF-8',//Transfer data in json format success: function () { if(j>0){ alert("Delete images successfully!");//Pop up Delete images successfully! Prompt box } $("#delete").show();//Show Delete button $("#confirmdelete").hide();//Hide Confirm Delete button favorite_fun()//Show remaining images in favorites after deletion } }); }
    back end
    # This function is deleting favorites@app.route('/deleteFavorite', methods=['GET', 'POST'])def deleteFavorite(): print("deleteFavorite") favorite = 'static/favorite' if not gfile.Exists(favorite): # if favorite is not existing os.mkdir(favorite) # Create a new folder if request.method == 'POST' or request.method == 'GET': print(request.method) # checkedData stores the received data, which is the file name of the selected pictures checkedData = request.get_json() print(checkedData) imgname = [] # imgname saves the path of the selected images for i in range(0, len(checkedData['checkedData'])): imgname.append('static/favorite/'+str(checkedData['checkedData'][i])) # Get the path of the selected images print(imgname[i]+'has been deleted') os.remove(imgname[i]) # Remove the picture from the path from the favorite folder return jsonify(checkedData)
    tagfront end
    function tag_fun(str){ $('#div2').hide();//Hide content from favorites $('#load').show();//Display loading animation $.ajax({ url: 'tag', type: 'POST', cache: false, contentType: false, enctype: 'multipart/form-data', processData: false, success: function (response) {//Response returns the tag of each image $("#confirm").hide();//Hide the Confirm collection button $("#addFavorite").show();//Show Add to favorites button $('#table').show();//Display result table $('#resultStats').show();//Display result statement $('#load').hide();//Hidden loading animation $('#clearrow').show();//Show clearrow button if (str == 'all'){//If you choose all tag //Show all images $("#img0").show(); $("#img1").show(); $("#img2").show(); $("#img3").show(); $("#img4").show(); $("#img5").show(); $("#img6").show(); $("#img7").show(); $("#img8").show(); } else{ //If the tag of the image is not equal to the selected tag, hide it, otherwise display it if (response.image0 != str) {$("#img0").hide();} else {$("#img0").show();} if (response.image1 != str) {$("#img1").hide();} else {$("#img1").show();} if (response.image2 != str) {$("#img2").hide();} else {$("#img2").show();} if (response.image3 != str) {$("#img3").hide();} else {$("#img3").show();} if (response.image4 != str) {$("#img4").hide();} else {$("#img4").show();} if (response.image5 != str) {$("#img5").hide();} else {$("#img5").show();} if (response.image6 != str) {$("#img6").hide();} else {$("#img6").show();} if (response.image7 != str) {$("#img7").hide();} else {$("#img7").show();} if (response.image8 != str) {$("#img8").hide();} else {$("#img8").show();} } } }); }
    back end
    # This function is tag@app.route('/tag', methods=['GET', 'POST'])def tag(): print("tag") if request.method == 'POST' or request.method == 'GET': print(request.method) # Create a file to read the stream object and read each tag file animalsfile_object = open('database/tags/animals.txt') babyfile_object = open('database/tags/baby.txt') birdfile_object = open('database/tags/bird.txt') carfile_object = open('database/tags/car.txt') dogfile_object = open('database/tags/dog.txt') femalefile_object = open('database/tags/female.txt') flowerfile_object = open('database/tags/flower.txt') foodfile_object = open('database/tags/food.txt') indoorfile_object = open('database/tags/indoor.txt') lakefile_object = open('database/tags/lake.txt') malefile_object = open('database/tags/male.txt') nightfile_object = open('database/tags/night.txt') peoplefile_object = open('database/tags/people.txt') plant_lifefile_object = open('database/tags/plant_life.txt') portraitfile_object = open('database/tags/portrait.txt') riverfile_object = open('database/tags/river.txt') seafile_object = open('database/tags/sea.txt') skyfile_object = open('database/tags/sky.txt') structuresfile_object = open('database/tags/structures.txt') sunsetfile_object = open('database/tags/sunset.txt') transportfile_object = open('database/tags/transport.txt') treefile_object = open('database/tags/tree.txt') waterfile_object = open('database/tags/water.txt') try: # Read the contents of each tag file and store it as a string animalsfile_context = animalsfile_object.read() babyfile_context = babyfile_object.read() birdfile_context = birdfile_object.read() carfile_context = carfile_object.read() dogfile_context = dogfile_object.read() femalefile_context = femalefile_object.read() flowerfile_context = flowerfile_object.read() foodfile_context = foodfile_object.read() indoorfile_context = indoorfile_object.read() lakefile_context = lakefile_object.read() malefile_context = malefile_object.read() nightfile_context = nightfile_object.read() peoplefile_context = peoplefile_object.read() plant_liftfile_context = plant_lifefile_object.read() portraitfile_context = portraitfile_object.read() riverfile_context = riverfile_object.read() seafile_context = seafile_object.read() skyfile_context = skyfile_object.read() structuresfile_context = structuresfile_object.read() sunsetfile_context = sunsetfile_object.read() transportfile_context = transportfile_object.read() treefile_context = treefile_object.read() waterfile_context = waterfile_object.read() finally: # Close file animalsfile_object.close() babyfile_object.close() birdfile_object.close() carfile_object.close() dogfile_object.close() femalefile_object.close() flowerfile_object.close() foodfile_object.close() indoorfile_object.close() lakefile_object.close() malefile_object.close() nightfile_object.close() peoplefile_object.close() plant_lifefile_object.close() portraitfile_object.close() riverfile_object.close() seafile_object.close() skyfile_object.close() structuresfile_object.close() sunsetfile_object.close() transportfile_object.close() treefile_object.close() waterfile_object.close() image_path = "/result" # Read each image file in the result folder image_list = [os.path.join(image_path, file) for file in os.listdir('static/result') if not file.startswith('.')] for i in range(0, 9): image_list[i] = ('\n'+str(re.findall("\d+", image_list[i])[0])+'\n') # image_list存储每个图片文件的序号 # image_tag stores the tag of each image file image_tag =[] for i in range(0, 9): # Determine which tag each image belongs to if image_list[i] in animalsfile_context: image_tag.append('animals') elif image_list[i] in babyfile_context: image_tag.append('baby') elif image_list[i] in birdfile_context: image_tag.append('bird') elif image_list[i] in carfile_context: image_tag.append('car') elif image_list[i] in dogfile_context: image_tag.append('dog') elif image_list[i] in femalefile_context: image_tag.append('female') elif image_list[i] in flowerfile_context: image_tag.append('flower') elif image_list[i] in foodfile_context: image_tag.append('food') elif image_list[i] in indoorfile_context: image_tag.append('indoor') elif image_list[i] in lakefile_context: image_tag.append('lake') elif image_list[i] in malefile_context: image_tag.append('male') elif image_list[i] in nightfile_context: image_tag.append('night') elif image_list[i] in peoplefile_context: image_tag.append('people') elif image_list[i] in plant_liftfile_context: image_tag.append('plant_life') elif image_list[i] in portraitfile_context: image_tag.append('portrait') elif image_list[i] in riverfile_context: image_tag.append('river') elif image_list[i] in seafile_context: image_tag.append('sea') elif image_list[i] in skyfile_context: image_tag.append('sky') elif image_list[i] in structuresfile_context: image_tag.append('structures') elif image_list[i] in sunsetfile_context: image_tag.append('sunset') elif image_list[i] in transportfile_context: image_tag.append('transport') elif image_list[i] in treefile_context: image_tag.append('tree') elif image_list[i] in waterfile_context: image_tag.append('water') images = { 'image0': image_tag[0], 'image1': image_tag[1], 'image2': image_tag[2], 'image3': image_tag[3], 'image4': image_tag[4], 'image5': image_tag[5], 'image6': image_tag[6], 'image7': image_tag[7], 'image8': image_tag[8] } print(images) return jsonify(images)
    3 how to run3.1 StartRun the rest-sever.py file to start the server
    Start the search engine by typing 127.0.0.1:5000 in the browser.

    3.2 Upload image and display search resultsUpload a local file and click the Search button to display the search results.

    3.3 TagAfter getting the search results, click on the different tag buttons to display the images of the different tags under the results.

    3.4 Add to favoritesClick the Add to favorites button, the selection box is displayed under the picture, select the picture, click the confirmation button, add the favorites, and the collection is successful (the picture is not prompted if no picture is selected)
    Add to favorites

    Add successfully

    3.5 Show my favoritesClick the favorite button to display my favorites:

    3.6 Delete favorite imagesClick the Delete button, the selection box will be displayed next to the picture, select the picture, click the confirmation button, delete the picture, the prompt is deleted successfully (the picture is not prompted if no picture is selected)
    Delete

    Delete successfully

    Delete result

    由于包含图片集以及索引集,文件较大无法上传,所以下载文件中只包含代码以及readme。完整文件附上百度网盘地址及其提取码
    链接:https://pan.baidu.com/s/1PCg-Uzogh64KupmXthoiuQ
    提取码:bsoe
    0 评论 2 下载 2019-05-24 11:09:42 下载需要5点积分
  • 基于Face++的人脸融合及换脸系统

    1.项目介绍在本项目中,我们实现了对人脸图片数据的三种处理:人脸互换(face swap)、人脸融合(face morph)以及基于特征向量的人脸处理(eigen face)。
    1.1 人脸互换(face swap)人脸互换部分主要实现的功能是,给定任意两张人脸图片,通过一系列操作,使两个人的脸部交换,这部分需要的问题有:

    不同的人的脸部结构千差万别,同一个人也会因为角度、面部表情的不同而导致差别,即如何实现不同图片的人脸对齐
    不同人脸的肤色、光照不同,即不同图片的面部亮度不同,在换脸后如何与整体亮度统一
    不同人脸的纹理不同,比如老人的皱纹等,如何实现换脸后纹理的统一

    1.2 人脸融合(face morph)在人脸融合部分,我们需要实现给定任意两张人脸图片和融合度α,通过一系列操 作,实现两个人脸的融合。这一部分的困难在于:

    人脸结构的检测与分割。对于给定的人脸图片,人脸的结构差异很大
    人脸融合度的构建。对于给定的融合度 α,如何对两张图片的人脸取样与映射

    1.3 本征脸(eigen face)在这一部分,需要对较大的数据集(几百张,几千张人脸图片)进行处理,通过主成分分析的方法,得到一定数量的人脸主成分。这一部分的主要困难在于数据集的预处理,我们需要将不同图片中的人脸对齐,才能进行后续的处理。
    2.算法结构与处理过程2.1 人脸变换2.1.1 人脸关键点检测实现人脸变换的第一步,便是人脸关键点的检测,得到图片中的人脸的结构。在这 里,我们利用旷视face++的API来定位人脸关键的集合{V1,V2,…,Vn},其中关键点的数量可 以选择 83 或者 106。
    左:人脸关键点与凸包;右:德劳内三角集。

    2.1.2 计算凸包在获取人脸关键点集合后,我们需要计算这些关键点的凸包(convex hull)(凸包是一个计算几何(图形学)中的概念:在一个实数向量空间 V 中,对于给定集合 X,所有包含 X 的凸集的交集 S 被称为 X 的凸包。X 的凸包可以用 X 内所有点(X1,…Xn)的凸组合来构造.)在这里,我们计算凸包是为了获取这些人脸关键点组成的一个人脸区域。
    2.1.3 德劳内(Delaunay)三角划分在获得凸包以后,我们对凸包内的人脸关键点进行德劳内三角的划分。德劳内三角划分能将我们的凸包区域进行分割,并且更易于保留人脸的细节部分,并且因为获取仿射变换需要原图片和目标图片的各三个点,正好对应于原图和目标图的对应的德劳内三角。
    2.1.4 进行仿射变换在获得原图片和目标图图片的德劳内三角以后,我们需要寻找两张图对应的三角形对,对这样的每一对三角形,我们可以计算得到一个仿射函数。这个仿射函数将被用于三角形对之间的仿射变换。重复这个操作,直到所有区域都操作完毕,我们得到了人脸的位置变换。
    2.1.5 无缝融合在上述人脸仿射变换后,我们得到人脸结构和位置的变换,但我们没有对人脸区域亮度进行调整,这样会造成人脸区域和其他区域的颜色协调的问题。所以最后我们用 opencv 的无缝融合函数 seamlessClone()来实现无缝融合操作。
    2.2 人脸融合2.2.1 人脸关键点检测和 2.1.1 部分一样,我们首先需要确定给定图片的人脸的结构,所以需要检测人脸的关键点。在这里我们仍然使用旷视 face++的人脸关键点识别 API,选择 108 点检测。
    2.2.2 定义融合度要实现两张人脸的融合,我们还要定义融合度 α(0=<α<=1):

    M 是融合后的图像,I,J 是两张待融合图像。我们定义 α,使 α 趋于 0 使,越来越接近图 像 I;α 趋于 1 时,越来越接近图像 J;α 等于 0.5 时,相当于两张图像的平均。
    2.2.3 采样点加权在获得两张人脸的关键点后,我们再对两张人脸进行加权平均:

    通过这个式子,我们计算加权后的人脸关键点的位置。
    左:采样点加权后的人脸结构;右:德劳内三角划分。

    2.2.4 德劳内三角划分如同 1.1.3,我们对获取的人脸关键点进行德劳内三角划分,不同的是,我们为了获取人脸框架结构在整张图中的位置,需要手动添加图片四个角以及四边中心点作为辅助点,再进行德劳内三角划分。这样,我们就得到非常详细的人脸结构。
    2.2.5 图像融合在经过以上步骤后,我们进行人脸融合。首先,我们对源图像的人脸关键点对我们在 2.2.3 中得到的加权后的人脸关键点进行仿射变换。由对应的德劳内三角形确定的三个点,我们可以确定一个仿射变换;对所有的三角形进行这样的操作,我们就得到了仿射后的人脸图片。对两张源图进行这样的操作,我们就得到两个仿射后的人脸图片,再运用我们定义的融合度 α 进行加权平均,最终得到我们的目标图片。

    2.3 本征脸2.3.1 数据预处理此次我们用于测试的数据集有两个,一个是 NBA 现役球员,另一个是女外女明星。对于我们的人脸图片数据集,我们首先要做的是将它们的大小固定为统一的大小。然后,我们需要将每张人脸配准定位,一个方法是将每张图片的人的眼睛定位于同一水平线上,并且使每张图片的两眼中心点在相同的位置。
    2.3.2 主成分分析在预处理图片后,我们对图片的数据进行主成分分析。首先,对于每张 NxN 大小的图片,我们将其转化成一个(NxNx3)x 1 的向量。当有 M 张图片时,我们便有一个(NxNx3)x M 的矩阵。
    在计算主成分之前,我们先来对我们的图片矩阵数据求平均值向量,这是为了后面的方差和协方差准备的。在获取了平均值向量之后,我们来计算主成分。在数数学上,我们通过线性代数中的矩阵特征值分解,可以找到矩阵的特征值和相应的特征向量,我们将这些特征值按从大到小排列,越大的特征值说明数据在这个特征值对应的特征向量的方向上 的方差越大,这就是我们要找的主成分。在这里,我们可以设置参数,来选择主成分的数量。例如,我们选择前十大的特征值和对应的特征向量。
    2.3.3 获得本征脸在获得主成分之后,我们将选取的主成分矩阵,按原来图片矩阵转化为向量的逆操 作,将矩阵变成图片矩阵。
    3.代码结构3.1 人脸互换相关文件:faceswap.py

    get_points 函数。用于封装好 Face++的 API,得到图片的 83 个人脸特征点,并存储在一个列表当中,方便后续处理
    AffineTransform 函数。该函数输入源图、源图的德劳内三角、目标图的德劳内三角和给定的大小。通过计算获取仿射变换函数,并且进行仿射变换,输出变换后的图片
    DelaunayTriangles函数。用于计算我们人脸关键点的德劳内三角。我们应用openCV的Subdiv2D 函数,可以得到我们的德劳内三角
    warpTriangle 函数。通过德劳内三角的划分,我们定义这个函数,用来实现对德劳内三角的仿射变换,并且最终将左右变换后的三角形区域合并,得到我们的目标脸
    最后是主函数,调用这些定义好的函数,读取我们的图片进行处理

    3.2 人脸融合有关文件:morph.py

    detect 函数。用于封装好 Face++的 API,得到图片的 108 个人脸特征点,并存储在一个字典当中,方便后续处理
    addBorderPoints 函数。该函数功能是给处理图片准备要划分三角形的时候,把图片四个顶点和四条边的中点的坐标添加到带划分的点中,这让得劳内三角形能完整的划分 整张图片
    delaunaryTriangles 函数。由特征点的坐标生成其用来划分这些特征点的得劳内三角形
    affineTransform 函数。根据划分好的三角形,我们可以用来计算一张图的三角形到另外一张图所对应的三角形的仿射变换,该函数在 morphingTriangle 函数中使用
    morphingTriangle 函数。对两个仿射后的人脸图片,运用我们定义的融合度 α 进行加权平均,最终得到我们的目标图片


    morphing 函数。总的融合函数的接口,得到融合图片的脸部特征点的坐标,与新图片的脸部划分三角形坐标,调用 morphingTriangle 函数得到新的融合图片
    getPic 函数。用于实现 GUI 中根据 alpha 生成一张图片的功能
    get20Pics,create,sortKey,createGIF 函数。用于实现 GUI 生成一张 gif 图片的功能

    3.3 本征脸人脸相关文件:eigenface_origin.py

    readImages 函数。从指定的文件夹中读取图片文件,将其转化成 numpy 矩阵,放到一个 list 中。在此过程中,每张图片翻转后又保存了一次
    createdatamatrix 函数。将保存图片的 numpy 矩阵压平。(每张图片压平)
    createnewface 函数。产生特征脸并且用 opencv 可视化界面展示
    resetslidervalues 函数。在 opencv 可视化界面中,点击一下图片,就重置所有 bar
    总体思路:从文件夹中读取图片,变成矩阵,每张图片 flatten,用求平均向量,特征向量,再计算出 eigenface。

    3.4 GUI 部分有关文件:

    mainPage.py 主界面 GUI
    morphGUI.py 融合脸功能界面 GUI
    swapGUI.py 换脸功能界面 GUI
    eigenGUI.py 特征脸界面 GUI

    4.开发环境在本次项目中,算法几接口 API 都是由 pyhton3.5(python3.6)完成的,其中还使用了openCV 库。GUI 使用 pyqt 完成。API 使用 face++人脸关键点接口。
    5.可执行文件及使用的数据集5.1 数据集我们有 2 个数据集,其中一个 image_swap 是用于测试换脸操作的;另外一个 image_eigen 数据集是用来测试本征脸的,是 NBA 现役球员的图片。人脸融合测试图片,一定要是放在与该py文件夹中相同的文件夹,所以没有专门的数据集,可以从其他地方复制一些图片,进行测试。
    5.2 可执行文件及运行方式在 windows 下,我们有可执行文件 face#.exe。执行方式:双击打开,会看到我们 GUI 的初始界面。在界面中我们会看到三个按钮,这是我们 GUI 实现的三个功能,及人脸互换、人脸融合和本征脸。点击其中一个按钮,会进入下一层界面,具体的操作可以参考我们附加提交的操作演示视频。




    在 macOS 系统下,可以通过 python 文件名.py 命令运行我们的 python 文件。
    6.项目成果与反思6.1 项目成果6.1.1 人脸互换
    从测试结果图中,我们可以看到,我们的人脸互换效果还是非常好的:从前景图上截取的脸部,能够非常好地与背景图融合;在边缘部分,没有明显的边界;在人脸的姿势上,也根据背景的姿势得到了调整,能够与背景姿势相一致。当然我们也可以看到,在第二个结果中,由于前景脸和背景脸的脸部颜色相差太大,导致结果图中的脸部与额头等地方有明显的不一致,这是因为我们在做人脸互换的时候,只根据人脸关键点确定的凸包进行变换,而人脸关键点是没有包含额头及脖子部分的。这是我们人脸互换做的不好的地方
    6.1.2 人脸融合
    同样,对于我们人脸融合的结果,也非常好。在它融合度为 0.5 的情况下,包含了两张源图中一样多的特征,让我们能在结果图中能找到明显的属于两张源图的特征。但是同样存在一些问题,比如在上面一张结果中,我们可以看到头发部分有重影。这是因为两张源图片的头发差异部分存在白色的背景,当进行融合时,由于有两部分的特征,所以会造成重影;但是第二张结果图中,我们没有看到这个现象,这是因为两张源图的差异部分没有很白的背景颜色。
    6.1.3 本征脸
    对于我们的本征脸,我们在此解释一下左侧拖条的含义:首先我们这部分是对人脸数据集进行主成分分析,所谓的主成分就是数据中方差最大的一些方向,对应到人脸数据中,便是影响数据集中人脸的形态的最大的方向,或者说数据集中人脸变化最大的一些特征。比如,NBA 球员数据集中,球员的肤色有黑有白,这是变化非常大的特征,所以应该会有一个主成分只指向球员肤色的黑白的。而之所以右侧的结果会很模糊,正是因为我们计算出的主成分代表了人脸数据集中变化最大的特征,所以这些特征组成的图片必定是非常模糊非常不整齐的。
    6.2 项目思考与改进6.1.1 人脸互换我们的人脸互换算法依赖于人脸关键点锁定的区域,基本上限定于眉毛-两侧脸颊-下巴组成的区域,当两张源图的脸部颜色差别很大时,会出现结果图中脸部与额头差别很大的问题。所以,我们设想后续的优化是,将脸部的区域扩大到额头,乃至整个脖子,但是由于一般人脸检测的算法只限于原先的区域,如何检测这些扩展的区域,是一个较难解决的问题。
    6.1.1 人脸融合对于人脸融合中出现的结果图中脸部区域以外的重影问题,我们设想的优化是,将脸部以外的区域,全部设置成其中一张源图的背景区域。
    6.2.3 本征脸对于主成分分析得出来的若干个对人脸变化影响最大的特征,其中一些我们能够直观地看出来,例如肤色的黑白等,但有一些特征并不能很容易地得知与什么相关,需要我们进一步测试、观察与分析。
    1 评论 1 下载 2019-05-24 10:28:03 下载需要11点积分
  • 基于汇编语言实现的动画心程序

    一、软件背景介绍本程序是设计一彩色动画贺卡的程序,一个颜色方框从四周向中间由大变小,并留下痕迹,中心位置出现一颗红“心”。 其功能为:

    按任意键进入图形显示
    按空格键重复出现“心”
    按回车键将重复整个图形
    按q键退出程序

    由程序要实现的显示效果可见,程序中要引入多种图案,具体由方框、对角线、任意水平垂直线、“心”等来形成整个图案。所以要在程序中出现多次各种图案来填充整个图形。
    要显示出方框由大变小的效果程序采用绘制方框,再清除此方框,再显示更小一个方框的方法来实现。而方框本身和红心的绘制可采用字符拼凑和直接用图形模式制作。按q键退出的功能可以采用在动画执行完成后调用键盘输入中断,再判断输入的是否为q键,如果是则退出,不是则继续等待键盘输入。
    程序的运行过程:
    如图1所示,开始画面:

    如图2所示,按任意键,进入图形显示;按回车键,重新显示这个图形;绘制各个方格,进行下方颜色填充;按q键,则退出。

    本程序主要是利用BIOS功能调用的10H中断类型实现的。利用其12号功能,设置像素点的颜色、坐标和个数,从而画出图形需要的线段,如画方框、画斜线、画水平垂直线,以及采用行、列坐标连续写像素的方法填充图形等。画完图形之后延时,清屏。同时,利用dos功能调用的08号功能,从键盘输入字符。如果是任意键,则进入图形显示;如果是回车键,则重新显示这个图形;如果是空格键,则改变“心”的大小;如果是“q”键,则退出。
    该系统只是简单的运用字符进行设计组成相应的图像,运用masm编程工具、link连接程序在debug环境下进行调试而成。在系统中界面中,首先是按下任意键进入动画心界面。然后会按Q键退出的提示信息,其中包含2个隐藏信息:按空格键刷新心型图案,来实现动态心;按其余键是重复动画心的显示。
    其中主要运用的指令有:push(压入堆栈);Pop(从堆栈中释放首地址的内容);sub(减法指令);add(加法指令);mov(传送指令);call(过程调用指令);lea(地址传送指令);inc(加一指令);cmp(比较指令);jmp(跳转指令);jnz(条件跳转指令)。
    程序为一彩色动画的程序,一个颜色方框从四周向中间由大变小,并留下痕迹,中心位置出现一颗红“心”。
    本程序运行后的功能有:

    程序运行时显示提示信息,并提示按任意键进入动画心界面
    让一个方框向中间由大变小,并留下痕迹,然后出现一个心
    按空格键开始刷新心形图案
    按其他键首先清除图像,然后重复动画
    按Q键退出程序。

    二、核心算法思想本程序主要的模块有三个:第一是图形显示模块,构建基础的图形界面;第二是主程序,核心算法包括画方框子程序、画对角线子程序、画任意水平垂直线子程序、由大到小画方框子程序,填充子程序、画心子程序等;第三则是时间与界面控制程序,包括延迟子程序,清屏子程序。
    此处着重论述部分算法:
    画方框
    进入显示动画心的界面时,首先出现的是方框。采用了mov,sub,inc,cmp,jnz等指令进行了定义,来显示出方框的逐步显示。先是设置cx,dx,bx各个寄存器不同的初值,即设置像素点的颜色、坐标和个数,接着由大到小画方框,每画一个方框之前,各个寄存器都要设置不同的初值。
    画对角线
    和画方框差不多,每画一条对角线之前各个寄存器也要设置不同的初值。
    画任意水平垂直线
    画十字线之前,各个寄存器如cx,bx,dx也需要设定不同的初值。
    主程序设计
    先设置图形显示方式为320*200彩色图形方式、背景色为蓝色和设置彩色组,再利用其12号功能,设置像素点的颜色、坐标和个数,从而画出图形需要的线段,如画方框、画斜线、画水平垂直线,以及采用行、列坐标连续写像素的方法填充图形等。画完图形之后延时,清屏。同时,利用dos功能调用的08号功能,从键盘输入字符。如果是任意键,则进入图形显示;如果是回车键,则重新显示这个图形;如果是空格键,则改变“心”的大小;如果是“q”键,则退出。
    延时子程序
    先push cx,dx,即先让cx,dx进栈,接着设置dx寄存器的初值为25,利用其12号功能设置cx的初值为2801,利用其13号功能使之循环。在进行循环次数计数(即dx→dx-1)后,判断循环是否结束:如(dx)≠0,继续循环,转移到目标地址d12所在的指令,否则顺序执行。然后dx,cx顺序出栈。最后返回。
    清屏子程序
    先设置功能号al及寄存器bx,cx,dx的初值,接着进入line循环程序。在line循环程序中,先清除缓冲区并请求指定的输入功能,接着利用BIOS功能调用的10H中断类型实现显示器输出控制。在进行循环次数计数(即cx→cx+1)后,判断循环是否结束:如(cx)≠320,则继续循环,转移到目标地址line所在的指令,否则再设置寄存器cx的初值,在进行循环次数计数(dx←dx+1)后,判断循环是否结束:如(dx)≠200,则继续循环,转移到目标地址line所在的指令,否则顺序执行。最后返回。
    三、核心算法流程图先设置图形显示方式为320*200彩色图形方式,背景为蓝色和设置彩色数组,在利用其12号功能,设置像素点的颜色,坐标和个数,从而画出图形需要的线段,如画方框,画斜线,画水平垂直线,以及采用行,列坐标连续写像素的方法填充图形等。画完图形之后延时,清屏。同时,利用dos功能调用08号功能,从键盘输入字符,如果是任意键,则进入图形显示;如果是回车键,则重新显示这个图形;如果是空格键,则改变心大小;如果是q键,则退出。以下是程序的核心算法流程图。
    主程序流程图

    程序流程图

    四、开发中遇到的问题在编写主程序时遇到了一些问题,我们在运行时发现了错误,后来发现在调用dos的08号功能时应该在AL中存放输入字符的ASCII码,而我们直接输入的字符。在画方框、对角线、任意水平线时先是设置cx,bx,dx各个寄存器赋予不同的初值,但我们在开始的时候没有把初值设置好,结果造成了程序的出错,后来经过调整才保证了程序的正常运行。
    然后最为重要的问题是,我们的函数执行顺序遇到了问题。期初,我们想的是先打印三个方框的同时进行画斜线的子程序,但这样的问题在于他们之间的执行顺序会在退出主程序造成麻烦,平添了许多代码,同时造成了资源浪费的问题。之后我们尝试着以更简单的思路线性执行子函数,并且构建了延迟子函数,以保证打印出效果很好,画面看起来也更加完善一些。
    延时子程序应该先push cx,dx,即先让cx,dx进栈,接着设置dx寄存器的初值为25,利用其12号功能设置cx的初值为2801,利用其13号功能使之循环。在进行循环次数计数(即dx→dx-1)后,判断循环是否结束:如(dx)≠0,继续循环,转移到目标地址d12所在的指令,否则顺序执行。然后dx,cx顺序出栈。最后返回。
    但我们在在编写延时子程序时,由于我没有压栈dx和cx,没有把外循环和内循环设置好,在运行程序时出现了错误。后来我对dx和cx进行压栈,把循环次数和循环语句设置好了,才把延时子程序编写好。
    进行清屏子程序时先设置功能号al及寄存器bx,cx,dx的初值,接着进入line循环程序。在line循环程序中,先清除缓冲区并请求指定的输入功能,接着利用BIOS功能调用的10H中断类型实现显示器输出控制。在进行循环次数计数(即cx→cx+1)后,判断循环是否结束:如(cx)≠320,则继续循环,转移到目标地址line所在的指令,否则再设置寄存器cx的初值,在进行循环次(dx←dx+1)后,判断循环是否结束:如(dx)≠200,则继续循环,转移到目标地址line所在的指令,否则顺序执行。最后返回。
    而在编写清屏子程序时遇到的最大困难就是编写line循环程序了,我们开始没有把该循环程序的循环次数设置正确,后来经过一点点的改正才编写正确,(cx)≠320继续循环,转移到目标地址line所在的指令,否则再设置寄存器cx的初值,在进行循环次数计数(dx←dx+1)后,判断循环是否结束。
    最后进行调试的时候,代码的错误比我们想像的要多很多,而且犯了很多不该犯的错误,比如打错操作符等低级问题,这给我们敲响了警钟,做事确实要更认真才行。但有些BUG确实是难以寻找,绘制出来的图画完全不对,其中就有一段是该将cx寄存器的值给bx,但由于失误而写反了,这个问题我们找了很久才找到,确实让我们印象深刻。
    找到问题,修改问题,有时候确实要比写代码都复杂,细心真的很重要。最后的调试阶段中,很多错误我们都是闻所未闻的,百度了很多相关知识才能去修复一个错误,比如远程调用和近程调用就要加以区分。
    五、心得体会这次实训通过老师给的一些建议,经过自己的改写,实现要求。先做简单的输出,一步步的再做其它图案,在实际操作过程中犯的一些错误还会有意外的收获,感觉实训很有意思。在具体操作中对这学期所学的汇编语言的理论知识得到巩固,达到实训的基本目的,也发现自己的不足之处,在以后的上机中应更加注意,同时体会到在整个程序的编写中,调试占了很大部分的时间。
    通过上机我发现上机实训更为重要,特别是要想写好汇编语言的程序,必须认真对待代码的每一个细节,还必须熟悉的掌握debug命令,这对程序的调试是非常重要的。
    通过实际操作,学会了汇编语言程序编程的基本步骤、基本方法,开发了自己的逻辑思维能力,培养了分析问题、解决问题的能力。深刻体会到“没有做不到的,只有想不到的”,“团结就是力量”,“实践是检验真理的标准”,“不耻下问”等的寓意。
    0 评论 2 下载 2019-05-23 18:52:16 下载需要3点积分
  • 基于PHP的网上商城

    第一章 需求分析1.1 引言伴随着Internet的蓬勃发展,网络购物中心作为电子商务的一种形式正以其高 效、低成本的优势,逐步成为新兴的经营模式和理念,人们已不再满足于信息浏览 和发布,而是渴望着能够充分享受网络所带来的更多的便利。的确,客户足不出户 便可以方便快捷的选购自己喜欢的商品,这正是网络购物中心为客户带来的好处。 网络商城将传统的商务流程电子化、数字化,一方面以电子流代替了实物流,可以大量减少人力、物力,降低了成本;另一方面突破了时间和空间的限制,使得 交易活动可以在任何时间、任何地点进行,从而大大提高了效率网络商城所具有的 开放性和全球性的特点,为企业创造了更多的贸易机会。网络商城使企业可以以相 近的成本进入全球电子化市场, 使得中小企业有可能拥有和大企业一样的信息资源, 提高了中小企业的竞争能力。网络商城重新定义了传统的流通模式,减少了中间环节,使得生产者和消费者的直接交易成为可能,从而在一定程度上改变了整个社会 经济运行的方式。网络商城一方面破除了时空的壁垒,另一方面又提供了丰富的信 息资源,为各种社会经济要素的重新组合提供了更多的可能,这将影响到社会的经 济布局和结构。 现在的购物商场成蓬勃向上发展的。
    1.2 需求分析一个网络购物系统,首先我们要保证客户能够很方便进行商品选择,系统应该具有分类选择商品功能,系统要实现购买功能。在系统的后台,管理员能够管理商品,商品分类,以及客户购买订单。 因此分析,本系统主要由前台和后台两部分组成,前台为客户端,顾客可以在此处购买商品,后台为商品管理端,实现对商品和订单的管理。
    第二章 系统分析2.1 开发环境根据用户的需求和实际的考察与分析,确定商城的开发环境,具体如下:

    服务器:从稳定性、广泛性及安全性方面综合考虑,采用市场主流的Web服务器软件Apache服务器
    数据库:采用最受欢迎的开源SQL数据库管理系统和被誉为PHP黄金搭档的MySQL
    开发框架:选用具有快速、兼容、开源、简单易学等特点的轻量级国产PHP开发框架—ThinkPHP

    2.4 系统运行环境该网上商城可运行在分辨率为1920×1080的chrome浏览器下。
    2.3 功能结构商城分为前台模块和后台模块。下面分别给出前、后台的功能结构图。


    2.4 目录结构目录结构即为think php 目录结构,再次不做介绍。
    第三章 数据库设计3.1 商品分类表(itcast_category)


    字段名
    数据类型
    描述




    cid
    Int unsigned
    主键ID,自动增长


    cname
    varchar(20)
    商品分类名称


    pcname
    varchar(20)
    父类分类名称



    3.2 商品表(itcast_goods)


    字段名
    数据类型
    描述




    gid
    varchar(255)
    主键ID,自动增长


    gname
    varchar(255)
    商品名称


    price
    int
    商品价格


    thumb
    varchar(255)
    商品图片路径


    status
    Enum(‘no’,’yes’)
    是否上下架,上架为yes,否则为no


    description
    text
    商品描述


    stock
    int
    商品库存


    cid
    Int unsigned
    商品分类ID


    sales
    int
    商品销量


    turn
    Int unique
    排序码,自动增长



    3.3 会员信息表(itcast_member)


    字段名
    数据类型
    描述




    mid
    Int unsigned
    主键ID,自动增长


    user
    varchar(20)
    会员昵称


    email
    varchar(30)
    会员电子邮件地址


    pwd
    char(32)
    会员登陆密码


    birthday
    date
    会员生日



    3.4 会员收货地址表(itcast_address)


    字段名
    数据类型
    描述




    aid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    consignee
    varchar(20)
    收货人姓名


    phone
    varchar(11)
    电话号码


    postcode
    varchar(6)
    邮政编码


    address
    varchar(255)
    收货地址


    freight
    int
    运费



    3.5 购物车表(itcast_shopcart)


    字段名
    数据类型
    描述




    scid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    addTime
    timestamp
    加入购物车时间


    gid
    varchar(255)
    商品ID


    num
    tinyint(3)
    商品数量



    3.6 购买记录表(itcast_record)


    字段名
    数据类型
    描述




    rid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    time
    timestamp
    购买时间


    gid
    varchar(255)
    商品ID


    num
    tinyint(3)
    商品数量


    price
    Int unsigned
    商品单价



    3.7 折扣商品表(itcast_discountgoods)


    字段名
    数据类型
    描述




    dgid
    Int unsigned
    主键ID,自动增长


    gid
    varchar(255)
    商品ID


    discount
    int
    折扣



    第四章 具体功能实现4.1 前台具体功能4.1.1 公共部分

    显示登录后的用户昵称、退出登录
    分类搜索商品
    进入各个功能模块
    登录按钮
    登录后查看购买记录、进入购物车



    购物车简要信息:总价(折扣前)、数量
    商品分类,点击即可查看该分类下的商品



    显示当前销量最高的商品


    网站相关信息
    联系方式

    4.1.2 主页
    滚动广告


    推荐商品


    新品推荐

    4.1.3 商品列表页
    商品列表页
    例如,在任一页面的导航栏的搜索框中输入关键词——手机,选择“手机”分类,点击“搜索”按钮。

    之后就可进入商品列表页,该页面显示了所有在指定分类下与关键词相关的商品。

    此时可点击排序下拉菜单,对商品进行排序(默认按照新品排序),例如按照价格升序排列

    类似还可进行:“新品”、“价格降序”、“销量”方式排序。

    折扣商品列表页
    若点击导航栏的“特价优惠”链接,即进入折扣商品列表页

    4.1.4 商品详情页
    查看商品信息
    在任何页面的商品列表点击指定商品,即可查看该商品的详细信息。

    此时,点击“相关商品”按钮,可查看该与该商品相关的其他商品。
    有两种查看方式:



    加入购物车
    会员登录后,在详情页选择购买数量,点击“加入购物车”按钮即可将选择的商品加入购物车。
    4.1.5 会员注册、登录在任一页面的导航栏点击“登录”按钮,即可进入下图所示页面:

    左部为注册框,右部为登录框。

    注册
    在注册框中输入要创建账户的邮箱地址,点击“创建”按钮

    进入下图所示页面填写会员详细信息。

    点击“注册”按钮,即完成了会员注册操作。

    登录
    在登录页面填写正确的邮箱、密码和验证码,点击“登录”按钮即可成功登录。

    若验证码填写错误,页面将显示如下提示信息,并返回登录页面。
    若用户名或密码填写错误,页面将显示如下提示信息,并返回登录页面。
    4.1.6 购物车
    简介
    登录成功后即进入购物车页面(新用户购物车为空),用户可在该页面对加入购物车的商品进行购买。

    假设会员已将自己心仪的商品加入到购物车,购物车页面将自动计算总价。


    更改购物车商品
    此时,会员可通过点击每个商品的“+”、“-”按钮对该商品数量进行增加和减少操作。

    若会员不想购买某件商品,可点击“×”按钮从购物车中删除该商品。


    购买商品
    点击“购买”按钮即将购物车中的商品全部购买。

    4.1.7 购买记录点击上方导航栏的“购买记录”按钮或点击下方页脚的“购买记录”超链接

    即可进入购买记录页查看购买记录。

    4.2 后台具体功能4.2.1 登录页面在登录页面填写正确的用户名、密码和验证码,点击“登录”按钮即可成功登录。

    若验证码填写错误,页面将显示如下提示信息,并返回登录页面。
    若用户名或密码填写错误,页面将显示如下提示信息,并返回登录页面。
    4.2.2 公共部分
    显示标题
    显示管理员名称
    “前台首页”超链接
    “退出登录”按钮



    左部导航栏,点击链接可进入相对应模块
    4.2.3 首页欢迎页面,引导管理员进行操作。

    4.2.4 商品添加在左侧导航栏点击“商品添加”链接进入商品添加页面。
    依次按要求填写商品信息,上传图片。

    点击“确定”按钮,即完成商品添加操作。
    若商品编号重复,则不添加该商品,直接进入商品修改页修改该商品。

    4.2.5 商品查看、修改、删除
    查看
    在左侧导航栏点击“商品列表”链接进入商品列表页面。

    管理员可对指定分类下的商品进行排序(默认对所有商品按照新品排序),例如对“手机—手机”分类按照价格升序排序:


    修改
    点击“修改”可以对指定商品属性进行修改(商品编号不可修改)。


    删除
    点击“删除”

    点击“确定”

    可以看到商品列表中编号为“562390304003”的商品已经被删除。

    4.2.6 查看、添加、删除商品分类
    查看商品分类
    在左侧导航栏点击“商品分类”链接进入商品分类列表页面


    添加商品分类
    点击商品分类列表页的“添加分类”按钮,即可进入商品分类添加页面。例如,选择一级分类为“电脑/办公”,分类名称为“服务器”


    删除商品分类
    点击商品分类列表页每一个商品分类对应的的“删除”
    可以看到商品分类列表页中名为“服务器”的商品分类已经被删除。

    查看会员信息
    在左侧导航栏点击“会员管理”链接进入会员信息列表页面

    点击每个会员的“查看详情”操作,可以查看该会员的详细信息和购买记录

    第五章 总结与心得体会通过这次网上商城开发,让我清楚认识到软件工程的重要程度,软件项目涉及到以下阶段,即计划阶段、需求分析、软件设计、编码、测试阶段、运行维护等。经过一段时间的努力,我们终于完成了网上商城网站系统,基本实现了题目的基本要求。总的来说,在做这个毕业设计的过程中,我们查阅了大量关于网上销售的相关资料,切实地按照软件工程的步骤,从需求分析,概要设计,详细设计,数据库设计,再到编码,调试运行,测试等步骤。从中我学到了很多东西,对我们来说,无论是理论还是实践上都是一个较大幅度的提高,可以说是理论到实践的一个飞跃。我还了解了软件开发的大体过程,在当今竞争激烈的社会中只有学到本领才能有立足之地,通过这次综合实验也使我们知道做软件开发的辛苦,首先要有足够的耐心,要勇于面对密密麻麻的代码,无数遍的调试,和无数遍的修改,但是,当调试成功时,你就会感到这些努力的意义,成功的喜悦。软件开发,还要注意借鉴,查看已有的例子的代码,这样可以节省大量的时间,同时也实现了代码重用。此外,我知道了基础课的重要,要学好一门编程语言,一定要动手,实践是最好的方法!
    1 评论 2 下载 2019-05-23 17:50:58 下载需要13点积分
  • 基于QT的网络五子棋游戏程序的设计与实现

    摘 要在现在如此发达并且得到广泛应用的现代网络技术下,集趣味性,娱乐性,益智性,并且包含网络功能的休闲类游戏以及成为了人们选择网络休闲游戏的要求。
    系统采用当今广为流行的五子棋游戏为模版,利用C++的第三方GUI设计工具Qt为程序设计界面,并结合软件工程的思想开发一款基于网络的五子棋游戏对弈软件。本软件采用P2P的模式,利用一个服务端来辅助各个客户端之间的查找和链接,服务端用于记录当前各个客户端的游戏状态,并将实时更新的各个客户端的状态发送到其他客户端,方便客户端加入其他客户端建立的游戏主机,并建立P2P链接,进行游戏。客户端是玩家用于建立游戏主机或者加入游戏进行五子棋对弈的主程序,具有当今五子棋游戏常见的聊天功能、悔棋功能、认输功能、计分功能、玩家断网处理等。
    玩家只需运行客户端,连接到指定的服务端,就可以加入其他的游戏主机进行游戏,或者建立游戏主机,等待其他客户端的加入进行游戏对弈。
    关键字: 多线程,Qt,socket
    ABSTRACTWith the development and widely usage of computernetwork, it has been a requirement for a casual online game to be interesting,entertaining and intelligence-improving.
    This paper implements a online Gobang game based onthe popular Gobang, used third-party C++ GUI library Qt and combined softwareengineering model. The software uses P2P model, which uses a sever end to helpclients find and connect with each other and help record the status of everyclient and send it to other clients in order to make it convenient to join thehost and establish the P2P connection between clients to start the game. Clientis the main program for user to create or join the Gobang game and have thefunctionalities of chatting, regretting, yielding, scoring and connectionproblems handling.
    All user needs to do is run a client, connect to thespecific server, then he can join a host or create a host himself and wait forother clients to join and have a game.
    Key words:multithreading, Qt, socket
    1 绪论1.1 课题研究意义五子棋[1]是起源于中国古代的传统黑白棋种之一。现代五子棋日文称之为“连珠”,英文称之为“Renju”、“Gobang”或“FIR”(Five in a Row 的缩写),亦有“ 连五子”、“五子连”、“串珠” 、“五目”、“五目碰”、“五格”等多种称谓。
    五子棋不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。五子棋既有现代休闲的明显特征“短、平、快”,又有古典哲学的高深学问“阴阳易理”;它既有简单易学的特性,为广大人民群众所喜闻乐见,又有深奥的技巧和高水平的国际性比赛;它的棋文化源渊流长,具有东方的神秘和西方的直观;既有“场”的概念,亦有“点”的连接。它是中西文化的交流点,是古今哲理的结晶。
    五子棋起源于古代中国,发展于日本,风靡于欧洲。对于它与围棋的关系有两种说法,一说早于围棋,早在“尧造围棋”之前,民间就已有五子棋游戏;一说源于围棋,是围棋发展的一个分支。在中国的文化里,倍受人们的青睐。古代的五子棋的棋具与围棋相同。五子棋大约随围棋一起在我国南北朝时先后传入朝鲜、日本等地。从此,五子棋经过了不断的改良,例如棋盘由原来的纵横各十七道改为现行的纵横各十五道的五子棋专用棋盘等等。二十世纪初,五子棋传入欧洲并迅速风靡了全欧洲。通过一系列的变化,使五子棋这一简单的游戏系统化、规范化,最终成为今天的职业连珠五子棋,同时也成为一种国际比赛棋。
    在网络发达的今天,对于二人对弈的五子棋,开发一款带有网络功能的五子棋游戏,能够更加方便身在异地的玩家进行游戏。
    1.2 课题研究目的五子棋在我国的历史可谓是源远流长,喜爱它的玩家也是大有人在。但目前的五子棋软件一般都是单机版的,游戏无法在异地的玩家之间进行,因此为了方便异地的玩家在一起进行五子棋游戏,设计出一款基于网络版的五子棋游戏,玩家可以通过网络挑选自己喜爱的对手并同其进行比赛,比赛过程中可以进行聊天,达到相互交流经验的作用,同时能让身处各地的玩家连接到服务器进行五子棋对弈。
    1.3 研究现状分析目前常见的单机类五子棋游戏可分为“人机模式”与“双人模式”。“人机模式”指人与按照事先编写好的五子棋落子算法程序来进行游戏的计算机进行对弈,“双人模式”指两个人通过使用同一台计算机,交替落子来进行游戏。这两种方式,都需要玩家双方在同一台计算机面前进行完成,因此局限性很大,玩家不能再异地的不同的计算机上来进行游戏。
    随着现代计算机网络的迅速发展,网络上的计算机之间的通信已经变得十分便捷。这也为身在异地间的用户利用网络来进行游戏提供了途径。
    网络版的五子棋软件利用网络连接协议TCP/IP,在不同地方的玩家之间建立一个TCP连接,并用这个连接来交换数据,进行通信。因此,网络版的五子棋软件在游戏之前必须确定对网络参数设置的正确性,设置正确之后,才能进行玩家双方的TCP成功连接。连接成功后,双方轮流下棋,同时将每一步落子的信息通过网络连接传送给对方,使双方棋盘上的状态保持一致。
    与单机版的五子棋软件相比,网络版的五子棋软件也提供了所有单机版的五子棋软件所具有的功能,例如:倒计时器、“认输”、“和棋”和“悔棋”等。但对于网络版的五子棋来说,这些功能的同步都是通过网络信息交互来实现的,因此,网络版的五子棋游戏实现过程较单机版要复杂一些。
    在胜负判断方面,单机版的五子棋软件与网络版的五子棋软件可以采用完全相同的算法(如果不考虑先手禁手等)。一方玩家下完一步棋后,系统会自动判断当前落子之后,玩家是否胜利,如果没有胜利,再将落子的信息传送给对方,如果判断结果为胜利,则直接发送胜利消息给对方。每次落子之后,都要进行这样一次胜负判断定的过程。另外,由于网络版的五子棋的游戏双方可能不在同一个地点,因此,提供聊天功能很有必要。
    目前常见的网络五子棋的游戏形式又可分为两种,一种是以QQ五子棋(如图1.1)为代表的桌面应用程序的游戏形式,一种是网页flash(如图1.2)的游戏形式。

    系统结构方面,QQ五子棋游戏采用了C/S结构,而网页flash的五子棋采用的是B/S结构。
    功能方面,二者均提供了常见的悔棋、计分、计时、聊天、认输等功能。均未设计五子棋中“先手禁手”等规则。不同之处在于,常见的网页五子棋对于一方断网的情况不能及时发现,发现后也只是做出提醒另外一方的操作。而QQ五子棋能够对一方断网的情况及时发现,并且采用电脑托管或者直接结束比赛并计算相应分数的操作。
    游戏流畅度方面,网页flash的游戏形式受限于浏览器解析网页代码的速度限制,游戏载入时间明显过长。而QQ五子棋只需要基本的Windows操作系统即可,运行环境要求较低,运行流畅。
    1.4 各章内容简介本文第一章介绍了课题的研究背景,第二章简单介绍了实现程序所用到的技术介绍,第三章对程序需求进行了分析,第四章为系统的设计思想以及详细设计,第五章介绍了系统的实现过程,第六章展示了系统的效果,最后第七章总结了系统已完成的工作和下一步待完成的工作。
    2 实现技术2.1 P2P模式2.1.1 P2P模式介绍P2P[2](peer-to-peer, 简称P2P)又称对等互联网络技术,是一种网络新技术,依赖网络中参与者的计算能力和带宽,而不是把依赖都聚集在较少的几台服务器上。请注意与point-to-point之间的区别,peer-to-peer一般译为端对端或者群对群,指对等网中的节点;point-to-point一般译为点对点,对应于普通网络节点。P2P网络通常用于通过Ad Hoc连接来连接节点。这类网络可以用于多种用途,各种文件共享软件已经得到了广泛的使用。P2P技术也被使用在类似VoIP等实时媒体业务的数据通信中。
    纯点对点网络没有客户端或服务器的概念,只有平等的同级节点,同时对网络上的其它节点充当客户端和服务器。这种网络设计模型不同于客户端-服务器模型,在客户端-服务器模型中通信通常来往于一个中央服务器。
    2.1.2 P2P模式的优势P2P网络的一个重要的目标就是让所有的客户端都能提供资源,包括带宽,存储空间和计算能力。因此,当有节点加入且对系统请求增多,整个系统的容量也增大。这是具有一组固定服务器的Client-Server结构不能实现的,因为在上述这种结构中,客户端的增加意味着所有用户更慢的数据传输。
    P2P网络的分布特性通过在多节点上复制数据,也增加了防故障的健壮性,并且在纯P2P网络中,节点不需要依靠一个中心索引服务器来发现数据。在后一种情况下,系统也不会出现单点崩溃。
    当用P2P来描述Napster 网络时,对等协议被认为是重要的,但是,实际中,Napster 网络取得的成就是对等节点(就像网络的末枝)联合一个中心索引来实现。这可以使它能快速并且高效的定位可用的内容。对等协议只是一种通用的方法来实现这一点。
    2.1.3 P2P模式的应用点对点技术有许多应用。共享包含各种格式音频,视频,数据等的文件是非常普遍的,实时数据(如IP电话通信)也可以使用P2P技术来传送。
    有些网络和通信渠道,像Napster,OpenNAP,和IRC@find,一方面使用了主从式架构结构来处理一些任务(如搜索功能),另一方面又同时使用P2P结构来处理其他任务。而有些网络,如Gnutella和Freenet,使用P2P结构来处理所有的任务,有时被认为是真正的P2P网络。尽管Gnutella也使用了目录服务器来方便节点得到其它节点的网络地址。
    2.2 Qt2.2.1 Qt介绍
    UI(User Interface)也可以称之为用户接口或使用者接口,是系统和用户之间进行交互和信息交换的媒介,它实现信息的内部形式与人类可以接受形式之间的转换。
    Qt[3]是由挪威TrollTech公司于1995年底出品,之后被诺基亚公司收购并维护的一个跨平台的C++图形用户界面应用程序框架。广泛用于开发GUI程序,这种情况下又被称为部件工具箱。也可用于开发非GUI程序,比如控制台工具和服务器。自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围类数千种成功的应用程序的基础,被GoogleEarth、KDE、西门子公司等使用。它是诺基亚(Nokia)的Qt Development Frameworks部门的产品。
    经过多年发展,Qt不但拥有了完善的C++图形库,而且近年来的版本逐渐集成了数据库、OpenGL库、多媒体库、网络、脚本库、XML库、WebKit库等等,其核心库也加入了进程间通信、多线程等模块,极大的丰富了Qt开发大规模复杂跨平台应用程序的能力,真正意义上实现了其研发宗旨“Code Less; Create More; Deploy Anywhere.”。
    2.2.2 Qt模块模块化 QtC++ 类库提供一套丰富的应用程序生成块 (block),包含了构建高级跨平台应用程序所需的全部功能。Qt模块结构图如图2.1:

    其中本系统设计到更多的Qt的消息响应机制和多线程如下:
    1.Qt的消息响应机制:信号与槽:
    Qt利用信号与槽(signals/slots)机制取代传统的callback来进行对象之间的沟通。当操作事件发生的时候,对象会发提交一个信号(signal);而槽(slot)则是一个函数接受特定信号并且运行槽本身设置的动作。信号与槽之间,则通过QObject的静态方法connect来连结。
    信号在任何运行点上皆可发射,甚至可以在槽里再发射另一个信号,信号与槽的连结不限定为一对一的连结,一个信号可以连结到多个槽或多个信号连结到同一个槽,甚至信号也可连接到信号。
    以往的callback缺乏类型安全,在调用处理函数时,无法确定是传递正确型态的参数。但信号和其接受的槽之间传递的数据型态必须要相符合,否则编译器会提出警告。信号和槽可接受任何数量、任何型态的参数,所以信号与槽机制是完全类型安全。
    信号与槽机制也确保了低耦合性,发送信号的类并不知道是哪个槽会接受,也就是说一个信号可以调用所有可用的槽。此机制会确保当在”连接”信号和槽时,槽会接受信号的参数并且正确运行。
    2.多线程
    Qt的运行绪支持是独立于平台的运行绪类,采用信号与槽机制,实现类型安全的运行绪间通讯。这使得它易于开发具可移植性的多线程Qt应用程序。并能充分利用多核架构,获得最佳运行性能,还能根据可用的处理器内核数自动调整使用的运行绪数。多线程程序设计也是一个执行耗时操作而不会冻结用户界面的有效典范。
    2.2.3 Qt的优势1.面向对象
    Qt的良好封装机制使得Qt的模块化程度非常高,可重用性较好,对于用户开发来说是非常方便的。Qt提供了一种称为signals/slots的安全类型来替代callback,这使得各个元件之间的协同工作变得十分简单。
    2.丰富的API
    Qt包括多达250个以上的C++类,还提供基于模板的collections,serialization,file,I/Odevice,directorymanagement,date/time类。甚至还包括正则表达式的处理功能。
    3.优秀的跨平台能力
    使用Qt开发的软件,相同的代码可以在任何支持的平台上编译与运行,而不需要修改源代码。会自动依平台的不同,表现平台特有的图形界面风格。

    Linux/X11:用于X Window System(如Solaris、AIX、HP-UX、Linux、BSD)。支持KDevelop和EclipseIDE集成Mac:用于Apple Mac OS X。基于Cocoa框架。支持Universal Binary。支持以Xcode编辑、编译和测试。Windows:用于Microsoft Windows。支持Visual Studio集成Embedded Linux:用于嵌入式Linux。可以通过编译移除不常使用的组件与功能。通过自己的视窗系统QWS,不需依赖X Window System,直接写入Linux帧缓冲。可以减少存储器消耗。并提供虚拟帧缓冲QVFb,方便在桌面系统上进行嵌入式测试。Windows CE / Mobile :用于Windows CESymbian:用于Symbian platformMaemo:用于Maemo
    4.大量的开发文档
    对于Qt的各个模块,都有对应的帮助文档和编写好的例子供开发人员参考。
    3 系统需求分析服务端程序运行流程图如图3.1:

    打开服务端后,用户首先设置服务参数,然后等待客户端连接。
    当收到客户端的连接时,将目前已经连接到服务端的所有客户端的状态(正在游戏中,建立了主机并等待玩家加入)发送到刚刚连接进来的客户端。
    如果收到客户端请求:请求建立游戏主机、请求加入别人的游戏主机进行游戏,则服务端更新此客户端的状态,然后将最新的状态发送给各个客户端。
    客户端程序运行流程图如图3.2:

    打开客户端后,用户首先设置服务器参数,然后连接服务端。连接成功后,客户端会收到来自服务端的其他客户端的当前状态,用户可以选择是建立游戏主机,还是加入别人的游戏主机进行游戏。
    1.功能需求
    程序主要核心功能如图3.3:


    服务器开始、退出
    服务端程序能够开启、关闭五子棋游戏的服务。关闭服务时,能够给当前已连接的客户端发送关闭服务消息。
    玩家开始、退出
    玩家进入游戏主机之后,需要点击准备才能开始游戏,若玩家在游戏时退出游戏,需要向对手发送退出游戏的信息。
    聊天功能
    玩家在对弈过程中可以发送信息进行交流。
    悔棋
    玩家在对弈过程中,能够进行悔棋请求,如果对方答应悔棋请求,则系统撤销双方玩家最近一次的落子;若对方不答应悔棋请求,则游戏继续。
    认输
    玩家在对弈过程中,可以直接选择认输,不需要经过对方同意。
    计分
    程序能保存当前两个玩家从开始第一句对弈,到其中一方离开游戏这期间的战绩情况。
    断网处理
    当其中一方玩家意外断网时,程序能通过超时相应判断出对方丢失了网络连接并作出处理。

    2.环境需求

    装有windows或者linux的操作系统的计算机两台计算机的CPU不低于P4,内存不小于64M10/10Mbps自适应网卡每台计算机各一块每台计算机都应配置有TCP/IP协议
    3.用户界面需求

    界面友好、美观界面简洁操作界面方便、易懂
    4.异常处理需求

    处理端口号冲突时的异常当出现错误落子位置时,此次操作无效如果网络非正常断开,则终止此局游戏
    4 系统设计4.1系统设计思想本系统的能够实现一个在网络上供玩家进行对战的网络版的五子棋游戏,玩家只要登陆到服务器上,然后选择其它已创建好的游戏主机,进行五子棋对战游戏。或者自己建立一个游戏主机,等待对方的加入。
    在C/S 模式游戏中,服务端一般提供所有用户的全局信息,并能提供客户之间的信息转发,客户之间的通讯必须通过服务端进行。因为在多个客户能够连接到同一台服务端上,所以服务端必须用线程负责每个用户的通讯和消息处理。
    但是考虑到如果采用这种思路,当客户端达到一定的数量之后,会增加服务端的负荷,而且当两个玩家开始游戏之后,服务端只需知道玩家双方是否退出游戏以及双方的游戏结果即可,而玩家之间的对弈信息、聊天等信息大可不必经过服务端。
    基于以上考虑的原因,决定采用P2P的网络模式:服务端程序开启服务后,通过一个线程监听客户端的连接,一旦有客户端连接,服务端便为该客户端建立连接并启动一个特定的线程,利用该连接不断获取客户端操作请求,从而更新游戏大厅信息,让其他玩家及时了解到哪些客户端建立了游戏主机,哪些客户端之间正在进行游戏对弈。而当客户端加入了另外一个客户端建立的游戏主机时,二者便建立起P2P连接,用于传送二者之间的对弈信息,操作请求(悔棋,认输等),聊天信息等。设计思想的结构拓扑图如图4.1:

    4.2 系统总体设计系统使用Socket技术与Qt多线程机制结合,使用C/S模式与P2P模式结合,在进行客户与服务端以及各个客户端之间进行信息的交互。
    其中,对客户端进行的各个操作请求产生的数据包发送的对象如表4.1:



    客户端操作类型
    数据包发送对象




    用户登录请求
    服务器


    用户建立主机请求
    服务器


    用户加入游戏请求
    服务器


    网络丢失确认
    服务器


    对方网络丢失信息通知
    服务器


    发送认输通知
    客户端


    发送落子信息
    客户端


    发送聊天内容
    客户端


    发送悔棋请求
    客户端



    用户程序之间进行交互的协议如图4.2:

    其中,“数据类型”字段如表4.2:



    消息类型
    说明




    COMM_SERVER_CONN_FAILED
    连接服务器失败


    COMM_SERVER_CONN_SUCCESSFUL
    连接服务器成功


    COMM_SERVER_CLOSE
    服务器关闭


    COMM_SERVER_GAMEINFO
    服务器发送大厅信息


    COMM_CLIENT_QUITGAME
    客户端退出游戏


    COMM_CLIENT_DISCONN
    客户端请求断开服务器


    COMM_CLIENT_CONN
    客户端请求连接服务器


    COMM_CLIENT_CREATE
    客户端请求创建主机


    COMM_CLIENT_JOIN
    客户端请求加入游戏


    COMM_CLIENT_GAMESTART
    双方准备完毕,游戏开始


    COMM_CLIENT_GAMEOVER
    某方玩家胜利,游戏结束


    COMM_CLIENT_GAMEOP
    玩家游戏操作:落子


    COMM_CLIENT_CHAT
    玩家游戏操作:发送聊天信息


    COMM_CLIENT_UNDO
    玩家游戏操作:悔棋


    COMM_CLIENT_UNDO_YES
    玩家游戏操作:悔棋回复yes


    COMM_CLIENT_UNDO_NO
    玩家游戏操作:悔棋回复no


    COMM_CLIENT_LOSE
    玩家游戏操作:认输


    COMM_CLIENT_LOSTCONN
    玩家发送消息:对手超时,判定为掉线



    请求方需要发出请求时,便把消息类型设置为如上对应的消息类型(例如:服务器连接请求),并设置请求相关的数据(例如:需要连接的服务器的IP以及端口),最后发送此数据包。
    数据字段根据第一个字段的不同设置不同的数据,有可能为包含五子棋落子位置的信息;有可能为空数据(例如:连接服务器成功则数据字段为空,连接服务器失败则数据字段为连接服务器失败的原因)。
    接收方在接受到此数据包之后,首先判断出数据包的类型什么,然后利用接受到的数据(例如:服务器的IP以及端口)以及数据包类型(例如:服务器连接请求)来处理数据(例如:是否接受此客户端的连接),处理完毕之后,设置响应数据包类型为具体的响应类型(例如:连接成功),并且设置响应相关的数据(例如:连接成功则没有数据,连接失败则数据为连接失败的原因),最后发送此数据包。
    例如某个客户端在连接服务器时,详细流程如图4.2:

    客户端想服务端发送连接请求,数据包设置如图中“数据包(1)”,服务端收到连接请求之后,判断出此时已达服务端最大的连接数,然后将数据包设置如图中“数据包(2)”,然后将此数据包发送给刚刚的客户端,通知客户端此次连接因为服务器已经达到了最大连接数,拒绝客户端的连接。
    4.3 系统工作流程4.3.1 服务端工作流程基于以上设计,服务端只负责本地游戏服务的开启,以及各个客户端的连接,建立主机,加入游戏等请求,因此服务端的工作流程如图4.3:

    服务端首先设置服务参数,然后开启本地服务,等待客户端的连接。
    当接受到客户端的连接之后,建立一个新线程,用于接受客户端的操作请求,父线程则继续等待客户端的连接。
    当子线程收到客户端发来的操作请求时,则根据对应的请求类型做相应的处理。
    4.3.2 客户端工作流程基于以上设计,客户在与服务端交互时采用C/S模式,与其他玩家对弈时,采用P2P模式,因此客户端的工作流程如图4.4:

    客户端连接到服务端之后,首先获取到服务端发送来的各个客户端的状态,然后选择是自己建立游戏主机,还是加入别人的游戏。
    游戏中,客户端与客户端之间会建立P2P连接,传递各种游戏操作信息。
    4.4 五子连珠算法设计五子棋游戏中,如果一方胜利,则胜利时的五子连珠必定与玩家最后落下的那颗棋子相关。因此每当用户落下一颗棋子时,程序便判断包括这颗棋子在内的纵向、横向、斜向的五颗棋子有没有五子相连的情况,若有,则此方胜利,反之,继续比赛。
    五子棋程序中,采用一个二维数组保存棋盘的每个位置的当前状况(白色棋子,黑色棋子,没有落子)。当其中一方胜利时,在棋盘记录中,五子相连的方向的当前状态应同为白子或同为黑子。
    以斜向为例,胜利时的可能情况如图4.5所示:(所有圆均表示同一个玩家落下的棋子,填充色的圆表示最后落下的子):

















    每次落下一颗棋子时,程序判断斜向这5种情况以及其他几个方向类似情况是否为同色,若是,则胜利,反之,继续比赛。
    程序伪代码:
    curPosX,curPosY : 当前落子的横纵坐标count : 棋盘格数(19)winColor : 当前落子玩家所持有的棋子的颜色chessboardRecord[count][count] : 记录棋盘各个位置的落子的颜色的二维数组for I from 4 to 1 step 1 if curPosX<0 or curPosY<0 then continue; end if for J from 0 to 5 step 1 if curPosX>count-1 or curPosY>count-1 then break end if if chessboardRecord[curPosX][curPosY]<>winColor break end if else if j=4 return true end if end forend for
    5 系统实现5.1 主要的数据结构// 用于记录某个棋子是主机落下的还是对手落下的enum playerRole enum playerRole{ HOST, GUEST};
    // 记录当前游戏状态是playingGame(正在游戏)还是readyToPlayingGame(等待对方按下准备按钮,然后开始游戏)enum playerStatus enum playerStatus{ playingGame, readyToPlayingGame};
    // 用户程序之间交互协议enum comm_request_typeenum comm_request_type{ //连接服务器失败 COMM_SERVER_CONN_FAILED, //连接服务器成功 COMM_SERVER_CONN_SUCCESSFUL, //服务器关闭 COMM_SERVER_CLOSE, //服务器发送大厅信息 COMM_SERVER_GAMEINFO, //退出游戏 COMM_CLIENT_QUITGAME, //请求断开服务器 COMM_CLIENT_DISCONN, //客户端请求连接服务器 COMM_CLIENT_CONN, //客户端请求创建主机 COMM_CLIENT_CREATE, //客户端请求加入游戏 COMM_CLIENT_JOIN, //双方准备完毕,游戏开始 COMM_CLIENT_GAMESTART, //某方玩家胜利,游戏结束 COMM_CLIENT_GAMEOVER, //玩家游戏操作:落子 COMM_CLIENT_GAMEOP, //玩家游戏操作:发送聊天信息 COMM_CLIENT_CHAT, //玩家游戏操作:悔棋 COMM_CLIENT_UNDO, //玩家游戏操作:悔棋回复yes COMM_CLIENT_UNDO_YES, //玩家游戏操作:悔棋回复no COMM_CLIENT_UNDO_NO, //玩家游戏操作:认输 COMM_CLIENT_LOSE, //玩家发来消息,对方掉线 COMM_CLIENT_LOSTCONN};
    // 用户程序之间交互数据包的格式struct msg_request_structstruct msg_request_struct{ //请求类型 qint8 request; //数据 QString data;};
    5.2 服务端的实现5.2.1 界面实现服务端能够开启本地服务,以及实时查看游戏大厅信息:哪些玩家正在对弈,哪些玩家在等待其他玩家加入等。因此服务端应具有两个界面,分别用来配置本地服务以及显示游戏大厅信息。如图5.1和图5.2:

    5.2.2 主要的成员变量// 用户程序之间交互的数据结构msg_request_struct* msg_req_struct;
    // 记录对弈双方的IP信息,若主机在等待其他玩家加入,则其对手IP地址置为“-”QList<QPair<QString, QString> >playerFightInfo;
    // 保存服务端本地开启的服务套接字QTcpServer *tcpServer_player;
    5.2.3 功能实现1.客户端连接限制功能
    考虑到服务端运行环境不同,所能承受的负荷也不同,因此加入“客户端连接限制”功能,防止过多的客户端连接导致服务端的负荷过大。
    在服务端的服务配置界面,可以设定服务端允许的最大连接数,当客户端的连接数达到了服务端设置的最大连接数的时候,服务端便不允许其他客户端连接。
    核心代码:
    QTcpSocket *tcpSocket_player = tcpServer_player->nextPendingConnection();DataClass::curConnCount++; if(DataClass::curConnCount > DataClass::maxConnectionLimit){ DataClass::curConnCount--; tcpSocket_player->close(); tcpSocket_player = NULL; return ;}
    2.广播游戏大厅信息(建立主机,加入游戏,退出游戏等)
    服务端与客户端之间的信息交互采用C/S模式,因此当有客户端更新并向服务端发送信息(例如建立主机,加入游戏,退出游戏等)之后,服务端应及时将更新之后的信息广播出去,使得其他客户端及时更新大厅信息。
    核心代码:
    QString qsNull = "-";playerFightInfo.push_back(qMakePair(clientAddr, qsNull));updateGameInfo();QList<QTcpSocket* > allTcpSocket = tcpServer_player->findChildren<QTcpSocket *>(); QString data; for(int i=0; i<playerFightInfo.size(); i++)data += playerFightInfo[i].first + " " + playerFightInfo[i].second + "_";for(int i=0; i<allTcpSocket.size(); i++){DataClass::sendMsg(COMM_SERVER_GAMEINFO, data, tcpSocket);}
    客户端收到服务端发送过来的data之后,用正则表达式分析出对弈双方的信息,然后更新本地显示。
    5.3 客户端的实现5.3.1 界面实现客户端具有一个配置连接服务器的界面,用于输入连接服务器信息,如图5.3:

    登陆服务器成功之后,会显示从服务器接受到得玩家对弈信息,用户这时可以选择建立游戏主机等待别人加入,或者是加入别人的主机进行对弈,如图5.4:

    玩家选择了建立游戏主机或者加入游戏后,会弹出游戏界面等待开始游戏,如图5.5:

    5.3.2 主要的成员变量// 记录超时时间的定时器QTimer timer;
    // 记录当前棋盘的落子信息int chessboardRecord[19][19];
    // 用户程序之间交互的数据结构msg_request_struct* msg_req_struct;
    // 当前玩家的身份(参考5.1的playerRole数据结构)playerRole plyRole;
    // 当前游戏状态(参考5.1的playerStatus数据结构)playerStatus plyStatus;
    // 下一步该谁落子(参考5.1的playerRole数据结构)playerRole whosTurn;
    // 记录对战成绩int win, lose;
    // 记录当前已经落下的棋子的信息:落子的位置,谁落的这颗子QList<QPair<QPoint, enum playerRole> > pieceRecord;
    5.3.3 功能实现1.玩家开始、退出
    开始时输入玩家的姓名,同时下方会显示出相应的IP地址。在输入相关的信息后就会弹出游戏界面,而游戏设置的对话框随即会自动隐藏,释放。这时就开始等待对方的加入,等有人加入后就可以开始游戏了。或者直接加入别人建立的主机。如果不想玩了就可以按退出按钮,一方退出游戏后,另一方就会显示尚未连线,然后等待下一个玩家的进入。功能详细流程如图5.6:

    核心代码:
    // 建立游戏主机DataClass::sendMsg(COMM_CLIENT_CREATE, "", tcpSocket_server); GameStatus gameStatus;gameStatus.setPara(tcpSocket_server);connect(tcpSocket_server, SIGNAL(readyRead()), &gameStatus, SLOT(getNewDataFromServer()));gameStatus.setPlayerRole(HOST);gameStatus.exec();
    // 加入游戏int row = ui.clientStatus_client_TBW->currentRow();if(ui.clientStatus_client_TBW->item(row,1)->text() != "-"){ QMessageBox::information(this,"错误","此主机正在对战中..."); return ;}DataClass::sendMsg(COMM_CLIENT_JOIN,ui.clientStatus_client_TBW->item(row, 0)->text(), tcpSocket_server); GameStatus gameStatus;gameStatus.setPara(tcpSocket_server);connect(tcpSocket_server, SIGNAL(readyRead()), &gameStatus, SLOT(getNewDataFromServer()));gameStatus.setPlayerInfo(ui.clientStatus_client_TBW->item(row,0)->text());gameStatus.setPlayerRole(GUEST);gameStatus.exec();
    // 退出游戏if(plyRole == HOST){ plyStatus = readyToPlayingGame; ui.chatLog_client_TE->clear(); ui.record_me_client_LE->setText("胜利:" + QString::number(win) + "场,失败:" + QString::number(lose) + "场!"); ui.record_rivar_client_LE->setText(""); ui.addr_rivar_client_LE->setText(""); QMessageBox::information(NULL,"提示","对方退出游戏!");}else if(plyRole == GUEST){ ui.gameReady_client_BTN->setDisabled(false); closeByServer = true; this->close();}
    2.用户之间聊天
    在下棋的同时可以通过聊天窗口和对方进行聊天会话,写完信息之后点击发送,就可以在公共的显示栏中看到自己发出的信息和对方回应的信息。进而开始用户之间的交流,同时所用的组合框的下拉菜单中会有一些简便的备用的语句,可以点击随即发送,这样就能省去部分打字的时间。
    核心代码:
    if(ui.sendwords_client_LE->text() == ""){ QMessageBox::information(NULL,"错误","请先输入文字!"); return ;}ui.chatLog_client_TE->append("自己: " + ui.sendwords_client_LE->text());DataClass::sendMsg(COMM_CLIENT_CHAT,ui.sendwords_client_LE->text(),tcpSocket_player);ui.sendwords_client_LE->setText("");
    3.悔棋、认输
    在游戏中,玩家可以发出悔棋请求,如果对方同意悔棋,则程序自动回退一步;若对方不同意悔棋,则请求无效。玩家也可以发出认输请求,程序自动结束本轮游戏并计分,认输请求不需要对方同意。悔棋功能详细流程如图5.7:

    核心代码:
    // 悔棋 if(whosTurn != plyRole) { QMessageBox::information(NULL,"错误","轮到你下棋的时候你才能发出悔棋请求!"); return ; } if(!(DataClass::checkDialog("确认","确认悔棋?"))) return ; whosTurn = (plyRole == HOST ? GUEST : HOST); DataClass::sendMsg(COMM_CLIENT_UNDO,"",tcpSocket_player);
    // 收到悔棋响应(同意悔棋)QMessageBox::information(NULL,"提示","对方同意悔棋!");whosTurn = plyRole;if(pieceRecord.size() > 0){ chessboardRecord[pieceRecord[pieceRecord.size() - 1].first.x()-1][pieceRecord[pieceRecord.size() - 1].first.y()-1] = 0; pieceRecord.removeAt(pieceRecord.size() - 1);}if(pieceRecord.size() > 0){ chessboardRecord[pieceRecord[pieceRecord.size() - 1].first.x()-1][pieceRecord[pieceRecord.size() - 1].first.y()-1] = 0; pieceRecord.removeAt(pieceRecord.size() - 1);}update();
    // 收到悔棋响应(不同意悔棋)whosTurn = plyRole;QMessageBox::information(NULL,"错误","对方不同意悔棋请求!");
    // 认输if(!(DataClass::checkDialog("确认","确认认输?"))) return ;DataClass::sendMsg(COMM_CLIENT_LOSE,"",tcpSocket_player); if(plyRole == GUEST) ui.gameReady_client_BTN->setDisabled(false);ui.undo_client_BTN->setDisabled(true);ui.lose_client_BTN->setDisabled(true);plyStatus = readyToPlayingGame;lose += 1;ui.record_me_client_LE->setText("胜利:" + QString::number(win) + "场,失败:" + QString::number(lose) + "场!");ui.record_rivar_client_LE->setText("胜利:" + QString::number(lose) + "场,失败:" + QString::number(win) + "场!");
    4.计分
    游戏开始的时候双方的战绩均为零,当一轮过后战绩就会被刷新一次,各自几胜几败很清晰的出现在界面上,由于最先下棋的玩家胜利的几率会很大,所以根据各自的胜负情况会交替双方先手的顺序,达到公平的目的。
    5.断网处理
    当客户端在等待对方操作时超时,则测试是否能连接服务器。若能连接,则向服务器发送对方以断网的相关信息并结束比赛计算分数;若不能连接,则提示用户已丢失与服务器和对方的连接。断网处理的详细流程如图5.8:

    核心代码:
    QTimer timer;connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));timer.start(30000); if(tcpSocket_player->isValid()) return ;QMessageBox::information(NULL,"提示","对方超时!");dataInit();ui.undo_client_BTN->setDisabled(true);ui.lose_client_BTN->setDisabled(true);ui.send_client_BTN->setDisabled(true); if(plyRole == GUEST){ DataClass::sendMsg(COMM_CLIENT_LOSTCONN,"HOST " +ui.addr_rivar_client_LE->text(),tcpSocket_server); if(tcpSocket_player) { tcpSocket_player->close(); tcpSocket_player = NULL; } this->close();}else if(plyRole == HOST){ DataClass::sendMsg(COMM_CLIENT_LOSTCONN,"GUEST " + ui.addr_rivar_client_LE->text(),tcpSocket_server); plyStatus = readyToPlayingGame; ui.chatLog_client_TE->clear(); ui.record_me_client_LE->setText("胜利:" + QString::number(win) + "场,失败:" + QString::number(lose) + "场!"); ui.record_rivar_client_LE->setText(""); ui.addr_rivar_client_LE->setText(""); if(tcpSocket_player) { tcpSocket_player->close(); tcpSocket_player = NULL; } if(tcpServer_player) { tcpServer_player->close(); tcpServer_player = NULL; }}
    5.3.4 五子连珠算法核心代码(4个方向中,以斜向为例):
    checkWinLURD(int last_x, int last_y, int winColor)函数:for(int j=4; j>-1; j--){ if(last_x - j <0 || last_y - j < 0) continue; for(int z=0; z<5; z++) { if(last_x - j + z > 18 || last_y - j + z > 18) break; if(chessboardRecord[last_x - j + z][last_y - j + z] != winColor) break; else if(z == 4) return true; }}return false;
    6 系统效果展示6.1 服务端效果展示服务端配置页面(默认为监听端口为45454端口,默认连接上限为50),如图6.1:

    开启服务有,当有客户端连接时,左下角会实时更新当前连接数。当客户端建立游戏主机之,服务端也会及时显示,如图6.2:

    当有玩家加入游戏时,服务端也能及时更新,如图6.3:

    6.2 客户端效果展示
    客户端连接服务器配置页面,如图6.4:

    客户端登陆后,会实时显示当前连接到服务端的游戏主机信息,如图6.5:

    玩家此时可以选择加入别人的游戏主机(双击主机列表),游戏界面如图6.6:

    点击“准备”按钮之后,即可开始游戏,如图6.7:

    游戏过程中,玩家可以聊天,如图6.8:

    游戏过程中,玩家可以点击“悔棋”、“认输”、“退出”按钮达到相应功能。
    7 结论7.1 已完成的工作目前本软件已完成的工作如下:

    了解五子棋游戏的落子规则,并设计相应的判断胜利算法。
    通过查阅相关资料,对于本软件设计了一个比较简单且快速有效的五子连珠算法。
    学习网络编程技术,掌握C/S模式网络程序的基本编程方法。
    经分析,本软件最终采用了P2P的网络模式,相比于C/S模式,P2P模式使得本软件效率更高。
    学习GUI程序设计,设计五子棋游戏的程序界面。
    本软件已实现一个操作方便,界面美观的五子棋游戏程序。
    设计并实现网络五子棋的服务器和客户端程序。
    本软件已实现服务器和客户端程序,服务端用于收集客户端的游戏状态信息,客户端用于玩家之间的对弈。
    设计并实现悔棋、认输、计分、聊天等游戏辅助功能。
    本软件已实现常见的游戏功能:悔棋,认输,计分,聊天等功能。

    另外,程序还实现了断网处理:当对方玩家1分钟之内没用做任何操作,则系统认为此玩家已丢失网络连接,结束此局比赛,并且让丢失连接的玩家退出此游戏主机。
    7.2 下一步待完成的工作本系统和其它的网络五子棋软件相比,功能上还存在着一定的差距,例如:本系统必须要知道主机的IP地址以及端口号才能建立连接、第三方观看的玩家不能加入棋局等等。
    要想设计出更完善的网络五子棋软件,仍然需要做大量的工作:
    1.服务器的设置
    在网络中将其中一台计算机设置成为固定的服务器,其IP地址和端口号是固定不变的,客户端可以直接连接到此服务器上,然后通过服务器上的信息选择对手或者创建游戏主机,这样就省去了玩家必须知道游戏主机的IP地址和端口号的麻烦了。
    2.界面需要进一步美化
    界面可以适当进行美化,提高游戏的娱乐性。
    3.提高通信的可靠性
    在采用同TCP连接的一般情况下,网络五子棋软件都可以正常稳定的工作,但是仍不能保证其任何情况下都能正常稳定,一旦由于某种原因使得网络传输过程中的数据丢失,将使双方的游戏数据不一致,游戏便无法继续进行。因此,可以考虑一方断网之后,计算机智能代替下棋的功能。
    4.扩充功能。
    增加记录当前棋谱的功能,以供五子棋残局爱好者能在日后研究。
    致谢从论文选题到搜集资料,从程序的设计到反复调试,从论文的成型到反复修改,期间经历了太多的喜悦、痛苦和彷徨。如今,伴随着这篇毕业论文的最终成稿,自己甚至还有一点成就感。
    我要感谢我的导师。他为人随和热情,治学严谨细心。在闲聊中她总是能像知心朋友一样鼓励你,在论文的写作和措辞等方面她也总会以“专业标准”严格要求你,从选题、定题开始,一直到最后论文的反复修改、润色,老师始终认真负责地给予我深刻而细致地指导,帮助我开拓研究思路,精心点拨、热忱鼓励。正是老师的无私帮助与热忱鼓励,我的毕业论文才能够得以顺利完成,谢谢老师。
    我要感谢班里的同学们,在百忙之中抽出时间帮助我搜集文献资料,帮助我理清论文写作思路,对我的论文提出了诸多宝贵的意见和建议。
    参考文献[1] Wikipedia.五子棋[EB/OL].[2011-04-07]. http://zh.wikipedia.org/zh/五子棋
    [2] Wikipedia.点对点技术[EB/OL].[2011-04-02].http://zh.wikipedia.org/wiki/点对点技术
    [3] Wikipedia.Qt[EB/OL].[2011-04-03].http://zh.wikipedia.org/wiki/Qt
    [4] Bruce Eckel.Thinking in Java[M].北京:机械工业出版社, 2007.
    [5] Thomas H.Cormen,Charles E.Leiserson,Ronald L.Rivest,CliffordStein.Introduction to Algorithms[M].北京:机械工业出版社, 2006.
    [6] Jasmin Blanchette,Mark Summerfield.C++ GUI Qt 4[M].北京:机械工业出版社, 2009.
    [7] 蔡志明.精通Qt4编程(第二版)[M].北京:机械工业出版社, 2011.
    [8] 严蔚敏,吴伟民.数据结构(C语言版)[M].北京:清华大学出版社, 2007.
    [9] 张银犬. 基于P2P技术的信息资源共享模式研究[EB/OL].[2005-11-25]. http://epub.cnki.net/grid2008/detail.aspx?filename=TSGJ200505012&dbname=CJFD2005
    [10] 顾军,王恒莉,徐丽. P2P网络模型的分析与探讨[EB/OL].[2005-10-01]. http://epub.cnki.net/grid2008/detail.aspx?filename=JISJ200510015&dbname=CJFD2005
    [11] 马睿. 基于Qt的TCP网络编程研究与应用[EB/OL].[2011-01-12]. http://epub.cnki.net/grid2008/detail.aspx?filename=FJDN201011067&dbname=CJFD2010
    3 评论 27 下载 2018-09-27 23:33:59 下载需要10点积分
  • 基于QT与mplayer的视频播放器

    一、课题的介绍和课题的任务1.1 课题的介绍本次课程设计我选的题目是视频播放器。目前市场上各种应用商店上有各种播放器,而且功能都很强大。一直对里面的具体实现有兴趣,所以这次就尝试着写了个视频播放器。并且加进去了一些很实用的功能,比如截屏和录屏。
    1.2 课题的任务包括视频的播放暂停、音量的加减、播放速度的设置、以及视频文件的添加删除、屏幕的截屏以及录屏。画面不卡顿。播放时进度条能刷新。
    二、设计的要求
    能进行视频的播放
    能暂停正在播放的视频
    能播放正在暂停的视频
    能调节视频播放的视频
    能进行视频播放进度的调节
    能停止正在播放的视频
    能添加新的视频文件到播放列表
    能删除播放列表中选中的视频
    能改变视频播放的速度
    能对正在播放的视频进行截屏
    能录制正在播放的视频

    三、系统的分析和系统中类的设计
    窗口类QMainWidndow
    按钮类QPushButton
    标签类QLabel
    列表类QList
    滑动条类QSilider
    显示类QWidge

    四、系统的实现及调试系统模块框架图

    程序具体实现
    4.1 添加文件进视频播放列表
    代码实现:
    void mplayer_video::on_open_clicked(){ QStringList filenames =QFileDialog::getOpenFileNames(this,tr("选择文件"),"/",tr("视频文件(*mp3 *mp4 *wma *3gp *wav *avi *flv *rmvb *mkv *ts *wmv)")); // *号和前面的要隔开至少一个空格,不能连起来。 if(filenames.count()!=0) ui->list->addItems(filenames);}
    4.2 双击列表选项进行播放
    代码实现:
    void mplayer_video::on_list_itemDoubleClicked(QListWidgetItem *item){ speed=1; ui->lupin_lable->setVisible(false); play(item->text());}void mplayer_video::play(QString fileName){ QStringList arg1; process->kill(); process=new QProcess(this); arg1 << fileName; arg1 << "-slave";//默认情况下,mplayer接受键盘的命令,而"-slave"使其不再接受键盘事件,而是作为后台程序运行, //接受以“\n”结束的命令控制,这样我们可以在进程中给他发送命令,而不需要操作键盘了. arg1 << "-quiet"; //尽可能的不打印播放信息 arg1 << "-zoom"; //视频居中,四周黑条,全屏播放 arg1 << "-wid" << QString::number((unsigned int)(ui->widget->winId())); // "-wid <窗口标识>" 是指让MPlayer依附于那个窗口, //ui->widget->winId() 这个指令就是用来获取widget的标识码 , //这样视频播放的时候,就在这个部件里播放,相当于给他固定起来。 connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(dataRecieve())); process->start(arg,arg1); //开始播放进程 ui->zanting->setText("暂停");}void mplayer_video::dataRecieve(){ process->write("get_time_length\n"); process->write("get_time_pos\n"); process->write("get_percent_pos\n"); while(process->canReadLine()) { QByteArray b=process->readLine(); if(b.startsWith("ANS_TIME_POSITION")) { b.replace(QByteArray("\n"),QByteArray("")); QString s(b); currentStr=s.mid(18); ui->horizontalSlider->setValue(s.mid(18).toFloat()); //更新进度条 } else if((b.startsWith("ANS_LENGTH"))) { b.replace(QByteArray("\n"),QByteArray("")); QString s(b); totalTime=s.mid(11); ui->label_time->setText(currentStr+"秒/"+totalTime+"秒 "); //显示时间进度 ui->horizontalSlider->setRange(0,s.mid(11).toFloat()); } else if((b.startsWith("ANS_PERCENT_POSITION"))) { b.replace(QByteArray("\n"),QByteArray("")); QString s(b); currentPercent=s.mid(21); ui->percentLabel->setText(currentPercent+"%"); } }}
    4.3 播放与暂停的实现
    代码实现:
    void mplayer_video::on_zanting_clicked(){ ui->lupin_lable->setVisible(false); process->write("pause\n"); //ui->list->currentRow()!=-1 判断是否有选中某条数据 //play(ui->list->item(ui->list->currentRow())->text()); 播放选中的某条数据 if(ui->zanting->text()=="播放") { if(ui->list->count()==0) QMessageBox::warning(this,"提示","播放列表为空",QMessageBox::Yes); else { connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(dataRecieve())); process->write("get_time_length\n"); process->write("get_time_pos\n"); process->write("get_percent_pos\n"); ui->zanting->setText("暂停"); } } else { disconnect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(dataRecieve())); ui->zanting->setText("播放"); }}
    4.4 快进与回退代码实现:
    void mplayer_video::on_back_clicked(){ process->write("seek -10\n");}void mplayer_video::on_quick_clicked(){ process->write("seek +10\n");}
    4.5 加速与减速代码实现:
    void mplayer_video::on_back_clicked(){ process->write("seek -10\n");}void mplayer_video::on_quick_clicked(){ process->write("seek +10\n");}
    4.6 停止播放代码实现:
    void mplayer_video::on_stop_clicked(){ process->write("quit\n"); ui->horizontalSlider->setSliderPosition(0); ui->label_time->clear(); ui->percentLabel->clear(); ui->zanting->setText("播放");}
    4.7 从播放列表中删除中选中的视频
    代码实现:
    void mplayer_video::on_del_clicked(){ if(ui->list->currentRow()==-1) QMessageBox::warning(this,"提示","未选中项目或列表为空",QMessageBox::Yes); else { ui->list->takeItem(ui->list->currentRow()); process->close(); ui->horizontalSlider->setSliderPosition(0); //更新进度条 ui->label_time->clear(); ui->percentLabel->clear(); }}
    4.8 加速播放与减速播放
    代码实现:
    定义一个全局静态浮点型变量 speed并初始化为1,每次点击加速,speed的值乘以二,每次点击减速,speed的值除以2
    void mplayer_video::on_jiansu_clicked(){ speed=speed/2; process->write(QString("speed_set "+QString::number(speed)+" 2\n").toUtf8());}void mplayer_video::on_jiasu_clicked(){ speed=speed*2; process->write(QString("speed_set "+QString::number(speed)+" 2\n").toUtf8());}
    4.9 视频播放进度条的刷新这是本程序里最复杂的部分之一。
    主要实现是用connect函数链接进程与DataRecieve函数
    代码实现:
    void mplayer_video::dataRecieve(){ process->write("get_time_length\n"); process->write("get_time_pos\n"); process->write("get_percent_pos\n"); while(process->canReadLine()) { QByteArray b=process->readLine(); if(b.startsWith("ANS_TIME_POSITION")) { b.replace(QByteArray("\n"),QByteArray("")); QString s(b); currentStr=s.mid(18); ui->horizontalSlider->setValue(s.mid(18).toFloat()); //更新进度条 } else if((b.startsWith("ANS_LENGTH"))) { b.replace(QByteArray("\n"),QByteArray("")); QString s(b); totalTime=s.mid(11); ui->label_time->setText(currentStr+"秒/"+totalTime+"秒 "); //显示时间进度 ui->horizontalSlider->setRange(0,s.mid(11).toFloat()); } else if((b.startsWith("ANS_PERCENT_POSITION"))) { b.replace(QByteArray("\n"),QByteArray("")); QString s(b); currentPercent=s.mid(21); ui->percentLabel->setText(currentPercent+"%"); } }}
    4.10 声音滑动条对声音的调节
    代码实现:
    void mplayer_video::on_voice_slider_sliderMoved(int v){ if(ui->zanting->text()=="播放") { process->write(QString("volume "+QString::number(v)+" 2\n").toUtf8()); process->write("pause\n"); } else{ process->write(QString("volume "+QString::number(v)+" 2\n").toUtf8()); }}
    4.11 设置图片按钮
    代码实现:
    QPixmap pixmap; pixmap.load("G:/mplayer_video/voice.jpg"); ui->voice->setFixedSize(pixmap.width(),pixmap.height()); ui->voice->setIcon(pixmap); ui->voice->setIconSize(QSize(pixmap.width(),pixmap.height())); ui->voice->setToolTip("音量"); ui->voice->show();
    4.12 屏幕截图
    代码实现:
    void mplayer_video::on_jietu_clicked(){ cout<<"ok"<<endl; process->write("pause\n"); if(ui->zanting->text()=="播放") { cout<<"1"<<endl; QPixmap snapImage = QPixmap::grabWindow(QApplication::desktop()->winId()); QString filename; QString slcStr; filename = QFileDialog::getSaveFileName(this,tr("保存图片"),"./未命名",tr("PNG(*.png);;JPG(*.jpg);;BMP(*.bmp)"),&slcStr);//弹出保存图片的文件窗口 if(slcStr.left(3)=="JPG") { if(filename.right(3)!="jpg") { filename+=".jpg"; } } if(slcStr.left(3)=="PNG") { if(filename.right(3)!="png") { filename+=".png"; } } if(slcStr.left(3)=="BMP") { if(filename.right(3)!="bmp") { filename+=".bmp"; } } if(filename!=NULL) { snapImage.save(filename); } } else { cout<<"2"<<endl; disconnect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(dataRecieve())); ui->zanting->setText("播放"); QPixmap snapImage = QPixmap::grabWindow(QApplication::desktop()->winId()); QString filename; QString slcStr; filename = QFileDialog::getSaveFileName(this,tr("保存图片"),"./未命名",tr("PNG(*.png);;JPG(*.jpg);;BMP(*.bmp)"),&slcStr);//弹出保存图片的文件窗口 if(slcStr.left(3)=="JPG") { if(filename.right(3)!="jpg") { filename+=".jpg"; } } if(slcStr.left(3)=="PNG") { if(filename.right(3)!="png") { filename+=".png"; } } if(slcStr.left(3)=="BMP") { if(filename.right(3)!="bmp") { filename+=".bmp"; } } if(filename!=NULL) { snapImage.save(filename); } connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(dataRecieve())); process->write("get_time_length\n"); process->write("get_time_pos\n"); process->write("get_percent_pos\n"); ui->zanting->setText("暂停"); }}
    4.13 屏幕录制代码实现:
    void mplayer_video::on_lupin_clicked(){ if(ui->lupin->text()=="录屏") { photoStart(); timer->start(50); ui->lupin->setText("停止"); } else { timer->stop(); ui->lupin->setText("录屏"); }}void mplayer_video::photoStart(){ QDir *d = new QDir(); QString dirName("D:\\video"); if(!d->exists(dirName)) { d->mkdir(dirName); }}void mplayer_video::timerUpdate(){ QPixmap p; QString dirNamee("D:\\video\\"); dirNamee=dirNamee+QString::number(number)+".jpg"; p = QPixmap::grabWindow(QApplication::desktop()->winId()); p.save(dirNamee); number+=1;}
    4.14 屏幕录屏播放代码实现:
    void mplayer_video::on_lupinstart_clicked(){ process->write("quit\n"); QString dirName("D:\\video"); qdir = new QDir(dirName); qdir->setSorting(QDir::Reversed|QDir::Time); list = qdir->entryInfoList(); number2=0; timer2->start(100);}void mplayer_video::on_stop_bofang_clicked(){ timer2->stop(); ui->lupin_lable->setVisible(false);}
    五、系统的使用说明打开程序,点击添加文件进行视频播放列表视频的添加。双击列表中某条记录进行视频的播放,播放按钮自动转换成暂停按钮。点击播放将会播放视频,点击暂停视频会暂停。除了一些视频播放器的基本功能外,本程序还加入了屏幕截图与屏幕录制的功能。具体操作用按钮可以实现。
    4 评论 50 下载 2018-11-15 11:06:06 下载需要8点积分
  • 基于Python的图片及音频搜索引擎

    在此次实验报告中将会分为五个部分进行单独分析,这五个部分分别是:爬虫、界面、文本搜索、图片搜索以及音频搜索。
    一爬虫1.1 库
    requests
    urllib

    1.2 实现功能
    能够通过requests.get获得网页上的内容,并使用json.loads进行解析
    能够快速定位歌曲、专辑的信息,包括专辑名、歌手、歌词、专辑简介、发行时间、流派、发行公司等
    能够从网页上下载歌曲和图片

    1.3 实现过程
    爬取QQ音乐网站的榜单,通过榜单下载歌曲
    分析发现搜索各首歌曲的 url 地址中的区别在于其中的一段内容代表的是搜索输入的文字的编码,利用这一特性模拟搜索从榜单上爬取下来的歌曲
    通过分析网页的 url 地址,发现每一首歌曲都有自己特定的”media_mid”属性,这个属性是各个歌曲的下载url地址唯一不同的地方。通过将该属性替换到url中的固定位置,可以完成歌曲、专辑、歌词定位
    定位到歌曲、专辑、歌词、图片地址后完成文本的写入

    1.4 难点
    reques 取得 url 地址是搜索页面下的单曲一栏,然而这里面并没有歌词,故将搜索歌曲的页面从单曲切换到歌词


    由于该页面中有大量中文字符,会出现json无法解析的编码,故使用正则表达式作类似处理
    专辑信息只能通过歌曲的 js 代码中获得的 media_mid 属性定位后,再通过正则表达式来提取

    二、界面2.1 库
    Wow.min.js
    animate.css
    jquery.min.js

    2.2 实现的功能2.2.1 翻页
    由于文本搜索的结果较多,故使用javascript实现了分页的效果。设置最大显示量为20个结果,每页显示4个结果,共五页。 并实现了翻页、上一页、下一页的功能。
    效果如下

    代码如下
    <script language="JavaScript" type="text/javascript" > var total="20";//最大数据量 var pagesize="4";//每页的显示量 var pagenum=Math.ceil(total/pagesize);//一共有多少页 //生成导航 function initpage(nowpage){ var pagenav='<a href="#" onclick=up('+ nowpage+');>上一页</a>'; for(i=1;i<=pagenum;i++){ pagenav += (i==nowpage)?'<span class="now">' + i + '</span>':'<a class="number" href="#" onclick=goToPage(' + i + ');>' + i + '</a> '; } pagenav += '<a href="#" onclick=down('+ nowpage+');>下一页</a>'; document.getElementById('pages').innerHTML=pagenav; } //翻页 function goToPage(page){ var start=(page-1)*pagesize; var max=page*pagesize; for(vs=0;vs<total;vs++){ var obj=document.getElementById('item_' + vs); obj.style.display="none"; } if(max>total)max=total; for(v=start;v<max;v++){ var obj=document.getElementById('item_' + v); obj.style.display="block"; } initpage(page); } //上一页 function up(nowpage){ if(nowpage==1)return false; if(nowpage>1)goToPage((nowpage-1)); } //下一页 function down(nowpage){ if(nowpage==pagenum)return false; if(nowpage<pagenum)goToPage((nowpage+1)); }</script>
    2.2.2 轮播图可自动滚动播放海报,亦可点击左右切换按钮或下方圆形选择按钮切换海报。
    效果如下

    代码如下
    //轮播图函数function jAutoPlay() { if (curIndex < $jUlLis.length) { curIndex++; } else { curIndex = 1; $jUl[0].style.left = 0; } $jUl.stop().animate({ left: -liWidth * curIndex }); if (jOlIndex < $jOlLis.length-1) { jOlIndex++; console.log(jOlIndex) } else { jOlIndex = 0; } $jOl.children().eq(jOlIndex).addClass("current").siblings().removeClass("current");}
    2.2.3 NEW SONGS板块从上部飞入slideInDown的动画效果当下拉至NEW SONGS这一板块时,图片从左到右依次从上部向下飞入指定位置。

    当检测到鼠标滑过图片区域时,图片下部的歌曲信息由原先的一行专辑名再增加一行歌手名。

    2.2.4 PROMOTION板块的bounceInDown弹跳飞入动画效果当检测到鼠标进入图片区域时,浮于图片上显示图片的专辑名、歌手名等信息。

    代码如下
    <!-- t-promot-start --><div class="t-promot w"> <div class="title"> <h2>PROMOTION<br><span>热门推荐</span></h2> </div> <div class="t-promot-t clearfix"> <a href="javascript:;" class="t-a-imgs wow bounceInDown" data-wow-delay="0.2s"><span><i>你身边还有我<br/>阿里郎</i></span><img src="/static/images/t-promot1.jpg"/></a> <a href="javascript:;" class="t-a-imgs wow bounceInDown" data-wow-delay="0.3s"><span><i>别对我动情<br/>关晓彤</i></span><img src="static/images/t-promot2.jpg"/></a> <a href="javascript:;" class="t-a-imgs wow bounceInDown" data-wow-delay="0.2s"><span><i>新世界<br/>小贱</i></span><img src="static/images/t-promot3.jpg"/></a> <a href="javascript:;" class="t-a-imgs wow bounceInDown" data-wow-delay="0.2s"><span><i>星星照亮回家的路<br/>庞龙</i></span><img src="/static/images/t-promot4.jpg"/></a> </div></div>
    2.2.5 回到顶部按钮按钮置于页面右侧,点击即可返回到页面顶部。

    代码如下
    //返回顶部var timer = null;$(".t-btn7").click(function(){ var leader = scroll().top; clearInterval(timer); timer = setInterval(function(){ var target = 0; var step = (target-leader)/10; step = step>0 ? Math.ceil(step):Math.floor(step); leader = leader+step; window.scrollTo(0,leader); if(target == leader){ clearInterval(timer); } },25)})function scroll() { // 开始封装自己的scrollTop if(window.pageYOffset != null) { // ie9+ 高版本浏览器 // 因为 window.pageYOffset 默认的是 0 所以这里需要判断 return { left: window.pageXOffset, top: window.pageYOffset } } else if(document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTD return { left: document.documentElement.scrollLeft, top: document.documentElement.scrollTop } } return { // 未声明 DTD left: document.body.scrollLeft, top: document.body.scrollTop }}
    2.2.6 上传图片、音频使用 GET 方法提交表单,用户浏览得到文件及其文件名,将文件名传递给处理函数以进行进一步的处理。
    代码如下
    <!--search部分 start--><div style="margin-top:50px;margin-left:50px"> <form action="/a_s" method="GET"> <input type="file" name="uploadfile" id="uploadfile" style="height:35px;color:#68838B" value="Browse..." /> <button type="submit">上传</button> </form></div><!--search部分 end-->
    2.3 问题及解决遇到的问题主要是在界面的整合过程中。在运行代码过程中报错“Codec with name ‘Lucene410’does not exist”查阅一些资料后,发现大部分出现此类问题的情况是由于缺少 META-INF/services 这样的文件夹,可以通过将 jar 包中的META-INF复制到src目录的方式解决。但多次尝试之后发现并不可行,再次研究思考报错内容后,认为问题在于 整合界面的同学所使用的 Lucene 的版本为 4.9.0 而写音频搜索的同学的Lucene 的版本为4.10.0,两者版本不一,使用版本低的lucene时会报错。整合界面的同学使用了miniconda安装了4.9.0版本,利用conda update 更新lucene 后,版本仍为 4.9.0,原因应是 miniconda 中的 lucene 最高版本号是4.9.0,故采取了卸载原来安装的4.9.0版本的lucene,通过lucene PPT中提到的第二种安装方式安装了4.10.0版本的lucene最终解决了这一问题。
    三、文本搜索3.1 库
    lucene
    jieba

    3.2 原理使用jieba与lucene对文本内容进行分词以此实现快速搜索
    3.3 实现步骤
    建立索引
    实现搜索文件

    四、图片搜索4.1 库
    H5py
    OpenCV
    Numpy

    4.2 原理利用 SIFT 算法对图片进行特征提取,然后利用 Hash 对图片特征分类建立数据库,在搜索过程中对待检索图片做同样操作以完成匹配。
    由于sift特征具有尺度不变性、旋转不变性等良好特征,故图片进行匹配时,两张图片相似度要求并不是很高。
    4.3 实现步骤对爬虫得到的文件中的所有图片用OpenCV的内置sift进行特征提取:
    sift=cv2.SIFT()
    将每张图片提取的sift特征做归一化处理
    r,c=des.shapeargmaximum=np.argmax(des)argminimum=np.argmin(des)nmax=argmaximum/cnmin=argminimum/cmaximum=des[nmax][argmaximum-nmax*c]minimum=des[nmix][argmiximum-nmix*c]
    将归一化处理后的数据使用直方图统计,并将 bins 设为下图所示:
    b=[0.07, 0.1, 0.13, 0.16, 0.19, 0.22, 0.25]
    以得到的直方图为依据做Hash处理,将得到的矩阵变为字符串储存为一个字典的 key 值,然后再将具有同样 Hash 值得图片信息作为 value 储存入该字典中:
    if nt dic.has_key(hashname): grphash=f.create_group(hashname) dic[hashname]=grphashimgname="img"+str(count)desname="des"+str(count)grpimg.create_dataset(imgname, data=imgori)grppath.create_dataset(imgname, data=imgpath)dic[hashname].create_dataset(imgname, data=des)
    检索图片时,对待检索图片进行上述相同操作之后,从H5py中取得同样Hash值的图片信息,然后对这些图片一一使用欧氏距离匹配:
    bf=cv2.BFMatcher()keys=imggroup.keys()paths=[]matchimgs[]for k in keys: des1=imggroup[k][:] matches=bf.match(des1, des) d=0 for j in matches: d+=j.distance if d<400 path.append(imgpaths[k].value) matchimgs.append(imgs[k][:])
    4.4 测试从网上下载各类不同的专辑图片来对库中的图片进行匹配,可以得到:

    所使用的 bins 划分是能够比较均匀的将库中的图片划分在各个不同的 Hash值下面的
    网上的图片与库中原有的图片的欧式距离是小于400的
    库中有4913首歌曲,搜索到对应图片的时间需要3s,但若将搜索嵌入到搜索引擎中,页面上出来结果大致需要6s,猜测与电脑渲染页面速度有关

    4.5 对比与使用颜色空间创建Hash值相对比,该使用sift特征值来Hash的办法可以排除颜色的干扰,也就是即使亮度、灰度有变化,也不会影响图片的匹配。
    五、音频搜索5.1 库
    ffmpeg
    wave
    pyaudio
    mysqldb

    5.2 原理基于内容的音频检索与基于内容的图片检索在实际实现上面临着类似的问题,包括巨大的信息量以及特征点相对位置对于搜索结果的巨大影响,对于检索的准确度要求以及对于检索速度的要求同样限制着音频检索的实现。这里参照图片检索的相关方式对音频检索进行了尝试,主要思想是在音乐的频谱中提取频率峰值特征点,基于这些特征点对不同的歌曲进行匹配。(主要用lucene解决了音频匹配速度问题)
    5.3 实现步骤5.3.1 音频格式转码网页爬虫下载后的音频格式为 m4a,为使用 wave 库,需对下载的音频进行转码,将其转为wav格式,这里使用安装在linux中的ffmpeg进行转码,转码后的文件存入与原文件相同的目录下,与原文件只有后缀名不同。
    代码:
    try: subprocess.call(["ffmpeg", "-i", origin, newsr]) sss.addsong(newsr2, extra, album)except: continue
    目录下的文件组成:

    5.3.2 提取音频特征打开步骤1转码后的音频文件,该文件的一个声道是一个一维数组的形式,图形化如图:

    这种时间对应幅值的一维数组信息量巨大,难以提取特征信息,故我们利用傅里叶变换将其转化到频域中:

    转化的结果:

    这样,音频信息转为了其对应的众多频率波形的叠加,而人耳正是靠着所听到的音乐的特定频率序列来区分音频的。在频域信号的众多特征中,我们认为峰值特征是最具代表性的,这种特征代表着这一频率能量最大,它容易提取,同时对于噪声有一定的容忍度。如果只是简单地提取整首歌曲中的一些峰值点,则我们会丧失重要的时间信息,故应当对整段音频进行分段,对每一段音频做傅里叶变换并提取特征峰值点,这样我们在得到特征点的同时也较好地保留了时间的信息。
    这里将音频的1s分成40个块,在每一块的4个对应区间中得到4个峰值点,这4个峰值点组成的序列即是在一个块中得到的音频指纹。
    5.3.3 数据存储为了应用的方便,这里将得到的指纹序列存入了mysql数据库中,如图所示:

    5.3.4 匹配方法这里设计了两种匹配方法,dtw以及分词法匹配,考虑到对检验速度的要求,实际应用中选择了后者,但如果要求更高的精度,建议选择前者:
    dtw,即动态时间规划,这种方法适于匹配两个不同长度的音频文件,主要思想是找出穿过m*n矩阵的最佳路径,图中上方和左方为我们要匹配的两个不同长度的音频文件,中间为这两个音频文件构造的矩阵,矩阵中每一个格点为两音频中对应点的相似度,中间的线即为找出的最佳路径:

    具体实施原理及方法参照:http://blog.csdn.net/zouxy09/article/details/9140207
    分词法匹配,这种方法的精度不高,但相比于dtw的匹配所花费的时间要大大减小。主要思想是把音频特征提取过程中提取出的特征点(即四个数字组成的序列)看作是文字检索中的一个词,如将2,40,13,77看作是文字搜索中的一个词,并用空格将这些词隔开,使用lucene中的whitespace分词器对特征点组成的“文章”进行分词以及语法分析,构建索引。这样,对特征点的检索就变成了简单的文字检索。
    5.3.5 搜索主要搜索过程:

    从本地上传一个音频文件
    对该音频文件进行转码,将其转化为wav格式
    对转码后的音频文件提取特征点
    用逻辑与连接这些特征点,构建query,与索引结果进行匹配,返回得分最高的50个结果

    5.4 测试测试结果显示,dtw将测试音频与库中的音频进行匹配时平均每一首歌需花费1.5s,这表明当库的容量非常大时其所消耗的时间将是难以想象的,因此,我们选择用lucene的分词系统对音频进行过滤。最终测试的结果是,其中百分之八十的正解将会出现在lucene打分结果的前五名中,有大概百分之15的正解将会出现在5至40名中,只有极少数的结果会出现在40名之后(出现这种现象的主要原因是不同的音频可能有相同的特征点,并且一些音频中含有的某种特征点的数量可能比正确结果的数量更大),故这种方法的精度可能不如dtw,但在一定程度上可以满足检索的基本要求。
    1 评论 1 下载 2019-05-23 11:42:23 下载需要11点积分
  • 基于C语言链表实现的订餐系统

    1、课程设计简介1.1 要求参加本课程设计的学生,应当认真完成本课程设计的全部过程。并以最终课程设计成果来证明其独立完成各种实际任务的能力。从而,反映出理解和运用本课程知识的水平和能力。
    1.2 意义和目的意义
    C语言是电子科学与技术专业的重要专业基础课。在很多后续课程中,都要使用到C语言。
    学生通过对C语言的学习,已经具备了使用C语言编写简单的应用程序的能力。为了加强程序设计基础,开设课程设计课,使学生对C语言有更全面的理解,进一步提高运用C语言编程解决实际问题的能力,同时,为后续课程的学习夯实基础。
    目的

    提高用程序设计解决实际问题的能力
    通过提出算法、指定输入输出来设计一个解决方案
    用C语言合理地开发两个简洁有效的程序代码来实现该设计
    测试程序是否工作且满足设计指标并评价其效率

    1.3 内容链表操作题(包括建立、插入、删除、打印等)(参考教材);
    2、具体实现2.1 程序总体方案2.1.1 程序设计思想通过在主函数循环调用各个函数,从而实现对链表的连续操作,最终实现链表的新建、打印、查找、保存、读取、更新、删除等等操作,进而管理订单与用户信息,并保存用户的各种信息,方便下一次操作。链表是以订单号排序,当出现订单号相同时会有提示,并结束本次操作,成功实现一个订单号对应一个客户。
    2.1.2 程序结构或流程图
    2.2 改进方案2.2.1 改进一2.2.1.1 改进方案刚开始链表是按插入顺序进行排列的,搜索时的时候也没有按序号检索,总体看起来很乱。所以,在增加、修改等需要增加新的客户信息的时候,调用按订单号排序的函数,这样打印的时候就显得很工整,有条理。
    2.2.1.2 具体实现void Insert(Pointer *Head){ int in_number; Pointer p,q,r; p=q=*Head; printf("请输入号码:"); scanf("%d",&in_number); while(p!=NULL) { if(in_number==p->number) { printf("已存在号码!"); } else { q=p; p=p->next; } } r=(Pointer)malloc(sizeof GuestLink); r->next=NULL; if(q==NULL) *Head=r; else q->next=r; printf("请输入姓名:"); scanf("%s",r->name); printf("请输入人数"); scanf("%f",&r->sum); printf("请输入时间"); scanf("%s",r->time);}
    2.2.2 改进二2.2.2.1 改进方案一开始没有建立数据的时候,是无法提取数据的,而要是在开头的时候先提取数据的话,就会出现读不到数据,然后指针Head不为NULL,而接下来的操作就会出现问题。所以,在无法找到数据的时候,我们就返回一个指针Head=NULL,这样,头指针还是指向空函数,说明我们没有成功读取数据,或者未保存任何数据信息。这样在接下来的操作就不会出错了。
    2.2.2.2 具体实现void Insert(Pointer *Head){ int in_number; Pointer p,q,r; printf("请输入编号:\n"); scanf("%d",&in_number); p=q=*Head; while(p!=NULL) { if(p->number==in_number) { printf("已经有相同编号:"); return; } else { q=p;p=p->next; } } r=(Pointer)malloc(sizeof(GuestLink));//没有*号 r->next=NULL; if(r==NULL) { printf("分配空间失败"); return; } if(q==NULL)//如果是空表,判断空表用q!!!! *Head=r; else { q->next=r; } r->number=in_number; printf("请输入姓名:\n"); scanf("%s",r->name); printf("请输入人数:\n"); scanf("%d",&r->sum); printf("请输入时间:\n"); scanf("%s",r->time);}
    2.2.3 改进三Nothing ORZ…
    2.2.3.1 改进方案当我们建立链表的时候是把链表数据保存在内存里面,而删除之类操作内存还没有被释放,这样会增加电脑的内存占用,同时会增加能量的电脑能量消耗,这样不利于电脑的稳定工作,且浪费资源,这时我们就需要把那些没用的链表内存释放了。通过free(*p)函数实现对内存的释放。
    2.2.3.2 具体实现删除一个订单只需要
    void Delete(Pointer *Head){ int flag=1; int number; Pointer p,q; printf("请输入要删除的数据编号:"); scanf("%d",&number); p=q=*Head; while(p!=NULL&&flag) { if(p->number==number) { if(p==*Head) { *Head=p->next;free(p);//删除结点后要及时释放内存!!! } else { q->next=p->next; free(p); } flag=0; } else { q=p; p=p->next; } } if(flag) printf("没有找到可以删除的数据!!");}
    2.2.3.3 实现效果添加订餐客户信息

    按照客户时间升序排序

    保存到文件

    查看文件信息

    3、总体小结链表题考察了我们对于链表知识的理解,要求熟练掌握链表的建立,结点的插入,查找,删除,输出等方法。所以,着重复习了书本中关于链表的知识,以便能够更加好的完成程序。通过上一次课程设计的锻炼,对于本次课程设计的要求和内容偶遇了更加深的理解。通过本次课程设计,对链表的知识也有了进一步的掌握和理解。刚开始不够细心,在调试过程中遇到的问题,通过及时的查找书籍寻找答案,并根据所学知识,理清思路去解决,不断完善程序。在设计过程中,不断的通过与同学交流、上网找资料、查阅书籍等等方法,克服每一个难题,并与同学在讨论中增加了相互感情,扩大了自己的知识面,让一些以前不知道的,或者更加实用的方法得以用在自己的程序代码上。
    通过这次课程设计,我了解到编程并不是那么的容易,也并不是那么多难,关键是需要有:

    编程习惯
    对算法的认知能力
    语言掌握能力

    除了对编程能力的认识外,我还知道了,C语言并不是那么的难,关键是看你怎样去了解代码,使用代码,让自己的程序更加的完善,对资源的利用更加的充分。
    4、参考文献[1] 陈显刚.c语言程序设计[M].北京:北京理工大学出版社,2007.
    [2] 谭浩强.c语言程序设计[M].北京:清华大学出版社,1998.
    [3] 徐新华.c语言程序设计教程[M].北京:中国水利水电出版社,2001.
    [4] 伍一,等.c语言程序设计与实训教程[M].北京:清华大学出版社,2007.
    [5] 徐翠霞. c语言程序设计案例教程[M].北京:北京大学出版社,2008.
    [6] 徐卓群等.数据结构,高等教育出版社,1987.
    [7] 陈小平.数据结构导论,经济科学出版社.2000.
    1 评论 1 下载 2019-05-22 22:23:28 下载需要7点积分
  • 基于PHP和MySQL数据库实现的学生成绩管理系统

    一、项目介绍1.1 课程设计的题目学生成绩管理系统
    1.2 系统的总体功能描述1.2.1 基本要求学校希望建立一个学生成绩在线公布系统,对学生成绩信息进行存储、管理和发布,并能通过分类、查询、统计等操作从数据库中获取有效信息,在方便学生了解自己各科考试成绩的同时,各院系可对学生成绩有直接和明确的掌握。
    1.2.2 基本功能
    系统设置:考试科目、等级分值等参数的设置、权限设置、更改密码等
    学生查询:通过学号和密码,学生可查询每学期各科成绩
    成绩录入:提供学生成绩的添加功能,并能从表格和其他数据库文件中导入信息
    信息更新:提供学生成绩信息的删除和修改和功能
    信息统计:按院系、专业、班级分类汇总、统计。可查询学生的成绩分布,不及格人数,最低分、最高分和平均分
    信息打印:主要提供成绩的分类打印功能

    1.2.1 登录功能
    输入管理员,老师,学生的id和密码会登录到不同界面,与之对应有不用的功能和权限。
    1.2.2 管理员功能
    添加考试

    在输入框输入需要添加的考试科目,点击添加,完成添加考试科目内容
    删除分数

    输入要删除分数的名字和对应的科目,点击删除即可删除分数
    修改分数

    输入需要需改分数的姓名和对应的科目对应的新成绩,点击修改完成修改分数功能
    修改密码

    管理员可以修改任何id的密码
    1.3 开发工具本系统后台数据库采用MySQL 数据库,该数据库系统在安全性、准确性和运行速度方面有绝对的优势,并且处理数据量大,效率高;前台sublime text作为主要开发工具,使用B/S模式,开发简单。
    1.4 开发环境
    编辑器:Sublime Text
    系统开发语言:HTML + CSS + PHP
    数据库:MySQL

    二、需求分析2.1 概括描述随着在校大学生人数的不断增加,教务系统的数据量也不断的上涨。学校工作繁杂、资料重多,虽然各类管理信息系统已进入高校,但还未普及,而对于学生成绩管理来说,目前还没有一套完整的、统一的系统。因此,开发一套适和大众的、兼容性好的系统是很有必要的。
    2.2 用户特点本系统有管理员,老师,学生。系统的管理员主要是对学生信息以及学生成绩进行一些操作。这些操作包括对学生信息的添加、对学生成绩的录入以及对学生成绩进行分析等。老师对学生成绩的录入,自己密码的修改,学生查询自己的成绩和统计自己的成绩,修改自己密码。
    2.3 可行性分析目前,随着办公信息化的开展,高校的扩招,新生入学以及期末考试结束后,学校都需要对一些繁琐的流程进行管理,通过一个基于B/S架构的管理系统,可以很好的将这一个过程进行化繁为简。此项目具有普遍性,能够应用于很多学校。因此,该类型系统可以大量投入使用。
    2.4 数据项管理员id,密码,老师id,密码,学生id密码,考试科目,考试成绩
    2.5 数据结构
    管理员:权限最高,管理员ID,管理员登录密码
    老师:老师ID,老师登录密码
    学生:学生ID,学生密码,学生考试科目,考试成绩

    2.6数据存储管理员ID,管理员登录密码,老师ID,老师登录密码,学生ID,学生密码,学生考试科目,考试成绩
    三、数据库概念结构设计3.1 局部ER图
    管理员(ID,密码)
    老师(ID,密码)
    学生(ID,密码,科目,成绩)

    四、数据库逻辑结构设计4.1 关系模式
    管理员(ID,密码)
    老师(ID,密码)
    学生(ID,密码,科目,成绩)

    4.2 存储过程信息->数据库
    4.3 触发器建立触发器以实现分数删除功能,当删除一个学生的分数时将此学生的相关信息全部删除。
    4.4视图这部分主要是考虑使用方便性和效率问题,主要借助视图手段实现。
    4.5索引使用经典存取方法,建立索引:

    学生成绩经常用到而且按其排序,在学生成绩名称属性建立索引。
    用户ID是本软件的重要的数据,在ID属性建立索引。

    五、应用系统功能结构图
    六、各功能模块程序流程图及其说明6.1 添加考试输入框获取用户输入的考试科目,通过post方法发送到服务器,添加到数据库
    6.2 删除模块输入框获取用户输入要删除的分数,通过post方法发送到服务器,MqSQL数据库匹配对应的分数,然后删除
    6.3 修改密码模块通过更新数据库达到修改密码的功能
    6.4 查询模块查询对应的分数,使用对应的MySQL语句
    七、程序源代码及其说明7.1 登录
    7.2 查询

    7.3 服务器端对数据库的读取和写入操作采用JDBC进行数据库的连接与操作。
    7.4 客户端与服务器端的通信采用Socket进行通信。
    7.5 系统主界面管理员主界面

    学生主界面

    教师主界面

    八、总结课程设计中遇到的主要问题和解决方法;创新和得意之处;课程设计中存在的不足,需进一步改进的设想;课程设计的感想和心得体会。
    8.1 主要问题和解决方法界面的排版问题,还有用户输入的数据怎样传给数据库和php处理。
    排版问题使用css的定位解决,绝对定位能让控件定位到我想要的地方。数据传输使用post方法,在php要使用_POST+name接受控件输入的内容。
    8.2 创新和得意之处系统界面简单单调,但是功能齐全,开发容易,使用XAMPP集合环境,不需要单独安装数据库和web服务器,使用起来方便简单。
    8.3 不足之处功能过域简单,更加复杂的功能没有实现,系统存在SQL注入等安全问题,界面也不够美观,进一步的学习和研究,学习更多css、html、php、mysql,进一步掌握安全问题,维护更好的网站。
    8.4 心得体会在这次课程设计中,重点是要掌握数据库设计方法。数据库设计特点:三分技术,七分管理,十二分基础数据。在数据库建设中不仅涉及技术,还设计管理。要建设好一个数据库应用系统,开发技术固然重要,但相比之下则管理更重要。
    进行数据库的设计首先必须了解与分析用户需求。需求分析是整个设计过程的基础,是最困难、最耗时的一部分。需求分析做的不好,甚至会导致整个数据库设计返工重做。需求分析的任务是通过详细的调查现实世界要处理的对象,充分了解用户的各种需求,然后在此基础上确定系统功能。系统还必须充分考虑今后可能的扩充和改变。
    概念结构设计是整个数据库设计的关键,他通过对用户需求进行综合、归纳与抽象,形成一个独立与具体DBMS的概念模型。逻辑结构设计是将概念结构转换为某个DBMS所支持的数据模型,并对其进行优化。概念结构的特点:能真实、充分的反映现实世界,包括失误和事物之间的联系,能满足用户对数据的处理要求,是对现实世界的一个真实模型;易于理解,从而可以用它和不熟悉计算机的用户交流意见,用户的积极参与是数据库设计成功的关键;易于更改,当应用环境和应用要求改变时,容易对概念模型修改和扩充;易于向关系、网状、层次等各种数据模型转换。
    本系统主要针对的用户是学生、教务员和老师。该设计的优点是:不同用户的功能都单独存储在数据库中相应的表中使他们可以很方便地满足自己的需求。不足是:存在部分数据冗余,不利于该数据库向大型数据库扩展。但该设计基本能满足三类的用户的需求,完成简单的功能。时间有限,部分功能还没有实现,有待完善!
    通过将近一个星期的设计,我对数据库在生活中的广泛应用有了更深的体会,同时也加深了对MySQL的认识,了解了相关的SQL的知识,尤其是对数据库的原理与开发程序的应用有了更进一步的认识和了解。 课程设计是我们数据库专业课程知识综合应用的实践训练,在课设中也遇到了很多问题,经过向老师询问和跟同学们交流,认真的思考,积极解决问题,终于解决了那些问题。在解决问题的过程中,我也学到了很多知识,更是让我把课本的知识应用到实际之中,让我了解了我们学习的知识有什么用,增强我的自信心和学习的动力。同时感谢老师和同学在课程设计过程中的帮助!
    九、参考文献[1] 王珊.数据库技术与应用[M].北京:清华大学出版社,2005.
    [2] 姚卿达.数据库设计[M].北京:高等教育出版社,1987.
    [3] 戴维尔.JavaScript程序员教程[M].电子工业出版社.2010.333-334.
    [4] 武欣 PHP和MySQL Web开发(原书第4版)机械工业出版社9787111262817
    5 评论 74 下载 2018-11-30 19:10:01 下载需要10点积分
显示 0 到 15 ,共 15 条
eject