适用于 macOS 的 Qt - 特定问题
本页面概述了 Qt 在 macOS 支持方面的主要问题。有关 macOS 的术语和具体流程,请参阅https://developer.apple.com/。
Aqua
Aqua 风格是 macOS 平台不可或缺的一部分。与 Cocoa 类似,Qt 提供的控件外观符合《macOS 人机界面指南》中的描述。请注意,尽管 Qt Widgets 在内部使用 AppKit 来实现外观和操作体验,但它并未将每个 Qt Widget 都作为封装的原生控件来呈现。
Qt Widgets 图库页面包含了一些采用 macOS 平台主题的应用程序示例图片。
macOS 的 Qt 属性
以下列出了一组可用于调整 macOS 上应用程序的实用属性:
- Qt::AA_PluginApplication
- Qt::AA_DontUseNativeMenuBar
- Qt::AA_MacDontSwapCtrlAndMeta
- Qt::WA_MacOpaqueSizeGrip
- Qt::WA_MacShowFocusRect
- Qt::WA_MacNormalSize
- Qt::WA_MacSmallSize
- Qt::WA_MacMiniSize
- Qt::WA_MacAlwaysShowToolWindow
- Qt::Sheet
- Qt::Drawer
- Qt::MacWindowToolBarButtonHint,
- QMainWindow::unifiedTitleAndToolBarOnMac
macOS 始终对屏幕进行双缓冲处理,因此Qt::WA_PaintOnScreen 属性无效。此外,无法在绘制事件之外进行绘制,因此 Qt::WA_PaintOutsidePaintEvent 同样无效。
右键单击
QContextMenuEvent 类为 macOS 应用程序提供了右键单击支持。这将映射为上下文菜单事件,例如,显示弹出式选项的菜单。这是右键单击最常见的用途,并且在 macOS 单按钮鼠标支持下,该操作会映射为 Control 键点击。
国际化
macOS 上的应用程序会在其 `Info.plist ` 中声明所支持的语言。 随后,系统会将应用程序支持的语言与用户的语言偏好进行匹配,以确定应用程序启动时使用的区域设置。这进而决定了通过 `QLocale::uiLanguages()` 反射出的语言排序,以及 AppKit 等系统框架如何获取其本地化资源(如菜单标题和字符串)。
由于 Qt 应用程序默认不提供翻译,因此针对CMake 和qmake 项目生成的默认Info.plist 文件会将 CFBundleAllowMixedLocalizationsYES ,以便系统框架能够选择最符合用户语言偏好的本地化版本——即使该本地化版本在应用程序本身中不可用。一旦您通过qt_add_translations 向应用程序添加翻译, CFBundleAllowMixedLocalizations 该键值将自动被移除,并替换为 CFBundleLocalizations,其中列出了您支持的所有语言。对于qmake ,此过程必须手动完成。
菜单栏
Qt 会检测菜单栏并将其转换为 Mac 原生菜单栏。通常情况下,此功能会自动集成到现有的 Qt 应用程序中。但是,如果您有特殊需求,Qt 的实现目前会从活动窗口(例如QGuiApplication::focusWindow()) 开始,并通过以下测试来选择菜单栏:
- 如果窗口具有QMenuBar ,则使用该菜单栏。
- 如果窗口是模态窗口,则使用其菜单栏。如果未指定菜单栏,则使用默认菜单栏(如下文所述)。
- 如果窗口没有父窗口,则使用默认菜单栏(如下文所述)。
这些检查将沿着父窗口链一直向上进行,直到满足上述规则之一为止。 如果其他方法均失败,则会创建一个默认菜单栏。Qt 中的默认菜单栏是一个空菜单栏。不过,您可以通过创建一个没有父窗口的QMenuBar 来创建一个不同的默认菜单栏。最先创建的那个将被指定为默认菜单栏,并在需要默认菜单栏时被使用。
使用原生菜单栏会对 Qt 类带来某些限制。下文列出的限制部分提供了更多信息。
Qt 通过 `QMenuBar` 支持全局菜单栏。macOS 用户期望在屏幕顶部有一个菜单栏,Qt 尊重这一习惯。
此外,用户期望某些约定能得到遵守,例如应用程序菜单应包含“关于”、“偏好设置”、“退出”等选项。Qt 处理了这些约定,尽管它并未提供直接与应用程序菜单交互的手段。
每个QAction 都拥有一个menuRole 属性,用于控制应用程序菜单项的特殊布局;但默认情况下,menuRole 的值为TextHeuristicRole ,这意味着菜单项将根据其text 被自动检测。
其他标准菜单项(如“剪切”、“复制 ”、“粘贴 ”和“全选”)既适用于您的应用程序,也适用于某些原生对话框(如QFileDialog )。请务必使用标准快捷键创建这些菜单项,以便在对话框中启用相应的编辑功能。 目前尚无针对这些菜单项的MenuRole 标识符,但当QAction 采用默认的TextHeuristicRole 时,它们将与应用程序菜单项一样被自动检测到。
特殊键
为了使 Qt 应用程序在 macOS 上表现符合预期,Qt::Key_Meta 、Qt::MetaModifier 和Qt::META 枚举值对应于标准 Apple 键盘上的 Control 键,而Qt::Key_Control 、Qt::ControlModifier 和Qt::CTRL 枚举值则对应于 Command 键。
Dock
可以与Dock进行交互。可通过在应用程序的主窗口中调用QWindow::setIcon()来设置图标。setIcon()调用可根据需要随时进行,从而实现图标的轻松更新。
无障碍功能
许多用户通过辅助设备与 macOS 进行交互。Qt 的目标是让您的应用程序自动实现这一功能,从而符合该平台的公认规范。Qt 使用 Apple 的辅助功能框架为残障用户提供访问支持。
库与部署支持
Qt 支持 macOS 中的结构,例如框架(Frameworks)和软件包(bundles)。了解这些结构非常重要,因为它们会直接影响应用程序的部署。
Qt 提供了一个部署工具macdeployqt,用于简化部署流程。文章《Qt for macOS - 部署》对部署流程进行了更详细的说明。
作为框架的 Qt 库
默认情况下,Qt 会构建为一组框架。框架是 macOS 首选的库分发方式。Apple 的《框架编程指南》网站提供了关于框架的更多信息。
需要注意的是,框架始终会链接到库的发布版本。若需要 Qt 框架的调试版本,请使用DYLD_IMAGE_SUFFIX 环境变量来确保加载调试版本:
export DYLD_IMAGE_SUFFIX=_debug此外,您还可以暂时互换调试版和发布版,具体方法详见Apple 的《调试魔法》(Debugging Magic)技术说明。
如果您不想使用框架,只需使用-no-framework 配置 Qt 即可。
./configure -no-framework基于 Bundle 的库
若要在 macOS 应用程序包(即应用程序目录)中使用某些动态库,请在应用程序包目录下创建一个名为Frameworks 的子目录,并将动态库放置于此。如果动态库的安装名称为@executable_path/../Frameworks/libname.dylib,应用程序将能够找到该动态库。
如果您使用qmake 和Makefile,请使用QMAKE_LFLAGS_SONAME 设置:
QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/此外,您还可以通过在命令行上使用install_name_tool(1) 来修改安装名称。
DYLD_LIBRARY_PATH 环境变量将覆盖这些设置,以及任何其他默认路径,例如在/usr/lib及类似默认位置中查找动态库。
库的组合
若要构建一个结合了 Qt 动态库的新动态库,则需要引入 `ld -r ` 标志。这样,重定位信息就会存储在输出文件中,从而该文件可作为后续 `ld ` 运行的对象。具体操作是:在 `.pro ` 文件中设置 `-r ` 标志,并配置 `LFLAGS ` 设置。
初始化顺序
dyld(1) 会按其被链接到应用程序的顺序调用全局静态初始化器。如果某个库与 Qt 链接,并且(通过您自己库中的全局初始化器)引用了 Qt 中的全局变量,请在将应用程序与该库链接之前,先将其与 Qt 链接。 否则,由于 Qt 的全局初始化器尚未被调用,结果将无法确定。
编译时标志
当您需要定义 macOS 特定代码时,以下标志会很有帮助:
Q_OS_DARWIN当 Qt 检测到您处于基于 Darwin 的系统(如 macOS 或 iOS)上时,该标志会被定义。Q_OS_MACOS当您处于 macOS 系统上时,该宏会被定义。
注意: 在 Qt 5 及更高版本中,Q_WS_MAC 不再被定义。
若需为特定版本的 macOS 编写代码,请使用/usr/include/AvailabilityMacros.h 中定义的可用性宏。
QSysInfo 和 QOperatingSystemVersion 的文档中包含有关运行时版本检测的信息。
macOS 本机 API 访问
访问包路径
macOS 应用程序的结构是一个目录(以.app 结尾)。该目录包含子目录和文件。将某些项目(例如插件和在线文档)放置在此应用程序包中可能会很有用。以下代码返回应用程序包的路径:
#ifdef Q_OS_MAC
QString bundlePath=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 时遇到以下错误信息:
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。此问题仅在将 MySQL 驱动程序构建到 Qt 中时才会出现,不会影响插件或静态构建。
D-Bus 和 macOS
在 macOS 上,QtDBus 模块默认会动态加载 libdbus-1 库。这意味着,即使在未安装相关库的 macOS 系统上,链接了QtDBus 模块的应用程序也能正常加载,但它们将无法连接到任何 D-Bus 服务器,也无法通过QDBusServer 成功打开服务器。
若要使用 D-Bus 功能,您需要安装 libdbus-1 库,例如通过 Homebrew、Fink 或 MacPorts 进行安装。如果您计划将应用程序部署到其他系统,建议将这些库包含在应用程序的软件包中。 此外,请注意 macOS 上没有系统总线,且会话总线只有在将 launchd 配置为管理它之后才会启动。
菜单操作
- 当将“QMenu ”转换为 Mac 本机菜单栏时,QMenu 中包含多键组合快捷键(QKeySequence )的操作将无法正确显示,此时仅会显示第一个按键。不过,该快捷键仍会像在其他所有平台上一样生效。
- QMenu 原生菜单栏中使用的对象无法通过常规事件处理程序处理 Qt 事件。请在菜单本身上设置一个委托,以便接收这些变化的通知。或者,可以考虑使用QMenu::aboutToShow() 和QMenu::aboutToHide() 信号来跟踪菜单的可见性;这些信号提供了一种应适用于 Qt 支持的所有平台的解决方案。
- 默认情况下,Qt 会创建一个原生“退出”菜单项,该菜单项会对
CMD+Q快捷键做出响应。为QAction::QuitRole 角色创建一个QAction 将替换该菜单项。因此,替换操作应连接到QCoreApplication::quit 槽,或连接到一个用于终止应用程序的自定义槽。
原生控件
Qt 支持“sheets”(弹出式窗口),由窗口标志Qt::Sheet 表示。
通常,在提及原生 macOS 应用程序时,“原生”是指直接与底层窗口系统交互的应用程序,而非使用某种中间层的应用程序。Qt 应用程序作为“第一类公民”运行,就像 Cocoa 应用程序一样。我们内部使用 Cocoa 与操作系统进行通信。
符号可见性警告
在链接 C++ 库的上下文中,函数和对象被称为符号。符号的可见性可以是“default ”或“hidden ”。
出于性能考虑,Qt 以及许多其他库默认使用hidden 可见性来编译其源代码,并且仅在符号 intended to be used in user projects 时才将其标记为default 可见性。
遗憾的是,当某个库以hidden 可见性编译,而用户项目的应用程序或库以default 可见性编译时,Apple 链接器可能会发出警告。
如果项目开发人员希望消除该警告,则也需要将项目代码构建为hidden 可见性。
在 CMake 中,可以通过在您的CMakeLists.txt 中添加以下代码来实现:
set(CMAKE_CXX_VISIBILITY_PRESET hidden)在 qmake 中,只需在您的 `.pro ` 文件中添加以下代码即可:
CONFIG+=hide_symbols如果项目构建的是库,则库中任何 intended to be used in another library or application 的符号都必须显式标记为default 可见性。例如,可以通过在这些函数或类上添加Q_DECL_EXPORT 注释来实现。
由 CMake Xcode 项目生成的 xcarchive 中缺少 dSYM 包
由于 Xcode 中的一个错误以及CMake 的某些限制,在 Xcode 的归档任务期间,由 CMake 生成的 Xcode 项目将无法将应用程序的dSYM 包包含到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_executable 或qt_add_library 之前,在项目中设置该变量:
set(QT_USE_RISKY_DSYM_ARCHIVING_WORKAROUND ON)
...
qt_add_executable(app)© 2026 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.