Qt for macOS - 具体问题

本页概述了 Qt 中有关 macOS 支持的主要问题。macOS 术语和具体流程请访问https://developer.apple.com/。

Aqua 风格是 macOS 平台的重要组成部分。与 Cocoa 一样,Qt 提供的 widget 与macOS 人机界面指南中描述的外观相似。请注意,尽管 Qt 的部件在外观和感觉上使用了 AppKit,但它并不代表每个单独的 Qt Widget 都是封装好的本地控件。

Qt Widget 图库页面包含使用 macOS 平台主题的应用程序示例图片。

适用于 macOS 的 Qt 属性

下面列出了一组有用的属性,可用于调整 macOS 上的应用程序:

macOS 总是对屏幕进行双缓冲,因此Qt::WA_PaintOnScreen 属性不起作用。此外,不可能在绘制事件之外进行绘制,因此 Qt::WA_PaintOutsidePaintEvent 也不起作用。

鼠标右键点击

QContextMenuEvent 类为 macOS 应用程序提供了鼠标右键点击支持。这将映射到上下文菜单事件,例如,将显示弹出式选择的菜单。这是鼠标右键最常见的用法,并可映射到 macOS 单键鼠标支持的控制点击。

国际化

macOS 上的应用程序会在Info.plist 中声明其支持的语言。然后,系统会将应用程序的支持语言与用户的语言偏好进行匹配,以确定应用程序启动时的地域。这反过来又决定了通过QLocale::uiLanguages() 反映的有序语言,以及 AppKit 等系统框架如何获取其本地化资源,如菜单标题和字符串。

由于 Qt 应用程序开箱即不翻译,因此CMakeqmake 项目默认生成的Info.plist 设置为 CFBundleAllowMixedLocalizations设置为YES ,以允许系统框架选择最符合用户语言偏好的本地化,即使应用程序本身没有本地化。一旦你通过qt_add_translations 将翻译添加到你的应用程序,该 CFBundleAllowMixedLocalizations键将自动移除,取而代之的是 CFBundleLocalizations,列出您支持的所有语言。对于qmake ,这一过程必须手动完成。

Qt 可检测菜单栏并将其转换为 Mac 本地菜单栏。在现有的 Qt 应用程序中,这通常是自动完成的。不过,如果您有特殊需要,Qt 实现目前会从 Active 窗口(例如QGuiApplication::focusWindow() )开始选择菜单栏,并应用以下测试:

  1. 如果窗口有QMenuBar ,则使用该窗口。
  2. 如果窗口是模态的,则使用其菜单栏。如果未指定菜单栏,则使用默认菜单栏(如下文所述)。
  3. 如果窗口没有父窗口,则使用默认菜单栏(如下文所述)。

这些测试在父窗口链中一直进行,直到满足上述规则之一为止。如果其他规则都不符合,就会创建一个默认菜单栏。Qt 的默认菜单栏是一个空菜单栏。不过,您可以通过创建一个无父窗口QMenuBar 来创建一个不同的默认菜单栏。创建的第一个菜单栏将被指定为默认菜单栏,并在需要默认菜单栏时使用。

使用本地菜单栏会给 Qt 类带来某些限制。下文限制列表部分将提供更多信息。

Qt 通过QMenuBar 为全局菜单栏(Global Menu Bar)提供了支持。macOS 用户希望在屏幕顶部有一个菜单栏,而 Qt 则支持这一点。

此外,用户还希望某些惯例得到遵守,例如应用程序菜单应包含 "关于"" 首选项"、"退出 "等。尽管 Qt 并不提供与应用程序菜单直接交互的方式,但它可以处理这些约定。

每个QAction 都有一个menuRole 属性,用于控制应用程序菜单项的特殊位置;但默认情况下,menuRoleTextHeuristicRole ,这意味着菜单项将通过其text 自动检测。

其他标准菜单项,如剪切复制粘贴全选,既适用于应用程序,也适用于某些本地对话框,如QFileDialog 。请务必使用标准快捷方式创建这些菜单项,以便在对话框中启用相应的编辑功能。目前还没有MenuRole 标识符,但当QAction 使用默认TextHeuristicRole 时,它们会像应用程序菜单项一样被自动检测到。

特殊键

为了在 macOS 上为 Qt XML 应用程序提供预期行为,Qt::Key_MetaQt::MetaModifierQt::META 枚举值对应于标准 Apple 键盘上的 Control 键,而Qt::Key_ControlQt::ControlModifierQt::CTRL 枚举值对应于 Command 键。

底座

可以与 Dock 进行交互。图标可通过在应用程序的主窗口中调用 QWindow::setWindowIcon() 进行设置。setWindowIcon()调用可根据需要随时进行,从而提供一个易于更新的图标。

可访问性

许多用户使用辅助设备与 macOS 进行交互。Qt 的目标是在应用程序中自动实现这一点,使其符合平台上公认的做法。Qt 使用 Apple 的辅助功能框架为残疾用户提供辅助功能。

库和部署支持

Qt 为框架和捆绑包等 macOS 结构提供支持。了解这些结构非常重要,因为它们会直接影响应用程序的部署。

Qt 提供了一个部署工具macdeployqt,以简化部署过程。Qt for macOS - 部署》一文详细介绍了部署过程。

作为框架的 Qt 库

默认情况下,Qt 是作为一组框架构建的。框架是 macOS 首选的发布库的方式。Apple 的《框架编程指南》网站有更多关于框架的信息。

重要的是要记住,框架总是与发布版本的库链接。如果需要 Qt 框架的调试版本,请使用DYLD_IMAGE_SUFFIX 环境变量确保加载调试版本:

export DYLD_IMAGE_SUFFIX=_debug

或者,你也可以临时交换调试版本和发布版本,Apple 的 "Debugging Magic "技术说明中对此有详细介绍。

如果不想使用框架,只需用-no-framework 配置 Qt XML 。

./configure -no-framework

基于捆绑包的库

如果想在 macOS 应用程序捆绑包(应用程序目录)中使用一些动态库,可在应用程序捆绑包目录中创建一个名为Frameworks的子目录,并将动态库放在其中。如果动态链接库的安装名称为@executable_path/.../Frameworks/libname.dylib,应用程序就会找到该动态链接库。

如果使用qmake 和 Makefiles,请使用QMAKE_LFLAGS_SONAME 设置:

QMAKE_LFLAGS_SONAME  = -Wl,-install_name,@executable_path/../Frameworks/

或者,也可以使用install_name_tool(1) 在命令行中修改安装名称。

DYLD_LIBRARY_PATH 环境变量将覆盖这些设置和任何其他默认路径,例如查找/usr/lib内的动态链接库和类似的默认位置。

合并库

如果要结合 Qt XML 动态链接库构建新的动态链接库,需要引入ld -r 标志。这样,重定位信息就会存储在输出文件中,这样该文件就可以成为另一个ld 运行的对象。这可以通过在.pro 文件中设置-r 标志和LFLAGS 设置来实现。

初始化顺序

dyld(1) 调用全局静态初始化程序的顺序与它们链接到应用程序的顺序一致。如果一个库与 Qt 链接,并引用 Qt 中的全局(来自自己库中的全局初始化程序),请先将应用程序与 Qt 链接,然后再将其与库链接。否则,结果将是未定义的,因为 Qt 的全局初始化程序尚未被调用。

编译时标志

当你想定义 macOS 专用代码时,以下标志会有所帮助:

  • Q_OS_DARWIN 当 Qt 检测到您使用的是基于达尔文的系统(如 macOS 或 iOS)时定义。
  • Q_OS_MACOS 当您在 macOS 系统上时定义。

注意: 在 Qt 5 及更高版本中不再定义Q_WS_MAC

如果要为特定版本的 macOS 定义代码,请使用/usr/include/AvailabilityMacros.h 中定义的可用性宏。

QSysInfo 和 QOperatingSystemVerison 文档中有关于运行时版本检查的信息。

macOS 本地应用程序接口访问

访问捆绑路径

macOS 应用程序的结构是一个目录(以.app 结尾)。该目录包含子目录和文件。将插件和在线文档等项目放在此捆绑包中可能会很有用。以下代码会返回应用程序捆绑包的路径:

#ifdef Q_OS_MAC   QStringbundlePath=QString::fromNSString(NSBundle.mainBundle.bundlePath);    qDebug() << "Bundle path =" << bundlePath;
#endif

有关使用 NSBundle API 的更多信息,请访问Apple 开发者网站

QCoreApplication::applicationDirPath() 可用于确定捆绑包中二进制文件的路径。

使用本地 Cocoa 面板

Qt 的事件派发器比 Cocoa 提供的更加灵活,它可以让用户旋转事件派发器(以及运行QEventLoop::exec ),而无需考虑是否在屏幕上显示模式对话框(这是与 Cocoa 相比的区别)。因此,我们需要在 Qt 中进行额外的管理,以正确处理这个问题,不幸的是,这使得混合本地面板变得很困难。目前最好的方法是遵循下面的模式,我们用本地代码发布对函数的调用,而不是直接调用。这样我们就可以知道,在显示本地面板之前,Qt 已经干净利落地更新了所有待处理的事件循环递归:

#include <QtGui>

class NativeProxyObject : public QObject
{
    Q_OBJECT
public slots:
    void execNativeDialogLater()
    {
        QMetaObject::invokeMethod(this, "execNativeDialogNow", Qt::QueuedConnection);
    }

    void execNativeDialogNow()
    {
        NSRunAlertPanel(@"A Native dialog", @"", @"OK", @"", @"");
    }

};

#include "main.moc"

int main(int argc, char **argv){
    QApplication app(argc, argv);
    NativeProxyObject proxy;
    QPushButton button("Show native dialog");
    QObject::connect(&button, SIGNAL(clicked()), &proxy, SLOT(execNativeDialogLater()));
    button.show();
    return app.exec();
}

限制

MySQL 和 macOS

将静态 C 库链接到动态库时,如果同时定义了-prebind-multi_module ,似乎会出现问题。如果在链接 Qt.NET Framework 时收到以下错误信息,请将其删除:

ld: common symbols not allowed with MH_DYLIB output format with the -multi_module option
/usr/local/mysql/lib/libmysqlclient.a(my_error.o) definition of common _errbuff (size 512)
/usr/bin/libtool: internal link edit command failed

使用 -single_module 重新链接 Qt。这只是在 Qt 中构建 MySQL 驱动程序时出现的问题。它不会影响插件或静态构建。

D-Bus 和 macOS

QtDBus 模块默认在 macOS 上动态加载 libdbus-1 库。这意味着与QtDBus 模块链接的应用程序即使在没有该库的 macOS 系统上也能加载,但它们将无法连接到任何 D-Bus 服务器,也无法使用QDBusServer 打开服务器。

要使用 D-Bus 功能,需要安装 libdbus-1 库,例如通过 Homebrew、Fink 或 MacPorts 安装。如果要部署到其他系统,您可能需要在应用程序捆绑包中包含这些库。此外,请注意 macOS 上没有系统总线,会话总线只有在配置了 launchd 进行管理后才会启动。

  • QMenu 被转换为 Mac 本机菜单栏时,带有加速器的QMenu 中具有多个按键的操作 (QKeySequence) 将无法正确显示。将显示第一个键。不过,快捷键仍会像在所有其他平台上一样被激活。
  • QMenu 本地菜单栏中使用的对象无法通过正常的事件处理程序来处理 Qt 事件。请在菜单上安装一个委托,以便收到这些变化的通知。或者,也可以考虑使用 () 和 () 信号来跟踪菜单可见性;这些信号提供的解决方案应能在 Qt 支持的所有平台上运行。QMenu::aboutToShow QMenu::aboutToHide
  • 默认情况下,Qt 会创建一个本地退出菜单项,该菜单项将对CMD+Q 快捷方式做出反应。为QAction::QuitRole 角色创建QAction 将替换该菜单项。因此,替换动作应连接到QCoreApplication::quit 插槽,或一个用于停止应用程序的自定义插槽。

本地小工具

Qt XML 支持工作表,工作表由窗口标志Qt::Sheet 表示。

通常,在提及原生 macOS 应用程序时,原生指的是直接与底层窗口系统对接的应用程序,而不是使用某些中间层的应用程序。Qt 应用程序与 Cocoa 应用程序一样,是作为一等公民运行的。我们在内部使用 Cocoa 与操作系统通信。

符号可见性警告

在链接 C++ 库时,函数和对象被称为符号。符号可以具有defaulthidden 可见性

出于性能考虑,Qt XML 和许多其他库默认使用hidden 可见性编译其源代码,只有当要在用户项目中使用符号时,才将其标记为default 可见性。

遗憾的是,当一个库使用hidden 可见性编译,而用户项目应用程序或库使用default 可见性编译时,苹果链接器会发出警告。

如果项目开发人员希望消除警告,则需要在编译项目代码时也使用hidden 可见性。

在 CMake 中,只需在CMakeLists.txt 中添加以下代码即可:

set(CMAKE_CXX_VISIBILITY_PRESET hidden)

在 qmake 中,可在.pro 文件中添加以下代码:

CONFIG+=hide_symbols

如果项目构建了库,那么库中任何要在其他库或应用程序中使用的符号都必须明确标记为default 可见性。例如,可以用Q_DECL_EXPORT 来注释这些函数或类。

CMake Xcode 项目创建的 xcarchive 中缺少 dSYM 包

由于 Xcode 中的一个错误和某些CMake 限制,CMake 生成的 Xcode 项目将无法在 Xcode 的归档任务中将应用程序的dSYM bundle 包含到xcarchive 中。

Qt XML 提供了一种作为选择项的变通方法,这样dSYM 捆绑程序就会包含在xcarchive 中,但这是有代价的。也就是说,以下 CMake 功能将无法正常工作:

  • 任何$<TARGET_FILE:app> 生成器表达式都可能扩展为无效路径,无法指向应用程序二进制文件
  • 即使设置了CMAKE_RUNTIME_OUTPUT_DIRECTORY 变量及其相关的RUNTIME_OUTPUT_DIRECTORY 目标属性,也会被忽略
  • 其他未知问题

为减少上述问题,您可以

  • 仅在打算创建xcarchive 时启用变通方法,而不是在项目开发过程中启用
  • 确保只在项目根目录中添加可执行文件和库,而不是在add_subdirectory 调用中。

要启用变通方法,请使用以下选项配置项目:

cmake . -DQT_USE_RISKY_DSYM_ARCHIVING_WORKAROUND=ON

或在调用qt_add_executableqt_add_library 之前在项目中设置该变量:

set(QT_USE_RISKY_DSYM_ARCHIVING_WORKAROUND ON)

...

qt_add_executable(app)

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.