VisualProlog编程.ppt

上传人:sh****n 文档编号:6561967 上传时间:2020-02-29 格式:PPT 页数:149 大小:1.14MB
返回 下载 相关 举报
VisualProlog编程.ppt_第1页
第1页 / 共149页
VisualProlog编程.ppt_第2页
第2页 / 共149页
VisualProlog编程.ppt_第3页
第3页 / 共149页
点击查看更多>>
资源描述
第7章VisualProlog编程 本章介绍基于VisualProlog编程方面的知识 主要内容包括VisualProlog基础 VisualProlog的GUI编程 VisualProlog的逻辑层 VisualProlog的数据层 第7章VisualProlog编程 7 1VisualProlog基础7 2VisualProlog的GUI编程7 3VisualProlog的逻辑层7 4VisualProlog的数据层本章小结与习题 7 1VisualProlog基础 传统的Prolog与VisualProlog6之间的差别主要体现在如下几个方面 1 程序结构很明显 传统Prolog中所使用的结构与VisualProlog6中使用的结构 在理解的难易程度方面不同 主要包括如何规划来自定义 definitions 的声明 declarations 以及如何简要地说明程序必须使用指定关键字 keywords 进行查找的主目标Goal 2 文件考虑VisualProlog6提供了各种工具 以便将程序结构组织成不同类型的文件 3 作用域访问VisualProlog6可以挑选在其他模块中通过使用称为作用域标识 Scopeidentification 的概念而开发出来的功能 4 面向对象VisualProlog6程序可以编写成面向对象的程序 使用标准的面向对象特性 7 1VisualProlog基础 7 1 1程序结构7 1 2目标Goal7 1 3文件考虑7 1 4作用域访问7 1 5面向对象7 1 6一个完整的例子 family1 prj67 1 7程序的取舍7 1 8小结 7 1 1程序结构 VisualProlog的程序 从结构上讲 主要包括若干个段 即论域段 谓词段 子句段 目标段等 VisualProlog作为强类型的编译型语言 通常用论域段和谓词段来给出有关的声明或定义 7 1 1 1声明与定义 声明 Declaration 与定义 Definition 有着不同的含义 在Prolog中 当需要使用一个谓词的时候 就可以直接使用 无需事先向Prolog推理机做任何的通告 例如 在前面的例子中 grandFather谓词的子句就是利用传统的Prolog的谓词头和谓词体结构直接写下来的 我们不用在代码中再告知推理机这个谓词结构是后面需要使用的 类似地 当在传统的Prolog中使用一个复合论域时 无须首先告诫Prolog推理机关于使用该论域有何意图 只要感到需要 就可以直接使用一个论域 然而 在VisualProlog6中 在编写一个谓词的子句体代码之前 我们必须首先对编译器声明这样一个谓词的存在 类似地 在使用任何论域之前 也必须先进行声明 以便将这些论域的存在告知编译器 7 1 1 1声明与定义 在VisualProlog6中需要这种预先告知功能的原因本质上是为了保证将运行时间异常 runningexceptions 尽可能地转变为编译时间错误 compiletimeerrors 对于 运行时间异常 我们指的是只在运行所编译的程序期间出现的问题 例如 如果我们想使用一个整数作为一个函数的参数 但是却错误地使用了实数 这就会成为一个运行错误 这大都出现在使用其他编译器的程序中 但不是在VisualProlog6中 程序就会因此而失败 当声明已定义的谓词和论域时 这类位置语法 即哪一个参变量属于哪一个论域 就会对编译器起作用 因此 当VisualProlog6执行编译时 它将比较彻底的检查程序 以发现诸如此类的语法错误及其它错误 7 1 1 1声明与定义 由于VisualProlog6的这些特性 整个程序的效率因此提高了 程序员不必等到程序实际执行时才发现错误 事实上 对于实际编写程序的人 将体会到这大大地节约了时间 通常 运行时导致发生运行时间异常的条件如此难以捉摸 以致于错误可能会在很多年后才被发现 或者会在许多特别重要的情况或令人尴尬的场合表现出来 所有这些表明 编码中存在的论域和谓词要在定义前给出合适的声明 以给编译器详尽的指示 7 1 1 2关键字 一个VisualProlog6的程序包括一组被标点分为不同部分的代码 由特定的关键字告诉编译程序所要生成的类型 例如 关键字可以将谓词和论域的定义和声明区分开 通常 每一部分由一关键字开始 在每一部分结束时 一般没有关键字指示 新的关键字的出现表明前一部分的结束和下一部分的开始 对这一规则的例外是关键字 implement 和 endimplement 在这两个关键字中间的代码表示它们属于一个特殊的类 若有人不懂类的概念 可以把它看作程序的一个模块或一个部分 在本节中 我们将只介绍下述关键字 同样我们给出了这些关键字的用途 具体的句法可以从文档资料中学到 VisualProlog6中还有其它一些关键字 可以在以后的内容和文档资料中学到 本章需要掌握的关键字在下面分别描述 7 1 1 2关键字 Implement和endimplement在这里讨论的所有关键字中 这是惟一成对存在的 VisualProlog6把出现在这两个关键字之间的代码看成属于一个类 这个类名必须在 implement 这个关键字后给出 Open这个关键字用来扩展类作用域的可见性 它被用在implement关键字之后 Constants这个关键字用来表明一部分经常在程序中被使用的代码 例如 如果字符串 PDCProlog 在程序中多次出现 那么就可以定义一个它的缩写 constantspdc PDCProlog 注意 常量的定义以句号结束 与Prolog中的变量不同 常量应该以小写字母开头 7 1 1 2关键字 domains这个关键字用来标明程序中将要用到的论域 这种论域声明的句法中有许多变量的声明 用来指示许多将来在程序中要用到的论域 因为本节介绍VisualProlog基础性内容 我们将不对论域的具体细节进行讨论 总结一下 我们将声明那些用于论域的算符和构成算符变元的论域 算符和复合论域在本书的前面章节部分有详细解释 Classfacts这个关键字指定一个段 这个段用来声明将在程序代码中出现的事实 每一个事实由一个符号化的名字声明每个事实的变元和各个变元所属的论域 7 1 1 2关键字 Classpredicates这一段将包含在子句部分被定义的谓词的声明 同样 谓词的名称以及变元和论域也在这一段中被声明 Clauses在VisualProlog6代码的所有部分中 这一部分和传统的Prolog程序最为相似 它包含对已声明谓词的定义 我们会发现在这里使用的谓词与classpredicates部分中声明的谓词句法相同 Goal这一段定义是VisualProlog6程序的主要入口点 更详细的解释在下面给出 7 1 2目标Goal 在传统的Prolog中 只要谓词在代码中被定义了 Prolog核心程序就会被引导从那个谓词开始程序的执行 但是 在VisualProlog6中不是这样 作为一个编译程序 它要生成高效率的程序执行代码 在编译程序工作的时候 代码事实上并未被执行 所以 编译程序需要事先知道程序从哪个谓词开始执行 这样当程序被调用执行时 它就能从正确的地方开始 正如我们所期望的那样 这个编译好的程序可以再不需要VisualProlog编译程序和VDE而独立运行 为了实现这些功能 有一个专门由关键字goal指示的段 把它们作为没有自变量的特殊谓词考虑 这种谓词就是程序开始执行的地方 7 1 3文件考虑 通常 将程序的所有部分放在一个文件里是很麻烦的 这样会使程序难于理解 甚至有时会产生错误 VisualProlog6使用VDE可以将程序代码分成不同的文件 也可使用VDE将不同的代码写入不同的文件 借助这种方式 通过查找文件就可将经常用到的程序段找到 如果在许多文件中都要用到一个论域 那么可以在一个单独的文件中声明这个论域 然后这个文件可以被其他文件所访问 然而 为了简化这个专门教程 我们应该主要使用一个文件写这些代码 在构造程序的过程中 VDE可以自动生成更多当时可以忽略的程序 我们将在以后的内容中学到 7 1 4作用域访问 VisualProlog6将整个程序划分为不同的部分 每一部分定义一个类 在面向对象的程序语言中 类是一组程序代码和与之相关的数据的集合 这些内容在以后的叙述中将进行更多的解释 像前面提到的一样 对于不熟悉面向对象程序语言的学习者 可以将类类似地考虑为模块 通常 VisualProlog6在自己专门的文件中定义各个类 在程序执行的过程中 程序经常需要调用在另一个类中定义的谓词 相似地 在一个类中定义的数据和论域可能需要允许能被另一个不同的文件所访问 7 1 4作用域访问 VisualProlog6允许这些跨越类的代码数据引用 称为访问作用域 可以用一个例子来理解 假设在名为class1的类中定义了一个名为pred1的谓词 使用VDE在另一个文件中写出 我们在另一个文件class2中调用另一个名为pred2的谓词 下面就是如何在pred2的子句体中调用pred1的例子 pred3 pred2 class1 pred1 pred1isnotknowninthisfile Itisdefinedinsomeotherfile Henceaclassqualifierisneededpred3 7 1 4作用域访问 在上述例子中 可以看到pred2的子句体调用pred1和pred3这两个谓词 因为pred1在另一文件class1中被定义 因此将class1和 放在pred1的前面 这被称为是类的限定符 但是谓词pred3和pred2一样 在相同的文件中被定义 因此没有必要在谓词前加上 class2 来调用pred3 这种行为在专业上这样解释 pred3的访问作用域蕴含于pred2中 因此没有必要澄清pred3和pred2一样来自于同一个类 编译程序会在定义class2的范围内自动寻找pred3的定义 某一特定类定义的作用域范围被限制在某一特定文件中声明的类中 代码写在关键字implement和endimplement之间 在其中定义的谓词可以不用类名限定符和 符号作为前缀相互调用 7 1 4作用域访问 类的作用域范围可以通过使用open这个关键字予以扩充 这个关键字可以通知编译程序调用在其它文件中定义的谓词 常量 论域名 如果作用域范围扩充了的话 我们就不需要写类名限定符和 openclass1 pred3 pred2 pred1 Note class1 qualifierisnotneeded anymore asthescopearea isextendedusingthe open keywordpred3 7 1 5面向对象 VisualProlog的当前版本是一个强大的面向对象语言 如果需要的话 开发程序的整个代码会根据需要被放入合适的类中 即使对这种语言的面向对象特性不感兴趣 它也会自动进行 我们也会在本章给出的例子中发现这个特性 即使我们完全不使用任何该类生成的对象 代码一样会被插入到名为family1的类中 我们将会直接使用这个类中的谓词代码 7 1 6一个完整的例子 family1 prj6 让我们把所学的知识集合起来去写我们的第一个VisualProlog6的程序 这将包含在 Prolog基础 一章中所涉及的相同的基本逻辑 所有为本章所写的代码如下所示 并写入名为family1 pro的文件中 使用VisualProlog的VDE可完成代码的输入 本章所需要的更多文件将由VDE自动生成和维护 如何一步步使用VDE开发程序的步骤将在稍后做介绍 7 1 6一个完整的例子 family1 prj6 使用VDE开发程序的步骤简要概括如下 第一步 在VDE中生成一个新的控制台程序 在启动VisualProlog6的VDE之后 在project菜单中点击new菜单项 如图7 1所示 图7 1点击new菜单项 7 1 6一个完整的例子 family1 prj6 这时 一个对话框将被打开 参见图7 2所示 输入所有相关信息 确定UIStrategy框中填写的是Console而不是GUI 图7 2项目设置对话框 7 1 6一个完整的例子 family1 prj6 第二步 建立一个空的项目当项目被建立时 VDE将会显示如图7 3所示的项目窗口 此时 它还不知道这个项目依赖于哪个文件 它只是为这个项目构造出基本的源代码文件 图7 3项目窗口 7 1 6一个完整的例子 family1 prj6 使用build菜单中的build菜单项 参见图7 4 编译和连接一个空的项目 以产生一个实际上不做任何事的可执行文件 这时 也不会产生任何错误 图7 4点击build菜单 7 1 6一个完整的例子 family1 prj6 当建立一个项目时 看看VDE的消息窗口中显示的消息 如果看不见消息窗口 可使用VDE中的windows菜单打开 我们会发现 VDE已将所有相关的Prolog的基础类模型PFC放入该项目中 PFC的类包含了建立程序所需的基本功能类 7 1 6一个完整的例子 family1 prj6 第三步 使用实际的代码组装family1 proVDE插入的是非常基本的代码 并不完成什么功能 如图7 5所示 可以删除整个代码 只需要复制和粘贴本章中给出的family1 pro的代码到窗口中 图7 5VDE生成的基本代码 7 1 6一个完整的例子 family1 prj6 当完成了复制粘贴操作之后 family1 pro的窗口将会如图7 6所示 图7 6复制和粘贴的family1 pro代码 7 1 6一个完整的例子 family1 prj6 第四步 rebuild代码重新打开build菜单 若VDE发现源代码已经改变 它将会重新编译改动的部分 如果选用BuildAll这一菜单项 所有的模型都会被重新编译 对于大程序来说 这将会耗费一定的时间 BuildAll经常只是在一系列小的编译结束后使用 只是为了保证每一步都按序进行 7 1 6一个完整的例子 family1 prj6 在项目建立过程中 VDE不只执行编译操作 同样决定项目是否需要另外的PFC模型 并插入必要的代码 如果需要的话重新开始建立过程 这些可在消息窗口的消息中看到 参见图7 7 在那里会看到由于一些附加的描述 VDE要两次建立项目 图7 7消息窗口 7 1 6一个完整的例子 family1 prj6 第五步 执行程序我们现在完成了使用VisualProlog6编写的第一个应用程序 为了测试编译完的应用程序 可以使用窗口菜单命令中的build run命令 但是 这样会导致错误 原因是我们的小程序会为了函数功能去读取名为fa txt的文本文件 这个文本文件包含程序运行所需要的数据 我们将在以后分析这个文本的语法 现在我们需要使用文本编辑器来编写文本文件 并把它们放置在可执行文件停留的路径上 通常 可执行文件在主文件夹的名为exe的子文件夹中 7 1 6一个完整的例子 family1 prj6 下面让我们使用VDE来生成一个这样的文本文件 首先使用File New这一菜单项 将弹出创建项目的选项窗口 CreateProjectItem 从左边的列表中选取文本文件 Textfile 再选择文件可能停留的路径 可执行文件family1 exe的路径 然后给文件命名为fa txt 然后点击对话框上的Create按钮 直到文件名被给出 Create按钮才会失效 最后确保检查框Addtoprojectasmodule被选中 把该文件添加到项目中去的好处是它将使后继的调试可以进行 文件的内容如下 7 1 6一个完整的例子 family1 prj6 clausesperson Judith female person Bill male person John male person Pam female parent John Judith parent Bill John parent Pam Bill 7 1 6一个完整的例子 family1 prj6 虽然这是一个数据文件 我们会注意到这个文件的语法非常相似于普通的Prolog代码 即使现在程序已经被编译结束并以二进制格式存在 也可以通过改变运行程序的主要数据来改变输出 采用与一般Prolog相同的语法 这些数据被写入文本文件 因此VisualProlog6仿效了传统Prolog动态代码的编写能力 虽然并不是所有的特性都可能 但至少在这个例子中的复合论域可以被给出 文件fa txt中使用的语法必须和程序的论域定义保持一致 定义人的算符必须用person来表示 否则用来表示算符的复合论域将不能得到初始化 在前面的叙述中我们已经提到过算符和复合论域 现在 当我们使用热键shift F9来运行程序时 或使用窗口菜单中的Build Run命令 将看到如图7 8所示的内容 7 1 6一个完整的例子 family1 prj6 这个程序处理文件fa txt中的信息 并运行出所给人物的亲戚关系的逻辑顺序 图7 8消息窗口 7 1 7程序的取舍 程序的逻辑非常简单 我们已经在前面讨论过 在那里解释了在一个Prolog程序中正确的数据表示如何产生适当的结果 让我们现在来看看逻辑工作方式 开始的时候 main谓词将会被直接调用 然后会转向run谓词 run谓词所要做的第一件事情就是读取文件fa txt中的数据 一旦数据存在的话 程序就会系统地一个个地测试处理数据 并把测试结果写在控制台上 处理数据的机制是标准的回溯和递归机制 在这里并不详细描述关于Prolog如何工作的细节 只是简要给出了其内部工作的原理 当谓词run调用谓词father时 它并不会停在father谓词结束的地方 在谓词结束时fail的调用将驱使Prolog的推理机在father谓词中寻找另一个可执行的地方 这种行为就是回溯 因为Prolog推理机事实上可以在执行过的程序中回溯查找 7 1 7程序的取舍 这种情形会循环发生 就像重复或循环的过程 并在每一次循环中谓词father都将产生一个结果 最后在数据中给出的所有可能的father的定义被耗尽 谓词run别无选择只能转到谓词run的下一个子句体 这个谓词father 和其他谓词一样 被声明为不确定性的 关键字nondeterm可以被用来声明不确定性的谓词 通过回溯 不确定性谓词可以产生多个结果 如果在谓词的声明中使用no这个关键字 谓词就成为仅能给出一个解答的过程模型 在产生第一个结果后 father谓词将会停止 再也不能重新进入 而且 在这种不确定性谓词的子句体中必须注意截断 的用法 如果在father谓词的子句体的末尾有截断 并重复出现 谓词将只产生一个结果 即使它被声明为不确定性的 7 1 7程序的取舍 Anyflow的流模式告诉编译程序赋予谓词的参量可以是自由的也可以是约束的 没有什么限制 这就意味着和father谓词的定义一样 有可能同时产生父亲的名字 如果后代的名字是约束的话 和后代的名字 如果父亲的名字是约束的话 同样可以处理两者都受约束的情况 在这种情况下谓词会检查这样的关系是否存在于数据中 明确地说 anyflow流模式同样适用于当所有的father谓词里的参数是自由变量时的情况 这时 father谓词将在程序学习到的数据中产生多种父子关系的结合体 通过查询fa txt文件 像下面代码显示的那样 最后的用法是在run谓词中所执行的内容 我们会注意到当father谓词被调用时 father谓词中的变量x和y并不受约束 7 1 7程序的取舍 run console init stdIO write Loaddata n reconsult fa txt stdIO write nfathertest n father X Y stdIO writef isthefatherof n Y X fail 7 1 8小结 在这一节 我们了解到VisualProlog6中的程序非常类似于传统Prolog中的程序 有许多不同的关键字用来区分一个Prolog程序的不同部分 虽然VisualProlog是一种面向对象的语言 但也开发不使用面向对象特点的代码 这一节描述了基于控制台的应用程序 family1 并描述了如何构造这样一个程序 我们同样发现 通过把部分代码独立于编译的二进制应用程序而保存在数据文件中 可以模拟传统Prolog程序的动态工作机制 这种数据文件的句法非常相似于Prolog的句法 7 2VisualProlog的GUI编程 在这一节 我们将增加一个简单的GUI 图形用户接口 前端到family项目 这个项目是在前面一节中开发的 我们将学会直接在VisualProlog的VDE 可视化开发环境 中创建和编辑一个简单Windows程序的大多数GUI组件 我们将学习关于GUI事件 如点击GUI的特定部分上的一个按钮等 以及它们在VisualProlog程序中如何工作等有关说明 我们还将通过两个例子来学习关于模态对话框的有关知识 一个例子是由Windows操作系统内建的 另一个例子是我们自己构建的程序 7 2VisualProlog的GUI编程 7 2 1GUI概述7 2 2GUI对事件的响应7 2 3开始一个GUI项目7 2 4创建模态对话框7 2 5修改菜单7 2 6修改工具栏7 2 7在程序中添加主代码7 2 8压缩相关代码7 2 9分析所做的工作7 2 10运行程序7 2 11小结 7 2 1GUI概述 GUI是图形用户接口首字母的缩写 在Windows操作系统环境中 这个术语表示人们熟悉的窗口 窗口带有菜单栏 工具栏 窗口右上角的小按钮等 这些元素中的每一个元素都称作一个GUI组件 而且在良好设计的程序中 这些组件按照公认的规范进行工作 用程序设计术语来说 一个GUI完成两件事 它使用复杂的图形例程在计算机显示器的相应部位上安放和恢复图形图像 例如 一个窗口右上角带有x的小方框 它还控制鼠标和其他输入设备在这些图形区域上的行为 幸运的是 这些详细的程序设计是由操作系统完成的 作为一个程序员 并不需要编排这些图形 鼠标和键盘函数 Windows已经做了这一切 它还提供一个API 应用程序编程接口 可以用来建立任何程序所要求的GUI 此外 VisualProlog已经增加了一个更高层的功能 PFC Prolog基类 不仅有助于使用GUI 而且有助于其他领域的编程 7 2 1GUI概述 更有甚者 VisualProlog的VDE可以用来可视化地创建正在开发的程序所用的最终GUI实物模型 moch up 我们将使用前一节中family例子的处理逻辑 在一个GUI程序和控制台程序之间 存在许多差异 然而 他们也有类似之处 即控制台程序和GUI程序总是从一个固定的入口点 从Goal或过程 开始 控制台程序不显示图形元素 所以程序由用户提供输入 这些输入或者是来自程序员预先设置的初始参数 或者是来自程序员预先设置的直接询问 程序中各种活动的顺序和方向是由程序员确定的 用户不能够更改该顺序 在一个控制台程序中 程序从Goal或过程启动 然后逻辑上从那一点开始向前工作 严格地使用户通过由程序员设置的一系列逻辑步 7 2 1GUI概述 在GUI程序中 程序员可以做出灵活的选择 对于程序的哪一部分应当如何完成 可以给用户提供各种选项 例如 如果启动任何一个常规的Windows程序 一般来说它并不强迫我们做这做那 我们可以随意地浏览菜单而不点击其中可选的任何一项 考虑这样一种特殊的情节 File菜单有几个菜单项 我们可以使鼠标光标通过该菜单中的任何一项 而并不完成实际的菜单点选操作 事实上 通过如此这般地在一个程序的GUI上不经意的浏览操作 是人们达到掌控软件的常用方法之一 无论如何 当编写GUI程序时 程序员必须使用一种不同的策略 一方面 编程更加容易 因为对所实现的程序中的一切活动不再硬性刻板或严格控制 7 2 1GUI概述 但是另一方面 程序员必须知道每一个GUI组件是如何工作的 什么是常规公认的惯例 以及如何坚持这些习惯 以便于当使用这些程序时 用户不会产生不必要的惊奇 同样 有时很难预测由于某些GUI事件的触发所产生的结果 因此 程序员必须仔细地分析逻辑 以便无论GUI如何使用都能维持逻辑的有效性 仔细地调整这些情形 而程序的逻辑不可以进行调整 使用有礼貌的 易于理解的警告对话框 等等 7 2 2GUI对事件的响应 1 在一个GUI程序中 所有的GUI组件等待来自键盘或鼠标 或其他输入设备 如数字化仪等 的输入 2 来自这种输入设备的信息 鼠标的点击或其他的GUI活动 称为一个事件 event 3 一个事件处理器是专门等待和实际处理一个GUI事件的一段代码 4 因为一个事件处理器的代码 只是专门等待某一事件的发生 所以看起来似乎毫无逻辑可言 其他模块甚至不必知道在实际处理一个GUI事件的类内部秘密地发生的事情 在前面叙述的内容中 我们注意到 包含的所有逻辑都是良好的 它们在逻辑上完整地彼此连接在一起 但是在GUI程序的情形 处理GUI事件的部分程序代码是被分成片断的 并且被放到了不同的事件处理程序之中 7 2 3开始一个GUI项目 当我们在VisualProlog6的VDE中创建一个项目时 确保UI策略设置为GUI VDE则创建最初的处理GUI所需要的模块集合和GUI组件资源 主菜单 一个顶部的工具栏 一个底部的状态栏 About对话框和该程序的任务窗口 TaskWindow 图7 9family2项目设置对话框 7 2 3开始一个GUI项目 这样创建一个项目之后 就可以立即编译它 这是一个空的GUI程序 在这个时期 程序实际上什么也不做 然而 它拥有了一个GUI程序所期望的全部功能 VisualProlog已经直接构造了一个运转GUI程序的基本框架 并且已经提供了一些通常所需要的特性 例如 在应用程序的主窗口 称作任务窗口TaskWindow VisualProlog给出了另一个标题为Message的窗口 这个窗口用来作为内部的控制台程序 当程序员在程序中使用stdio write 谓词时 结果将直接在Message窗口输出 如果VisualProlog没有将PFC类stdio的输出重新定向到Message窗口 则那些串将不会被看到 因为按照缺省约定 一个GUI环境没有控制台区 7 2 3开始一个GUI项目 在一个控制台应用程序中 控制台总是可以作为一个黑板来使用 程序员可以在其上输出信息 这就是为什么这些应用程序被称为控制台应用程序的原因 在前面的章节里可以看到 在这样的应用程序中 我们直接使用stdio write 谓词向控制台输出 在这一阶段运行所编译的GUI程序时 只可以空阅程序的菜单 改变程序外部主窗口 即任务窗口 的大小 双击Message窗口在任务窗口之中进行全程缩放等等 VisualProlog为Message窗口恰好给出一个小的常常是很方便的弹出式菜单 在Message窗口内部任何地方右击鼠标 随即就会弹出一个小菜单 可以清除Message窗口的内容或进行其他的活动 7 2 4创建模态对话框 现在 我们给程序添加另一个GUI组件 供以后需要时使用 这个组件是一个对话框 它用来给程序提供要处理的一个人的名字 在项目树 projecttree 中 所有模块和资源都清晰地展现在树形菜单里 右击任务窗口 从弹出菜单的上下文菜单中选取 New 菜单项 如图7 10所示 图7 10激活创建项目条款对话框 7 2 4创建模态对话框 创建项目条款的对话框CreateProjectItem出现 如图所示 确保对话框左面的Diaglog选项被选中 并且在右面输入如图7 11所示的详细信息 图7 11创建项目条款对话框 7 2 4创建模态对话框 这样 一个缺省的对话框被创建了出来 如图7 12所示 可供进一步编辑加工之用 因为我们没有为这个对话框实现帮助功能 所以就可以点击一下特定的GUI组件 例如Help按钮 再按下DEL键即可将其删除 图7 12创建的缺省对话框 7 2 4创建模态对话框 然后 再分别选中其他两个按钮 移动到合适的位置 最后的结果变成如图7 13所示的那样 图7 13编辑缺省对话框 7 2 4创建模态对话框 通过使用对话框控件 我们给对话框插入一个静态文本 所谓静态 static 一词代表一个GUI元素是不可点击的 参考前面提到的图像 在点击静态文本创建按钮之后 可以在对话框中画一个矩形区域 所要输入的文本文字将出现在其中 如图7 14所示 图7 14在缺省对话框中增加控件 7 2 4创建模态对话框 随之编辑控件属性 EditControlAttributes 将出现 如图7 15所示 在Text字段下 输入 Person 所输入的文本将出现在对话框里面 图7 15编辑控件属性对话框 7 2 4创建模态对话框 以类似的方式 我们使用对话框编辑控件来插入一个可编辑的文本字段或一个编辑控件 如图7 16所示 图7 16编辑控件工具栏 7 2 4创建模态对话框 此时 我们先放下那个空的文本字段 转去改变常量 Constant 符号为某个有意义的符号常量 而不是由VDE提供的缺省常量 这个步骤如图7 17所示 这个常量在程序代码内部使用 以便程序代码引用这个编辑字段 图7 17在编辑控件属性对话框中改变常量 7 2 4创建模态对话框 我们构造的对话框的最终的情形 如图7 18所示 图7 18所创建的对话框 7 2 4创建模态对话框 每一个对话框有一个属性称为访问次序 我们需要完成的最后一步 就是设置这个控件在对话框中被访问的次序 即当用户按下TAB键时 对话框中的GUI组件与用户交互的次序 短语 访问控件 Visitacontrol 意思是说控件获得输入焦点 这是一个十分专业的术语 短语 控件接收输入焦点 thecontrolreceivingtheinputfocus 意思是说控件立即从键盘接收输入 例如 如果一个编辑字段具有输入焦点 就可以看到该编辑字段中闪烁的插字符号 表示该字段准备接收经由键盘输入的字符 为了改变访问次序 右击该对话框 在随即弹出的菜单中选取访问次序 VisitOrder 菜单项 一些小按钮将出现在该对话框的GUI组件上 如下图7 19所示 在这里可以看到 文本编辑控件 idc ancestordialog personname 的访问次序为3 而OK按钮的访问次序编号为1 7 2 4创建模态对话框 这就是说 当该对话框呈现给用户的时候 该文本编辑控件将不会立即接收键盘的字符 当用户按一下TAB键时 焦点将转移到Cancel按钮 只有再一次按下TAB键时 焦点才转移到该文本编辑控件 只有到了这个时候 该文本编辑控件才接收键盘的输入 简言之 相对于该对话框中的其他控件而言 编辑字段的访问次序编号是最后一个 图7 19确定对话框上控件的访问次序 7 2 4创建模态对话框 使用同样的对话框属性 DialogAttributes 参见图7 20所示 我们也可以把标题改为 Ancestorof 图7 20对话框属性 7 2 5修改菜单 现在我们将修改程序的主菜单 如前所述 VDE已经提供了一种在许多程序中都可见到的由标准菜单项组成的缺省菜单 我们要去编辑它以满足程序的功能需求 通过项目树 双击TaskMenu mnu项 可以看到如图7 21所示的情形 图7 21双击任务菜单项 7 2 5修改菜单 这将开始菜单编辑器 菜单编辑器的主对话框如图7 22所示 确保在主目录中选定编辑菜单 Edit 后 点击属性 Attributes 按钮 图7 22菜单编辑器 7 2 5修改菜单 接下来就打开了菜单项属性MenuItemAttributes对话框 如图7 23所示 我们可以从 Edit到 Query改变名字 Q前面的 标记指在主菜单中该字母下面有下划线 用户能通过该字母迅速进入菜单 我们可以点击上述对话框中的 Test 按钮来测试这个特性 VDE的顶端菜单将会暂时被现在设计的菜单所替换 然后我们就可以浏览 感觉其效果 按ESC键就可以返回VDE菜单的原来状态 图7 23菜单项属性对话框 7 2 5修改菜单 我们现在回到主任务菜单 TaskMenu 对话框 双击 Query入口 我们会注意到它仍然包含老的Edit菜单里的菜单项 Undo Redo Cut Copy与Paste 从主菜单条目入口 Query删除在里面看到的所有菜单项 接着把常量前缀ConstantPrefix设定为id query 常量前缀ConstantPrefix由VDE内部使用 用于描述表示各种菜单项的常量 这些常数被用来在代码内引用菜单项 图7 24在任务菜单对话框设定常量前缀 7 2 5修改菜单 现在我们要在Query主菜单下增加一些菜单条目 点击New 如图7 24所示 按钮 然后输入信息 进入图7 25所示的界面 图7 25利用菜单项属性增加子菜单 7 2 5修改菜单 最后一步是TaskWindow菜单的编辑 在缺省状态 当VDE创建菜单时 File Open菜单无法使用 必须找出该菜单项 从MenuItemAttributes对话框中去掉禁止标记 使它变为可用 如图7 26所示 图7 26菜单项属性对话框 7 2 5修改菜单 当关闭TaskMenu对话框时 VDE会请求确认是否想保存这个菜单 点击Save键保存 图7 27VDE确认对话框 7 2 6修改工具栏 工具栏的应用是GUI的另一个有用部分 通常 它包括具有各种菜单功能的按钮 简单地说 这些按钮就是这些菜单的快捷方式 现在我们来编写工具栏的程序 当开始创建一个项目时 VisualPrologVDE会为程序创建一个缺省的工具栏 在项目树中双击ProjectToolbar tb 如图7 28所示 图7 28在项目树中双击工具栏项 7 2 6修改工具栏 这将调用工具栏编辑器 如图7 29所示 图7 29项目工具栏编辑器 7 2 6修改工具栏 在上图中 顶端是我们正在编辑的工具栏 底端是编辑工具栏组件可以用到的各种控制按钮 在工具栏中 包含一组有预定图标图形的按钮 与一般GUI程序一样 如果我们希望 也可以改变这些图标的图形 需要指出的是 VisualProlog的VDE内部已经恰当地包含了一个非常好的图标编辑程序 对大的图标 VDE打开MS画图工具来编辑 这些按钮已经被订制为一套功能菜单项 但是当我们在编辑菜单项时 也要编辑工具栏按钮并且把它放在适当的位置 首先 我们对剪切 拷贝和粘贴等按钮进行删除处理 因为程序没有这些功能 为此 首先选择这些按钮 然后从工具栏中删除 删除以后 工具栏如图7 30所示 图7 30对话框属性 7 2 6修改工具栏 现在我们将把Undo Redo和Help按钮映射并表示为下列菜单项Query Father Query Grandfather 和Query Ancestorof 我们在前面提到过 在本章中我们将不改变那些按钮的图标图像 双击Undo工具栏按钮 在出现的按钮属性ButtonAttributes对话框内 将象征工具栏按钮的内部常量从id edit undo变为id query father 如图7 31所示 图7 31按钮属性对话框 7 2 6修改工具栏 在同一个对话框中 还应该将StatusText设置由Undo Undo变为Queryfathers Listofallfatherslistedinthedatabase上一行的分号将字符串分成两部分 第一部分作为按钮本身的工具提示而显示 而第二部分将在主窗口的状态行显示 运用同样的方法 改变Redo按钮的常量值为id query grandfather 改变Help按钮的常量值为id query ancestor of 这两个按钮的状态行内容也做适当的修改 7 2 7在程序中添加主代码 我们已经完成了程序中需要的全部GUI功能性工作 现在我们将开始插入逻辑代码 在做这些工作之前 我们来了解常用的程序运行方式和现在的GUI程序运行方式之间的一些重要差异 模块的内部已经包含了初级逻辑代码 但是现在依靠相应的GUI组件控制的逻辑 它将被扩展到几个模块 7 2 7在程序中添加主代码 让我们来增加一些程序必需的逻辑代码 打开TaskWindow pro 将插字编辑符定位在下面的代码行 factsthisWin vpiDomains windowHandle erroneous 图7 32任务窗口代码 7 2 7在程序中添加主代码 一旦找到那一行代码且设置了插字编辑符 见图7 32 就在那里插入下列代码 domainsgender female male classfacts familyDBperson stringName genderGender parent stringPerson stringParent classpredicatesfather stringPerson stringFather nondetermanyflow clausesfather Person Father parent Person Father person Father male 7 2 7在程序中添加主代码 classpredicatesgrandFather stringPerson stringGrandfather nondetermanyflow clausesgrandfather Person Grandfather parent Person Parent father Parent Grandfather classpredicatesancestor stringPerson stringAncestor nondetermanyflow clausesancestor Person Ancestor parent Person Ancestor 7 2 7在程序中添加主代码 ancestor Person Ancestor parent Person P1 ancestor P1 Ancestor classpredicatesreconsult stringFileName clausesreconsult Filename retractAll familyDB file consult Filename familyDB 上面的代码是程序的核心逻辑部分 在本节 我们直接将他们插入TaskWindow pro模块中 因为我们想了解GUI组件如何进入到程序的核心逻辑中执行各种动作 在更复杂的例子中 核心逻辑代码会分开存放 有时分散到好几个模块中 GUI模块 如TaskWindow 通过扩大模块的范围 分别引用这些模块 事实上 让程序的核心逻辑代码尽可能地分散到所有GUI相关的模块中是一个很好的实践 但在本节中我们将有意忽略这些 7 2 8压缩相关代码 既然已经考虑了核心逻辑 现在我们需要插入交互位 interactivebits 在项目树中右击TaskWindow win条目 该条目表示该主任务窗口的窗口资源 所有发生在这个窗口的点击 菜单等事件都由写给这个窗口的事件处理器处理 右击所选的项 就会弹出如图7 33所示的上下文菜单 从该菜单选择代码专家CodeExpert 7 2 8压缩相关代码 图7 33在右键弹出菜单中选择 7 2 8压缩相关代码 对话框和窗口专家 DialogandWindowExpert 如图7 34所示 帮助我们为特定窗口或对话框控件 交互式GUI组件 设置缺省的事件处理器 蓝色填充的圆圈表示没有给定控件的事件处理器 绿色的钩记号表示存在相关的事件处理器 图7 34对话框与窗口专家 7 2 8压缩相关代码 使用上面的对话框 确保事件处理器是为常量id file open所表示的菜单项设置的 现在 在项目树中点击TaskWindow pro模块 选中它 用Build菜单来编辑它 如果TaskWindow pro模块没有被选择 那么Build菜单中的Compile菜单将不可用 因此一定要谨慎 当用VisualProlog6编辑模块时 它要改造项目树 使所有相关的谓词 论域等清晰地展开 便于我们直接访问 因此我们要使用这些特点来保证VDE列出在TaskWindow pro模块中定义的所有谓词 然后即可对我们关心的谓词进行操作 7 2 8压缩相关代码 注意 VDE自动识别出TaskWindow pro需要一些附加模块 如图7 35所示的那样 对该模块的编译进行了两遍 图7 35消息窗口 7 2 8压缩相关代码 编译完成以后 当我们引用TaskWindow谓词时 会在项目树中看到所有的谓词 如图7 36所示 图7 36项目树中的谓词 7 2 8压缩相关代码 我们会发现taskwindow pro模块的谓词部分将显示出谓词onFileOpen 而原来它是不存在的 双击该谓词 就会直接打开编辑器 并定位于谓词onFileOpen的子句位置处 当该谓词有多重子句时 指针将指向第一个子句体 谓词onFileOpen被称作事件处理器 windows应用程序术语 作为程序员 不需要访问这个谓词 对应的GUI组件被激活时 windows会自动调用它 在这里单击该菜单项 缺省情况下为事件处理器插入了如下代码 onFileOpen MenuTag handled 0 7 2 8压缩相关代码 用下面的代码替换该代码如下 onFileOpen MenuTag handled 0 Filename vpiCommonDialogs getFileName txt Familydatafiles txt txt Allfiles Loadfamilydatabase reconsult Filename stdIO writef Database loaded n Filename onFileOpen MenuTag handled 0 7 2 8压缩相关代码 对于对话框和窗口专家 DialogandWindowExpert 确保Query Father Query Grandfather和Query Ancestorof 菜单项的事件处理程序设置如图7 37所示 图7 37设置菜单项的事件处理程序 7 2 8压缩相关代码 对于Query Father菜单项 在VisualProlog自动生成的子句体前添加如下子句体 onQueryFather MenuTag handled 0 stdIO write nfathertest n father X Y stdIO writef isthefatherof n Y X fail 对于Query grandFather菜单项 在VisualProlog自动生成的子句体前添加如下代码 onQueryGrandFather MenuTag handled 0 stdIO write ngrandFathertest n grandfather X Y stdIO writef isthegrandfatherof n Y X fail 7 2 9分析所做的工作 现在 也许我们会问 哪里是封装 其实 我们已将代码分成两部分 前面所讲到的是非交互式的逻辑核心 而这部分需要接收用户的输入 并且这些部分被存储为不同的事件处理器 至于其他的程序 它并不需要知道这些事件处理器在程序中怎样编写 下面就让我们添加一些交互代码到其他事件处理器中 在VisualProlog自动产生的缺省子句体前 为Query Ancestorof 菜单项增加如下子句体 onQueryAncestorOf MenuTag handled 0 X ancestorDialog getName thisWin stdIO writef nancestorof test n X ancestor X Y stdIO writef istheancestorof n Y X fail 7 2 9分析所做的工作 上面代码的目的就是从前面已经构造的ancestorDialog所提供的模态对话框中获取一个字符串 代码的谓词部分假设有一个全局可达的叫做getName的谓词 该谓词在ancestorDialog模块中可用 它将会返回某人的名字 这个人的祖先正是我们要寻找的对象 与在onFileOpen事件处理器中看到的一样 我们寻找一个从模态对话框返回的字符串 文件名 模态对话框本身被谓词vpiCommonDialogs getFileName 调用 它与我们从ancestorDialog获得字符串的过程采用了相同的策略 惟一不同的是 vpiCommonDialogs getFileName 提供了一个内置的标准的窗口模式对话框文件 但是对于自己家族的ancestorDialog 我们不得不做更多的编码工作 7 2 9分析所做的工作 编译这个程序时 可能会出现一个错误 如图7 38所示 错误的原因是因为谓词onQueryAncestorOf所期盼的全局可达谓词gerName从ancestorDialog pro模块进行调用 但是还没那样写 下面让我们来纠正这些错误 图7 38编译错误信息 7 2 9分析所做的工作 这个谓词在一个模块中定义 但是在其他模块中调用 因此需要我们确保这种声明不保存在 pro文件中 但是放在一个所有程序都能调用的位置 模块的类声明文件 extension cl 就是这样的位置 所以 让我们打开ancestorDialog cm 添加下面的代码 这个声明表示getName谓词在模块内执行 也能被其他模块调用 predicatesgetName vpiDomains windowHandleParent stringNamedeterm 在模块ancestorDialog pro里 添加该模块的相关核心逻辑 与Taskwindow pro设计的方式一样 就是将这些代码插入下面的代码行前 factsthisWin vpiDomains windowHandle erroneous 下面就是要插入的代码 7 2 9分析所做的工作 domainsoptionalString none one stringValue classfactsname optionalString none clausesgetName Parent Name name none Dlg ancestorDialog new Dlg show Parent one Name name 现在还有最后一个问题 我们需要改变OK按钮的事件处理器 这是必须的 以便对话框把由用户输入的值声明进name事实类 不做这些 上面的谓词将会发现一个空的字符串 VisualProlog给出的缺省代码如下 7 2 9分析所做的工作 predicatesonControlOK vpiDomains controlHandler ClausesonControlOK Ctrl CtrlType CtrlWin CtrlInfo handled 0 vpi winDestroy thisWin 上面的代码将被改写如下 predicatesonControlOK vpiDomains controlHandler clausesonControlOK Ctrl CtrlType CtrlWin CtrlInfo handled 0 EditCtrl vpi winGetCtlHandle thisWin idc ancestordialog personname Name vpi winGetText EditCtrl name one Name vpi winDestroy thisWin 现在我们已经完成了程序 如果现在编译并运行这个程序 将不会有任何错误 7 2 10运行程序 一旦开始执行程序 要注意到不会立即执行这些活动 在前面 我们已经介绍了控制台程序的工作方式 像前面说的一样 开始一个GUI程序就像要进入一个房间 在那里用户可以自由地完成他选择的任何事情 每个房间仅仅是等待用户决定给出的输入
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!