ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Qt C++数独游戏全解析:从源码编译到部署发布

Qt C++数独游戏全解析:从源码编译到部署发布 简介这是一款基于Qt框架实现的C数独游戏完整工程代码已经过测试并成功运行适合计算机相关专业学生、初学Qt的开发者及需要课程设计或毕业设计参考的读者同时也便于在现有代码上做二次功能扩展。压缩包内共包含五十九个文件整体大小约十九兆主要分为C源码、Qt界面文件、翻译文件与运行所需的动态库同时附有可直接启动的可执行程序便于先体验游戏效果再回到源码中逐一研读。工程按功能模块划分清楚主窗口、数独生成与求解逻辑、自定义对话框等分文件管理界面与业务逻辑相分离非常适合用来理解Qt的信号槽通信、控件布局以及自定义委托等要点。目前已有193人学习下载包内提供README说明文档从打开工程到编译运行都有清晰指引十分适合作为Qt项目练手素材或课设作业的基础也可以在此基础上继续增加计时、提示、难度选择等扩展玩法。1. 为什么这套基于Qt的C数独游戏值得照着敲一遍数独游戏看起来是个玩具却同时踩中了 C 对象设计、Qt Widgets、事件循环、资源编译这些课设高频考点所以这类 C 小游戏一直是下载量很大的题材。这套基于Qt的C数独游戏把完整工程塞在一个压缩包里qmake 工程、9×9 棋盘算法、自定义委托、难度对话框、qrc 资源、编译好的 exe 和 DLL 都在。下载后别只双击 exe要先弄明白三件事二维数组怎么变成屏幕上的棋盘换一台电脑为什么会缺 DLL如何不动核心算法改难度、换随机数。正在做课设的人照着它能一晚上跑通全流程刚入门 Qt 的也能从这条链看懂一个完整小游戏。下面按“先摸目录、再读关键类、最后补发布经验”的顺序拆。2. 解压后先别急着开 IDE目录结构、构建入口和第一眼日志很多从网上下载 Qt 课设资源的人第一反应是双击里面那个 sudoku.exe发现能跑就以为自己已经掌握源码了。这是最常见的学习误区。真正值得花时间看的是sudoku.pro以及它身后那一堆.h、.cpp、.ui文件。在打开 IDE 之前把文件分类一次后面定位问题会快得多。2.1 把源码、界面描述、资源和发布物分开理解从目录结构来看核心学习材料是main.cpp、mainwindow.h/cpp、sudoku.h/cpp、customdelegate.h/cpp、customdialog.h/cpp、enum.h。mainwindow.ui和customdialog.ui是 Qt Designer 保存的界面描述文件里面是 XML 布局和控件设置构建时由 Qt 的uic工具转成 C 代码所以手工改界面时不要只改.ui而不重新构建。image目录和img.qrc是另一类资源。qrc 文件把图片路径聚合成清单构建时用 Qt 的rcc工具把所有图片打进可执行文件里。这样做的好处是发布时不用额外带一个图片目录资源也不会因为路径改变而丢图标。可以先按这张表建立索引层级典型文件你需要关心什么工程入口sudoku.proqmake 模块依赖、源文件清单、资源清单源码逻辑sudoku.cpp/h、mainwindow.cpp/h数独算法、主窗口交互、游戏状态界面绘制customdelegate.cpp/h、customdialog.cpp/h三宫粗线、选中效果、难度与输入面板资源与发布img.qrc、image/、exe 与 Qt DLL图标字体、部署运行所需的动态库这里要特别提醒不要在源文件列表里看到sudoku.exe就停止思考。这个 exe 是作者在当前机器上、用特定 Qt 版本和编译器编出来的它的价值是“证明程序可以运行”不是“替代你编译”。你要验证自己的开发环境应该从打开sudoku.pro开始。2.2 构建入口为什么跳过图形界面直接敲 qmake 更稳sudoku.pro是整套工程的构建依据。如果你用 Qt Creator直接 Open Project 选这个文件它会自动调用qmake和编译器。但如果临时换到其他电脑、或者遇到编译缓存污染我会切到命令行重新走一遍构建这时候.pro就是唯一可信的入口。一个典型的数独工程配置是这样的QT core gui widgets TARGET sudoku TEMPLATE app CONFIG c11 SOURCES main.cpp mainwindow.cpp sudoku.cpp customdelegate.cpp customdialog.cpp HEADERS mainwindow.h sudoku.h customdelegate.h customdialog.h enum.h FORMS mainwindow.ui customdialog.ui RESOURCES img.qrcQT core gui widgets三个模块都不能省。core提供事件循环和非 GUI 基础类gui提供 QPainter、QIcon 这类绘制相关类widgets提供 QApplication、QDialog、QTableView 等界面组件。缺少gui模块时很多与绘制相关的头文件会直接编译不过报错信息往往还很难看。命令行构建顺序一般是mkdir build cd build qmake ../sudoku.pro CONFIGrelease mingw32-make -j4如果你用的是 MSVC 套件把最后一行换成nmake-j4只是并行编译参数CPU 核心多时可以加大到-j8。用命令行构建还有一个好处报错信息会按顺序刷出来不会像图形环境里那样一闪而过。只要能生成sudoku.exe基本可以排除源码编译问题后面再出错就集中在运行环境。2.3 程序入口只有一个 main.cpp但它负责的不只是启动窗口main.cpp在这个工程里非常朴素大多数 Qt Widgets 程序都是这个骨架#include QApplication #include mainwindow.h int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow win; win.show(); return app.exec(); }QApplication在构造时要做很多与平台相关的事比如加载底层平台插件、初始化事件系统这也是为什么有些程序在还没走到MainWindow构造就崩溃。win.show()只是把窗口映射到屏幕上真正让程序活起来的是app.exec()它进入事件循环并一直阻塞到窗口关闭。理解这个顺序后你调试时就能根据崩溃位置判断问题性质是发生在QApplication app构造过程中还是在MainWindow构造过程中或者是在事件循环里处理方向完全不同。这段代码里没有业务逻辑所以一般不需要改它。遇到“启动黑屏但进程还在”的情况问题通常在MainWindow构造或绘图函数里跟main.cpp关系不大。3. 把数独从二维数组变成可视化棋盘模型、视图与自定义委托的实现链数独游戏的难点不在界面而在“题面怎么表示、冲突怎么判断、格子怎么画得好看”。这个工程里sudoku.h/cpp是数据层mainwindow持有一块棋盘视图customdelegate负责绘制customdialog负责输入。把这条链读通整份源码的核心就拿到手了。3.1 棋盘数据层9×9 容器加“行、列、宫”三重校验Sudoku类最常见的数据结构是QVectorQVectorint外层是 9 行内层是 9 列。不直接用原生二维数组理由有两条一是QVector带边界检查拷贝和赋值相对安全二是和 Qt 的 model/view 模型对接时可以在data()函数里用QVariant直接返回。一个典型的数据层接口长这样#ifndef SUDOKU_H #define SUDOKU_H #include QVector class Sudoku { public: enum Difficulty { TryEasy, TryNormal, TryHard }; Sudoku(); bool setValue(int row, int col, int val); int value(int row, int col) const; void generate(Difficulty diff); private: bool isValid(int row, int col, int val) const; QVectorQVectorint m_board; }; #endifenum.h在这类工程里的作用通常是集中存放难度枚举、单元格状态枚举。单独放一个头文件是为了让sudoku.cpp、customdelegate.cpp都能引用而不互相 include 额外的类减少耦合。isValid要检查三件事同一行不能有重复数字同一列不能有重复数字所在 3×3 宫格内也不能有重复数字。常见实现是循环 9 次同时遍历行、列、宫格bool Sudoku::isValid(int row, int col, int val) const { for (int i 0; i 9; i) { if (m_board[row][i] val) return false; if (m_board[i][col] val) return false; int blockRow row / 3 * 3 i / 3; int blockCol col / 3 * 3 i % 3; if (m_board[blockRow][blockCol] val) return false; } return true; }注意blockRow和blockCol的换算row / 3 * 3先得到当前格子所在宫格的起始行i / 3让循环在第 0、1、2 行内部迭代col那边用i % 3得到列偏移。这一行公式如果记不住写成三组嵌套 for 也可以但可读性和性能会差一些。这套校验同时也是隐藏功能当你在界面输入一个数字但格子没反应多半是setValue在调用isValid后直接拒绝了输入而不是 UI 代码有 bug。3.2 界面怎么画出来QTableView 加自定义委托项目里没有用一堆QLineEdit拼棋盘而是把棋盘当作一个表格视图再用自定义委托接管单元格绘制。这样做的原因是数独的格子要有几种状态普通细线、3×3 宫分界处的粗线、被选中时的高亮背景。用QTableView加委托可以只画你需要画的不用维护几十个输入框的焦点逻辑。customdelegate.h里一般会继承QStyledItemDelegate重写paint()。下面这种画法是常见套路先让父类把数字、选中背景画好再在宫格边界补线void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const { QStyledItemDelegate::paint(painter, option, index); int col index.column(); int row index.row(); QRect r option.rect; if (col % 3 2) { QPen pen painter-pen(); pen.setWidth(2); painter-setPen(pen); painter-drawLine(r.right(), r.top(), r.right(), r.bottom()); } if (row % 3 2) { QPen pen painter-pen(); pen.setWidth(2); painter-setPen(pen); painter-drawLine(r.left(), r.bottom(), r.right(), r.bottom()); } }option.rect是当前单元格的矩形区域所有绘制都要以它为基准千万别用窗口绝对坐标否则在窗口移动或滚动时会画到错误位置。QStyledItemDelegate::paint被调用后painter的状态可能被父类改过所以再画线时应该重新取painter-pen()或显式指定颜色宽度避免出现线条粗细忽大忽小的玄学问题。在MainWindow构造函数里使用委托的方式通常是ui-tableView-setItemDelegate(new CustomDelegate(this)); ui-tableView-setEditTriggers(QAbstractItemView::CurrentChanged);setEditTriggers要和你的输入交互方式匹配。如果工程里实现了点击格子就能输入用CurrentChanged或DoubleClicked如果走的是自定义对话框输入可以不给QTableView开启编辑防止误触。3.3 自定义对话框难度选择与数据回传customdialog.ui对应的通常是一个模态对话框里面是难度下拉框、提示文字和确定取消按钮。Qt 里模态对话框用exec()打开用户按“确定”后返回QDialog::Accepted。这类对话框常见的坑是关掉窗口后主界面拿不到用户选项因为对话框对象已经销毁或者数据没有被保存到成员变量。常见做法是给CustomDialog写一个类似selectedDifficulty()的接口在主窗口调用后再取值CustomDialog dlg(this); if (dlg.exec() QDialog::Accepted) { Sudoku::Difficulty diff dlg.selectedDifficulty(); sudoku.generate(diff); refreshBoard(); }如果你要改造成“手动输入谜面”的界面就把输入框的数据也塞进对话框的返回接口里。重点在于exec()返回后对话框对象还在内存里只是被隐藏了可以在读取完数据后再delete或者像上面这样按值使用生命周期问题就少很多。到这里数据层、绘制层和输入层的代码线已经串起来。接下来最常见的事就是你把工程复制到另一台电脑上跑然后撞上各种 Qt 运行环境和版本问题。第四部分我把经常踩的几个坑按现象、原因、解决三个环节列出来。4. 运行与编译排查五个能靠复制粘贴解决的 Qt 数独项目报错这一章写给两种情况一是你自己编译时报错二是把资源里的 exe 复制到别处后启动失败。这里面的很多问题和数独算法无关而是 Qt 环境在捣乱。我的原则是先看错误文本的最后一个词不要一上来就改源码。4.1 编译期库版本混用和 MSVC 链接错误现象用qmake构建到一半弹出一行fatal: cannot mix incompatible Qt library (version ex50601) with this library或者执行新生成的 exe 时在启动阶段直接退出。原因ex50601表示程序链接到的 Qt 库版本是 5.6.1但运行时加载到的 Qt5Core.dll 是另一个版本也可能是同一个版本号但编译器类型不同。混合使用 MinGW 编译的 Qt 和 MSVC 编译的 Qt是最常见的触发场景。解决全部统一到同一套 Qt。我的做法是先删除工程目录下的build文件夹和.pro.user文件然后在 Qt Creator 里重新选 Kit如果 pro 里写了CONFIG c11编译器至少要支持 C11若后缀带msvc套件里的“Qt version”和“Compiler”必须配套。切到命令行时确认qmake来自同一个 Qt 根目录别在 PATH 里混了两条 Qt 路径。现象另一个高频报错是 MSVC 工程的LNK2019 unresolved external symbol和LNK2001后面往往跟着一串 Qt 类名。原因常见于.pro里忘了写QT widgets或SOURCES、HEADERS漏了文件导致某些类的方法、虚函数表找不到实现也可能是用 release 配置编译却链接了 debug 库。解决先补QT依赖再检查FORMS和RESOURCES是否齐全最后确认只生成单一版本避免 debug 和 release 混编。注意debug 库的 DLL 通常带d后缀比如Qt5Cored.dll发布时千万别把 debug 库拷出去。4.2 运行期QPA 平台插件、DLL 缺失和界面不刷新现象在 Linux 开发板上运行./sudoku -platform linuxfb终端报qt.qpa.plugin: could not find the Qt platform plugin linuxfb程序随即退出。原因Qt 的平台插件是按插件目录动态加载的libqlinuxfb.so没有安装到 Qt 的plugins/platforms目录里或者QT_QPA_PLATFORM_PLUGIN_PATH指向了错误位置。解决先确认报错里的插件名然后把环境变量指到正确的插件目录export QT_QPA_PLATFORM_PLUGIN_PATH$HOME/Qt/5.15.2/gcc_64/plugins/platforms export QT_QPA_PLATFORMlinuxfb ./sudoku这是嵌入式或纯命令行 Linux 环境里常见的 Qt 启动方式之一桌面 Linux 一般用xcb而不是linuxfb。改完环境变量仍然失败就用ldd sudoku检查动态库是否被旧路径拖住。现象Windows 上把sudoku.exe拷到另一台电脑双击后弹出Qt5Core.dll或Qt5Widgets.dll未找到。原因资源里的 exe 是作者本机链接过的 Qt 运行库目标电脑的 PATH 里没有这些 DLL或者 DLL 版本对不上。解决最简单的是让 DLL 和 exe 在同一个目录。如果你安装的 Qt 是完整版直接用官方部署工具补齐cd /d E:\sudoku\release C:\Qt\5.15.2\mingw81_32\bin\windeployqt.exe sudoku.exe运行后工具会把需要的Qt5Gui.dll、Qt5Widgets.dll、platforms/qwindows.dll、styles等自动复制到同目录比自己手工拷 DLL 可靠得多。提示在 Windows 上还要注意 32 位和 64 位exe 是 32 位就必须配 32 位的 Qt 套件和 DLL。现象程序能跑但用自定义对话框选完困难级别后棋盘没有刷新。原因MainWindow更新了内部Sudoku数据却没有通知 view或者通知了 model但表格视图的视口没有重绘。解决数据变化后调用一次视图的刷新接口ui-tableView-viewport()-update();这条语句适合放在所有修改数据的函数尾部比如setValue、generate、clearBoard之后保证“数据变了屏幕一定跟着变”。4.3 资源文件怪癖qrc 图标变空白现象把工程拷贝到新目录后按钮图标和棋盘背景变成空白但程序不报错。原因.ui文件里引用了相对路径的图片而实际图片放进了image目录新的工作目录和 qrc 里的路径对不上rcc没有把图片导出到可执行文件里。解决把资源引用改成 qrc 标准路径也就是以:/开头的路径例如:/image/icon.png。改完.qrc后必须重新运行qmake再make因为rcc不会自动感知图片变更。5. 给它换个玩法难度参数、随机出题与界面定制的三个方向如果你已经把程序跑通接下来就该考虑怎么让这份资源变成“你自己的代码”。这里我不建议一上来就大改算法先把三处最常见改法练熟替换随机数、拉开难度差距、换一张皮肤。这三个方向都不会破坏原有框架而且都能拿出去给答辩老师讲。5.1 用更稳定的随机数替代旧式 qrand早期 Qt 工程里常见qrand() % 81来定位一个格子。qrand依赖全局种子在线程环境下用起来别扭Qt 5.15 里已经被建议替换为QRandomGenerator。想做随机出题可以用下面这段逻辑随机挖掉格子#include QRandomGenerator int removed 0; int target 45; // 根据难度决定剩余提示数量 while (removed target) { int idx QRandomGenerator::global()-bounded(81); int row idx / 9; int col idx % 9; if (m_board[row][col] ! 0) { m_board[row][col] 0; removed; } }QRandomGenerator::global()是进程级单例不需要手动管理生命周期bounded(81)返回 0 到 80 的整数分布比取模运算更均匀。这里挖掉的格子越多难度越大。真正严谨的出题还要在每挖掉一个数字后验证“唯一解”否则会出现多个答案的题面做课设时至少保留 40 个以上已填数字不然题面很容易翻车。5.2 把难度枚举和界面选项做成一张映射表enum.h里的难度枚举如果不和前端控件对应用户选“简单”和“困难”可能得到一样的题。我一般会把映射关系写成一个独立方法避免逻辑散落在各个槽函数里Sudoku::Difficulty diff Sudoku::TryNormal; int hintCount 36; switch (userChoice) { case UI_EASY: diff Sudoku::TryEasy; hintCount 45; break; case UI_NORMAL: diff Sudoku::TryNormal; hintCount 38; break; case UI_HARD: diff Sudoku::TryHard; hintCount 28; break; default: break; }hintCount是生成题面时保留的格子数越小难度越高。注意界面控件的currentIndex未必等于枚举值调试时打一行qDebug() userChoice hintCount;就能发现“选困难但被 switch 吞掉”的情况。5.3 用 QSS 快速换肤注意别和委托绘制打架Qt Widgets 可以用 QSS 对控件做样式定制。把下面这段放在MainWindow构造函数里棋盘观感立刻会变ui-tableView-setStyleSheet( QTableView { background-color: #fdf6ec; } QTableView::item:selected { background-color: #ffe0b2; } );这段 QSS 只影响视图不影响模型和委托。如果换肤后背景色没变化原因是CustomDelegate::paint里自己用painter-fillRect覆盖了背景要修改委托里的填充色或去掉覆盖绘制。提示QSS 的优先级高于组件默认绘制但不会高于手动painter绘制这是刚接触时最容易忽略的黑匣子。6. 用 windeployqt 打一个干净包发布目录的检查清单如果这份作业要交到别的机器上演示就别只压缩源码目录。我一般会多留十分钟把发布目录整理成最小可运行集合。Qt 程序发布的关键只有一句话exe 所在目录必须能看见所有依赖 DLL 和平台插件。手工复制容易漏掉platforms和styles子目录所以用部署工具更稳。在 Windows 下我通常在 Qt 命令行里这么走mkdir E:\sudoku\release\deploy copy E:\sudoku\release\sudoku.exe E:\sudoku\release\deploy\ C:\Qt\5.15.2\mingw81_32\bin\windeployqt.exe ^ --release --no-translations ^ E:\sudoku\release\deploy\sudoku.exe--no-translations可以省掉一堆用不上的语言文件--no-opengl-sw在普通桌面程序里也建议加上它不依赖软件方式的 OpenGL 库。命令执行完后检查三个地方根目录有没有Qt5Core.dll、Qt5Widgets.dll和Qt5Gui.dll子目录platforms里有没有qwindows.dllstyles里有没有qwindowsvistastyle.dll。如果手上没有部署工具还可以在 Visual Studio 命令行里用dumpbin查依赖dumpbin /dependents sudoku.exe | findstr Qt5输出会列出exe直接依赖的 Qt DLL然后你按清单把库拷进部署目录。最后把整个deploy目录放到一台没有安装 Qt 的电脑上双击这一步才是真正的发布验证。从那以后我每次发布 Qt 小游戏都强制走一遍这个流程先清理 build再全量编译单独建 deploy 文件夹跑 windeployqt最后到干净的机器上双击一次。宁可做一次无用功也不要等答辩现场弹“缺少 Qt5Widgets.dll”。希望帮到你。本文还有配套的精品资源点击获取
返回列表