Qt for macOS - 特定の問題

このページでは、Qt の macOS サポートに関する主な問題を概説します。macOS の用語や具体的な処理については、https://developer.apple.com/ を参照してください。

Aqua

AquaスタイルはmacOSプラットフォームに欠かせないものです。Cocoaと同様に、QtはmacOSヒューマンインターフェースガイドラインに記述されているようなウィジェットを提供します。Qt のウィジェットはルック&フィールのために AppKit を使用していますが、個々の Qt ウィジェットをラップされたネイティブコントロールとして表現しているわけではないことに注意してください。

Qt Widget Galleryページには、macOS プラットフォームのテーマを使用したアプリケーションのサンプル画像があります。

macOS 用の Qt 属性

以下は、macOS 上のアプリケーションを微調整するために使用できる便利な属性の一覧です:

macOS は常にスクリーンをダブルバッファリングするので、Qt::WA_PaintOnScreen 属性は効果がありません。また、Qt::WA_PaintOutsidePaintEvent も効果がありません。

マウスの右クリック

QContextMenuEvent クラスは、macOS アプリケーションのためにマウスの右クリックをサポートします。これはコンテキストメニューイベントにマッピングされます。これはマウスの右クリックの最も一般的な使用方法であり、macOSのワンボタンマウスサポートではコントロールクリックにマッピングされます。

国際化

macOSのアプリケーションは、アプリケーションのInfo.plist の一部として、サポートする言語を宣言します。システムは、アプリケーションのサポート言語とユーザーの言語設定を照合し、アプリケーションが起動するロケールを決定します。これにより、QLocale::uiLanguages() を通して反映される順序の言語が決定され、AppKit などのシステムフレームワークが、メニュータイトルや文字列などのローカライズされたリソースをどのようにピックアップするかが決定されます。

Qt アプリは最初から翻訳されているわけではないので、デフォルトで生成されるCMakeqmake プロジェクトのInfo.plist は次のように設定されています。 CFBundleAllowMixedLocalizationsYES に設定します。これは、システムフレームワークがユーザーの言語設定に最も合うローカライズを選択できるようにするためです。qt_add_translations を使ってアプリケーションに翻訳を追加すると、qt_add_translationsCFBundleAllowMixedLocalizationsキーは自動的に削除され CFBundleLocalizationsに置き換わります。qmake の場合、このプロセスは手動で行う必要があります。

Qt はメニューバーを検出し、Mac ネイティブのメニューバーにします。これを既存の Qt アプリケーションに組み込むのは、通常は自動的です。しかし、特別なニーズがある場合、Qtの実装は現在、アクティブなウィンドウ(例えば、QGuiApplication::focusWindow())から開始し、以下のテストを適用することでメニューバーを選択します:

  1. ウィンドウにQMenuBar があれば、それが使われます。
  2. ウィンドウがモーダルの場合、そのメニュー・バーが使用されます。メニュー・バーが指定されていない場合、デフォルトのメニュー・バーが使用されます(以下に記述します)。
  3. ウィンドウに親がない場合、デフォルトのメニュー・バーが使用される(以下で説明する)。

これらのテストは、上記のルールのいずれかが満たされるまで、親ウィンドウの連鎖の上まで続きます。すべてが失敗した場合、デフォルトのメニューバーが作成されます。Qt のデフォルトのメニューバーは空のメニューバーです。しかし、親のないQMenuBar を作成することで、別のデフォルト・メニュー・バーを作成することができます。最初に作成されたものがデフォルトのメニュー・バーとなり、デフォルトのメニュー・バーが必要になったときに使用されます。

ネイティブ・メニュー・バーの使用は、Qt クラスに特定の制限をもたらします。以下の制限事項のセクションに詳細があります。

Qt は、QMenuBar でグローバルメニューバーのサポートを提供します。macOS のユーザーは、画面の上部にメニューバーがあることを期待しており、Qt はこれを尊重します。

例えば、アプリケーションメニューには「バージョン情報」、「環境設定」、「終了」などが含まれます。Qtは、アプリケーションメニューと直接対話する手段は提供しませんが、これらの規約を処理します。

QAction にはmenuRole プロパティがあり、アプリケーションメニュー項目の特別な配置を制御します。しかし、デフォルトではmenuRoleTextHeuristicRole になっており、メニュー項目はtext によって自動検出されます。

CutCopyPasteSelect Allのような他の標準的なメニュー項目は、あなたのアプリケーションでも、QFileDialog のようないくつかのネイティブダイアログでも適用できます。これらのメニュー項目を標準ショートカットで作成し、対応する編集機能がダイアログで有効になるようにすることが重要です。現時点ではMenuRole の識別子はありませんが、QAction にデフォルトのTextHeuristicRole が設定されている場合には、アプリケーションのメニュー項目と同様に自動検出されます。

特殊キー

macOS 上の Qt アプリケーションに期待される動作を提供するために、Qt::Key_MetaQt::MetaModifierQt::META の enum 値は標準の Apple キーボードの Control キーに対応し、Qt::Key_ControlQt::ControlModifierQt::CTRL の enum 値は Command キーに対応します。

ドック

ドックとのインタラクションが可能です。アプリケーションのメイン・ウィンドウからQWindow::setWindowIcon()を呼び出すことで、アイコンを設定できます。setWindowIcon()は何度でも呼び出すことができ、簡単に更新できるアイコンを提供します。

アクセシビリティ

多くのユーザは、補助デバイスを使って macOS を操作します。Qt の目的は、アプリケーションのアクセシビリティを自動化することです。Qt は Apple のアクセシビリティフレームワークを使用し、障害のあるユーザーへのアクセスを提供します。

ライブラリとデプロイのサポート

Qt はフレームワークやバンドルといった macOS の構造をサポートしています。これらの構造はアプリケーションのデプロイに直接影響するため、認識しておくことが重要です。

Qt はデプロイツールmacdeployqt を提供し、デプロイプロセスを簡素化します。Qt for macOS - Deploymentの記事で、デプロイプロセスの詳細を説明しています。

フレームワークとしての Qt ライブラリ

デフォルトでは、Qt はフレームワークとしてビルドされています。フレームワークは macOS が推奨するライブラリの配布方法です。Appleのフレームワークプログラミングガイドのサイトに、フレームワークについての詳細があります。

フレームワークは常にリリースバージョンのライブラリとリンクすることを覚えておくことが重要です。Qt フレームワークのデバッグバージョンが必要な場合は、DYLD_IMAGE_SUFFIX 環境変数を使って、デバッグバージョンがロードされるようにしてください:

export DYLD_IMAGE_SUFFIX=_debug

あるいは、デバッグ版とリリース版を一時的に入れ替えることもできます。これはApple の "Debugging Magic" テクニカルノートに記載されています。

フレームワークを使いたくない場合は、-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 が macOS や iOS のような Darwin ベースのシステムであることを検出したときに定義されます。
  • Q_OS_MACOS は、macOS システムを使用しているときに定義されます。

注意: Q_WS_MAC は Qt 5 以降では定義されなくなりました。

特定のバージョンの macOS 用のコードを定義したい場合は、/usr/include/AvailabilityMacros.h で定義されている availability マクロを使用してください。

QSysInfo と QOperatingSystemVerison のドキュメントに、実行時のバージョンチェックに関する情報があります。

macOSネイティブAPIアクセス

バンドルパスへのアクセス

macOSアプリケーションは、(.appで終わる)ディレクトリとして構造化されています。このディレクトリにはサブディレクトリとファイルが含まれています。プラグインやオンラインドキュメントなどのアイテムをこのバンドルの中に置くと便利です。次のコードは、アプリケーションバンドルのパスを返します:

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

NSBundle API の使い方の詳細については、Apple Developer Web サイトを参照してください。

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 のリンク時に以下のエラーメッセージが表示される場合、-s で再リンクしてください:

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 システムでもロードされますが、D-Bus サーバーへの接続に失敗し、QDBusServer を使用してサーバーを開くことに失敗します。

D-Busの機能を使うには、Homebrew、Fink、MacPortsなどでlibdbus-1ライブラリをインストールする必要がある。他のシステムにデプロイする場合は、これらのライブラリをアプリケーションのバンドルに含めるとよいでしょう。さらに、macOSにはシステムバスがなく、セッションバスはlaunchdがそれを管理するように設定された後にのみ起動することに注意してください。

  • アクセラレータを使用したQMenu で、複数のキーストロークを持つアクション (QKeySequence) は、QMenu が Mac ネイティブのメニューバーに変換されると、正しく表示されません。最初のキーが表示されます。しかし、ショートカットは他のすべてのプラットフォームと同様に有効になります。
  • QMenu ネイティブメニューバーで使用されるオブジェクトは、通常のイベントハンドラで Qt イベントを処理できません。このような変更を通知するために、メニュー自体にデリゲートをインストールしてください。あるいは、 () と () シグナルを使用して、メニューの可視性を追跡することを検討してください。これらは、Qt がサポートするすべてのプラットフォームで動作するソリューションを提供します。QMenu::aboutToShow QMenu::aboutToHide
  • デフォルトでは、Qt はCMD+Q ショートカットに反応するネイティブのQuitメニュー項目を作成します。QAction::QuitRole の役割のためにQAction を作成すると、そのメニュー項目は置き換えられます。したがって、置き換えアクションは、QCoreApplication::quit スロット、またはアプリケーションを停止するカスタムスロットのいずれかに接続する必要があります。

ネイティブウィジェット

Qt は、ウィンドウフラグQt::Sheet で表されるシートをサポートしています。

通常、macOSのネイティブアプリケーションを指す場合、ネイティブとは、中間レイヤーを使用するアプリケーションではなく、基礎となるウィンドウシステムに直接インターフェイスするアプリケーションを意味します。Qtアプリケーションは、Cocoaアプリケーションと同様に、第一級市民として実行されます。オペレーティング・システムと通信するために、内部的にCocoaを使用しています。

シンボルの可視性に関する警告

C++ ライブラリをリンクする場合、関数やオブジェクトはシンボルと呼ばれます。シンボルには、default またはhidden のどちらかの可視性があります。

パフォーマンス上の理由から、Qt や他の多くのライブラリはデフォルトでhidden の可視性でソースをコンパイルし、ユーザープロジェクトで使用する場合にのみdefault の可視性でシンボルをマークします。

残念ながら、あるライブラリがhidden でコンパイルされ、ユーザープロジェクトのアプリケーションやライブラリがdefault でコンパイルされると、Apple のリンカーは警告を出すことがあります。

プロジェクト開発者が警告を消したい場合は、プロジェクトコードもhidden visibility でビルドする必要があります。

CMakeでは、CMakeLists.txt に以下のコードを追加することで可能です:

set(CMAKE_CXX_VISIBILITY_PRESET hidden)

qmake では、.pro ファイルに以下のコードを追加してください:

CONFIG+=hide_symbols

プロジェクトがライブラリをビルドする場合、ライブラリ内のシンボルが他のライブラリやアプリケーションで使用されることを意図している場合は、default visibilityを明示的にマークする必要があります。例えば、そのような関数やクラスには、Q_DECL_EXPORT のアノテーションを付けます。

CMake Xcode プロジェクトで作成された xcarchive で dSYM バンドルが見つからない

Xcode のバグとCMake の制限により、CMake で生成された Xcode プロジェクトは、Xcode のアーカイブタスク中に、アプリケーションのdSYM バンドルをxcarchive に含めることができません。

Qt は、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)

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。