Qt5入门第一课:从Hello Qt理解信号槽、对象树与构建系统
1. 项目概述这不是“Hello World”而是Qt开发者的第一个真正入口“《Learn Qt5》学习笔记一Hello Qt”——看到这个标题很多刚接触跨平台GUI开发的朋友第一反应是“哦又一个入门级‘Hello World’示例”。但我要说这完全不是。在Qt生态里“Hello Qt”远不止于控制台打印一行字或弹出一个空白窗口它是一整套开发范式的首次具象化落地信号与槽的初次握手、元对象系统的悄然启动、QWidget生命周期的第一次完整走通、qmake或CMake构建流程的首次闭环验证。我带过十几期Qt入门实训发现超过70%的初学者卡在“能跑通但不懂为什么能跑通”这个节点上——比如改了.pro文件却不知道QT widgets这行到底激活了哪些头文件和链接库比如点了按钮没反应反复检查connect写法却忽略了Q_OBJECT宏缺失导致moc未生成比如窗口一闪而过调试半天才发现app.exec()被漏写了。这些都不是代码错误而是对Qt底层契约理解的断层。“Hello Qt”真正的价值恰恰在于它用最简结构暴露出整个框架最关键的几条“筋脉”。它不教你怎么写复杂界面但它必须让你看清事件循环怎么接管主线程、UI线程如何与用户交互解耦、资源管理为何要依赖父对象树而非裸指针。这篇笔记就是我当年在某高校嵌入式实验室带学生做Qt移植时手把手拆解的第一个Demo的真实复盘。没有花哨效果只有三段核心代码、四个关键配置项、五处极易忽略的编译细节——全部来自实测环境Ubuntu 22.04 Qt 5.15.2 GCC 11.4所有路径、命令、报错信息均按真实终端输出还原。如果你正准备从零开始学Qt或者已经写过几个小工具却总在调试阶段莫名崩溃那这篇笔记里的每一个标点都可能是你少踩三天坑的关键。2. 内容整体设计与思路拆解为什么必须从“非图形化”版本切入2.1 经典误区一上来就建QWidget窗口大错特错绝大多数Qt教程开篇就是“新建一个Widget项目→拖个按钮→连个槽→运行”看似高效实则埋下三重隐患隐患一构建系统黑箱化IDE自动生成的.pro文件里混着CONFIG c11、TARGET untitled、TEMPLATE app等十余行配置初学者根本分不清哪些是Qt必需、哪些是编译器可选、哪些是IDE默认添加。更致命的是当后续需要交叉编译到ARM平台时这些隐藏配置会直接导致qmake生成错误的Makefile。隐患二对象生命周期模糊化拖拽生成的MainWindow类中ui-setupUi(this)自动创建所有子控件但ui指针本身是谁new的谁delete的QMainWindow析构时是否会自动释放ui这些问题在简单场景下无感一旦加入多线程或动态加载UI就会触发野指针崩溃。隐患三信号槽机制表层化Qt Designer里右键“转到槽”生成的on_pushButton_clicked()函数让新手误以为“connect只是语法糖”。实际上QObject::connect()背后是QMetaObject::activate()调用涉及元对象信息注册、信号索引查找、参数序列化等完整链路。不亲手写一次connect(btn, QPushButton::clicked, this, MyWidget::handleClick)永远体会不到取地址符对成员函数指针的强制要求。因此本笔记采用“三阶递进式”设计第一阶纯命令行仅用#include QApplication和#include QLabel不依赖任何UI类验证Qt基础库链接是否正常第二阶最小QWidget手动创建QApplication→QWidget→QLabel→show()全程不使用ui文件厘清对象父子关系第三阶信号槽实战增加QPushButton和QMessageBox用原始connect()语法绑定点击事件暴露Q_OBJECT宏与moc机制的强依赖。提示这种“反直觉”的设计并非炫技。我在某工业设备公司做Qt框架重构时发现产线软件崩溃日志90%集中在QTimer超时回调访问已销毁对象。根源正是早期团队跳过基础环节直接用Designer生成代码导致开发者对QObject的内存管理模型缺乏敬畏。2.2 工具链选择逻辑为什么坚持用qmake而非CMake当前Qt官方虽主推CMake但本笔记仍选用qmake原因有三错误反馈更精准当QT widgets缺失时qmake报错为error: undefined reference to QWidget::QWidget(QWidget*)直指链接库问题而CMake在find_package(Qt5 REQUIRED COMPONENTS Widgets)失败时常报CMake Error at CMakeLists.txt:12 (add_executable): No SOURCES given to target误导开发者以为源码路径错误。配置粒度更可控qmake的.pro文件支持条件编译win32 { LIBS -lws2_32 } unix:!macx { LIBS -lpthread }这种平台相关配置在CMake中需写if(WIN32)等多行判断对新手极不友好。与Qt Creator深度绑定Qt Creator的“构建套件”Kit管理直接映射qmake配置。当需要切换MinGW/MSVC/Clang工具链时只需在GUI中修改Kit无需手动改写CMakeLists.txt——这对嵌入式开发者调试不同ABI环境至关重要。注意本笔记所有qmake配置均通过qmake -v验证版本兼容性。Qt 5.15.2要求qmake 3.1若系统自带qmake版本过低如Ubuntu 22.04默认qmake 3.0必须先执行sudo apt install qt5-qmake更新否则QT widgets会静默失效。2.3 环境隔离策略为什么拒绝全局安装Qt很多教程建议sudo apt install qt5-default这在个人学习时看似省事实则埋雷雷区一版本碎片化Ubuntu仓库的Qt 5.15.2实际是5.15.2~dfsg-2ubuntu1比官方源码包缺少qtwebengine模块且qmake路径为/usr/lib/x86_64-linux-gnu/qt5/bin/qmake与Qt Creator默认路径不一致。雷区二权限污染sudo make install会将头文件写入/usr/include/当后续需要升级Qt时apt upgrade可能覆盖自定义编译的库导致undefined symbol错误。因此本笔记强制采用离线安装包本地路径配置方案从Qt官网下载qt-opensource-linux-x64-5.15.2.runSHA256校验值a1b2c3...运行时指定安装路径为$HOME/Qt5.15.2将$HOME/Qt5.15.2/5.15.2/gcc_64/bin加入PATH并设置QTDIR$HOME/Qt5.15.2/5.15.2/gcc_64。这样做的好处是每个项目可独立指定Qt版本qmake -query QT_INSTALL_HEADERS返回路径即为当前生效路径避免“明明装了新版本却调用旧库”的诡异问题。3. 核心细节解析与实操要点从零构建Hello Qt的七处生死关3.1 第一行代码的编译本质#include QApplication究竟做了什么很多人认为#include只是复制头文件内容但在Qt中这一行触发了三重机制头文件包含链QApplication头文件实际包含qcoreapplication.h→qobject.h→qmetatype.h最终引入qglobal.h定义的Q_DECL_EXPORT等宏。若QT core未在.pro中声明qglobal.h中的#ifdef QT_CORE_LIB分支将被跳过导致QApplication构造函数声明不可见。符号导出控制Qt库采用隐式链接implicit linkingQApplication的实现位于libQt5Core.so和libQt5Gui.so中。#include本身不链接库但qmake通过QT widgets自动向LIBS变量追加-lQt5Widgets -lQt5Gui -lQt5Core。宏定义注入qglobal.h根据QT_VERSION宏定义Q_OS_LINUX、Q_CC_GNU等平台标识符。若qmake未正确识别GCC版本Q_CC_GNU可能未定义导致Q_DECL_OVERRIDE等C11特性失效。实操验证方法# 查看qmake实际生成的编译命令 qmake -dry-run | grep g\\\|clang\\ # 输出应包含 # g -c -pipe -O2 -Wall -W -D_REENTRANT -fPIC -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB ...若输出中缺失-DQT_WIDGETS_LIB说明.pro文件中QT widgets未生效需检查是否误写为QT widget少s。3.2.pro文件的四行铁律每行都是编译成功的前提一个能稳定编译的.pro文件必须包含以下四行缺一不可QT core widgets gui # 必须显式声明依赖模块不能只写widgetsgui是widgets的父依赖 TARGET helloqt # 可执行文件名空格会被转义为下划线故hello qt会生成hello_qt TEMPLATE app # 告诉qmake生成应用程序而非库lib或子目录subdirs SOURCES main.cpp # 源文件列表路径错误时qmake不报错但make时报no rule to make target常见陷阱详解陷阱1QT widgetsvsQT widgets guiQt 5中widgets模块依赖gui模块但qmake不会自动补全依赖链。若只写QT widgetsQLabel头文件可包含但链接时undefined reference to QPainter::QPainter()——因为QPainter在libQt5Gui.so中。陷阱2TARGET含空格的灾难若写TARGET hello qtqmake生成Makefile时会将目标名转义为hello_qt但qmake -query TARGET仍显示hello qt导致make clean无法删除正确文件残留hello_qt.o引发下次编译冲突。陷阱3SOURCES路径的绝对禁忌错误写法SOURCES /home/user/project/main.cpp绝对路径正确写法SOURCES main.cpp相对路径且确保在.pro同目录原因qmake生成的Makefile中$(OBJECTS)变量基于SOURCES相对路径计算绝对路径会导致main.o生成在根目录make找不到依赖关系。3.3main()函数的三重守门员缺一不可的初始化链条标准Qt程序的main()函数如下#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); // 守门员1事件循环初始化 QLabel label(Hello Qt!); // 守门员2GUI对象创建 label.show(); // 守门员3窗口显示与事件注册 return app.exec(); // 守门员4启动事件循环注意这是第四重 }这里存在一个经典误解return app.exec()是“启动程序”实则它是唯一阻塞点。app.exec()内部执行创建QEventLoop对象调用QEventDispatcherUNIX::processEvents()监听X11事件当收到Expose事件窗口显示完成时才真正绘制QLabel若label.show()后未调用app.exec()程序立即退出窗口来不及渲染。实测对比注释掉return app.exec()→ 终端无报错但窗口不出现将app.exec()改为app.processEvents()→ 窗口闪现后立即关闭因processEvents()只处理一次事件队列在label.show()前加label.resize(200, 50)→ 窗口尺寸固定避免依赖系统默认大小。实操心得我在调试某医疗影像软件时发现DICOM图像加载后界面卡死。最终定位到QApplication::exec()被意外包裹在if(debugMode)条件中导致发布版失去事件循环。教训是app.exec()必须作为main()的最后一行且不可条件化。3.4QLabel背后的内存管理真相为什么不用new也能安全初学者常困惑“QLabel label(Hello Qt!)是栈对象label.show()后main()结束栈空间被回收窗口为何不崩溃”答案在于Qt的对象树Object Tree机制QLabel继承自QObject其构造函数接受QObject* parent参数默认为nullptr当parent非空时子对象会在父对象析构时自动deleteQApplication对象在main()结束前不会析构因此label的生命周期由app管理更关键的是QWidget::show()会调用createWinId()创建X11窗口句柄该句柄由QApplication的QEventDispatcher统一管理与C对象生命周期解耦。验证方法QLabel* label new QLabel(Hello Qt!); // 堆分配 label-setParent(app); // 显式设父对象 label-show(); // 即使不delete labelapp析构时也会自动清理但强烈不推荐此写法——因为QApplication的析构顺序不可控可能导致QWidget析构时QApplication已部分销毁。正确做法永远是栈对象优先必要时用QScopedPointer智能指针。3.5 字体与编码的隐形杀手中文乱码的终极解决方案QLabel label(你好Qt)在Linux终端常显示方块根源是Qt字体渲染链的三重编码转换源码文件编码.cpp文件必须保存为UTF-8无BOM格式VS Code默认符合但Notepad需手动设置Qt字符串编码QString内部使用UTF-16你好字面量经QTextCodec::setCodecForLocale(QTextCodec::codecForName(UTF-8))转换系统字体匹配QFontDatabase::addApplicationFont()需加载中文字体否则回退到DejaVu Sans等无汉字的字体。一劳永逸方案#include QFontDatabase int main(int argc, char *argv[]) { QApplication app(argc, argv); // 强制设置应用字体解决90%中文乱码 QFont font(Noto Sans CJK SC, 10); app.setFont(font); // 加载系统中文字体Ubuntu 22.04默认路径 QFontDatabase::addApplicationFont(/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc); QLabel label(你好Qt); label.show(); return app.exec(); }注意Noto Sans CJK SC是Google开源字体在Ubuntu/Debian系中预装。若系统无此字体可用fc-list :langzh查看可用中文字体替换addApplicationFont()参数。3.6 构建过程的五个关键节点从qmake到可执行文件的完整链路运行qmake make表面简单实则经历五个不可跳过的编译阶段阶段命令关键产物常见失败点1. qmake解析qmake helloqt.proMakefile.pro语法错误如写成2. moc预处理moc main.cpp -o moc_main.cppmoc_main.cpp缺少Q_OBJECT宏导致moc不生成信号槽代码3. C编译g -c main.cpp -o main.omain.o头文件路径错误-I参数缺失4. 链接g main.o -lQt5Widgets -o helloqthelloqt库文件路径错误-L参数未指向libQt5Widgets.so所在目录5. 运行时加载./helloqt窗口显示LD_LIBRARY_PATH未包含Qt库路径报libQt5Widgets.so.5: cannot open shared object file诊断技巧若make报moc_main.cpp: No such file or directory说明moc未执行检查Q_OBJECT宏是否在类声明中若./helloqt报symbol lookup error执行ldd ./helloqt \| grep not found定位缺失库若窗口显示但无文字用strace -e traceopenat ./helloqt 21 \| grep font查看字体文件加载路径。3.7 调试信息的黄金组合让崩溃现场无所遁形Qt程序崩溃时Segmentation fault提示过于笼统。必须启用三重调试开关编译器调试信息在.pro中添加CONFIG debug QMAKE_CXXFLAGS_DEBUG -g3 -O0-g3生成完整调试符号含宏定义-O0禁用优化避免变量被编译器优化掉。Qt断言增强在main()开头添加qInstallMessageHandler([](QtMsgType type, const QMessageLogContext context, const QString msg) { QByteArray localMsg msg.toLocal8Bit(); switch (type) { case QtDebugMsg: fprintf(stderr, Debug: %s (%s:%u, %s)\n, localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: fprintf(stderr, Warning: %s (%s:%u, %s)\n, localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: fprintf(stderr, Critical: %s (%s:%u, %s)\n, localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: fprintf(stderr, Fatal: %s (%s:%u, %s)\n, localMsg.constData(), context.file, context.line, context.function); abort(); } });GDB调试脚本创建.gdbinit文件handle SIGPIPE nostop noprint set follow-fork-mode child catch throw run启动gdb ./helloqt后自动捕获C异常抛出点。实操心得某次调试QTimer::singleShot崩溃GDB显示QTimer对象已被析构。最终发现是QObject::deleteLater()调用后QTimer仍在发送信号。解决方案在deleteLater()前调用timer-stop()并用connect(timer, QTimer::timeout, timer, QObject::deleteLater, Qt::DirectConnection)确保同步销毁。4. 实操过程与核心环节实现手把手完成Hello Qt的十二步全流程4.1 环境准备三分钟搭建纯净Qt开发环境步骤1验证系统基础组件# Ubuntu 22.04必备工具 sudo apt update sudo apt install build-essential libgl1-mesa-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxkbcommon-dev libwayland-dev注意libxcb1-dev是X11后端必需若缺失QApplication构造时会报Could not connect to any X display即使在SSH终端也需安装。步骤2下载并安装Qt 5.15.2# 下载离线安装包约1.2GB wget https://download.qt.io/official_releases/qt/5.15/5.15.2/qt-opensource-linux-x64-5.15.2.run chmod x qt-opensource-linux-x64-5.15.2.run # 静默安装到$HOME/Qt5.15.2避免sudo ./qt-opensource-linux-x64-5.15.2.run --script install.qsinstall.qs脚本内容function Controller() {} Controller.prototype.WelcomePageCallback function() { gui.clickButton(buttons.NextButton); } Controller.prototype.CredentialsPageCallback function() { gui.clickButton(buttons.NextButton); } Controller.prototype.IntroductionPageCallback function() { gui.clickButton(buttons.NextButton); } Controller.prototype.TargetDirectoryPageCallback function() { gui.currentPageWidget().TargetDirectoryLineEdit.setText(installer.value(HomeDir) /Qt5.15.2); gui.clickButton(buttons.NextButton); }步骤3配置环境变量echo export PATH$HOME/Qt5.15.2/5.15.2/gcc_64/bin:$PATH ~/.bashrc echo export QTDIR$HOME/Qt5.15.2/5.15.2/gcc_64 ~/.bashrc source ~/.bashrc qmake -v # 验证输出QMake version 3.1, Using Qt version 5.15.24.2 创建项目结构严格遵循Qt工程规范步骤4建立标准目录结构mkdir -p ~/projects/helloqt/{src,build} cd ~/projects/helloqt touch src/main.cpp touch helloqt.pro目录结构必须为helloqt/ ├── helloqt.pro # 项目配置文件与src同级 └── src/ # 源码目录 └── main.cpp # 主程序文件为什么helloqt.pro不能放在src/内因为qmake默认以.pro文件所在目录为项目根目录SOURCES main.cpp会从该目录搜索若.pro在src/内则需写SOURCES src/main.cpp违背Qt Creator默认约定。步骤5编写最小可行.pro文件# helloqt.pro QT core widgets gui TARGET helloqt TEMPLATE app SOURCES src/main.cpp关键点SOURCES路径必须相对于.pro文件位置此处src/main.cpp表示helloqt/src/main.cpp。4.3 编写核心代码逐行解析Hello Qt的每一处精妙设计步骤6编写src/main.cpp非图形化验证版// src/main.cpp - 验证Qt基础库链接 #include QApplication #include QDebug // 使用QDebug替代printf支持Qt日志系统 int main(int argc, char *argv[]) { qDebug() Qt library version: QT_VERSION_STR; // 输出5.15.2 qDebug() Compiled for: QT_VERSION; // 输出0x050f02 // 创建QApplication验证核心模块 QApplication app(argc, argv); qDebug() QApplication created successfully; return 0; // 不调用app.exec()仅验证库链接 }编译验证cd ~/projects/helloqt qmake make ./helloqt # 输出 # Qt library version: 5.15.2 # Compiled for: 327426 # QApplication created successfully步骤7升级为图形化版本// src/main.cpp - 完整Hello Qt #include QApplication #include QLabel #include QFont #include QFontDatabase int main(int argc, char *argv[]) { QApplication app(argc, argv); // 解决中文乱码Ubuntu 22.04专用 QFont font(Noto Sans CJK SC, 12); app.setFont(font); QFontDatabase::addApplicationFont(/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc); QLabel label(Hello Qt! 你好Qt); label.resize(300, 80); label.move(100, 100); // 设置窗口位置可选 label.show(); return app.exec(); }步骤8构建与运行cd ~/projects/helloqt qmake make ./helloqt此时应看到一个300×80像素的窗口居中显示中英文混合文本。4.4 进阶功能添加按钮与消息框的信号槽实战步骤9扩展main.cpp添加交互功能#include QApplication #include QLabel #include QPushButton #include QMessageBox #include QVBoxLayout #include QWidget #include QFont #include QFontDatabase class HelloWidget : public QWidget { Q_OBJECT // 关键必须声明Q_OBJECT宏 public: HelloWidget(QWidget *parent nullptr) : QWidget(parent) { // 创建布局 QVBoxLayout *layout new QVBoxLayout(this); // 创建标签 QLabel *label new QLabel(Hello Qt! 你好Qt, this); label-setFont(QFont(Noto Sans CJK SC, 14)); layout-addWidget(label); // 创建按钮 QPushButton *btn new QPushButton(点击我, this); layout-addWidget(btn); // 连接信号槽原始语法非lambda connect(btn, QPushButton::clicked, this, HelloWidget::onButtonClicked); } private slots: void onButtonClicked() { QMessageBox::information(this, 提示, 按钮被点击了); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QFontDatabase::addApplicationFont(/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc); app.setFont(QFont(Noto Sans CJK SC, 10)); HelloWidget widget; widget.resize(400, 200); widget.show(); return app.exec(); }步骤10修正.pro文件适配新代码QT core widgets gui TARGET helloqt TEMPLATE app SOURCES src/main.cpp HEADERS src/main.cpp # 添加HEADERS触发moc处理Q_OBJECT宏注意HEADERS src/main.cpp是hack技巧——因Q_OBJECT在.cpp中需强制qmake将其视为头文件以运行moc。更规范做法是分离头文件但本笔记为简化流程采用此法。4.5 构建与调试全流程记录步骤11完整构建日志实录# 清理旧构建 rm -rf build mkdir build cd build # 生成Makefile指定Qt路径 qmake -makefile -o Makefile ../helloqt.pro # 编译关键查看moc是否执行 make VERBOSE1 21 | grep -E (moc|g\\|link) # 输出应包含 # moc ../src/main.cpp -o moc_main.cpp # g -c -pipe -O2 -Wall -W -D_REENTRANT -fPIC -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB ... -o main.o ../src/main.cpp # g -Wl,-O1 -o helloqt main.o moc_main.o -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread步骤12运行时问题排查若窗口显示但按钮无响应执行objdump -t ./helloqt | grep onButtonClicked确认符号是否存在若无输出说明moc未生成槽函数检查Q_OBJECT宏是否在类声明后、构造函数前若有输出但点击无反应用qInstallMessageHandler捕获QObject::connect警告。5. 常见问题与排查技巧实录来自真实项目的12个高频故障5.1 编译阶段典型问题速查表故障现象根本原因一键修复命令error: ‘QApplication’ was not declared in this scope#include QApplication缺失或QT core未在.pro中声明qmake -query QT_INSTALL_HEADERS确认头文件路径检查.pro中QT coreundefined reference to QApplication::QApplication(int, char**)链接库缺失libQt5Core.so未找到export LD_LIBRARY_PATH$HOME/Qt5.15.2/5.15.2/gcc_64/lib:$LD_LIBRARY_PATHmoc: Cannot create ./moc_main.cppmoc可执行文件不在PATH中which moc确认路径若为/usr/bin/moc则与Qt版本不匹配改用$HOME/Qt5.15.2/5.15.2/gcc_64/bin/mocerror: ‘Q_OBJECT’ macro expands to ‘staticMetaObject’Q_OBJECT宏所在类未继承QObject或其子类检查类声明class HelloWidget : public QWidgetQWidget继承自QObject5.2 运行时崩溃问题深度解析问题1窗口显示后立即崩溃GDB显示QThreadStorageData::get()原因QApplication对象在main()作用域外被析构但QThread仍在访问线程局部存储。解决方案确保QApplication app(argc, argv)声明在main()开头且不被任何{}作用域包裹。问题2点击按钮后程序退出strace显示exit_group(0)原因QMessageBox::information()调用后HelloWidget对象被deleteLater()销毁但main()中widget是栈对象deleteLater()无效导致QMessageBox析构时访问已销毁的父窗口。修复代码void onButtonClicked() { QMessageBox::information(this, 提示, 按钮被点击了); // 添加延时销毁避免父窗口提前析构 QTimer::singleShot(0, this, QObject::deleteLater); }问题3中文显示为方块fc-match Noto Sans CJK SC返回DejaVuSans.ttf原因字体名称匹配失败QFontDatabase::addApplicationFont()加载的字体未被QFont识别。终极方案// 强制使用字体文件路径创建字体 QFont font; font.setFamily(Noto Sans CJK SC); font.setPointSize(12); app.setFont(font); // 并行加载字体文件双重保险 int fontId QFontDatabase::addApplicationFont(/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc); if (fontId -1) { qWarning() Failed to load Noto Sans CJK; }5.3 跨平台部署避坑指南坑1Linux下可运行Windows下报api-ms-win-crt-runtime-l1-1-0.dll is missing原因Qt 5.15.2 Windows版依赖Visual C 2015-2019运行库而目标机器未安装。解决方案# Windows下使用windeployqt工具 $HOME/Qt5.15.2/5.15.2/msvc2019_64/bin/windeployqt.exe --release --no-opengl-sw ./helloqt.exe该命令自动拷贝msvcp140.dll等依赖。坑2Ubuntu 20.04编译的程序在22.04运行报GLIBC_2.33 not found原因GLIBC版本向前兼容但不向后兼容。22.04的GLIBC 2.35高于20.04的2.31。解决方案在最低目标系统如20.04上编译或使用patchelf降级patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 --set-rpath $OR

相关新闻

当硬核逻辑遇见文学通感:技术笔记温情润色实验的深度复盘

当硬核逻辑遇见文学通感:技术笔记温情润色实验的深度复盘

深秋十月的夜晚,书房里弥漫着陈皮普洱茶的沉静醇香。我靠在椅背上,翻阅着这两个星期以来通过大模型进行的一场特殊的“文字风格重构实验”。 在长期的技术训练中,程序员的语言习惯往往被编译器和工程规范所深深规训。我们习惯了用断言代替感受…

2026/10/15 1:46:06 阅读更多 →
AI 培训公司的资质和案例,分别能说明什么?

AI 培训公司的资质和案例,分别能说明什么?

这不是顺序问题,是分工问题「先看资质还是先看案例」这个问法,隐含了一个假设——两者是可比的同类项。实际上它们衡量的是完全不同的东西:谁先谁后不重要,各占多少权重才重要。先说一个实用的比例:对预算有限、团队不…

2026/10/15 1:46:06 阅读更多 →
企业 AI 技能培训的费用,由哪几项构成?

企业 AI 技能培训的费用,由哪几项构成?

数字差在哪:四个变量同样问「企业 AI 培训要多少钱」,两家企业拿到的数字可能差出好几倍。这不是随意定价,而是两边买的根本不是同一样东西:一家买的是半天通用课,另一家买的是围绕本企业任务设计、带课后跟进的内训。…

2026/10/15 1:46:06 阅读更多 →

最新新闻

Spring Boot实战:高校学生电子履历管理系统设计与实现

Spring Boot实战:高校学生电子履历管理系统设计与实现

折腾完这套高校学生电子履历管理平台,最大的感觉就是:简历系统看着不起眼,实际做起来比想象中复杂得多。这个项目虽然以毕业设计的形式展开,但功能覆盖了学生端、企业端、管理员端的完整闭环,既要把 Java Web 环境下常…

2026/10/15 8:18:28 阅读更多 →
SpringBoot+Android电影推荐系统开发实战

SpringBoot+Android电影推荐系统开发实战

1. 项目概述这个基于SpringBoot和Android的电影推荐系统,是我去年为一个本地影院开发的商业项目。核心目标是解决用户在海量影片中"选择困难"的问题,通过个性化算法为用户推荐可能感兴趣的电影。系统采用B/S架构,后台用SpringBoot实…

2026/10/15 8:18:28 阅读更多 →
【股票交易】第 19 章 成本结构、经营杠杆与规模效应

【股票交易】第 19 章 成本结构、经营杠杆与规模效应

回到目录 文章目录 19.1 成本是经营选择的结果 经营选择先决定成本结构 固定与变动取决于时间和业务范围 灵活性、控制与承诺之间的取舍 19.2 收入变化怎样放大为利润变化 成本不能同步调整时,利润会放大波动 宝洁收入增加以后,利润怎样变化 经营杠杆与盈亏平衡的计算条件 用…

2026/10/15 8:18:28 阅读更多 →
中国开源年会侧记:社区治理、AI开源与生态转型

中国开源年会侧记:社区治理、AI开源与生态转型

今年的开源年会,我不仅去了,还提前半小时到场,结果在签到处排了快二十分钟。这种久违的拥挤感,说实话,反而让人心里踏实。作为常年泡在开源社区里的开发者,我亲眼看着国内开源活动从偏技术沙龙变成全产业链…

2026/10/15 8:18:28 阅读更多 →
PolyMetatron 系统提示词全解:用 FOL、CNF 与 HOL 构建逻辑问题求解与知识综合管道

PolyMetatron 系统提示词全解:用 FOL、CNF 与 HOL 构建逻辑问题求解与知识综合管道

提示工程AI 安全治理 【免费下载链接】TheBigPromptLibrary A collection of prompts, system prompts and LLM instructions 项目地址: https://gitcode.com/gh_mirrors/th/TheBigPromptLibrary 点击查看 免费下载 导读 本文以 TheBigPromptLibrary 仓库收录的 P…

2026/10/15 8:18:28 阅读更多 →
WebSocket全双工真相:不是同时收发,而是帧级流控

WebSocket全双工真相:不是同时收发,而是帧级流控

1. 为什么全双工不是“同时收发”那么简单——从一个被反复误解的协议说起你肯定用过实时聊天、在线协作文档、股票行情推送,甚至网页版远程桌面。这些功能背后,几乎都站着同一个沉默的功臣:WebSocket。但很多人一听到“全双工”,…

2026/10/15 8:17:28 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →