Qt для macOS — Специфические проблемы
На этой странице описаны основные проблемы, связанные с поддержкой macOS в Qt. Терминология macOS и специфические процессы можно найти на сайте https://developer.apple.com/.
Aqua
Стиль Aqua является неотъемлемой частью платформы macOS. Как и Cocoa, Qt предоставляет виджеты, которые выглядят так же, как описано в Руководстве по интерфейсу пользователя macOS. Обратите внимание, что, хотя виджеты Qt используют AppKit для внешнего вида, каждый отдельный виджет Qt не представлен как обернутый родной элемент управления.
На странице Галерея виджетов Qt представлены примеры изображений приложений, использующих тему платформы 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::Key_Meta, Qt::MetaModifier и Qt::META соответствуют клавишам Control на стандартной клавиатуре Apple, а значения перечислений Qt::Key_Control, Qt::ControlModifier и Qt::CTRL соответствуют клавишам Command.
Панель задач
Взаимодействие с панелью задач возможно. Иконку можно установить, вызвав 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();
} Ограничения
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 создаёт нативный элемент меню Quit, который будет реагировать на ярлык
CMD+Q. Создание QAction для роли QAction::QuitRole заменит этот элемент меню. Поэтому действие замены должно быть подключено к слоту QCoreApplication::quit или к настраиваемому слоту, который завершает приложение.
Нативные виджеты
Qt поддерживает листы, представленные флагом окна Qt::Sheet.
Обычно, когда речь идёт о нативном macOS-приложении, нативный означает приложение, которое взаимодействует напрямую с базовой системой окон, а не использует какой-либо промежуточный слой. Приложения Qt работают как полноправные пользователи, как и Cocoa-приложения. Мы используем Cocoa внутри для взаимодействия с операционной системой.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.0/macos-issues.html