初学 Qt(一)

初学 Qt 之从零开始的中国象棋小游戏(一)

最近对 Qt 这个跨平台 C++ 图形应用程序框架很感兴趣,闲暇时间多学了一下,收获很多,也踩了不少坑,在这里记录一下,分享心得。

Qt 的安装

安装 Qt 并不麻烦,就是网速有点慢。推荐使用国内镜像代理下载。

首先进入 Qt 官网,在 Try Qt 处点击 Download Qt,填完基本信息后,点击提交就可以下载 Qt 下载器了。当然,你也可以直接去国内镜像站上下载 Qt 下载器。

打开 Qt 下载器,注册 Qt 账户,并登录,同意协议。若要使用国内镜像代理,点击左下角的配置图标。

然后从百度上选择一个国内镜像站,我这里选用中科大的镜像站,参考中科大镜像站上的帮助文档,我们需要把 http://mirrors.ustc.edu.cn/qtproject/online/qtsdkrepository/windows_x86/root/qt/ 加入到 repository 列中(注意这是在 Windows 操作系统下),加入后先别急着关,可以点击 test 测试一下镜像站连接是否成功。现在,下载器就会从国内镜像站中获取下载数据了,速度会快不少。

之后的安装就很无脑了,一路选择确定和安装位置,然后选择需要安装的 Qt 部件。推荐安装最新的 Qt 6 或者 Qt 5.12,最好选择所有的 Qt 核心功能,以及合适的编译器和调试器,当然别忘了相关的开发和设计工具,比如 Qt Creator 和 CMake、Ninja 等。

点击确认后,就可以等待下载完成了!

如果安装上遇到困难,或者单纯不想看文字,建议去b站上看视频跟着走一遍,亲测有效。

学习 Qt

网上关于 Qt 的学习资料相对丰富,但质量参差不齐。当然类似字典功能的官方文档是最好的选择,这里推荐几个比较详细的中文资料网站:

Qt 学习之路 2

Qt 快速入门系列教程

信号槽

上面的系列教程已经很详细地介绍了 Qt 相关知识。这里提取些重点简单强调一下。首先介绍信号槽,这是 Qt 框架中最有特点的机制,它可以帮助我们解耦复杂程序流程,增强技术设计能力。

信号槽的概念来自观察者模式。当某个事件被触发后(如按钮检测到自己被按下),该对象(按钮)就会发出一个信号。注意:这种发出是没有目的的,类似广播。接下来,若想让另一个对象(控制器开关)接受到该信号,它就会使用连接函数 connect(),将发送者(即按钮对象)和自己的一个触发函数(称为槽)连接起来,表示当发送者发出信号给接收者后,被连接的槽函数会自动回调。

为更好地说明,以程序中的一段代码为例:

1
2
3
4
connect(ui->startbutton, SIGNAL(clicked(bool)),
this, SLOT(startGame()));

void startGame(const QString& path = ":/opening.json");

上述代码的 connect() 函数使用的是 Qt 4 的版本。当 startbutton 被点击后,会发出 clicked(bool) 信号,而 this 是接收者,收到信号后就会调用 startGame() 槽函数。

在 Qt 4 中,信号和槽函数必须用 SIGNALSLOT 这两个宏包裹,且中间不能出现任何参数的变量名。从源代码中看到,这两个宏仅仅是将函数名转换成了字符串

1
2
# define SLOT(a)     "1"#a
# define SIGNAL(a) "2"#a

这样做的弊端非常明显:Qt 4 不支持将全局函数或者 Lambda 表达式传入 connect()。一旦出现连接不成功的情况,Qt 4 是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的调试难度。

因此,尽量使用 Qt 5 提供的新机制:

1
2
connect(scene_->getRecorder(), &Recorder::recordHistory,
this, &MainWindow::writeHistory);

Qt 5 中信号和槽是类型安全的:信号的签名必须与槽函数的签名兼容。其实槽函数的参数可以比信号的参数更少,从而忽略额外的参数。这时编译器可以基于函数指针的语法检测类型是否匹配。

Qt 的视图框架

Qt 采用了基于元素的视图框架,主要由三个部分组成:元素(item)、场景(scene)、视图(view)。基于元素意味着每一个组件都是一个独立的元素(item),它们都被独立地添加在场景(scene)中,而观众需要从不同的角度(view)观察整个场景。类比于歌舞剧,舞台即是场景,而演员和道具等都是元素,需要被加入到舞台中,视图便是布置在舞台周边的摄像机,给观众从不同角度欣赏歌舞剧。基于元素的视图框架是很多窗口开发框架都会用到的概念,它有别于面向过程式的描述方式(先确定两个端点,然后连线,最后形成矩形等),它要求程序员先创建一个场景,再加入各种元素,然后确定一个视图用于观察。

在象棋程序中,我创建了一个场景,加入了所有的棋子,并将它们放在数组中统一管理,尔后创建一个视图,用来反馈棋盘的信息(因为是象棋,视图处理相对简单)。因为棋手在下棋时需要指定移动的棋子和位置,因此很多的事件响应操作必须由场景完成。此外,行棋规则受限于棋子的类型和当前所处的位置,在对棋子的行为编程时也需要获得全局的棋盘信息。

具体到代码中,有以下几个类需要特别关注:

  • QGraphicsScene Qt 图形场景类
  • QGraphicsItem Qt 的图形元素基类
  • QGraphicsPixmapItem 有 Pixmap 的图形元素类
  • QGraphicsItemAnimation 关于元素的动画行为类

关于中国象棋

工程源代码可从 github 下载。所有模块使用 C++ 实现,AI 算法采用传统的最小最大搜索算法实现,其中,评价函数以及数值参考了《PC游戏编程——人机博弈》中的内容。下图是试运行画面:

目前进展如下:

已完成的

  • 基本游戏界面搭建
  • 基本行棋流程、行棋动画
  • 悔棋功能
  • 加载、保存游戏功能,使用 JSON 文件格式记录了每局的对战情况。
  • 记录行棋历史功能
  • 将军情况的检测(部分)
  • 象棋的热座对战模式

还未完成的

  • 回合限定计时功能
  • 远程玩家对战功能(双人游戏)
  • 对局的复盘模式
  • AI 对战情景的进一步优化

初学 Qt(一)
https://dingfen.github.io/2021/09/24/2021-9-24-Qt-Chess/
作者
Bill Ding
发布于
2021年9月24日
更新于
2024年4月9日
许可协议