精华内容
下载资源
问答
  • 文章从系统模型和心理模型需要很好匹配的角度出发,通过引用大量软件产品设计的例子,分析了为什么有些产品难以使用,并详细分析了该原则在编程语言的发展过程中的体现
  • 信息工程学院1 Java 程序设计 实习报告 JAVA图形用户界面 实验六 Java 图形用户界面 1实验目的 1掌握图形用户界面基本组件 2了解如何使用布局管理器对组件进行管理 3掌握 Java 事件处理机制 2实验内容 实验题 1 编写...
  • 针对Web应用系统中应用环境和用户需求易于变化的问题,将柔性软件思想与Web界面的设计结合起来,提出了一个具有动态重配置能力的基于构件的柔性Web用户界面模型。该模型把描述构件显示样式的模板和适应业务数据结构...
  • JSP model2即:Jsp+Servlet+JavaBean,JSP model2实际上就是MVC,MVC包括模型,视图,控制器三部分,这个例子基于mvc思想实现了用户注册功能。
  • java(swing,awt)图形用户界面编程100多个案例,里面的例子好好学学
  • 操作系统用户界面

    千次阅读 2020-08-03 19:30:57
    操作系统用户界面 3.1 操作系统启动过程 3.1.1 BIOS和主引导记录MBR 实模式和保护模式 实模式(实地址模式。REAL MODE) 程序按照8086寻址方法访问0h - FFFFFh(1MB)空间 寻址方式:物理地址(20位)=段地址:偏移...

    操作系统用户界面

    3.1 操作系统启动过程
    3.1.1 BIOS和主引导记录MBR

    实模式和保护模式
    实模式(实地址模式。REAL MODE)

    • 程序按照8086寻址方法访问0h - FFFFFh(1MB)空间
    • 寻址方式:物理地址(20位)=段地址:偏移地址。
    • CPU单任务运行

    计算机的工作模式有两种,一个叫做实模式,一个叫做保护模式,在计算机刚刚加电启动的过程中计算机处实模式下面,实模式也叫REAL MODE。
    实模式的特点就是在实模式下面程序安装8086的方式来 工作能够访问的内存空间是1MB在0h 一 FFFFFh这个范围里面。
    1MB地址空间地址有20位采用段地址:偏移地址的方式来表示20位地址。段地址16位,偏移地址16位,CPU采用单任务运行,这是CPU刚开机的时候使用实模式。

    保护模式(内存保护模式,PROTECT MODE)

    • 寻址方式: 段(32位)和偏移量(32位),寻址4GB空间
      • 段的属性:起始地址,存取属性,权限级别,…
    • 段页式寻址机制(段,页)
    • 虚拟地址,进程,封闭空间
    • 应用程序和操作系统运行的环境都被保护
    • CPU支持多任务

    另外一种模式叫做保护模式,在操作系统启动起来以后,计算机也进入到了保护模式下面,保护模式也叫内存保护模式PROTECT MODE 保护模式的寻址方式和实模式发生了完全不同的变化。
    在保护模式下面,段的概念有了新的含义,在保护模式下面采用了段页式的方式来进行寻址,用到了虚拟地址,应用程序和操作系统运行环境都被保护起来了。保护是保护了内存,CPU是多任务。
    在这里插入图片描述
    在实模式下面可以存取1MB存储空间,1MB是这样分配的,前面640K是基本内存,中间128K属于显卡,最末尾的256K属于BIOS。
    在这里插入图片描述
    BIOS又可以细分为以上三类,C0000 - - C7FFF属于显卡的BIOS;C8000 - - CBFFF属于IDE控制器的BIOS;最后F0000 - - FFFFF 最后的64KB属于系统BIOS。
    系统BIOS
    Basic I/O System (Firmware ,固件)

    • 基本输入/输出系统
    • 位置:F0000 - FFFFF
    • 功能
      • 系统启动配置
      • 基本的设备I/O服务
      • 系统的加电自检和启动

    在这里插入图片描述
    BIOS不属于硬件也不属于软件,我们把它叫做固件Firmware,固件是以硬件的方式存在,但是内部是软件是程序,以硬件形式存在的程序或软件我们把它叫做固件。
    BIOS的中文就是基本的输入输出系统,顾名思义它能够进行系统的输入和输出。
    它的位置位于最后的64KB,BIOS的功能有三个,第一个是完成系统的启动配置;第二个就是提供设备的基本IO服务;第三个是系统的加电自检和启动。

    系统启动配置
    CMOS设置

    在这里插入图片描述
    对系统的参数进行配置,对系统的日期,系统的启动分区,对系统的启动设备进行选择,我们对键盘上面大小写键进行设置,对数字键进行设置,都是在里面进行设置,这个设置也叫C模式设置,对系统重要的参数进行配置

    基本的设备I/O服务
    BIOS使用的中断类型号为10H ~ 1FH
    在这里插入图片描述
    BIOS提供的IO服务采用中断的方式来呈现,如上图13H对于软盘I/O 调用,软盘的输入输出服务,我们要去读写软盘可以使用13H中断。
    INT 13H磁盘读写服务
    在这里插入图片描述
    如上图,13号中断的02号子功能可以帮助为读扇区,03H功能可以写一个扇区
    在这里插入图片描述
    比如说13H的02H子功能可以提供以上参数来读一个扇区,读扇区读到ES:BX这地方是软盘扇区里面读出来的内容把它写到缓冲区的地址,这是BIOD提供的基本输入输出功能。
    加电自检POST

    • Power On Slef -Test(加电自检)
    • 初始化基本硬件
      • CPU,内存显卡
    • 自检正常不提示,错误则通过喇叭提示。
      BIOS提供加电自检和自举,计算机按下这个电源键,那它就开始进行加电自检,在加电子检的过程中会初始化一些基本硬件,比如说CPU、内存、显卡…这一些最核心的硬件进行初始化,如果在自检过程中发现异常会发出dilidili的提示声,不同的错误提示的声音是有所差别的。

    按下PowerOn 或者Reset键执行第1行
    执行FFFF0处的指令
    在这里插入图片描述

    计算机在按下电源键或按下复位键的之后CPU开始执行指令执行程序,按下电源键或按下复位键,计算机执行的第一条指令是什么指令呢?或者说执行第一条指令地址在哪里呢?地址在FFFF0处,为什么开机或复位的时候第一条指令位于FFFF0处这个位置呢?这与CPU内部寄存器的初始化有关系,CS:IP这两个寄存器在初始化之后分别是FFFF和0,所以CPU复位以后执行的第一条指令就在这个位置。
    那么内存的FFFF0的位置分别是什么指令呢?
    是一条跳转指令JUMP POST;这条指令就跳转到加电自检,这是加电自检的工作。

    在这里插入图片描述

    加电自检完成以后会继续查找显卡的BIOS,调用显卡的BIOS,来完成显卡的初始化工作,然后依次检查其它设备的BIOS并且调用这些BIOS来执行相应设备的初始化,之后显示启动画面,启动画面会显示BIOS的信息,芯片组的型号,以及主板的信息。
    在这里插入图片描述

    再往后面就是从硬盘、软盘或光驱或U盘来读入相应的操作系统,到底是从哪一个设备读取的操作系统,要看我们在C模式设置下面设定的是哪一个设备,是我们的启动设备。
    一般情况下都是选择硬盘启动,操作系统起来以后会由操作系统来接管计算机。这是BIOS的加电自检和自举的一个功能。
    在这里插入图片描述

    在刚才BIOS自取的过程中,它会去读硬盘上面或软盘上面存放的操作系统。
    在BIOS读硬盘的过程中,它会首先去读硬盘或软盘的一个特殊的扇区,这个扇区就是硬盘或软盘的首扇区,这个首扇区里面放的内容,我们叫做主启动记录MBR。MBR的内容存放在硬盘/软盘的首扇区里面,放的内容是和操作系统启动的相关信息(Main Boot Record)比如说操作系统放在那个路径下面,操作系统需要什么样子的参数,这些和操作系统启动有关的信息都放在首扇区里面,我们把首扇区里面的内容把它叫做主启动记录。这个记录512个字节,它的结束符很特殊0xAA55h。
    前面512个字节可以根据实际的情况存放有效的代码。
    在这里插入图片描述
    主启动记录与硬盘的分区以及格式化的关系

    在这里插入图片描述

    上面的白色大方框代表整个硬盘,硬盘的第一个扇区很特殊,我们用它来存放MBR也就是主启动记录。接下来的空间再来进行分区。
    在这里插入图片描述
    中间第二个大框把它作为一个分区把它分给Windows用,我们用Windows FAT32/NTFS进行格式化,剩下的空间在来分个区域。
    在这里插入图片描述
    这里是Linux 分区,我们可以用Linux里面的EXT2/EXT3/…对它进行一个格式化,剩下的空白是其他分区,其他的文件 系统。
    现在已经有三个分区,那三个分区它在格式化的时候,它会在每个分区的首扇区记录下一些特殊的内容,每一个分区的首扇区都是非常特别的区域,在每一个分区首扇区放的内容我们叫分区启动信息。而首扇区的内容我们叫做主启动记录。
    剩下三个分区的首扇区我们叫做分区启动记录PBR。
    在这里插入图片描述
    主启动扇区的内容它的作用就是用来启动操作系统,用来加载操作系统,或者进行多操作系统的启动管理。
    在这里插入图片描述

    具体来说MBR里面的内容有三方面的功能,用来提供菜单让用户有多操作系统的选择,或者是加载核心文件,直接去加载操作系统,再或者进行跳转工作跳转到其他可启动的区域去执行操作系统的加载实现一个跳转。

    在这里插入图片描述
    这是关于MBR的内容
    在这里插入图片描述
    上图是BIOS和MBR的运行过程,在加电启动的过程中,用户可能会进行一个C模式的设置(硬盘启动)比如说我们要设置极端几系统从硬盘启动或者从U盘启动,从光驱启动都可以,我们可能会进行一个C模式的设置,接下来BIOS它会去读取MBR的内容,把它读取到内存里面去,接下来就把控制权交给MBR。
    MBR里的程序就知道应该去如何读取操作系统,把操作系统读到内存里面来,接下来就是MBR它会读取启动的分区表,找到启动的活动分区,并且确认其他的活动分区都不是确认的接下来活动分区就会读取第一个分区,也就是分区引导记录PBR,并且把PBR加载到内存里面去,接下来就是PBR它会去控制后面的引导过程。

    3.1.2 操作系统启动过程

    在这里插入图片描述
    操作系统的启动是指的从加电开始到用户的工作环境建立好,把桌面准备好的过程叫做操作系统的启动过程。
    从加电开始到最终用户可以直接使用计算机桌面出现,这个过程包含三个步骤,初始引导、核心初始化、系统初始化。

    在这里插入图片描述

    我们首先来看初始引导,初始引导的目的是要把操作系统的核心装入到 内存并且使之接管计算机系统。
    操作系统的核心是以文件的方式存在于硬盘上面,在安装操作系统的时候,操作系统的核心就是以文件的方式从光驱上面或者从U盘这种安装盘上面拷贝到硬盘特定的存储路径下面。

    初始引导的过程,首先计算机加电,加电就跳转到加电自检上面JUMP POST 开机的第一条指令,加电自检完成以后,就去执行BIOS里面的启动程序。
    BIOS里面有一个启动程序,启动程序的作用就是去读取0面0道第一扇区的内容(MBR)0面0道第一扇区,也就是分区的首扇区,或是硬盘的首扇区或是软盘的首扇区,就是MBR,把MBR的内容加载也就是加载MBR里面的引导程序到内存里边来。
    引导程序进入内存以后就去运行引导程序,引导程序会根据相关参数,读取硬盘指定位置的文件读到内存里,MBR的引导程序,它知道该如何读取操作系统的内核文件,操作系统的内核文件放在什么地方,该如何初始化操作系统内核,在引导程序里面都有相关的参数,引导程序的作用就是把操作系统的内核把它读到内存里面去,并且让操作系统内核运行起来,这是初始内核过程,这个过程用到几个程序,第一个是启动程序,
    这个启动程序位于BIOS里面,第二个是引导程序,引导程序位于MBR里面位于硬盘的首扇区或者位于启动设备的首扇区一样。引导程序的作用就是去引导OS内核,这是第三个我们要关注的程序,操作系统内核。
    在这里插入图片描述
    核心初始化的目的就是让操作系统的内核初始化系统的核心数据。比如说完成寄存器的初始化,完成内存的初始化,完成页表的初始化以及构建核心进程,这是核心初始化。
    在这里插入图片描述
    核心初始化完成以后就是系统初始化,系统初始化的作用就是为用户最终使用系统做准备,使系统处于待命状态。
    什么为待命状态?
    桌面出现或者是控制台出现,我们看到桌面看到控制台就可以正常使用计算机。这个过程我们叫做系统初始化,系统初始化的主要工作包括初始化文件系统,初始化网络系统,初始化控制台,初始化图形界面,等等。

    在这里插入图片描述
    特定的操作系统它的启动过程
    Windows启动过程首先加电自检,加电自检完成以后,BIOS进行一个初始引导,BIOS从MBR里面读取引导程序,把引导程序装到内存,接下来的引导程序就知道要怎么启动Windows,这个引导程序它就知道应该先去启动DOS7.0,然后把DOS7.0调入内存,这是调入操作系统的核心,接下来Windows就开始接管系统,然后进行核心初始化,进行系统初始化。
    在这里插入图片描述

    Windows核心初始化过程就包括对于资源的状态核心的数据进行相应的初始化,对于系统初始化最重要的就是GUI的生成,使系统处于最终的待命状态出现桌面或者出现控制台,这个核心初始化过程和系统初始化过程这些过程非常复杂就不去细究它的细节

    在这里插入图片描述

    LINUX启动过程,首先是加电自检,然后BIOS把MBR读入内存,MBR里面的引导程序就回去加载Linux内核的映像,Linux内核映像前面是非压缩的,后面是压缩的所以KERNEL内核映像加入到内核区域之后它是边解压边执行,最后内核初始化内核启动最终控制台出现或者是,桌面出现。
    Linux 的内核映像是一个压缩映像,当然前面有一部分代码是非压缩的,后面大部分是压缩的这样可以节省内核的内存,尤其是在嵌入式系统里面对内核的大小是比较敏感的。

    在这里插入图片描述

    内核完成引导以后,加载init程序,加载init进程,init编程它的编号是1,就是1号进程,1号进程它的主要作用就像它的名字所暗示的一样就是完成初始化的。
    init进程执行这个/etc/inittab脚本,通过这个脚本始化,不同的用户具有不同的运行级别,脚本是有差异的。
    脚本文件主要是能完成键盘的设置,字体的设置还有网络设置,装载一些特定的模块,这是init程序init进程。
    在这里插入图片描述
    Linux登陆的过程,上图流程图显示的是Linux的登陆过程,init进程首先可以通过fork函数去执行登陆getty,去执行终端的登录,fork是一个创建进程的函数,接下来等待用户的登录,用户输入用户名,读取密码,验证密码,验证不正确就退出重来,如果匹配的话就往下面执行,执行exec,不同的用户有不同的shell,执行这个用户对应的shell,接下来读取用户输入的命令,用户在控制台上面输入什么命令呢?在sh:的地方读取用户输入的命令,执行这个命令,如果用户输入的是退出命令,就回到最上面重新来登陆。这个linux里面用户登录过程。
    3.2 操作系统生成

    我们平时上面用户用的操作系统我们都是从网上下载的或是购买的,我们这一节要介绍我们如何生成一个自己的操作系统,对现有的操作系统进行优化,或者裁剪。

    在这里插入图片描述

    操作系统的生成指的是满足特定的硬件环境和用户需要,组装和构建操作系统的过程。
    特定的硬件环境,不同的硬件配置就需要操作系统对不同的硬件有响应的支持,硬件变化了那操作系统对应的模块也应该发生变化。
    用户的需求,有些用户可能需要使用到网络,有的用户可能不需要使用到网络,有的用户可能需要使用到光驱,有的用户可能不用光驱,根据 用户的需要,根据用户的需要改变操作系统的功能模块,根据硬件环境的变化改变操作系统的功能模块。
    通过操作系统满足硬件环境满足用户的需要,我们来组装和构建一个新的操作系统。这个过程我们称为操作系统的生成。
    在这里插入图片描述

    操作系统生成的主要步骤第一步就是根据硬件的环境和用户的要求配置功能模块和构造参数。
    第一步就是要根据硬件的配置和用户的要求来配置操作系统里面各个功能模块,操作系统由很多功能模块组成,有些模块我们要,有些模块我们不要,根据用户的要求和硬件的配置我们来决定,不同的用户对操作系统的工作参数要进行改变,不同的运行环境也需要对操作系统的参数进行变换。这是生成操作系统的第一步配置功能模块和构造参数。第一步做完以后它的结果就是修改操作系统的源代码。第二步就是要构建操作系统映像,构建的意思就是build的意思,源代码已经对它进行配置参数进行调整,第二步自然是编译操作系统的源代码,重新把它编译为二进制的映像。

    在这里插入图片描述

    操作系统生成前提,要生成一个新的操作系统的前提有三个,第一个是操作系统是由可拆装的模块构成的,操作系统本身是由一个个可拆剪,可拆装的模块构成。如果整个操作系统它是一个整体,所有的功能都密不可分,那么这样的操作系统我们就没有办法对它进行一个重新的配置,重新的参数调整。
    第二个是需要交互式的配置工具,我们要有工具而且是交互性的工具去改变操作系统现有的配置,改变现有的参数,要有这样子的交互工具。
    第三个是要有映像的构建工具,我们要能够把第二步获得的源代码对它进行一个重新的编译。要有一个编译工具一个build工具把它重新生成二进制文件。这是生成操作系统的三个前提。
    在这里插入图片描述

    Linux操作系统满足我们之前说的三个前提,上图是linux官网,官网首页列出了最新的linux版本,我们要生成新的linux,生成自己的linux往往需要在这个网站上下载特定的源代码,然后对它进行重新的配置,编译,然后让它生成一个新的自定义的linux。

    在这里插入图片描述
    linux操作系统生成可以分为以上6步。

    在这里插入图片描述

    第一步是首先获取Linux源代码,版本很多,可以用最新版本的也可以用老一些的版本,我们linux网站上面下载某个版本的源代码,然后把它解压。

    在这里插入图片描述
    第二部就是要选择和启动内核控制程序,要生成操作系统有三个前提条件,其中有一个就是要有一个交互式的配置工具,我们需要选一个合适的配置工具,用来配置内核,在linux里面有六个命令,或者说有六个工具来对内核进行交互性的配置

    用得最多的是 make meuconfig,采用文本选择界面这样一种形式和用户交互,在字符终端上面给出一个类似于窗口的交互界面。

    在命令行里面输入make menuconfig就会出现这个界面
    在这里插入图片描述
    make menuconfig这个代码应该在linux内核源代码解压以后,在主要文件夹里面输入这个命令才能 出现这个一个界面,也就是说这个命令一定要
    命令原代码解压之后在主目录中输入这样的命令才会出现这样一个界面,这个界面是类似于窗口的图形界面,在这里面我们可以通过上下的光标键,来选择操作系统的各个模块,每个模块前面有个标识,通过反复按下空格键,前面的标识会有变化,有什么变化呢,如果这个模块我们需要把它编译到内核里面,模块标识符号就会选中
    在这里插入图片描述
    会变成M或者变成Y表示我们要选中这个模块放到内核里面,如果我们不要这个模块我们认为硬件配置上面或者用户需求里面不需要这个模块i那我们通过反复切换空格键中括号内容会变成空我们不要这个模块。

    我们也可以通过make xconfig通过这个命令对计算机进行配置,配置界面类似于这样一个界面,
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    我们在第二步的交互配置过程中,我们会遇到类似这样的一些选项,这些选项都代表操作系统一些不同的模块或者是不同的一些系统配置参数,比如说Loadable module support它就表示我们需不需要操作系统 对模块进行支持,linux它是一个可以支持模块机制的操作系统,我们需不需要linux对这个模块进行支持呢,在这些条目里面可以做相应的配置。

    比如说我们可以选择CPU的类型,你是windowsCPU还是ARM的CPU,还是PowerPC的cpu可以在Rrocessor family 进行选择,
    这是根据硬件的配置,来改变操作系统的一些参数。

    在这里插入图片描述
    比如说上图有PCI卡的支持关于并口的支持,我们在标准的PC机里面都有PCI卡和并口的,嵌入式系统里面由于硬件是由用户定制的,在有一些嵌入式系统里面就不一定有PCI卡,不一定有并口,
    所以我们在这样子的操作系统里面可以不对并口做支持,不需要操作系统对PAI卡有支持,还有块设备,软盘,网络这些模块在操作系统里面可要可不要,要根据硬件的配置,根据用户的要求
    在这里插入图片描述
    再比如说TCP/IP模块网络设备的支持,还有串口的支持声卡的支持,USB的支持,等等… 这些可以根据用户的要求用户的配置来选择,所以这些内容是我们第二步在进行交互配置的时候需要去理解和应用的。
    第三步做完以后默认的操作系统源代码就已经被我们修改了,根据用户的要求根据硬件的实际配置情况,我们已经把源代码进行了一个修改,所以我们接下来要做的就是重新编译新的内核。

    在这里插入图片描述

    我们可以先后使用 make dep,make clean,make bzImage把内核编译出来,编译出来的结果就是一个二进制文件。
    在这里插入图片描述

    第五步就是要编译安装相应的模块可以使用 make modules 、make modules_install这两个命令,第一个命令是编译模块,第二个命令式安装模块。

    在这里插入图片描述
    第六步就是要让我们刚刚编译好的操作系统内核要生效,所以第六步骤就是要启动新内核让新内核来控制计算机。
    要让新内核来控制计算机油这样几个操作我们要把编译出来的新内核,新的映像文件把它拷贝到/boot/bzImage这个文件目录下面。
    接下来就要来配置GRUB,这个GRUB是一个非常主流的配置多操作系统配置启动过程的工具。
    对 /boot/grub/grub.conf这个配置文件对它进行配置。
    上图下方的内容
    title 用来指示我们新编译的内核,这个newLinux build by Zhang San Feb.28,2018
    这个提示信息出现在多操作系统选择界面的提示信息,这是我们自定义的。

    root(hd0,1)表示启动设备,启动路径。kernel表示操作系统对应的映像文件就是/boot/bzImage
    它的根目录 /dev/hda2,我们按照/dev/hda2这个模版修改grub.conf这个文件。

    不同发行版本的linux里面这个配置的过程有差异,一定要根据自己选用的linux版本参造这一种思想来配置启动过程,不能完全照抄,比如说路径可能有变化,配置文件可能有变化,格式可能有变化
    在这里插入图片描述

    Linux操作系统的生成可以参考以上几个网址

    3.3 用户界面

    3.3.1 用户界面概念

    在这里插入图片描述
    用户界面的定义,用户界面指的是操作系统提供给用户,控制计算机的一种机制,又称为用户接口。
    用户通过操作系统提供的机制来使用计算机,我们把这样一种交互机制我们称为用户界面。

    在这里插入图片描述
    用户界面有两种,一种叫做操作界面,一种叫做系统调用。
    典型的操作界面

    在这里插入图片描述

    我们可以通过操作系统提供的图形界面来使用计算机,我们在绝大多数的时候,普通用户基本上都是使用这种图形界面来使用计算机。

    我们可以通过图形界面上面的图标、按钮、菜单、工具栏这一些图形元素来操作计算机,计算机操作系统提供给用户的手段去控制计算机。
    在这里插入图片描述

    我们有时候也会去用黑白命令界面,控制台方式的界面去控制计算机,我们会在控制台上输入各种命令来使用计算机,这也属于操作命令的一种。
    在这里插入图片描述
    有时候也可以编写批处理命令来完成一系列的动作,这也属于一种操作界面。
    在这里插入图片描述
    有图形用户接口,有键盘命令,键盘命令我们可以在控制台上面输入各种各样的命令,键盘的命令又包括普通的命令,批处理程序还有shell 脚本。
    普通命令是最常见的,批处理程序在windows里面有一种*.BAT的程序属于批处理程序,可以自动执行一系列相关的操作,shell脚本程序属于linux里面的都属于操作界面
    在这里插入图片描述
    上图是典型的DOS命令,也是操作系统提供给用户用来控制计算机的重要的形式。

    在这里插入图片描述
    这是linux里面一些常规的命令,必须要熟悉。

    在这里插入图片描述
    批处理是普通命令的集合,按批来执行一系列命令,而且这些命令之间有一定的逻辑关系。

    可以通过变量替换,条件,转移,循环,注释这样一些基本编程的语法来编程批处理程序。批处理程序由command来编译执行,在windows里面批处理文件的后缀.BAT

    把1~100之间的整数累加起来,我们知道这样累加的结果是5050,我们要写这样一个批处理程序要怎么写?
    在这里插入图片描述
    批处理程序里面有一些语法一些关键词,里面有一条for循环语句,因为我们要从1累加到100这是一个for循环,“/l”表示后面要处理的数据集它是一个列表表示list。后面,括号里面表示一个数据集,数据集是从1累计到100每次递增1,这个双百分号i“%%i”表示i是一个自变量,双百分号%%表示变量的一个定义,这个for循环的意思是说从1到100,每次累加1,从这样一个自变量做一个循环,在这样一个循环体里面,做sum +=%%i,这个循环的意思就是从1累加到100,最后把结果打印出来。
    3.3.2 Shell 脚本编程

    在这里插入图片描述
    shell 是linux里面用来和用户进行交互的界面,shell上面是用户,下面是操作系统,shell 也属于用户和操作系统之间交互的机制。

    在这里插入图片描述

    交互过程,用户发布命令,命令交给shell,shell传给操作系统。
    shell的表现形式通过控制台来执行用户的命令这样的表现形式
    所以在控制台使用的各种命令都是通过shell的方式和计算机进行交互。操作系统在内部执行命令以后把结果通过shell返回给用户展示出来,这是shell大概的一个工作过程

    在这里插入图片描述
    shell在历史上主要有两种比较经典的类型,最早是Bsh,Bsh的特点就是有非常强的脚本编程能力。另外一个是Csh,Csh非常适合命令的交互使用,还有一个Ksh,Ksh结合了Bsh和Csh的优势。Bash是目前主流的一种shell 。Bash它是Bsh的升级也结合Ksh的优点。Bash是目前非常主流的。

    在这里插入图片描述

    像命令行的编辑功能、命令的历史管理功能,命名的别名功能,还有管道的重定向,shell的脚本编程都是Bash的功能。
    在这里插入图片描述
    在这里插入图片描述
    在linux里面我们要输入一个命令,输入命令前面几个字符之后,敲TAB键,它就能自动帮我们帮外补全。在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    这是重定向的操作

    在这里插入图片描述

    比如说我们可以用ls命令显示/etc下面的子目录和文件的结构,把我接这个展示的结果重定向到etcdir.log 这样一个文件里面。在控制台上面我们看不到ls命令的执行结果,这个命令的执行结果已经写到了etcdir.log里面,这个就是重定向。我们从ls命令的输出结果从控制台上面重定向到etcdir.log这个文件里面。

    在这里插入图片描述
    管道实际上是重定向的一个升级应用,管道的符号是一根竖线,管道操作符用于连接管道左右两个命令,讲管道操作符左边命令的执行结果也就是输出作为管道右边的输入,对于管道前面命令来说,它的输出做了重定向,对于管道后面来说,它的输入做了重定向。
    在这里插入图片描述
    在这里插入图片描述
    同一个命令也可以使用多个管道命令操作符,把多个命令连接起来。这是关于管道的功能

    在这里插入图片描述

    shell是用户和操作系统之间进行交互的一个机制,shell里面除了支持普通命令的执行,它也支持shell脚本程序。

    在这里插入图片描述
    在shell里面如何执行脚本编程,脚本也叫做script,脚本是通过类似于程序的方式来执行具有一定逻辑顺序的命令序列来完成较复杂的功能和人机交互。
    非常类似windows里面的批处理程序,脚本程序 它以文本文件的形式表现,脚本程序放在文本文件里面,是一个可读的文本文件。
    脚本文件是shell命令语句的集合,这个集合当然是一个有序的集合,有逻辑顺序的命令集合。

    在这里插入图片描述
    上图是一个脚本程序,这个脚本程序它的作用是用来帮助用户安装一个程序,我们编写好一个程序,把它打包交给用户去安装。这个脚本就是用来帮助用户去安装程序包的。

    ldconfig是使配置文件生效。rm删除文件,删除目录,rm -rf这个表示要删除一个文件夹。把文件夹里面的子目录子文件夹都把它删掉。

    在这里插入图片描述

    脚本程序 它是由shell环境来解释执行的。这个脚本程序不需要编译,执行脚本程序,执行脚本文件它需要可执行的属性,一个文件的可执行程序用x表示,所以我们编写完一个脚本文件以后,我们需要chmod+ xMyScript.sh 这样一条命令去给脚本文件增加可执行属性,Myscript.sh是脚本文件的名称。我们根据实际情况来改变。chmod +x 是给脚本程序加可执行程序的属性,比如说我们不给这个脚本程序加上可执行的属性,那么这个脚本程序可能无法执行。

    在这里插入图片描述

    上图代码的功能是让用户在键盘上面输入大写小写的Y都可以,它就输出yes;如果用户输入的是大写小写的N就输出no。
    上图这样的小程序根据用户输入做出相应的反馈,脚本程序执行以后就是下图的结果:
    在这里插入图片描述
    当用户输入y的时候,控制台输出yes,如果这个脚本程序再执行一遍,用户输入n,那控制台就会输出no。
    在这里插入图片描述

    read从键盘读入一行字符,把各个字段赋给相应的变量。后面的answer是一个变量,如果用户在键盘上面输入了相应的字符,这个字符 就会赋给answer。
    在这里插入图片描述

    -n是用来指定输入字符的个数,当我们键盘上面输入字符的个数达到-n指定数量的时候,就自动结束输入,字符串后面会自动加上一个回车符表示输入结束。

    -p这个输入它会给出提示信息,当我们要在键盘上面进行输入的时候呢,我们希望在控制台上面能够有个提示,我们要输入什么东西,-p表示要用户建立一个字符建立一个选择。
    在这里插入图片描述

    这里面还有echo关键字,echo它的作用是用来显示字符串。
    echo的使用形式echo[-n] 字符串,使用方式 -n 这个参数是可有可无的,后面是字符串,这个字符串就是要在屏幕上面要显示的字符串。这个字符串可以加引号,也可以不加引号。如果这个字符串加引号的话那么就是原模原样在屏幕上面显示。如果是不加引号,那么这个字符串里的空格如果是两个空格或多个空格,就会把它压缩成一个空格再把它显示出来。

    如果我们要显示的字符串上面有很多空格,那么连续的多个空格就会压缩为一个空格。

    -n的选项是用来控制输入字符串以后要不要回车要不要换行,加-n 就表示输入字符串之后不换行,用户如果要继续输入的话,就是在之前输入的字符串后面继续进行输入。

    在这里插入图片描述
    在刚才的例子里面也涉及到了case的语法结构,case在c语言里面是一个多分支的选择。

    case 是语法; expr是变量;in是用来检查这个变量它的取值是什么样的一个值,如果expr这个表达式上的值是pattern1那么就执行commands1 这样一个分支;如果expr的值是pattern2 这个值,那么就执行commands2 的分支下面的类似。
    case语句借宿esac
    esac就是case反过来写
    read 我们要读一个字符串读到answer 里面这个字符串是多少我们用-n 1表示,只要你输入一个字符,如果输入的字符是大写的Y或者是小写的y就执行echo yes:: 分支,输出yes;如果输入是大写的N或者是小写的n就执行echo no::这是执行的结果,用户输入的是y,当用户输入一个字符的时候,下面就会显示yes

    在这里插入图片描述

    上图的例子是要输入两个整数,x,y两个整数来比较他们的大小。
    echo -n “Please input x,y:”要求用户输入两个整数x,y;这是一个提示让你输入x,y。
    read x,y 是读入x,y。
    echo “x = $x, y= $y”这是把我们输入的x,y打印出来。让我们在确认一下x等于多少,y等于多少。接下来就是一个if语法块。
    if语句的结束是fi,fi就是if反过来写。

    在这里插入图片描述

    这是if语句的基本结构。
    在这里插入图片描述
    这个程序的执行结果,如果我们输入的是20、30
    就会在屏幕上面有
    x= 20,y = 30 的提示。
    输出字符串x比y要小。
    在这里插入图片描述
    这是if语法块shell脚本程序的例子。

    在这里插入图片描述
    脚本程序写好以后我们 有三种方法来运行脚本程序,方法一是直接运行,我们直接在控制台上面输入脚本的名称。
    在操作系统里面可能会装不同版本的shell,到底用那个shell来执行脚本程序?用缺省版本的shell
    ,每个用户登录以后对于缺省得用户对应。

    在这里插入图片描述

    方法二使用特定版本的shell来执行脚本程序。
    我们可以在控制台上面直接指定用bash这样的shell来执行脚本程序。
    指定shell 有时候会需要指定路径,大多数时候我们没有指定路径那系统要能够找得到对应shell的路径。

    第三种情况就是我们在脚本程序里面,首行加上#!/bin/bash 这样一行内容就意味着执行这样子的脚本程序就用/bin/bash 这个shell 来执行。这是在脚本程里面指定用什么shell来执行脚本。

    3.4 系统调用

    在这里插入图片描述

    用户界面分两类 一类是操作界面,另一类叫做系统调用。

    在这里插入图片描述
    上图程序有一个函数add,这个add函数是我们自己定义的,把两个整形变量相加,返回它的和。这个函数可以是自己编写的也可以利用第三方的函数。
    总之在上图的应用程序里面我们用了add这个函数,我们在编程的时候经常会用到各种各样的函数,这些函数或者这些子模块可能是自己编写的可能是系统提供的,可能是某个第三方提供的。

    在这里插入图片描述

    上图是DOS汇编的程序,它的功能是我们要在屏幕上显示“Hello!”的字符串。

    这些程序很简单我们利用了DOS里面的21号中断09号功能来显示字符串。

    这个程序编译运行以后在屏幕上面就会打印出hello,这个程序用到了09号功能就是在屏幕上面利用显卡输出字符串。这涉及到了底层的硬件操作。21号中断的09号功能这是DOS操作系统给用户提供的一个功能,我们可以看成是一个函数或者是子模块,供应用程序调用。

    在这里插入图片描述
    这是linux里面的一个c程序。打印Hello World 然后程序退出,printf()和exit()都是系统提供的函数,这两个操作都涉及到外设或显卡的操作。因为我们在屏幕上面输入字符串,涉及到了进程的操作,我们知道exit是结束进程,结束程序的函数,这个程序也涉及到了底层操作系统内核的操作。

    在这里插入图片描述
    系统调用使操作系统内核为应用程序提供的服务或者是函数,我们再刚才的例子里面遇到21h(09)21号中断的09号功能,printf()和exit()都属于操作系统内核给我们提供的服务,这个服务都是在底层进行的,有的做硬件操作,有的对进程进行操作。在操作系统里面也有很多类似的函数供应用层程序来调用,我们把这样的函数和服务叫做系统调用。
    系统调用是在内核里面实现的,在用户程序里面我们需要调用sys_foo()这个函数,但这个函数是在内核空间实现的,我们把类似这样的函数模块服务叫做系统调用,系统调用也是操作系统提供给用户的交互机制。

    在这里插入图片描述
    系统调用的特点,系统调用一般涉及核心资源或者是硬件的操作,类似printf() ,exit()函数都是在内核对硬件资源进行一个操作;
    系统调用运行于核态,这与普通函数不一样,普通函数它也可能和用户程序一样都运行在用户态,但是系统调用一定是运行在内核空间,由操作系统提供的;

    在这里插入图片描述
    每一个系统调用都有一个唯一的编号,操作系统会提供一系列的系统调用给用户来使用,于是linux里面有两百多个系统调用给用户使用。不同版本的linux内核提供系统调用的数量不一样,有两三百个,每一个都有唯一的编号来区分。

    在这里插入图片描述
    系统调用在调用过程中会产生在中断。而且这个中断是自愿中断,用户程序要调用系统调用sys_foo(),在调用这一刻会产生中断,这个中断是程序员安排的,程序员需要在sys_foo 这个地方实现系统调用,这样的中断我们叫做自愿中断程序员安排的中断。
    只要使用了系统调用就一定会产生中断。
    在这里插入图片描述
    call X 表示调用X号系统调用,在操作系统内核里面提供了N个系统调用。当应用程序要调用X号系统功能,在这个过程中会产生中断,当我们调用call X 的时候立即产生中断,虚线框里面就是发生中断的过程,发生了中断,系统要对它进行响应。
    首先保护现场,这是标准的中断过程,我们根据系统调用号X来找到正确系统调用的入口,我们找到X号地址找到X号功能,X号功能执行完以后进行中断返回,返回到中断的地方,继续往下运行。这是系统调用产生的过程或中断响应过程。

    在这里插入图片描述
    DOS系统调用例子,DOS里面利用的是21号中断,让用户进行系统调用,在DOS里面所有的系统调用最终都会通过21h中断来完成。
    这这个中断之前用AH来接受用户指定的系统调用号。
    若调用09号系统调用,09号系统调用就是输出字符串,在屏幕上面显示,所以在DOS里面是利用AH寄存器来存放系统调用的编号最后产生21h中断。

    在这里插入图片描述

    在21号中断里面可以调用很多系统调用。

    在这里插入图片描述

    linux里面使用80号中断来实现系统调用,我们要显示Hello 字符串我们可以使用80h中断,在linux里面用EAX寄存器接受系统调用号。EAX指定系统调用号为4,就意味着我们要在屏幕上显示字符串。这是linux里面使用EAX寄存器来使用调用号。
    在这里插入图片描述
    在这里插入图片描述

    在内核实现的时候printf()和exit()都会引发中断,我们成为隐式系统调用,表面上是普通的API函数实际上在内部会转换为“INT 80h”这样的指令会引发中断

    在这里插入图片描述

    在这里插入图片描述
    把c程序转化为汇编,我们能够很清楚的发现,

    在这里插入图片描述

    红框模块指的就是printf,里面有INT80这样子的终端指令,用EAX来指定系统调用号4号
    在这里插入图片描述
    蓝框代码里面也转换为了INT 80终端指令用EAX指定1号系统调用,结束进程。
    我们把c代码转换为汇编代码会发现系统调用确实会转换为终端指令,普通的函数转换成汇编以后不包括INT这样的中断指令,这是隐式系统调用的特点。

    在这里插入图片描述
    应用程序调用了XYZ这样的系统调用,这个程序先是在用户态工作,这个系统调用会在c库里面转换为包含INT80指令的汇编代码。这是一条中断指令包含80号中断,在linux里面系统调用都会转换为包含INT指令的代码,会引发中断。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    中断服务程序会负责响应中断源,把用户调用的xyz函数转向到系统内部的调用,在操作系统内部会在xyz前面加“sys_”+xyz这样的前缀,表示现在内核实现系统调用。

    在这里插入图片描述

    展开全文
  • 图形用户界面

    2012-11-23 13:28:42
    一:常用的GUI构件 JButton, JCheckBox, JRadioButton, JComboBox, JList, JTextField, JTextArea, JPanel JSlide; 二:基于GUI程序设计的一些基本思想
  • 良好交互性的图形用户界面,一些较为实用的功能
  • 设计实现了一个基于数字笔的用户界面设计工具,该工具以面向笔式界面领域,基于场景的设计思想来设计系统中的界面场景组织关系以及场景之间的动态切换方式,设计者与工具间的交互自然流畅、快捷高效。通过手势识别...
  • 智能Agent越来越多地应用于用户界面设计。通过使用Agent,用户界面(也包括整个系统)就被分解为不同的可以互相通信的Agent。界面Agent作为用户与系统之间的中介,使得用户的输入不再是方法的调用,而是以通信的方式...
  • 详细介绍了OpenGL地形绘制的过程,及其中LOD算法思想、UI介绍等
  • java的GUI编程(Graphic User Interface,图形用户接口),是在它的抽象窗口工具箱(Abstract Window Toolkit,AWT)上实现 ...的,java.awt是AWT的工具类库,其中包括了丰富的图形、用户界面元件和布局管理器的支持。
  • 用户界面与业务逻辑的分离

    千次阅读 2018-07-30 21:35:39
    用户界面模块(UI) •接受用户输入及呈现数据 -业务逻辑模块(Business Logic) •根据用户需求处理数据 用户界面与业务逻辑如何交互? (架构原则?) 2、基本设计原则 功能模块之间需要进行解耦 .....

    前面分别实现了计算器程序的用户界面业务逻辑

    基本程序架构一般包含:

     -用户界面模块(UI) :接受用户输入及呈现数据 

     -业务逻辑模块(Business Logic) :根据用户需求处理数据 

    基本设计原则 

    功能模块之间需要进行解耦 ,核心思想:强内聚,弱耦合 

     -每个模块应该只实现单一的功能 

     -模块内部的子模块只为整体的单一功能而存在 

     -模块之间通过约定好的接口进行交互 

    在工程开发中接口

     -广义:接口是一种契约(协议,语法,格式,等)

     -狭义:面向过程:接口是一组预定义的函数原型 ;面向对象:接口是纯虚类(C#和Java直接支持接口) 

    用户界面与业务逻辑的交互 

     -用户界面使用业务接口处理用户输入数据展示处理后的数据!业务逻辑用来实现接口!

     -模块之间仅通过接口进行关联 :必然存在模块会使用接口 ,必然存在模块实现对应的接口 

     -模块间的关系是单项依赖的 :避免模块间存在循环依赖的情况 ,循环依赖是糟糕设计的标准之一 

    计算器应用程序的整体架构 

               QCalculatorUI(用户界面类)使用接口功能

               QCalculatorDec(核心算法类)实现接口功能

               QCalculator 将用户界面与核心逻辑算法关联

    核心思想整理:

           1、业务逻辑类(QCalculatorDec)继承接口(ICalculator),实现接口提供的功能;

           2、用户界面类(QCalculatorUI)对象通过成员变量接口指针保存业务逻辑对象地址(UI类提供成员的set方法)

           3、QCalculator 创建业务逻辑类对象用户界面类对象,并使用用户界面类提供的set方法将其与业务逻辑类对象关联

           4、至此用户界面类对象可以顺利通过接口使用业务逻辑类提供的核心函数

    完整代码  Calculator.pro 

    ICalculator.h

    #ifndef _ICALCULATOR_H_
    #define _ICALCULATOR_H_
    
    #include <QString>
    
    class ICalculator
    {
    public:
        virtual bool expression(const QString& exp) = 0;
        virtual QString result() = 0;
    };
    
    #endif
    

    QCalculator.h

    #ifndef _QCALCULATOR_H_
    #define _QCALCULATOR_H_
    
    #include "QCalculatorUI.h"
    #include "QCalculatorDec.h"
    
    class QCalculator
    {
    protected:
        QCalculatorUI* m_ui;
        QCalculatorDec m_cal;
    
        QCalculator();
        bool construct();
    public:
        static QCalculator* NewInstance();
        void show();
        ~QCalculator();
    };
    
    #endif // QCALCULATOR_H
    

    QCalculator.cpp

    #include "QCalculator.h"
    
    QCalculator::QCalculator()
    {
    }
    
    bool QCalculator::construct()
    {
        m_ui = QCalculatorUI::NewInstance();
    
        if( m_ui != NULL )
        {
            m_ui->setCalculator(&m_cal); // UI与业务逻辑相关类对象产生关联
        }
    
        return (m_ui != NULL);
    }
    
    QCalculator* QCalculator::NewInstance()
    {
        QCalculator* ret = new QCalculator();
    
        if( (ret == NULL) || !ret->construct() )
        {
            delete ret;
            ret = NULL;
        }
    
        return ret;
    }
    
    
    void QCalculator::show()
    {
        m_ui->show();
    }
    
    QCalculator::~QCalculator()
    {
        delete m_ui;
    }
    

    QCalculatorUI.h

    #ifndef _QCALCULATORUI_H_
    #define _QCALCULATORUI_H_
    
    #include <QWidget>
    #include <QLineEdit>
    #include <QPushButton>
    
    #include "ICalculator.h"
    
    class QCalculatorUI : public QWidget
    {
        Q_OBJECT
    private:
        QLineEdit* m_edit;
        QPushButton* m_buttons[20];
        ICalculator* m_cal; // UI通过接口指针保存业务逻辑相关类对象的地址,当通过setCalculator设置关联后即可通过接口使用业务逻辑提供的函数
    
        QCalculatorUI();
        bool construct();
    private slots:
        void onButtonClicked();
    public:
        static QCalculatorUI* NewInstance();
        void show();
        void setCalculator(ICalculator* cal);   
        ICalculator* getCalculator();
        ~QCalculatorUI();
    };
    
    #endif
    

    QCalculatorDec.h

    #ifndef _CALCULATORCORE_H_
    #define _CALCULATORCORE_H_
    
    #include <QString>
    #include <QStack>
    #include <QQueue>
    
    #include "ICalculator.h"
    
    class QCalculatorDec : public ICalculator  // 实现接口
    {
    protected:
        QString m_exp;
        QString m_result;
    
        bool isDigitOrDot(QChar c);
        bool isSymbol(QChar c);
        bool isSign(QChar c);
        bool isNumber(QString s);
        bool isOperator(QString s);
        bool isLeft(QString s);
        bool isRight(QString s);
        int priority(QString s);
        bool match(QQueue<QString>& exp);
        QString calculate(QQueue<QString>& exp);
        QString calculate(QString l, QString op, QString r);
        bool transform(QQueue<QString>& exp, QQueue<QString>& output);
        QQueue<QString> split(const QString& exp);
    public:
        QCalculatorDec();
        ~QCalculatorDec();
        bool expression(const QString& exp);   
        QString expression();
        QString result();
    };
    
    #endif
    

    QCalculatorUI.cpp

    #include "QCalculatorUI.h"
    #include <QDebug>
    
    QCalculatorUI::QCalculatorUI() : QWidget(NULL, Qt::WindowCloseButtonHint)
    {
        m_cal = NULL;
    }
    
    bool QCalculatorUI::construct()
    {
        bool ret = true;
        const char* btnText[20] =
        {
            "7", "8", "9", "+", "(",
            "4", "5", "6", "-", ")",
            "1", "2", "3", "*", "<-",
            "0", ".", "=", "/", "C",
        };
    
        m_edit = new QLineEdit(this);
    
        if( m_edit != NULL )
        {
            m_edit->move(10, 10);
            m_edit->resize(240, 30);
            m_edit->setReadOnly(true);
            m_edit->setAlignment(Qt::AlignRight);
        }
        else
        {
            ret = false;
        }
    
        for(int i=0; (i<4) && ret; i++)
        {
            for(int j=0; (j<5) && ret; j++)
            {
                m_buttons[i*5 + j] = new QPushButton(this);
    
                if( m_buttons[i*5 + j] != NULL )
                {
                    m_buttons[i*5 + j]->resize(40, 40);
                    m_buttons[i*5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);
                    m_buttons[i*5 + j]->setText(btnText[i*5 + j]);
    
                    connect(m_buttons[i*5 + j], SIGNAL(clicked()), this, SLOT(onButtonClicked()));
                }
                else
                {
                    ret = false;
                }
            }
        }
    
        return ret;
    }
    
    QCalculatorUI* QCalculatorUI::NewInstance()
    {
        QCalculatorUI* ret = new QCalculatorUI();
    
        if( (ret == NULL) || !ret->construct() )
        {
            delete ret;
            ret = NULL;
        }
    
        return ret;
    }
    
    void QCalculatorUI::show()
    {
        QWidget::show();
    
        setFixedSize(width(), height());
    }
    
    void QCalculatorUI::onButtonClicked()
    {
        QPushButton* btn = dynamic_cast<QPushButton*>(sender());
    
        if( btn != NULL )
        {
            QString clickText = btn->text();
    
            if( clickText == "<-" )
            {
                QString text = m_edit->text();
    
                if( text.length() > 0 )
                {
                    text.remove(text.length()-1, 1);
    
                    m_edit->setText(text);
                }
            }
            else if( clickText == "C" )
            {
                m_edit->setText("");
            }
            else if( clickText == "=" )
            {
                if( m_cal != NULL )
                {
                    m_cal->expression(m_edit->text()); // 使用接口(QCalculator对象构造时UI与业务逻辑产生关联)
                    m_edit->setText(m_cal->result());   
                }
            }
            else
            {
                m_edit->setText(m_edit->text() + clickText);
            }
        }
    }
    
    void QCalculatorUI::setCalculator(ICalculator* cal)
    {
        m_cal = cal;
    }
    
    ICalculator* QCalculatorUI::getCalculator()
    {
        return m_cal;
    }
    
    QCalculatorUI::~QCalculatorUI()
    {
    
    }
    

    QCalculatorDec.cpp

    #include "QCalculatorDec.h"
    
    QCalculatorDec::QCalculatorDec()
    {
        m_exp = "";
        m_result = "";
    }
    
    QCalculatorDec::~QCalculatorDec()
    {
    
    }
    
    bool QCalculatorDec::isDigitOrDot(QChar c)
    {
        return (('0' <= c) && (c <= '9')) || (c == '.');
    }
    
    bool QCalculatorDec::isSymbol(QChar c)
    {
        return isOperator(c) || (c == '(') || (c == ')');
    }
    
    bool QCalculatorDec::isSign(QChar c)
    {
        return (c == '+') || (c == '-');
    }
    
    bool QCalculatorDec::isNumber(QString s)
    {
        bool ret = false;
    
        s.toDouble(&ret);
    
        return ret;
    }
    
    bool QCalculatorDec::isOperator(QString s)
    {
        return (s == "+") || (s == "-") || (s == "*") || (s == "/");
    }
    
    bool QCalculatorDec::isLeft(QString s)
    {
        return (s == "(");
    }
    
    bool QCalculatorDec::isRight(QString s)
    {
        return (s == ")");
    }
    
    int QCalculatorDec::priority(QString s)
    {
        int ret = 0;
    
        if( (s == "+") || (s == "-") )
        {
            ret = 1;
        }
    
        if( (s == "*") || (s == "/") )
        {
            ret = 2;
        }
    
        return ret;
    }
    
    bool QCalculatorDec::expression(const QString& exp)
    {
        bool ret = false;
        QQueue<QString> spExp = split(exp);
        QQueue<QString> postExp;
    
        m_exp = exp;
    
        if( transform(spExp, postExp) )
        {
            m_result = calculate(postExp);
    
            ret = (m_result != "Error");
        }
        else
        {
            m_result = "Error";
        }
    
    
        return ret;
    }
    
    QString QCalculatorDec::result()
    {
        return m_result;
    }
    
    QQueue<QString> QCalculatorDec::split(const QString& exp)
    {
        QQueue<QString> ret;
        QString num = "";
        QString pre = "";
    
        for(int i=0; i<exp.length(); i++)
        {
            if( isDigitOrDot(exp[i]) )
            {
                num += exp[i];
                pre = exp[i];
            }
            else if( isSymbol(exp[i]) )
            {
                if( !num.isEmpty() )
                {
                    ret.enqueue(num);
    
                    num.clear();
                }
    
                if( isSign(exp[i]) && ((pre == "") || (pre == "(") || isOperator(pre)) )
                {
                    num += exp[i];
                }
                else
                {
                    ret.enqueue(exp[i]);
                }
    
                pre = exp[i];
            }
        }
    
        if( !num.isEmpty() )
        {
            ret.enqueue(num);
        }
    
        return ret;
    }
    
    bool QCalculatorDec::match(QQueue<QString>& exp)
    {
        bool ret = true;
        int len = exp.length();
        QStack<QString> stack;
    
        for(int i=0; i<len; i++)
        {
            if( isLeft(exp[i]) )
            {
                stack.push(exp[i]);
            }
            else if( isRight(exp[i]) )
            {
                if( !stack.isEmpty() && isLeft(stack.top()) )
                {
                    stack.pop();
                }
                else
                {
                    ret = false;
                    break;
                }
            }
        }
    
        return ret && stack.isEmpty();
    }
    
    bool QCalculatorDec::transform(QQueue<QString>& exp, QQueue<QString>& output)
    {
        bool ret = match(exp);
        QStack<QString> stack;
    
        output.clear();
    
        while( ret && !exp.isEmpty() )
        {
            QString e = exp.dequeue();
    
            if( isNumber(e) )
            {
                output.enqueue(e);
            }
            else if( isOperator(e) )
            {
                while( !stack.isEmpty() && (priority(e) <= priority(stack.top())) )
                {
                    output.enqueue(stack.pop());
                }
    
                stack.push(e);
            }
            else if( isLeft(e) )
            {
                stack.push(e);
            }
            else if( isRight(e) )
            {
                while( !stack.isEmpty() && !isLeft(stack.top()) )
                {
                    output.enqueue(stack.pop());
                }
    
                if( !stack.isEmpty() )
                {
                    stack.pop();
                }
            }
            else
            {
                ret = false;
            }
        }
    
        while( !stack.isEmpty() )
        {
            output.enqueue(stack.pop());
        }
    
        if( !ret )
        {
            output.clear();
        }
    
        return ret;
    }
    
    QString QCalculatorDec::calculate(QString l, QString op, QString r)
    {
        QString ret = "Error";
    
        if( isNumber(l) && isNumber(r) )
        {
            double lp = l.toDouble();
            double rp = r.toDouble();
    
            if( op == "+" )
            {
                ret.sprintf("%f", lp + rp);
            }
            else if( op == "-" )
            {
                ret.sprintf("%f", lp - rp);
            }
            else if( op == "*" )
            {
                ret.sprintf("%f", lp * rp);
            }
            else if( op == "/" )
            {
                const double P = 0.000000000000001;
    
                if( (-P < rp) && (rp < P) )
                {
                    ret = "Error";
                }
                else
                {
                    ret.sprintf("%f", lp / rp);
                }
    
            }
            else
            {
                ret = "Error";
            }
        }
    
        return ret;
    }
    
    QString QCalculatorDec::calculate(QQueue<QString>& exp)
    {
        QString ret = "Error";
        QStack<QString> stack;
    
        while( !exp.isEmpty() )
        {
            QString e = exp.dequeue();
    
            if( isNumber(e) )
            {
                stack.push(e);
            }
            else if( isOperator(e) )
            {
                QString rp = !stack.isEmpty() ? stack.pop() : "";
                QString lp = !stack.isEmpty() ? stack.pop() : "";
                QString result = calculate(lp, e, rp);
    
                if( result != "Error" )
                {
                    stack.push(result);
                }
                else
                {
                    break;
                }
            }
            else
            {
                break;
            }
        }
    
        if( exp.isEmpty() && (stack.size() == 1) && isNumber(stack.top()) )
        {
            ret = stack.pop();
        }
    
        return ret;
    }
    

    main.cpp

    #include <QtGui/QApplication>
    
    #include "QCalculator.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QCalculator* cal = QCalculator::NewInstance();
        int ret = -1;
    
        if( cal != NULL )
        {
            cal->show();
    
            ret = a.exec();
    
            delete cal;
        }
    
        return ret;
    }
    

                                               

    展开全文
  • 美团App用户界面分析

    千次阅读 2018-11-13 16:13:00
    关于美团 美团网成立于2010年,合并前是...美团 App 用户界面分析 测试版本 V5.3 测试机型 iPhone6 (64GB) 测试时间 Jun,2015 导航界面分析 用户启动美团 App 后,进入后看见的便是美团 App 的首页(...

    关于美团

    美团网成立于2010年,合并前是中国销售额最大的独立团购 App。美团网2014年全年交易额突破460亿元,较去年增长180%以上,市场份额占比超60%,用户数超2亿~

    美团 App 用户界面分析

    1. 测试版本 V5.3
    2. 测试机型 iPhone6 (64GB)
    3. 测试时间 Jun,2015

    导航界面分析


    用户启动美团 App 后,进入后看见的便是美团 App 的首页(见图1)。首页设计风格会给用户的知觉带来第一印象,而App传递知觉的最主要方式就是视觉,颜色又是视觉的一个重要组成部分。美团首页的整体设计色调是淡绿色,看到就带给人一种闲适自然的感觉。绿色作为一种比较特殊的颜色,既不是冷色,也不是暖色,属于居中的颜色。代表清新,希望。能给人带来安全、平静、舒适之感。色彩心理学家指出,这种感觉可能是在人类进化的过程中出现的。因为对于原始人类来说,绿色的环境意味着充足的事物和水源,因此人对绿色会保有一种积极的情感。美团选择绿色作为主色调可能也是希望能够改善用户的情绪、增强其愉悦感。使用户在购物时有一个良好的物质环境和先前状态。

     
    图1

    接下来,根据眼球视觉从左到右,从上到下的浏览顺序,来分析出现的导航元素。首先页面顶部出现固定搜索框。考虑到用户使用手机团购的使用场景,不像电脑有着大屏幕和方便控制位置的鼠标,所以操作应当尽可能的简单。搜索可以让有明确购物需求的用户在最短时间内搜索到自己想要购买的商品,无需复杂的层级导航,操作便捷,给用户节约了大量时间。同时该搜索功能可以根据用户输入的内容自动进行内容匹配,免去了用户输入全部信息的内容,提供了良好的用户体验。

    接着出现在页面上部的是分类导航,通过把团购商品按大类进行区分,给用户提供了一个良好的引导入口。每个分类既使用了易识别的图标作为标示,免去了部分用户不喜欢阅读文字的烦恼。美团 App 不同类别之间有着控制间距留白,符合 Gestalt Principleproximity 原则,同时不同类别采用了不同的背景颜色,让用户知觉上明白每个圆形代表不同的类别。不同类别又具有一致的大小,相同间隔的留白,符合 similarity 原则,让用户感受到所有小的类别聚合起来就是一个导航的整体。同时配以文字解释,避免了图标歧义性。分类导航给那些并没有明确需求,但有大致购物需求的用户提供了一个浏览的渠道,在浏览商品的过程中寻找自己感兴趣的商品,给用户提供了除搜索以外的另一种购买情景,提高了购物的趣味性,在消费动机层面提供另外一种可能,即以无明确目的浏览的方式促成最终的购物转化,促进用户产生冲动型消费。此外,滑动Banner的交互效果充分利用了手机屏幕大小有限的特点。当界面控件位置受到大小限制的时候,动态交互策略可以为页面节省很大的空间。

    最后是出现在页面底部的固定 Tab 导航,负责 App 不同功能区的全局切换导航。当一个 App 包含许多页面和模块时,所有内容不能在一屏显示,Tab 导航和侧边抽屉导航是两种普遍采取的方式。Tab 导航方式的优点是让用户可以清晰认知App结构,避免多余操作。缺点就在于Tab 导航占据了屏幕的重要位置,在设计上显得不够简洁干净。侧边抽屉导航的优缺点与 Tab 导航恰恰相反。在团购 App 这种以提供功能为主要需求的 App 中,无疑 App 可用性的级别是要优先于美观性的。美团的 Tab 导航分为“团购”、“商家”、“我的”、“更多”四类。对于这四个分类,笔者有着自己不同的看法。“团购”、“我的”和“更多”都比较容易从其字面中看出其所包含的功能。然而,“商家”这个分类在概念上就不是特别清楚。通过“商家”进行团购和通过“团购”进行团购的区别在哪里,如果没有区别,那么单独开设这个类别的意义又何在。著名的建筑师路德维希·密斯·凡德罗说过一句名言—— less is more。即少就是多,简单的东西往往能给人提供更多的享受。用户是懒于选择的,多余的操作只会增加他们对于 App 的学习难度。尤其当他们缺乏区分选择的判断标准时,用户便会产生焦虑情绪,因为在有试错成本的前提下,人们都有避免犯错的倾向。为了避免这种成本,一些用户可能会选择放弃购物,这也是一种自我保护的行为来避免购买后冲突。因此笔者在这里建议对“商家” Tab 进行功能上的优化,将其与“团购”相同的功能进行合并,然后提炼出更具有代表性的功能作为单独的分支,让用户更加清楚的辨别每个 Tab 的特定作用。

    注册/登录/订单界面分析


    注册界面

    美团 App 的注册界面采取的是分布式渐进的注册流程(见图2)。《About Face》 中提出要把用户想象成非常聪明但又非常忙的人。对于提交表单之类繁琐的操作,应当将一个大任务分解成多个小任务来给用户执行。分布式操作首先在知觉上就带给用户简单的感觉,因而更愿意进行接下来的操作。其次,分布式操作减少了用户的脑力消耗,每一步只需执行非常简单的操作。同时,顶部的注册流程导航非常明确的告诉了用户每一步该做什么。当用户在有明确导航的指导下,即知道每一步的操作的目的的前提下,额外的点击次数用户是非常乐于接受的。

     
    图二

     

    登陆界面

    美团 App 的登录界面(见图3)支持美团账号和合作 App 账号两种登录方式,同时还创新的提供了无账号快捷登录方式(见图4)。合作 App 账号登录可以直接利用用户已有的账号信息,免去了用户注册的麻烦。无账号快捷登录则创新的提供了一种以手机验证码作为一次性密码的登录方式,集注册与登录一体。在团购这种以服务功能为主,无需大量用户个人信息的前提下,该登录方式使得第一次使用美团的用户在登录这个流程上达到极简。在输入验证码的时候还有一个用户体验亮点,程序会自动判断你需要输入的内容类型,比如输入数字验证码时下方键盘直接切换为数字键盘。贴心的人性化设计方便用户直接输入数字验证码而无需切换键盘模式。如果能购在保证安全性的前提下,模仿支付宝的提取短信验证码并自动填充的话,用户体验就更棒了。

     
    图三
     
    图四

     

    订单界面

    美团 App 的订单页面(见图5)也采用了分布式流程,分为立即抢购、提交订单和确认支付三个步骤。立即抢购提供了从团购详情进入提交订单的入口。在提交订单页面中可以选择团购数量,抵用券,和使用积分,最终显示你所需要支付的总金额。然后进入支付订单界面,默认使用支付宝支付,确认支付后完成整个流程。相同样式的橙色按钮引导用户不知不觉中实现目标。

     
    图5

     

    部分其他界面分析


    城市定位

    当用户登录美团 App 后,美团会根据用户当前城市坐标自动匹配所在城市的商品信息(见图6)。免去了用户手动选择城市的麻烦。因为大部分购买情境是发生在用户自购的情境下。同时美团也考虑到了代购赠送或异地购买的使用场景,因此也提供了城市切换功能。这样用户就可以团购非自身所在地区的团购,扩大了团购的可选择范围,兼顾了多种用户的需求。

     
    图6

    用户定位

    美团同时利用 LBS [Location Based Services],将用户所在位置周边的商家通过图形的方式呈现给用户(见图7)。用户可以在高德地图上直观看见自己和商家的相对位置,同时可以通过点击商家坐标查看商家信息,跳转到团购详情界面。这种获取移动端用户的位置信息并提供相应服务的功能,充分利用了用户使用手机进行团购的使用情境,给用户提供了一个便捷的方式来查看自己所在位置附近可以团购的商家,提高了用户浏览商家的便利性,进而促进了购买方式的便捷性。

     
    图7

    商品详情介绍

    商品详情介绍(见图8)需要向用户清楚高效的传达上列信息,使用户树立对商家和团购平台的喜爱和信任,促成最终的购物行为。团购详情界面第一屏显就示了商家信息、价格、销售量、评价和购买按钮,首先把最关键的信息告诉用户。顶部高清大图先让用户知觉上产生对商家的好感。其次是价格,原价和现价的对比满足了用户的求廉心理。接下来,橙黄色显眼的购买按钮可以在最短时间能促成交易转化。即使用户继续向下翻动浏览信息,购买按钮仍然会处于右上方显眼位置,确保交易的随时转化。下方显示了该商品特性(抢购时间、折扣度、随时退、销售量等),提升商品特殊性的同时增加了用户对该商品的信任程度。下方的总体评价,能够让用户第一时间对该商家服务质量产生一个大致的判断,从而决定是否购买。第二屏中,商家信息提供了商家的地理位置和联系方式,便于消费者进一步了解商家,强化信任感。之后的套餐详情和购买须知让消费者对消费内容有据可循,解决了消费者因为网上虚拟购买而产生的各种顾虑,再一次强化了消费时的安全感。下方是用户对商品的详细评价,文字与图片相结合,提供了极大的可信度,形成了一个良性的内容生成机制,不断的为后来的消费者提供建议。相比商家的自我阐述,用户更倾向于相信其他消费者的评价。美团 App 通过UGC [User Generated Content] 的方式实现了 O2O 的闭环,从线上购物转到线下消费最后转回线上评价。用户不再仅仅是观众,而是变成了互联网内容的生产者和供应者,用户可以通过评价的方式来维护自身的消费者权益,提升了消费者个体的参与感和安全感。同时在多人评论的过程中,形成了多个区域化的团购社会群体,彼此相互影响,将个体购物进一步暴露在社会群体的影响之下。同时需要注意的是,因为每一个人都可以生成内容,可能会有很多错误、虚假和片面的内容,所以消费者需要提高自己的判断能力。同时,美团作为第三方服务平台,需建立一个审核机制来避免商家自己刷评价或竞争对手恶意评价的不良情况发生。最后,在商品详情的下方,App 推荐了一些类似商品。基于用户的使用情境,当用户浏览某一商品详情并浏览至信息底部时,意味着很可能用户对该商品并没有强烈的购买需求。同时基于消费者的购物动机,用户对该类商品是有购买诉求的。此时,当用户浏览完该商品信息,仍然对商品不满意时,同类商品推荐恰好提供了一个便利的非结构导航将用户引导到合适的地方,增强了用户的购物体验。

     
    图8

    猜你喜欢

    Customization Service 能够带给消费者个性化的感受,没有哪两个人能够获得完全相同的体验,满足了消费者追求时尚和个性心理。因此,美团 App 会根据用户的个人信息、地理位置、浏览记录和历史购买信息向用户推荐用户可能喜欢的团购商品,提供消费者满意的私人定制服务(见图9)。此外,美团会基于用户的地理位置优先推荐符合用户需求且距离相对较近的商家。显示商家信息时,不仅显示了商家距离用户的大致距离,而且还显示了商家所处的地段名称。在团购模式中,除了价格因素影响消费者的购物行为以外,便利程度也是很大的影响因子,所以地段名称的人性化提示可以让用户基于经验在第一时间做出关于消费的决策,省去了点击查看地址详情的步骤。用户还可以通过用手指左滑删除不感兴趣的推荐团购,美团会在下次推荐时减少此类团购的推荐。同时,为了进一步保证推荐的准确性,美团还在猜你喜欢底部加入了“我的美团DNA”功能。有更高定制化需求的用户可以通过完善自己的美团 DNA 来获得更高质量的服务。

     
    图9

     

    美团 App 用户界面总结


    1. 功能层面
      设计界面前,应当先根据用户需求确定需要哪些功能,砍掉伪需求功能,给 App 瘦身,让 App 定位更加清晰。其次,确定功能的优先级。重要的功能摆在首页或者显眼位置,次重要的功能次之。比如团购App,任何与购物相关的功能都是主要功能,应给予较高的权重。美团将城市定位,个人位置定位,猜你喜欢,搜索,分类导航都放在了团购首页就是为了将主要功能置于用户最容易接触的地方,促进订单的转化率。我的美团 DNA 置于猜你喜欢的底部也在一个页面内遵循了主次顺序。

    2. 交互层面
      确定功能之后,需要对 App 进行精心的交互设计。清晰的导航,明确各个页面间跳转的逻辑,让用户清楚每步操作的作用。简化优化登录、注册、支付等辅助流程,让用户进行尽可能简单的操作。同时注重交互的细节层面,搜索自动匹配,地段名称的显示,输入验证码时显示数字键盘,相关商品推荐等交互细节,让用户在团购过程中不时感受到细节带来的温暖感动。

    3. 视觉层面
      App的视觉层面会给用户留下第一印象。当用户还未判断 App 所提供服务功能的质量好坏之前,仅仅通过 App 样式的美丑就已经定下了情感倾向——该 App 是高质量还是低质量。因此,App 用户界面的布局,色彩,图片,图标,文字,排版都需要专业人员进行设计,在视觉层给用户知觉留下良好印象,有助于用户进一步体验 App,使用其服务和功能。美团以绿色为基调的清新淡雅的设计风格就给用户在使用过程中带来一种闲适自由之感。同时也需要通过视觉的辅助来传来交互层的思想。美团中主要文字信息和主要功能按钮皆采用显眼的样式来吸引用户注意。同一操作流程的按钮都设计成同一风格,给用户以视觉信息的引导。

     

    转载于:https://www.cnblogs.com/tangbohu2008/p/9798862.html

    展开全文
  • Java基础---图形用户界面GUI(一)

    千次阅读 2015-11-29 20:40:02
    Frame f = new Frame("用户界面"); f.add(btn1,"North");//将btn1增加到f的北部 f.add(btn2,"South");//将btn2增加到f的南部 f.setSize(300,300); f.setVisible(true); btn2.setVisible(true); } } ...


    GUI:Graphical User Interface(图形用户接口)就是应用程序提供给用户操作的图形界面,包括窗口、菜单、工具栏和其它各种屏幕元素。

    图形界面程序可以使用各种各样的图形界面元素,如文本框、按钮、列表框、对话框等,我们将这些图形界面元素称为GUI组件,在这些组件上不能容纳其它的组件。

    容器其实也是一种组件,是一种比较特殊对的组件,它可以用来容纳其他的组件,如窗口、对话框、所有的容器类都是java.awt.Container的直接或间接子类,Container类是Component类的一个子类,由此可见容器本身也具有组件的功能和特点,也可以被当作基本组件一样使用。

    继承关系图


    AWT线程

    程序产生Frame对象时,创建了一个新的线程,我们称之为AWT线程。

    AWT事件处理机制

    对于这种GUI程序与用户操作的交互功能,Java使用了一种自己的专门方式,称之为事件处理机制。

    在事件处理机制中,我们需要理解三个重要的概念:

    事件:用户对组件的一个操作,称之为一个事件。

    事件源:发生事件的组件就是事件源。

    事件处理器:负责处理事件的方法。

    三者之间的关系


    事件处理器(事件监听器)首先与组件(事件源)建立关系,当组件接受到外部作用(事件)时,组件就会产生一个相应的事件对象,并把此对象传给之关联的事件处理器,事件处理器就会启动并执行行相关的代码来处理该对象

    Java程序对象对事件进行处理的方法放在一个类对象中的,这个类对象就是事件监听器。

    我们必须将一个事件监听器对象同某个事件源的某种事件进行关联,这样,当某个事件源上发生了某种事件后,关联的事件监听器对象的有关代码才会被执行。我们把这个关联过程称为向事件源注册事件监听器对象。

    事件用以描述发生了什么事情。AWT对各种不同的事件,按事件的动作(如鼠标操作)、效果(如窗口的关闭和激活)等进行了分类,一类事件对应一个AWT事件类。

    java.awt.event包中列出了所有的事件类。

    MouseEvent类对应鼠标事件,包括鼠标按下,鼠标释放,鼠标点击(按下后释放)等。

    WindowEvent类对应窗口事件,包括用户点击了关闭按钮,窗口得到与失去焦点,窗口被最小化等。

    ActionEvent类对应一个动作事件,它不是代表一个具体的动作,而是一种语义,如按钮或菜单被鼠标单击,单行文本框中按下回车键等都可以看作是ActionEvent事件。

    通过各种事件类提供的方法,我们可以获得事件源对象,以及程序对这一事件可能要了解的一些特殊信息。

    某一类事件,其中又包含触发这一事件的诺干具体情况。对一类事件的处理由一个事件监听器对象来完成,对于触发这一事件的每一种情况,都对应着事件监听对象中的一个不同方法。某种事件监听器对象中的每个方法名称必须是固定的,事件源才能依据事件的具体发生情况找到事件监听器对象中对应的方法。在面向对象的编程语言中,这种限定就是通过接口类来表示的。

    事件监听器接口的名称与事件的名称是相对应的,如MouseEvent的监听器接口名称MouserListener

    如果某个事件的监听器接口中只有一个方法,那么这个事件就是语义事件,如ActionListener中只有一个方法,ActionEvent就是一种语义事件,反之,则为低级事件

    用事件监听器处理事件

    示例(程序添加窗口关闭的代码)

    import java.awt.*;
    import java.awt.event.*;
    public class TestFram 
    {
    	public static void main(String[] args) 
    	{
    		Frame f = new Frame("我的图形界面");
    		f.setSize(300,300);
    		f.setVisible(true);
    		f.addWindowListener(new MyWindowListener());
    	}
    }
    
    class MyWindowListener implements WindowListener
    {
    	public void windowClosing(WindowEvent e)
    	{
    		e.getWindow().setVisible(false);
    		((Window)e.getComponent()).dispose();
    		System.exit(0);
    	}
    	public void windowActivated(WindowEvent e){}
    	public void windowClosed(WindowEvent e){}
    	public void windowDeactivated(WindowEvent e){}
    	public void windowDeiconified(WindowEvent e){}
    	public void windowIconified(WindowEvent e){}
    	public void windowOpened(WindowEvent e){}
    }
    在上面代码中,编写了一个新类MyWindowListener来实现窗口事件监听对象的程序代码,并调用Window.addWindowListener方法将事件组册到Frame类(Frame类继承java.awt.Window类)创建的框架窗口上。在WindowListener接口里有7个方法,正好对应窗口事件的7种情况,因为我们只想处理鼠标点击窗口标题栏上的关闭按钮这一事件,对其他窗口事件我们并不关心,所以,在MyWindowListener的代码中,我们只对windowClosing方法进行了编码,其他方法只是简单实现。

    总结:要处理GUI组件上的XxxEvent事件下的某种情况,首先要编写一个实现了XxxListener接口的事件监听器类,然后再XxxListener类和要处理的具体事件情况相对应的方法中编写处理程序代码,最后将类XxxListener创建的对象通过addXxxListener方法组册到GUI组件上。Xxx可以是各种不同的事件,如Window、Mouse、Key、Action等
    事件适配器

    为了简化编程,JDK针对大多数事件监听器接口定义了相应的实现类,我们称之为事件适配器(Adapter)类。

    在适配器中,实现了相应的监听器接口中所有的方法,但不做任何事情,子类子类只要继承适配器类,就等于实现了相应的监听器接口,如果要对某类事件的某种情况进行处理,只要覆盖相应的方法就可以了,其他的方法再也不用“简单实现”了。

    修改MyWindowListener,代码如下:

    class MyWindowListener extends WindowAdapter
    {
    	public void windowClosing(WindowEvent e)
    	{
    		e.getWinddow().setVisible(false);
    		((Window)e.getComponet()).dispose();
    		System.exit(0);
    	}
    }
    按钮的事件监听代码中要访问框架窗口 一种简单的办法就是将事件监听器的代码和产生GUI组件的代码放在同一个类中实现
    import java.awt.*;
    import java.awt.event.*;
    public class TestFrame implements ActionListener
    {
    	Frame f = new Frame("图形界面");
    	public static void main(String[] args)
    	{
    		TestFrame tf = new TestFrame();
    		tf.init();
    		
    	}
    	public void init()
    	{
    		Button btn = new Button("退出");
    		btn.addActionListener(new TestFrame());
    		f.add(btn);
    		f.setSize(300,300);
    		f.setVisible(true);
    	}
    	public void actionPerformed(ActionEvent e)
    	{
    		f.setVisible(false);
    		f.dispose();
    		System.exit(0);
    	}
    }
    事件监听器的匿名内置实现方式
    对上面例子,我们用匿名内置的语法进行改写
    import java.awt.*;
    import java.awt.event.*;
    public class TestFrame
    {
    	Frame f = new Frame("图形界面");
    	public static void main(String[] args)
    	{
    		new TestFrame().init();
    	}
    	public void init()
    	{
    		Button btn = new Button("退出");
    		btn.addActionListener(new ActionListener(){
    			public void actionPerformed(ActionEvent e)
    			{
    				System.out.println("zhixingle ");
    				f.setVisible(false);
    				f.dispose();
    				System.exit(0);
    			}
    		});
    		f.add(btn);
    		f.setSize(300,300);
    		f.setVisible(true);
    	}
    }
    事件处理的多重运用

    用户的一个操作,在触发了低级事件的同时,可能也会触发语义事件。一般情况下,如果语义事件能够满足我们的需求,我们就不再处理低级事件。

    用户在一个按钮上按下鼠标,触发了鼠标事件,也触发了按钮的动作事件,(需求:当按钮被点击后,我们除了要执行按钮对应的程序功能外,还希望鼠标按下时能改变按钮标题的内容,在按钮上要注册两个事件监听器对象,一处理鼠标事件,另一个处理按钮的动作事件。)

    通过查看这些addXxxListener方法,就知道了这个组件所支持的事件类型。

    一个组件上的一个动作可以产生多种不同类型的事件,因而可以向同一个事件源上注册多种不同类型的监听器,

    一个事件监听器对象可以注册到多个事件源上,即多个事件源的同一事件都由一个对象统一来处理

    一个事件源也可以注册对同一个事件进行处理的多个事件监听器对象,当这一事件发生时,各事件监听器对象依次被调用。


    高级事件处理

    默认情况下,组件屏蔽了对所有事件的响应,也就不管发生了什么情况,事件都不会在这个组件上发生,组件都不会产生任何事件对象。只有在一个组件上注册某种事件的事件监听对象后,组件才可以对这种事做出响应,当发生了对应这种事的情况后,事件才能在这个组件上发生,组件才会产生这种事件对象。

    即使没有在组件上注册事件监听器,我们只要调用了enableEvents函数,设置组件能够进行响应的事件,在响应的情况发生后,组件仍然能够产生对应事件。enableEvents函数定义如下:

    protected final void enableEvents(long eventsToEnable)

    参数eventsToEnable  processEvent(AWTEvent e)方法中参数类型是AWTEvent 在该类中可以看到这些常量的定义

    如鼠标移动事件对应的常量为MOUSE_MOTION_EVENT_MASK,这样,当我们想让组件响应鼠标移动事件时,可以使用enableEvents(AWTEvent.AWTEvent.MOUSE_MOTION_EVENT_MASK);语句来完成。

    import java.awt.*;
    import java.awt.event.*;
    class MyButton extends Button
    {
    	private MyButton friend;
    	public void setFriend(MyButton friend)
    	{
    		this.friend = friend;
    	}
    	public MyButton(String name)
    	{
    		super(name);
    		enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
    	}
    	protected void processMouseMotionEvent(MouseEvent e)
    	{
    		setVisible(false);
    		friend.setVisible(true);
    	}
    }
    
    public class TestMyButton
    {
    	public static void main(String[] args)
    	{
    		MyButton btn1 = new MyButton("你来抓我呀!");
    		MyButton btn2 = new MyButton("你来抓我呀!");
    		btn1.setFriend(btn2);
    		btn2.setFriend(btn1);
    		btn1.setVisible(false);
    		Frame f = new Frame("用户界面");
    		f.add(btn1,"North");//将btn1增加到f的北部
    		f.add(btn2,"South");//将btn2增加到f的南部
    		f.setSize(300,300);
    		f.setVisible(true);
    		btn2.setVisible(true);
    		
    	}
    }

    GUI组件上的图形操作

    Graphics类

    组件对象本身不提供这些操作的方法,它只提供一个getGraphics方法,getGraphics方法返回一个包含有该组件的屏幕显示外观信息的Graphics类对象,Graphics类提供了在组件显示表面绘画图形,打印文字,显示图像等操作方法。

    画直线

    import java.awt.*;
    import java.awt.event.*;
    public class DrawLine
    {
    	Frame f = new Frame("图形界面");
    	public static void main(String[] args)
    	{
    		new DrawLine().init();
    	}
    	public void init()
    	{
    		f.setSize(300,300);
    		f.setVisible(true);
    		f.addMouseListener(new MouseAdapter(){
    			int orgx;
    			int orgy;
    			public void mousePressed(MouseEvent e)
    			{
    				orgx=e.getX();
    				orgy = e.getY();
    			}
    			public void mouseReleased(MouseEvent e)
    			{
    				Graphics g = f.getGraphics();
    				g.setColor(Color.red);
    				g.setFont(new Font("隶书",Font.BOLD,30));
    				g.drawString(new String(orgx+","+orgy),orgx,orgy);
    
    				//f.getGraphics().setColor(Color.red);//设置绘图颜色为红色
    				f.getGraphics().drawLine(orgx,orgy,e.getX(),e.getY());
    			}
    		});
    	}
    }

    注:放大缩小就会没有了,因为AWT线程都会重新绘制组件,组件上原来绘制的图形也就不复存在了。

    组件重绘

    我们将程序窗口最小化后再恢复正常化显示,发现所绘图全部消失了,在组件大小改变或隐藏后又显示,AWT线程都会重新绘制组件,组件上原来绘制的图形也就不复存在了,这一过程称为“曝光”。要想让用户感觉到所绘的图形一直存在,我们只需在组件重新绘制后,立即将原来的图形重新画上去,这个过程是肉眼感觉不到的。AWT线程在重新绘制组件后,会立即调用组件的paint方法,所以我们的图形重绘代码应该在paint方法中编写。

    paint方法是这样定义的

    public void paint(Graphics g);

    可见,AWT线程已经获得了组件上的Graphics对象,并将它传递给了paint方法,在paint方法中绘图时只能使用这个Graphics对象。

    当我们想要执行paint方法中的程序代码时,应用程序不应直接调用paint方法,如果我们想执行paint方法中的程序代码,需调用Component.repaint方法,Component.repaint方法调用Component.update方法,Component.update再调用Component.paint。组件重绘关系如下图

    由于我们不可能直接进入到某个组件的paint方法中修改程序代码,我们需要定义一个继承了该组件的子类,在子类中覆盖paint方法,在新的paint方法中编写重绘图形程序代码,并将原来创建的组件对象改为由这个子类创建,就可以达到我们的目的了。

    我们修改上面代码,使其具有重绘效果:

    import java.awt.*;
    import java.awt.event.*;
    public class DrawLine extends Frame
    {
    	int orgX;
    	int orgY;
    	int endX;
    	int endY;
    	public static void main(String[] args)
    	{
    		new DrawLine().init();
    	}
    	public void paint(Graphics g)
    	{
    		//这里是重绘效果
    		g.drawLine(orgX,orgY,endX,endY);
    	}
    	public void init()
    	{
    		this.addMouseListener(new MouseAdapter(){
    			public void mousePressed(MouseEvent e)
    			{
    				orgX = e.getX();
    				orgY = e.getY();
    			}
    			public void mouseReleased(MouseEvent e)
    			{
    				endX = e.getX();
    				endY = e.getY();
    				Graphics g = getGraphics();
    				g.setColor(Color.red);
    				g.setFont(new Font("隶书",Font.BOLD,30));
    				//会被上面paint画的重绘
    				g.drawString(new String(orgX+","+orgY),orgX,orgY);//打印鼠标按下时的坐标文本
    				g.drawString(new String(endX+","+endY),endX,endY);//打印鼠标释放时的坐标文本
    				g.drawLine(orgX,orgY,endX,endY);
    			}
    		});
    		setSize(300,300);
    		setVisible(true);
    	}
    }

    如果我们想完全重绘窗口的内容,需要将每一条直线的坐标保存到一个集合类中,在paint方法中取出该集合中的每一条直线坐标,逐一绘制。首先我们创建一个新的类MyLine,对应所画的一条直线,该类中定义一个drawMe方法,用于将该直线在某个Graphics对应的组件上画出自己。
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    class MyLine
    {
    	private int x1;
    	private int y1;
    	private int x2;
    	private int y2;
    	public MyLine(int x1,int y1,int x2,int y2)
    	{
    		this.x1 = x1;
    		this.y1 = y1;
    		this.x2 = x2;
    		this.y2 = y2;
    	}
    	public void drawMe(Graphics g)
    	{
    		g.drawLine(x1,y1,x2,y2);
    	}
    }
    public class RerawAllLine extends Frame
    {
    	Vector vLines = new Vector();
    	public static void main(String[] args)
    	{
    		new RerawAllLine().init();
    	}
    	public void paint(Graphics g)
    	{
    		g.setColor(Color.red);
    		Enumeration e = vLines.elements();
    		while(e.hasMoreElements())
    		{
    			MyLine ln = (MyLine)e.nextElement();
    			ln.drawMe(g);
    		}
    	}
    	public void init()
    	{
    		this.addWindowListener(new WindowAdapter(){
    			public void windowClosing(WindowEvent e)
    			{
    				((Window)e.getSource()).dispose();
    				System.exit(0);
    			}
    		});
    		this.addMouseListener(new MouseAdapter()
    		{
    			int orgX;
    			int orgY;
    			public void mousePressed(MouseEvent e)
    			{
    				orgX = e.getX();
    				orgY = e.getY();
    			}
    			public void mouseReleased(MouseEvent e)
    			{
    				Graphics g = e.getComponent().getGraphics();
    				g.setColor(Color.red);
    				g.drawLine(orgX,orgY,e.getX(),e.getY());
    				vLines.add(new MyLine(orgX,orgY,e.getX(),e.getY()));
    			}
    		});
    		this.setSize(300,300);
    		setVisible(true);
    	}
    }

    图像操作

    我们可以通过Graphics.drawImage(Image img,int x,int y,ImageObserver)方法在组件上显示图像,其中,img参数是要显示的图像,x,y是图像显示的左上角坐标,observer是用于监视图像创建进度的一个对象。

    drawImage是一个异步方法,即使img对应的图像还没完全装载(在创建Image类对象时并没有在内存中创建图像数据)时,drawImage也会立即返回。如果程序想了解图像创建的对象传递给drawImage方法。如果程序不关心图像创建的进度消息,可以传递要显示图像的那个组件对象,因为Component类已实现了ImageObserver接口。

    显示图像的示例程序。

    import java.awt.*;
    import java.awt.event.*;
    public class DrawImage
    {
    	public static void main(String[] args)
    	{
    		Frame f = new Frame("图形界面");
    		/*会抛出java.lang.NullPointException异常,因为只有在组件已显示在窗口上时,getGraphics方法才能正确返回一个Graphics对象。
    		Image img = f.getToolkit().getImage("C:\\Users\\49921_000\\Desktop\\1.jpg");
    		f.getGraphics().drawImage(img,0,0,f);
    		*/
    		f.setSize(300,300);
    		f.setVisible(true);
    		f.addWindowListener(new WindowAdapter(){
    			public void windowClosing(WindowEvent e)
    			{
    				System.exit(0);
    			}
    		});
    		Image img = f.getToolkit().getImage("C:\\Users\\49921_000\\Desktop\\1.jpg");
    		//运行不再有错
    		f.getGraphics().drawImage(img,0,0,f);
    	}
    }
    上面图片并没有显示出来。这是因为在窗口初始化显示也会被调用paint方法, paint方法会擦除窗口上绘制的图形,这里drawImage方法先于paint方法执行,所以,drawImage方法绘制的图像被paint方法擦除掉了。

    对于上面问题,我们可以将图像放在paint方法中显示,修改后的代码

    import java.awt.*;
    import java.awt.event.*;
    public class DrawImage extends Frame
    {
    	Image img = null;
    	public static void main(String[] args)
    	{
    		DrawImage f = new DrawImage();
    		f.init();
    	}
    	public void init()
    	{
    		img = this.getToolkit().getImage("C:\\Users\\49921_000\\Desktop\\2.gif");
    		setSize(300,300);
    		setVisible(true);
    		this.addWindowListener(new WindowAdapter(){
    			public void windowClosing(WindowEvent e)
    			{
    				System.exit(0);
    			}
    		});
    		/*img放这里 编译运行后,有时图像立即就能正常显示出来,有时候图像并没有显示出来,而是在命令行窗口出现如下错误
    		java.lang.NullPointerException 改变窗口大小,这时图像就显示出来了。原因在于,这时候正好碰到了AWT线程调用paint
    		方法早于getImage方法情况,而在paint方法执行drawImage的时候,img对象仍然为null.
    		img = this.getToolkit().getImage("C:\\Users\\49921_000\\Desktop\\2.gif");
    		*/
    	}
    	public void paint(Graphics g)
    	{
    		getGraphics().drawImage(img,0,0,this);
    	}
    }

    在实际工作中,可能会碰到许多小细节,更希望知道老手们解决问题的方法和思想,对于程序中的

    Image img = f.getToolkit().getImage("c:\\1.gif");

    这行代码,可能会问:“如果是我自己写程序,我怎么能够知道产生一个Image对象,要用到这些方法和这些调用关系呢?”其实,最聪明的人是最会使用工具的人,特别是编写程序,如果我们有一个好的帮组系统,经常会有事半功倍的特效。JDK文档。有了这样的帮组系统,我们可以进行模糊查询了。由Image是抽象类,我们不能使用构造方法,只能通过某个方法来产生一个Image对象了,这个方法名,估计就是createImage,getImage之类的英文单组组合,按照这种思想,通过帮助索引查询,一般都会有较大的收获,我们查到了Toolkit.getImage(String path)方法,由于Toolkit是抽象类,不能直接创建,接着用getTookit又找到了Component.getToolkit方法返回Toolkit对象,这样顺藤摸瓜写出了这个完整的语句了。

    双缓冲的技术

    在画线重绘程序中,窗口重画时,需要逐一重新绘制窗口上原来的图形(直线,文本),如果原来的图形非常多,这个过程就显得比较慢了。一种改进的办法是,我们可以调用Component.createImage方法,在内存中创建一个Image对象,当我们组件上绘图时,也在这个Image对象上执行同样的绘制,即Image对象中的图像是组件表面内容的复制,当组件重画时,只需要将内存中的这个Image对象在组件上画出,不管组件上原来的图形有多少,在重绘时都只是一副图像而已,在组件上的图形非常多时,重绘数度明显提高,这就是一种被称为双缓冲的技术。

    应用示例:

    import java.awt.*;
    import java.awt.event.*;
    class  DrawLine extends Frame
    {
    	Image oimg = null;
    	Graphics og = null;
    	public static void main(String[] args) 
    	{
    		new DrawLine().init();
    	}
    	public void init()
    	{
    		setSize(300,300);
    		setVisible(true);
    		Dimension d = getSize();
    		oimg = createImage(d.width,d.height);
    		og = oimg.getGraphics();
    		addMouseListener(new MouseAdapter(){
    			int orgx;
    			int orgy;
    			public void mousePressed(MouseEvent e)
    			{
    				orgx = e.getX();
    				orgy = e.getY();
    				System.out.println(orgx+","+orgy);
    			}
    
    			public void mouseReleased(MouseEvent e)
    			{
    				/*Graphics g = getGraphics();
    				g.setColor(Color.red);//设置绘图颜色
    				g.setFont(new Font("隶书",Font.BOLD,30));//设置文本字体
    				g.drawString(new String(orgx+","+orgy),orgx,orgy);//打印鼠标按下时的坐标
    				g.drawString(new String(e.getX()+","+e.getY()),e.getX(),e.getY());//打印鼠标释放时的坐标
    				g.drawLine(orgx,orgy,e.getX(),e.getY());*/
    				System.out.println(e.getX()+","+e.getY());
    				og.setColor(Color.red);
    				og.setFont(new Font("隶书",Font.BOLD,30));
    				og.drawString(new String(orgx+","+orgy),orgx,orgy);//打印鼠标按下时的坐标
    				og.drawString(new String(e.getX()+","+e.getY()),e.getX(),e.getY());//打印鼠标释放时的坐标
    				og.drawLine(orgx,orgy,e.getX(),e.getY());
    			}
    		});
    		addWindowListener(new WindowAdapter(){
    			public void windowClosing(WindowEvent e)
    			{
    				System.exit(0);
    			}
    		});
    	}
    	public void paint(Graphics g)
    	{
    		if(oimg!=null)
    			g.drawImage(oimg,0,0,this);
    	}
    }
    
    如果在paint方法中没有使用if语句对oimg进行是否为null的检查,就会出现java.lang.NullPointException异常

    createImage是Component的一个方法,只有部件显示在桌面上之后才能去调用这个方法,部件显示时会调用它的paint方法,也就是createImage方法只能在第一次paint方法调用之后才能被调用。

    展开全文
  • 程序员如何进行用户界面设计

    千次阅读 2013-02-01 09:26:21
    程序员设计的用户界面,其实大多数并不是一个好...我认为主要是程序员编码的思想影响到了对用户界面的设计,很多程序员都有钻牛角尖、猎奇的倾向,喜欢所有东西都DIY,这也就促使他们在设计软件时,希望它非常灵活,很
  • Elm-comonad 声明式用户界面是未来-未来是共同的! Elm的思想实现,为 。
  • 界面设计与用户需求

    千次阅读 热门讨论 2016-08-18 17:14:28
     我们为志晟公司以及我们自身来开发订餐系统,从根本上来讲是为了方便企业管理,方便大家就餐的,所以我们从一开始呢就抱着一种能让大家用上我们系统的这样一种思想来实现这个系统,然而事情发展的却不是像我们想象...
  • 图形用户界面和游戏开发 基于tkinter模块的GUI GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本...
  • 软件界面设计思想方法

    万次阅读 2015-03-04 10:16:28
    15.1什么是好的软件界面 简而言之,好的软件界面应当是易用的和美观的。易用是交互设计的主要目标,美观是视觉设计的主要目标,交互设计和视觉设计完成后,最终靠编程来实现可...之后出现了图形用户界面,例如Windo
  • Qt图形用户界面编程技术入门

    万次阅读 2014-12-29 09:30:19
    本文向读者介绍利用Qt开发图形用户界面的应用程序的入门知识。这里,我们首先介绍了如何搭建Qt的开发环境,之后通过一些简单的示例程序来循序渐进地介绍Qt的“信号和槽”以及布局等基本概念。我们希望以此来帮助读者...
  • 利用Java Swing技术设计一个Email邮箱地址注册的图形用户界面应用程序。 要求:当用户输入完成后单击“立即注册”按,判断“密码”和“确认密码”文本框内容是否一致,如果一致在立即注册按钮上方显示用户输入的...
  • 软件工程之用户界面设计

    千次阅读 热门讨论 2014-01-01 20:23:42
    在人和机器的互动过程中,有一架美丽的彩虹桥,就是我们所说的界面,软件界面的美观与否,直接影响着用户的心情。让软件更加生动,有自己的个性,自成一家,独成一派,有着自己鲜明的风格,正如微软和苹果一样。 ...
  • Python图形用户界面和游戏开发

    千次阅读 2019-07-10 15:50:33
    GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter),从这个名字就可以看出它是基于Tk的...
  •                   基于tkinter的GUI界面编程 1.1GUI简介   ...图形用户界面(Graphical ...
  • Linux图形用户界面:KDE与GNOME的由来

    万次阅读 2018-08-11 15:51:25
    微软Windows在早期只是一个基于DOS的应用程序,用户必须首先进入DOS后再启动Windows进程,而从Windows 95开始,微软将图形界面作为默认,命令行界面只有在需要的情况下才开启,后来的Windows 98/Me实际上也都隶属于...
  • 提出一种面向用户的管理信息系统的界面设计思想,使软件的界面更加简洁友好,操作更加方便~
  • 另外就是要有要有如下架构思想:在服务器端 用一个HashMap,socket> 维护所有用户相关的信息,从而能够保证和所有的用户进行通讯。 客户端的动作: (1)连接(登录):发送userName 服务器的对应动作:1)界面显示...
  • VB.NET用户登录(三层架构思想)

    热门讨论 2011-12-29 16:18:09
    学习是一步一个台阶的,用面向过程的思想实现vb.net用户登录实际上很简单,但是高内聚、低耦合的原则给丢了,这个Demo主要实现了三层架构思想来实现用户登录。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 134,131
精华内容 53,652
关键字:

思想用户界面