Qt for macOS - 特定问题

本页概述了有关 Qt 在 macOS 中的主要问题。有关 macOS 术语和特定流程,请参阅https://developer.apple.com/

Aqua

Aqua 风格是 macOS 平台的一个重要组成部分。与 Cocoa 相同,Qt 提供的窗口小部件看起来与macOS 人机界面指南中描述的相同。请注意,尽管 Qt 的窗口小部件在底层使用 AppKit 进行外观和感觉,但它并不将每个单独的 Qt 窗口小部件表示为包装的本地控件。

Qt 窗口小部件画廊页面包含使用 macOS 平台主题的应用程序的示例图像。

macOS 的 Qt 属性

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

macOS 总是双缓冲屏幕,因此,Qt::WA_PaintOnScreen 属性没有效果。而且,无法在绘制事件之外进行绘制,因此 Qt::WA_PaintOutsidePaintEvent 也无效果。

鼠标右键点击

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

国际化

macOS 上的应用程序将其支持的语言作为应用程序的Info.plist的一部分进行声明。然后系统将匹配应用程序的支持语言与用户的语言首选项,以确定启动应用程序的区域设置。这反过来确定由 QLocale::uiLanguages() 反映的顺序语言,以及系统框架(如 AppKit)如何拾取其本地化资源,例如菜单标题和字符串。

由于 Qt 应用程序默认情况下不进行翻译,CMake 和 qmake 项目的默认生成的 Info.plistCFBundleAllowMixedLocalizations 设置为 YES,以允许系统框架选择与用户语言偏好最匹配的本地化版本,即使该本地化版本本应用自身不可用。一旦您通过 qt_add_translations 为您的应用程序添加了翻译,CFBundleAllowMixedLocalizations 键将自动删除,并替换为 CFBundleLocalizations,列出您支持的所有语言。对于 qmake,此过程必须手动完成。

Qt 会检测菜单栏并将它们转换为原生 Mac 菜单栏。将其适配到现有的 Qt 应用程序通常是自动的。但是,如果您有特殊需求,Qt 实现目前通过以下测试从活动窗口开始(例如,QGuiApplication::focusWindow())选择菜单栏:

  1. 如果窗口有一个 QMenuBar,则它将被使用。
  2. 如果窗口是模态的,那么它将使用菜单栏。如果没有指定菜单栏,则使用默认菜单栏(如下文所述)。
  3. 如果没有父窗口的窗口将使用默认菜单栏(如下文所述)。

这些测试会一直到父窗口链,直到满足上述规则中的任何一个。如果所有测试都失败,则创建一个默认菜单栏。Qt 中的默认菜单栏是一个空菜单栏。但是,您可以通过创建一个无父的 QMenuBar 来创建不同的默认菜单栏。第一个创建的将被指定为默认菜单栏,并在需要时使用。

使用原生菜单栏会对 Qt 类引入某些限制。下文中的 限制列表 部分有更多信息。

Qt 通过 QMenuBar 提供对全局菜单栏的支持。macOS 用户期望屏幕顶部有菜单栏,Qt 也会遵守这一规定。

此外,用户期望遵守某些约定,例如应用程序菜单应包含 关于偏好设置退出 等。Qt 处理这些约定,尽管它不提供与应用程序菜单直接交互的方式。

每个 QAction 都有一个 menuRole 属性,该属性控制应用程序菜单项的特殊位置;然而,默认情况下,menuRoleTextHeuristicRole,意味着菜单项将通过它们的 text 进行自动检测。

其他标准菜单项(如 剪切复制粘贴全选)既适用于您的应用程序,也适用于某些原生对话框(如 QFileDialog)。您需要使用标准快捷键来创建这些菜单项,以确保相应的编辑功能将在对话框中启用。目前,这些没有 MenuRole 标识符,但当 QAction 具有默认的 TextHeuristicRole 时,它们将被像应用程序菜单项一样自动检测。

特殊键

为了在macOS上提供Qt应用的预期行为,枚举值Qt::Key_MetaQt::MetaModifierQt::META与标准苹果键盘上的控制键相对应,而枚举值Qt::Key_ControlQt::ControlModifierQt::CTRL则与Command键相对应。

仪表板

与仪表板的交互是可能的。您可以通过从应用程序的主窗口中调用QWindow::setWindowIcon()来设置图标。setWindowIcon()调用可以按需多次进行,从而提供一个易于更新的图标。

无障碍

许多用户使用辅助设备来与macOS交互。Qt的目标是在您的应用程序中使这一点自动化,以便符合其平台上接受的实践。Qt使用苹果的无障碍框架为残疾人提供访问。

库和部署支持

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

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

Qt库作为框架

默认情况下,Qt构建为一系列框架。框架是macOS分发库的首选方式。《苹果框架编程指南》网站上有更多有关框架的信息。

请注意,框架始终与库的发布版本链接。如果希望使用Qt框架的调试版本,请使用环境变量DYLD_IMAGE_SUFFIX确保加载调试版本。

export DYLD_IMAGE_SUFFIX=_debug

或者,您可以根据苹果的“调试魔法”技术笔记临时交换调试和发布版本。

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

./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动态库组合在一起,则需要引入ld -r标志。然后,重新定位信息被存储在输出文件中,这样这个文件就可以成为另一个ld运行的题目。这是通过在.pro文件中设置-r标志和在LFLAGS设置中实现的。

初始化顺序

dyld(1)将按照它们链接到应用程序的顺序调用全局静态初始化器。如果一个库链接了Qt并引用了Qt的全局变量(从您自己的库中的全局初始化器),则在与库链接之前,请先与应用程序链接Qt。否则结果将是未定义的,因为Qt的全局初始化器还没有被调用。

编译时标志

以下标志当您想定义针对macOS的特定代码时很有用。

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

注意:Q_WS_MAC在Qt 5及以后版本中不再定义。

如果您想针对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提供的更灵活,并且允许用户在不考虑屏幕上是否有模态对话框显示的情况下(这与Cocoa相比是不同的)旋转事件分发器(并调用QEventLoop::exec)。因此,我们需要在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

QtDBus模块默认在macOS中动态加载libdbus-1库。这意味着链接QtDBus模块的应用程序甚至可以在没有库的macOS系统上加载,但它们将无法连接到任何DBus服务器,它们将无法使用QDBusServer打开服务器。

要使用D-Bus功能,您需要安装libdbus-1库,例如通过Homebrew、Fink或MacPorts来实现。如果您要将应用程序部署到其他系统,您可能希望将这些库包含在您的应用程序捆绑包中。另外,请注意,macOS上没有系统总线,并且只有当launchd配置为管理它会启动会话总线。

  • 当将QMenu(具有多个按键的快捷键QKeySequence)转换为Mac原生菜单栏时,不会正确显示操作。第一个键将会显示。然而,快捷键会在所有其他平台上照常激活。
  • 用于原生菜单栏的QMenu对象无法通过正常的事件处理器处理Qt事件。您需要在菜单本身上安装一个代理来通知这些更改。或者,您可以考虑使用QMenu::aboutToShow()和QMenu::aboutToHide()信号来跟踪菜单的可见性;这些提供了解决方案,应适用于Qt支持的所有平台。
  • 默认情况下,Qt创建了一个原生的退出菜单项,它将对CMD+Q快捷键做出反应。为QAction::QuitRole角色创建一个QAction将替换该菜单项。因此,替换操作应该连接到QCoreApplication::quit槽,或停止应用程序的自定义槽。

原生窗口小部件

Qt支持通过窗口标志表示的工作表,Qt::Sheet

通常,当我们提到原生macOS应用程序时,原生意味着一个直接与底层窗口系统接口的应用程序,而不是使用某些中介层。Qt应用程序作为一类公民运行,就像Cocoa应用程序一样。我们使用Cocoa内部与操作系统通信。

符号可见性警告

在链接C++库的上下文中,函数和对象被称为符号。符号可以是默认隐藏可见性

出于性能原因,Qt和其他许多库默认使用隐藏可见性编译源代码,并且只有当它们打算用于用户项目时才标记符号具有默认可见性。

遗憾的是,Apple链接器在当一个图书馆使用隐藏可见性编译,而一个用户项目的应用程序或图书馆使用默认可见性时会发出警告。

如果项目开发人员想要静音警告,他们需要使用隐藏可见性构建他们的项目代码。

在CMake中,可以通过将以下代码添加到你的CMakeLists.txt文件中来实现。

set(CMAKE_CXX_VISIBILITY_PRESET hidden)

在qmake中,可以通过将以下代码添加到你的.pro文件中来实现。

CONFIG+=hide_symbols

如果项目构建库,任何打算在另一个库或应用程序中使用的库符号必须明确标记为默认可见性。例如,可以通过使用Q_DECL_EXPORT注释此类函数或类来实现。

© 2024 Qt公司有限公司。本文档中包含的文档贡献是各自所有者的版权。本提供的文档遵循自由软件基金会发布的GNU自由文档许可协议版本1.3条款。Qt及其相关标志是芬兰和/或其他国家的The Qt Company Ltd.的商标。所有其他商标均为各自所有者的财产。