Qt для macOS — Особенности
Эта страница описывает основные проблемы, связанные с поддержкой macOS в Qt. Терминология macOS и специфические процессы можно найти на сайте https://developer.apple.com/.
Aqua
Aqua — важная часть платформы macOS. Как и Cocoa и Carbon, Qt предоставляет виджеты, внешне похожие на те, что описаны в Human Interface Descriptions. Виджеты Qt используют HIThemes для реализации внешнего вида. Другими словами, мы используем собственные API Apple для отрисовки. Дополнительная документация по Aqua находится на странице Руководства по пользовательскому интерфейсу macOS.
На странице Галерея виджетов в стиле Macintosh представлены примеры изображений виджетов, использующих тему macOS.
Атрибуты Qt для macOS
Ниже приведен список полезных атрибутов, которые можно использовать для настройки приложений на macOS:
- Qt::AA_MacPluginApplication
- Qt::AA_DontUseNativeMenuBar
- Qt::AA_MacDontSwapCtrlAndMeta
- Qt::WA_MacNoClickThrough
- Qt::WA_MacOpaqueSizeGrip
- Qt::WA_MacShowFocusRect
- Qt::WA_MacNormalSize
- Qt::WA_MacSmallSize
- Qt::WA_MacMiniSize
- Qt::WA_MacVariableSize
- Qt::WA_MacBrushedMetal
- Qt::WA_MacAlwaysShowToolWindow
- Qt::WA_MacFrameworkScaled
- Qt::WA_MacNoShadow
- Qt::Sheet
- Qt::Drawer
- Qt::MacWindowToolBarButtonHint,
- QMainWindow::unifiedTitleAndToolBarOnMac
macOS всегда выполняет двойную буферизацию экрана, поэтому атрибут Qt::WA_PaintOnScreen не имеет эффекта. Также невозможно рисовать за пределами события отрисовки, поэтому Qt::WA_PaintOutsidePaintEvent также не имеет эффекта.
Правый щелчок мыши
Класс QContextMenuEvent обеспечивает поддержку правого щелчка мыши для приложений macOS. Это будет сопоставлено с событием контекстного меню, например, меню, которое отобразит всплывающее меню выбора. Это самое распространённое использование правого щелчка мыши и соответствует событию нажатия клавиши Control с поддержкой мыши с одной кнопкой macOS.
Панель меню
Qt обнаруживает панели меню и преобразует их в родные панели меню Mac. Интеграция в существующие приложения Qt обычно происходит автоматически. Однако, если у вас есть особые потребности, текущая реализация Qt выбирает панель меню, начиная с активного окна (например, QGuiApplication::focusWindow() и применяя следующие проверки:
- Если у окна есть QMenuBar, то он используется.
- Если окно является модальным, то используется его панель меню. Если панель меню не указана, используется стандартная панель меню (как описано ниже).
- Если у окна нет родителя, используется стандартная панель меню (как описано ниже).
Эти проверки выполняются по цепочке родительских окон до тех пор, пока не будет выполнено одно из вышеперечисленных правил. Если ничего не подходит, будет создана стандартная панель меню. Стандартная панель меню Qt — пустая панель меню. Однако вы можете создать другую стандартную панель меню, создав QMenuBar без родительского окна. Первая созданная панель меню будет обозначена как стандартная и будет использоваться всякий раз, когда нужна стандартная панель меню.
Использование родных панелей меню накладывает определенные ограничения на классы Qt. Более подробная информация содержится в разделе с списком ограничений ниже.
Qt поддерживает глобальную панель меню с помощью QMenuBar. Пользователи macOS ожидают наличия панели меню в верхней части экрана, и Qt учитывает это ожидание.
Кроме того, пользователи ожидают соблюдения определённых соглашений, например, меню приложения должно содержать пункты О программе, Настройки, Выход и так далее. Qt обрабатывает эти соглашения, хотя не предоставляет средств для непосредственного взаимодействия с меню приложения.
Каждый QAction имеет свойство menuRole, которое управляет специальным размещением пунктов меню приложения; однако по умолчанию menuRole — TextHeuristicRole, что означает, что пункты меню будут автоматически распознаваться по их тексту.
Другие стандартные пункты меню, такие как Вырезать, Копировать, Вставить и Выделить всё, применимы как в вашем приложении, так и в некоторых встроенных диалогах, таких как QFileDialog. Важно создавать эти пункты меню со стандартными сочетаниями клавиш, чтобы соответствующие функции редактирования были активированы в диалогах. На данный момент нет MenuRole идентификаторов для них, но они будут автоматически распознаваться, так же как и пункты меню приложения, когда у QAction есть значение по умолчанию TextHeuristicRole.
Специальные клавиши
Для обеспечения ожидаемого поведения приложений Qt на macOS значения перечислений Qt::Meta, Qt::MetaModifier и Qt::META соответствуют клавишам Control на стандартной клавиатуре Apple, а значения перечислений Qt::Control, Qt::ControlModifier и Qt::CTRL соответствуют клавишам Command.
Панель Dock
Взаимодействие с панелью Dock возможно. Иконку можно установить, вызвав QWindow::setWindowIcon() из главного окна вашего приложения. Вызов setWindowIcon() можно делать столько раз, сколько нужно, предоставляя иконку, которая может быть легко обновлена.
Доступность
Многие пользователи взаимодействуют с macOS с помощью вспомогательных устройств. Qt стремится сделать это автоматическим в вашем приложении, чтобы оно соответствовало принятым практикам на данной платформе. Qt использует фреймворк Apple для обеспечения доступа для пользователей с ограниченными возможностями.
Поддержка библиотек и развертывания
Qt поддерживает структуры macOS, такие как Frameworks и пакеты. Важно понимать эти структуры, поскольку они напрямую влияют на развертывание приложений.
Qt предоставляет инструмент развертывания macdeployqt для упрощения процесса развертывания. Статья Qt для macOS — Развертывание более подробно рассматривает процесс развертывания.
Библиотеки Qt как Frameworks
По умолчанию Qt компилируется как набор Frameworks. Frameworks — предпочтительный способ распространения библиотек в macOS. На сайте Руководство по программированию Frameworks Apple содержится более подробная информация о Frameworks.
Важно помнить, что Frameworks всегда связываются с релизными версиями библиотек. Если требуется отладочная версия Qt framework, используйте переменные среды DYLD_IMAGE_SUFFIX, чтобы убедиться, что загружается отладочная версия:
export DYLD_IMAGE_SUFFIX=_debug
В качестве альтернативы, вы можете временно поменять местами отладочные и релизные версии, что описано в техническом примечании Apple "Debugging Magic".
Если вы не хотите использовать Frameworks, просто настройте Qt с -no-framework.
./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 и аналогичных стандартных расположениях.
Если вы используете более старые версии GDB, вам необходимо указывать полный путь к исполняемому файлу. Более поздние версии позволяют передавать имя пакета в командной строке.
Комбинирование библиотек
Если вы хотите создать новую динамическую библиотеку, объединив динамические библиотеки Qt 4, вам нужно добавить флаг ld -r. Тогда информация о перемещении сохраняется в выходном файле, так что этот файл может быть объектом другой ld операции. Это делается путём установки флага -r в файле .pro и настройками 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 содержит информацию о проверке версии во время выполнения.
Доступ к родным API macOS
Получение пути к пакету
Приложения macOS структурированы как каталог (заканчивающийся на .app). Этот каталог содержит подкаталоги и файлы. Может быть полезно помещать элементы, такие как плагины и онлайн-документацию, внутри этого пакета. Следующий код возвращает путь к пакету приложения:
#ifdef Q_OS_MAC
CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef,
kCFURLPOSIXPathStyle);
const char *pathPtr = CFStringGetCStringPtr(macPath,
CFStringGetSystemEncoding());
qDebug("Path = %s", pathPtr);
CFRelease(appUrlRef);
CFRelease(macPath);
#endif Примечание: Если macOS настроена на использование японского языка, ошибка приводит к тому, что эта последовательность завершается неудачно и возвращает пустую строку. Поэтому всегда проверяйте возвращённую строку.
Для получения дополнительной информации об использовании API CFBundle посетите сайт разработчиков Apple.
QCoreApplication::applicationDirPath() можно использовать для определения пути к исполняемому файлу внутри пакета.
Перевод меню приложения и встроенных диалогов
Элементы в меню приложения будут корректно объединены для локализованных приложений, но они не будут отображаться переведёнными, пока пакет приложения не будет содержать папку с локализованными ресурсами. в пакет приложения.
По существу, необходим файл с именем locversion.plist. Вот пример приложения с норвежской локализацией:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LprojCompatibleVersion</key>
<string>123</string>
<key>LprojLocale</key>
<string>no</string>
<key>LprojRevisionLevel</key>
<string>1</string>
<key>LprojVersion</key>
<string>123</string>
</dict>
</plist> После этого, при запуске приложения с предпочтительным языком, установленным на норвежский, пункты меню должны отображаться как Avslutt вместо Quit.
Руководство по программированию пакетов содержит информацию о пакетах и папке с локализованными ресурсами.
Смешивание Qt с кодом нативных платформ
Для добавления нативных элементов управления Cocoa в приложение Qt или для встраивания Qt в нативное приложение Cocoa доступны два класса: QMacCocoaViewContainer и QMacNativeWidget.
Использование нативных панелей 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();
} Ограничения
Fink
Если вы установили пакет Qt для X11 из Fink, он установит переменную окружения QMAKESPEC в darwin-g++. Это вызовет проблемы при сборке пакета Qt для macOS. Чтобы исправить это, просто сбросьте свою переменную QMAKESPEC или установите ее в macx-g++ перед запуском configure. Чтобы получить свежий дистрибутив Qt, выполните make confclean в командной строке.
MySQL и macOS
По-видимому, возникает проблема, когда оба -prebind и -multi_module определены при линковке статических C библиотек в динамические библиотеки. Если при линковке 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
перелинкуйте Qt с помощью -single_module. Эта проблема возникает только при построении драйвера MySQL в Qt. Она не влияет на плагины или статические сборки.
D-Bus и macOS
Модуль QtDBus по умолчанию динамически загружает библиотеку libdbus-1 на macOS. Это означает, что приложения, ссылающиеся на модуль QtDBus, будут загружаться даже на системах macOS, на которых нет этих библиотек, но они не смогут подключиться к любому серверу D-Bus и не смогут открыть сервер с помощью QDBusServer.
Для использования функциональности D-Bus необходимо установить библиотеку libdbus-1, например, через Homebrew, Fink или MacPorts. Вы можете включить эти библиотеки в пакет вашего приложения, если вы разрабатываете его для других систем. Кроме того, обратите внимание, что на macOS нет системного шины, а шина сеанса будет запущена только после того, как launchd будет настроен для ее управления.
Действия в меню
- Действия в QMenu с акцелерорами, имеющими более одной клавиши (QKeySequence), не будут отображаться корректно, когда QMenu переводится в нативную строку меню Mac. Будет отображена только первая клавиша. Однако сочетание клавиш по-прежнему будет активироваться, как и на всех остальных платформах.
- QMenu объекты, используемые в нативной строке меню, не могут обрабатывать события Qt через обычные обработчики событий. Установите делегата на меню, чтобы получать уведомления об этих изменениях. В качестве альтернативы, рассмотрите использование сигналов QMenu::aboutToShow() и QMenu::aboutToHide() для отслеживания видимости меню; это предоставляет решение, которое должно работать на всех платформах, поддерживаемых Qt.
Нативные виджеты
Qt поддерживает диалоговые окна, представленные флагом окна Qt::Sheet.
Обычно, когда речь идет о нативном приложении macOS, нативное означает приложение, которое взаимодействует непосредственно с базовой системой окон, а не то, которое использует какой-либо промежуточный слой. Приложения Qt работают как полноправные граждане, как и приложения Cocoa и Carbon. Внутри мы используем Cocoa для взаимодействия с операционной системой.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.6/osx-issues.html