Qt Widgets 架构与开发入门第十章-第十二章

第 10 章 对话框、剪贴板与拖放

10.1 标准对话框

Qt 提供一批开箱即用的对话框:

QString path = QFileDialog::getOpenFileName(this, "选择文件", QDir::homePath(),
                                            "文本 (*.txt);;所有 (*)");
QColor c    = QColorDialog::getColor(Qt::white, this);
QFont f     = QFontDialog::getFont(nullptr, this);
QString txt = QInputDialog::getText(this, "重命名", "新名称:");
QMessageBox::warning(this, "提示", "文件已修改,是否保存?");

10.2 自定义对话框

继承 QDialog,通过 accept() / reject() 关闭。同步展示:

MyDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
    useSettings(dlg.result());
}

模态阻塞事件循环之外的窗口(可选 Qt::WindowModal 只阻塞父窗口)。异步展示用 open() + 连接 accepted() / rejected() 信号。

10.3 剪贴板

QGuiApplication::clipboard()->setText("copied");
QString s = QGuiApplication::clipboard()->text();

要粘图或自定义 MIME 数据,用 QMimeData:clipboard()->setMimeData(new QMimeData)

10.4 拖放

拖出:

void MyList::mouseMoveEvent(QMouseEvent *e)
{
    if (!(e->buttons() & Qt::LeftButton)) return;
    auto *drag = new QDrag(this);
    auto *mime = new QMimeData;
    mime->setText(currentItem()->text());
    drag->setMimeData(mime);
    drag->exec(Qt::CopyAction | Qt::MoveAction);
}

接收:重写 dragEnterEvent 决定是否接受,dropEvent 处理数据。别忘了 setAcceptDrops(true)


第 11 章 资源、样式与国际化

11.1 Qt 资源系统(.qrc)

把图标、翻译、QSS 等打进可执行文件,发布时不用担心相对路径:

<!-- resources.qrc -->
<RCC>
  <qresource prefix="/">
    <file>icons/open.svg</file>
    <file>styles/dark.qss</file>
  </qresource>
</RCC>

代码里用 ":/icons/open.svg" 引用。CMake 打开 AUTORCC 后,只要把 .qrc 加到 add_executable 里即可。

11.2 Qt Style Sheets(QSS)

类 CSS 语法,用 QApplication::setStyleSheet() 或对单个 widget 应用:

QPushButton {
    background: #3D7EFF;
    color: white;
    border-radius: 6px;
    padding: 6px 14px;
}
QPushButton:hover  { background: #5B92FF; }
QPushButton:pressed{ background: #2A5FCF; }
QPushButton:disabled { background: #999; }

支持子控件选择器(QComboBox::drop-down)和属性选择器(QPushButton[flat="true"])。QSS 会覆盖平台原生风格,重度使用时关闭原生风格保持一致(QApplication::setStyle("Fusion"))。

11.3 国际化(i18n)

三步走:

  1. 源码里所有面向用户的字符串用 tr() 包起来:tr("打开文件")
  2. CMake 用 qt_add_translations 或手动跑 lupdate 生成 .ts,linguist 编辑翻译,lrelease 生成 .qm
  3. 运行时加载:
QTranslator tr;
if (tr.load(QLocale(), "myapp", "_", ":/i18n"))
    app.installTranslator(&tr);

翻译加载必须在创建 UI 之前,否则已经生成的字符串不会自动重译(除非重写 changeEvent() 处理 QEvent::LanguageChange)。

11.4 QSettings

跨平台持久化用户偏好:

QSettings s("MyCorp", "MyApp");
s.setValue("mainwindow/geometry", saveGeometry());
s.setValue("mainwindow/state",    saveState());

restoreGeometry(s.value("mainwindow/geometry").toByteArray());
restoreState(s.value("mainwindow/state").toByteArray());

Windows 默认写注册表,macOS 写 plist,Linux 写 ini。想统一用 ini,可以在 main()QSettings::setDefaultFormat(QSettings::IniFormat)


第 12 章 打包与部署

代码写完只是第一步,还得让别人的机器能跑起来。

12.1 Release 构建

  • Qt Creator 顶部构建套件旁切换到 Release(或 cmake --build build --config Release)。
  • Release 会关闭调试信息、开优化,体积和性能差异很大。
  • 想在 Release 保留符号方便追踪 crash,加 -DCMAKE_BUILD_TYPE=RelWithDebInfo

12.2 Windows:windeployqt

windeployqt --release --dir dist path\to\MyApp.exe

windeployqt 会把需要的 Qt DLL、平台插件(platforms/qwindows.dll)、样式、翻译一并拷到目标目录。MSVC 构建的还要带上 VC++ 运行时(vcredist)。

12.3 macOS:macdeployqt

macdeployqt MyApp.app -dmg

生成一个自包含的 .app 或直接打包成 .dmg。上架需要签名和公证(codesign + notarytool)。

12.4 Linux

Linux 生态碎片化,常见做法:

  • AppImage:单文件可执行,配合 linuxdeployqtlinuxdeploy + linuxdeploy-plugin-qt
  • Flatpak / Snap:发行版无关的沙箱包。
  • 传统 deb/rpm:交给发行版打包。

12.5 静态 vs 动态链接

  • 动态(LGPL 友好、默认):体积小,用户端要一起分发 Qt DLL。
  • 静态(需自己重编 Qt):单个 exe,但商用需要商业许可(LGPL 静态链接要额外满足重链接义务)。

12.6 版本与图标

  • Windows 上通过 .rc 文件加图标和版本信息,CMake 里 add_executable(app WIN32 main.cpp app.rc)
  • macOS 通过 Info.plist(CMake MACOSX_BUNDLE + MACOSX_BUNDLE_INFO_PLIST)。

附录 A 调试与常见陷阱

A.1 调试小工具

  • qDebug() << x:控制台打印,支持大多数 Qt 类型。
  • Q_ASSERT(cond):调试期断言。
  • qFatal("..."):直接终止程序并留栈。
  • QLoggingCategory:分类日志,可通过环境变量 QT_LOGGING_RULES="myapp.*=true" 动态开关。
  • GammaRay:Qt 官方(KDAB 维护)的运行时探查器,可以看对象树、信号槽连接、模型内容,排 UI 问题极好用。

A.2 高频踩坑清单

  • 忘写 Q_OBJECT → 信号槽不工作、qobject_cast 返回空。
  • 忘调 moc / AUTOMOC → 链接错误 undefined reference to vtable
  • 栈上 QWidget 又设了父对象 → 程序退出时崩溃(重复 delete)。
  • 跨线程直接操作 UI → 概率性崩溃,现象很随机。
  • paintEvent 外构造 QPainter(widget) → 警告 + 什么也画不出。
  • QStringLiteral("...") vs 普通 "...":热路径用前者避免运行时构造。
  • emit 关键字:纯装饰,写不写都能编译,但建议写,让读者一眼看出是信号。
  • setLayout / 顶层没加 layout → 缩放后控件挤在角落。
  • 在信号槽里 delete sender() → 用 deleteLater(),让事件循环处理完当前事件再释放。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值