setVisible()は非推奨?Qt QFileDialogのモーダル・非モーダル表示の違い

2025-05-27

機能

この関数は、引数visibleの値に応じて、ファイルダイアログ(ファイルを開く、または保存するための標準的なダイアログボックス)を表示するか、非表示にするかを設定します。

  • visiblefalseの場合: ファイルダイアログが非表示になります。
  • visibletrueの場合: ファイルダイアログが表示されます。

使用目的

通常、QFileDialogexec()メソッドを呼び出してモーダルダイアログとして表示されることが多いです。exec()はダイアログが閉じられるまで処理をブロックするため、setVisible()を直接呼び出す必要はあまりありません。

しかし、以下のようなケースでsetVisible()を使用することが考えられます。

  1. 非モーダルダイアログとしての使用
    QFileDialogを非モーダルダイアログとして使用する場合(つまり、ダイアログが表示されている間も他のウィンドウ操作を可能にする場合)に、プログラムの特定のタイミングで表示・非表示を切り替えるためにsetVisible(true)setVisible(false)を使用します。ただし、QFileDialogを非モーダルで使うケースは稀です。

  2. 特定の条件下での表示制御
    アプリケーションのロジックに基づいて、ファイルダイアログを一時的に隠したり、再表示したりする際に使用できます。例えば、ユーザーが特定の操作を行った場合にのみダイアログを表示したいが、ダイアログオブジェクト自体はプログラムの開始時に作成済みであるような場合です。

注意点

  • 非モーダル時の挙動
    setVisible(true)で非モーダルダイアログとして表示した場合、ユーザーがダイアログを閉じても、ダイアログは破棄されず、単に非表示になるだけです。再度表示したい場合はsetVisible(true)を呼び直します。
  • ネイティブダイアログとの関係
    Qtは、可能であればOSのネイティブファイルダイアログを使用しようとします。ネイティブダイアログが使用される場合、setVisible()の呼び出しが、Qtのウィジェットベースのダイアログを使用する場合と同じように動作しない可能性があります。Qtのウィジェットベースのダイアログを強制的に使用するには、QFileDialog::DontUseNativeDialogオプションを設定する必要があります。

例 (C++)

#include <QApplication>
#include <QFileDialog>
#include <QPushButton>
#include <QVBoxLayout>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);

    QPushButton *openButton = new QPushButton("ファイルダイアログを開く");
    QPushButton *hideButton = new QPushButton("ファイルダイアログを隠す");
    QPushButton *showButton = new QPushButton("ファイルダイアログを再表示");

    QFileDialog *fileDialog = new QFileDialog(&window);
    // ネイティブダイアログを使わないように設定(setVisibleの挙動を確認しやすくするため)
    fileDialog->setOption(QFileDialog::DontUseNativeDialog, true);

    QObject::connect(openButton, &QPushButton::clicked, [=]() {
        // 通常はexec()でモーダル表示
        // fileDialog->exec();
        // setVisible()で非モーダル表示
        fileDialog->setVisible(true);
    });

    QObject::connect(hideButton, &QPushButton::clicked, [=]() {
        fileDialog->setVisible(false); // ダイアログを非表示にする
    });

    QObject::connect(showButton, &QPushButton::clicked, [=]() {
        fileDialog->setVisible(true); // ダイアログを再表示する
    });

    layout->addWidget(openButton);
    layout->addWidget(hideButton);
    layout->addWidget(showButton);

    window.show();

    return app.exec();
}


ファイルダイアログが表示されない、または期待通りに動作しない

問題
setVisible(true) を呼び出してもファイルダイアログが表示されない、または表示されてもすぐに消えてしまう、あるいはユーザーの操作を受け付けない。

原因とトラブルシューティング

  • ダイアログのライフサイクル管理
    QFileDialog のインスタンスが適切に管理されていない場合、表示されるべき時にオブジェクトが破棄されていたり、無効な状態になっていたりすることがあります。

    • 解決策
      ダイアログのオブジェクトが有効なスコープ内にあり、必要な時にインスタンスが存在していることを確認してください。例えば、関数スコープで一時的に作成されたダイアログは、関数終了時に破棄されてしまうため、setVisible() で表示してもすぐに消えてしまいます。クラスのメンバー変数としてダイアログを保持するなどの方法を検討してください。
  • ネイティブダイアログの使用
    Qt は、プラットフォームのネイティブなファイルダイアログ(Windowsであればエクスプローラー風のダイアログ、macOSであればFinder風のダイアログ)を使用しようとします。ネイティブダイアログはQtのウィジェットシステムとは独立して動作するため、setVisible() のようなQtウィジェットのメソッドが直接的に影響しない場合があります。

    • 解決策
      もしQtのウィジェットとしてファイルダイアログを制御したい場合は、QFileDialog::DontUseNativeDialog オプションを設定して、Qt独自のファイルダイアログを使用するように強制します。
      QFileDialog dialog(this);
      dialog.setOption(QFileDialog::DontUseNativeDialog);
      dialog.setVisible(true); // この場合、setVisibleが効果を持つ可能性が高まる
      
      ただし、これによりOSの標準的なルックアンドフィールが損なわれる可能性があることに注意してください。

アプリケーションがフリーズする、またはクラッシュする

問題
QFileDialog::setVisible() を呼び出した後に、アプリケーションが応答しなくなる、またはクラッシュする。

原因とトラブルシューティング

  • 環境固有の問題(特にWindows)
    Windows環境では、サードパーティ製のシェル拡張機能(バックアップソフトウェアやクラウドストレージ同期ツールなど)がファイルダイアログの動作に干渉し、問題を引き起こすことがあります。これらは QFileDialog のネイティブダイアログを使用する際に顕著に現れます。

    • 解決策
      • 前述の QFileDialog::DontUseNativeDialog オプションを試す。
      • 問題が特定の環境でのみ発生する場合は、その環境にインストールされているサードパーティ製ソフトウェアを確認し、一時的に無効にしてテストする。
      • Qtのバグレポートシステムやフォーラムで同様の問題が報告されていないか検索する(例: QTBUG-41416 のようなケース)。
  • 不適切なシグナル/スロット接続
    ダイアログのシグナル(例: accepted(), rejected(), fileSelected()) とスロットの接続に問題がある場合、予期せぬ挙動やクラッシュを引き起こす可能性があります。特に、ダイアログが非表示になった後に、存在しないオブジェクトのスロットを呼び出そうとしたりすると問題が発生します。

    • 解決策
      シグナルとスロットの接続を確認し、特に QFileDialog のオブジェクトが破棄された後でも、そのオブジェクトへの接続が残っていないかを確認してください。QObject::connect の5番目の引数に Qt::QueuedConnection を使用するなど、安全な接続方法を検討することも有効です。
  • イベントループのブロック
    setVisible(true) で非モーダルダイアログを表示した場合でも、そのダイアログ内で何らかのブロック処理が行われると、メインイベントループが停止し、アプリケーション全体がフリーズしたように見えることがあります。

    • 解決策
      QFileDialog の内部で長時間かかる処理(例えば、ネットワークアクセスや巨大なディレクトリの読み込み)が行われていないか確認してください。もしそのような処理がある場合は、バックグラウンドスレッドで実行し、GUIをブロックしないように工夫する必要があります。

ダイアログのサイズや位置が正しくない

問題
setVisible(true) で表示されたファイルダイアログのサイズや位置が適切でない、または毎回同じ位置に表示される。

原因とトラブルシューティング

  • 非モーダルダイアログの管理の複雑さ
    exec() で表示されるモーダルダイアログは、通常、親ウィンドウに対して適切な位置に自動的に配置されます。しかし、setVisible() で表示される非モーダルダイアログの場合、手動で位置やサイズを設定する必要がある場合があります。
    • 解決策
      QFileDialog::setGeometry(), QFileDialog::move(), QFileDialog::resize() などのメソッドを使って、ダイアログの表示位置とサイズを明示的に指定します。
      QFileDialog *fileDialog = new QFileDialog(this);
      fileDialog->setOption(QFileDialog::DontUseNativeDialog);
      fileDialog->setGeometry(100, 100, 800, 600); // 例: x=100, y=100, width=800, height=600
      fileDialog->setVisible(true);
      
    • 注意
      非モーダルダイアログは、ユーザーが自由に移動・サイズ変更できるため、一度設定してもその後のユーザー操作によって位置が変わる可能性があります。

QFileDialog::setVisible() は、QDialog の一般的なメソッドである setVisible() の動作を継承していますが、QFileDialog はその性質上、ほとんどの場合モーダルダイアログ (exec()) として使用することを前提に設計されています。 setVisible() を使って非モーダルとして扱おうとすると、上記のようないくつかの予期せぬ挙動や問題に直面する可能性が高まります。



しかし、その動作を理解するために、敢えて setVisible() を使用したコード例をいくつか示します。繰り返しになりますが、ほとんどのアプリケーションでは QFileDialog::exec() または静的関数 (getOpenFileName() など) を使用する方が適切で推奨されます。

例 1: 基本的な setVisible() の使用 (非モーダル)

この例では、QFileDialog をインスタンス化し、ボタンのクリックで表示・非表示を切り替えます。ネイティブダイアログではなく、Qt のウィジェットベースのダイアログを使用するように設定することで、setVisible() の効果をより明確に確認できます。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug> // デバッグ出力用

class MyWindow : public QMainWindow
{
    Q_OBJECT // シグナル/スロットを使用するために必要

public:
    MyWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setWindowTitle("QFileDialog setVisible() Example");

        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *showButton = new QPushButton("ファイルダイアログを表示", this);
        QPushButton *hideButton = new QPushButton("ファイルダイアログを隠す", this);

        // QFileDialog をメンバー変数として保持し、ライフサイクルを管理
        fileDialog = new QFileDialog(this);
        // ネイティブダイアログではなく、Qtのウィジェットベースのダイアログを使用
        fileDialog->setOption(QFileDialog::DontUseNativeDialog);
        fileDialog->setWindowTitle("非モーダルファイルダイアログ");
        fileDialog->setFileMode(QFileDialog::ExistingFile); // 既存の単一ファイルを選択

        layout->addWidget(showButton);
        layout->addWidget(hideButton);

        // ボタンとQFileDialogのsetVisible()を接続
        connect(showButton, &QPushButton::clicked, fileDialog, &QFileDialog::setVisible);
        connect(showButton, &QPushButton::clicked, [this](){ fileDialog->setVisible(true); });

        connect(hideButton, &QPushButton::clicked, fileDialog, &QFileDialog::setVisible);
        connect(hideButton, &QPushButton::clicked, [this](){ fileDialog->setVisible(false); });

        // ファイル選択が完了したときに呼ばれるシグナル
        // 非モーダルなので、ユーザーが"開く"または"保存"をクリックしてもダイアログは閉じない
        // finished() シグナルで結果を受け取る
        connect(fileDialog, &QFileDialog::accepted, this, &MyWindow::onFileDialogAccepted);
        connect(fileDialog, &QFileDialog::rejected, this, &MyWindow::onFileDialogRejected);
    }

private slots:
    void onFileDialogAccepted()
    {
        QStringList selectedFiles = fileDialog->selectedFiles();
        qDebug() << "ファイルが選択されました:" << selectedFiles;
        // 選択後もダイアログは開いたままなので、手動で非表示にする
        fileDialog->setVisible(false);
    }

    void onFileDialogRejected()
    {
        qDebug() << "ファイル選択がキャンセルされました。";
        // キャンセル後もダイアログは開いたままなので、手動で非表示にする
        fileDialog->setVisible(false);
    }

private:
    QFileDialog *fileDialog; // QFileDialog をメンバー変数として宣言
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyWindow window;
    window.show();

    return app.exec();
}

#include "main.moc" // moc ファイルをインクルード (ビルドシステムが生成)

解説

  • onFileDialogAccepted()onFileDialogRejected() スロットでは、ユーザーがファイルを選択またはキャンセルした場合に fileDialog->setVisible(false); を呼び出してダイアログを明示的に非表示にしています。exec() とは異なり、setVisible() で表示されたダイアログは、ユーザーがボタンをクリックしても自動的に閉じないため、この処理が必要です。
  • connect(showButton, &QPushButton::clicked, [this](){ fileDialog->setVisible(true); }); のように、ボタンのクリックと setVisible() を接続しています。
  • fileDialog->setOption(QFileDialog::DontUseNativeDialog); は、setVisible() の動作を確実にするために重要です。これを設定しない場合、OSのネイティブダイアログが使われ、Qtのウィジェットとしての setVisible() が意図通りに動作しないことがあります。
  • QFileDialog *fileDialog; をクラスのメンバー変数として宣言し、MyWindow オブジェクトの寿命が尽きるまで QFileDialog オブジェクトが存在するようにしています。関数内でローカル変数として作成すると、関数が終了するとすぐにオブジェクトが破棄され、ダイアログが適切に表示されません。

例 2: QFileDialog::open()setVisible() の関係 (非同期処理)

Qt 5 以降では、QDialog::open() メソッドが導入され、これは exec() のようなブロッキング動作を避けつつ、ダイアログを表示し、finished() シグナルで結果を非同期に受け取る方法を提供します。QFileDialogQDialog を継承しているため、これを使用できます。open() の内部でも setVisible(true) が呼び出されることになります。

この方法は、GUI スレッドをブロックせずにファイルダイアログを表示したい場合に役立ちます。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug>

class MyAsyncWindow : public QMainWindow
{
    Q_OBJECT

public:
    MyAsyncWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setWindowTitle("QFileDialog open() Example (Async)");

        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *openFileButton = new QPushButton("ファイルを開く (非同期)", this);
        layout->addWidget(openFileButton);

        connect(openFileButton, &QPushButton::clicked, this, &MyAsyncWindow::onOpenFileClicked);
    }

private slots:
    void onOpenFileClicked()
    {
        QFileDialog *fileDialog = new QFileDialog(this);
        // 通常はネイティブダイアログが使われるが、ここでは明示的に指定しない
        // fileDialog->setOption(QFileDialog::DontUseNativeDialog); // 必要に応じて有効化

        fileDialog->setFileMode(QFileDialog::ExistingFile);
        fileDialog->setWindowTitle("ファイルを開く");

        // open() を呼び出すと非同期でダイアログが表示される
        // ダイアログが閉じられたら finished() シグナルが発せられる
        connect(fileDialog, &QFileDialog::finished, this, &MyAsyncWindow::onFileDialogFinished);

        fileDialog->open(); // ここでsetVisible(true)が内部的に呼ばれる
    }

    void onFileDialogFinished(int result)
    {
        QFileDialog *fileDialog = qobject_cast<QFileDialog*>(sender()); // シグナルを送信したオブジェクトを取得

        if (fileDialog) {
            if (result == QDialog::Accepted) {
                QStringList selectedFiles = fileDialog->selectedFiles();
                qDebug() << "選択されたファイル:" << selectedFiles;
            } else {
                qDebug() << "ファイル選択がキャンセルされました。";
            }
            fileDialog->deleteLater(); // ダイアログオブジェクトを安全に破棄
        }
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyAsyncWindow window;
    window.show();

    return app.exec();
}

#include "main.moc"

解説

  • ダイアログが不要になったら、fileDialog->deleteLater(); を呼び出して安全に破棄します。これは、スロットが実行された後にオブジェクトが破棄されることを保証します。
  • ダイアログの結果は、finished(int result) シグナルを通じて非同期に受け取られます。resultQDialog::Accepted または QDialog::Rejected になります。
  • fileDialog->open(); を呼び出すと、ダイアログが表示されますが、exec() のようにアプリケーションのイベントループをブロックしません。

この例は、例 1 の変形ですが、ファイルダイアログを一度作成し、その後必要に応じて表示・非表示を切り替えることを明確に示します。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug>

class ManagedFileDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    ManagedFileDialogWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setWindowTitle("QFileDialog Managed Visibility");

        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *toggleVisibilityButton = new QPushButton("ダイアログ表示/非表示", this);
        layout->addWidget(toggleVisibilityButton);

        // ダイアログはコンストラクタで作成し、メンバー変数として保持
        fileDialog = new QFileDialog(this);
        fileDialog->setOption(QFileDialog::DontUseNativeDialog);
        fileDialog->setWindowTitle("管理されたファイルダイアログ");
        fileDialog->setFileMode(QFileDialog::ExistingFile);

        // 初期状態では非表示
        fileDialog->setVisible(false);

        connect(toggleVisibilityButton, &QPushButton::clicked, this, &ManagedFileDialogWindow::onToggleVisibility);

        // ダイアログのAccepted/Rejectedシグナルを処理
        connect(fileDialog, &QFileDialog::accepted, this, &ManagedFileDialogWindow::handleFileDialogResult);
        connect(fileDialog, &QFileDialog::rejected, this, &ManagedFileDialogWindow::handleFileDialogResult);
    }

private slots:
    void onToggleVisibility()
    {
        // 現在の表示状態を切り替える
        fileDialog->setVisible(!fileDialog->isVisible());
        qDebug() << "ダイアログの可視性:" << fileDialog->isVisible();
    }

    void handleFileDialogResult()
    {
        if (fileDialog->result() == QDialog::Accepted) {
            QStringList selectedFiles = fileDialog->selectedFiles();
            qDebug() << "選択されたファイル:" << selectedFiles;
        } else {
            qDebug() << "ファイル選択がキャンセルされました。";
        }
        // 結果が返されたら、ダイアログを自動的に非表示にする
        fileDialog->setVisible(false);
    }

private:
    QFileDialog *fileDialog;
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    ManagedFileDialogWindow window;
    window.show();

    return app.exec();
}

#include "main.moc"
  • handleFileDialogResult() スロットでは、fileDialog->result() を使ってダイアログの結果(Accept/Reject)を判断しています。ダイアログが閉じても(accepted()rejected() シグナルが発せられても)setVisible() で表示されたダイアログは自動的に非表示にならないため、手動で fileDialog->setVisible(false); を呼び出しています。
  • onToggleVisibility() スロットでは、fileDialog->isVisible() を使って現在の表示状態を取得し、その逆の値を setVisible() に渡すことで、表示と非表示を切り替えています。
  • fileDialog->setVisible(false); を初期化時に呼び出し、ダイアログを隠れた状態から開始しています。


以下に主な代替方法を説明します。

QFileDialog::exec() (モーダルダイアログ)

これが QFileDialog を使用する最も一般的で推奨される方法です。exec() メソッドはダイアログをモーダル(アプリケーションの他の部分の操作をブロックする)に表示し、ユーザーがダイアログを閉じるまで呼び出し元のコードの実行をブロックします。

特徴

  • ネイティブダイアログ優先
    デフォルトでは、OSのネイティブファイルダイアログが優先的に使用されます。
  • シンプル
    最も簡単に実装でき、一般的なファイル選択のニーズに対応します。
  • 同期的な結果
    ダイアログが閉じられると、その結果(ファイルが選択されたか、キャンセルされたか)をすぐに受け取ることができます。
  • モーダル
    ダイアログが表示されている間、ユーザーは親ウィンドウや他のアプリケーションウィンドウを操作できません。

コード例

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug>

class ExecDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    ExecDialogWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setWindowTitle("QFileDialog::exec() Example");

        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *openFileButton = new QPushButton("ファイルを開く (exec)", this);
        layout->addWidget(openFileButton);

        connect(openFileButton, &QPushButton::clicked, this, &ExecDialogWindow::onOpenFileClicked);
    }

private slots:
    void onOpenFileClicked()
    {
        QFileDialog dialog(this);
        dialog.setFileMode(QFileDialog::ExistingFile); // 既存の単一ファイルを選択
        dialog.setWindowTitle("ファイルを開く");
        dialog.setNameFilter("テキストファイル (*.txt);;すべてのファイル (*.*)"); // フィルタ設定

        // exec() を呼び出し、結果を待つ
        if (dialog.exec() == QDialog::Accepted) {
            QStringList selectedFiles = dialog.selectedFiles();
            if (!selectedFiles.isEmpty()) {
                qDebug() << "選択されたファイル:" << selectedFiles.first();
            }
        } else {
            qDebug() << "ファイル選択がキャンセルされました。";
        }
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    ExecDialogWindow window;
    window.show();

    return app.exec();
}

#include "main.moc"

静的ヘルパー関数 (Static Helper Functions)

QFileDialog は、一般的なファイル選択操作のための便利な静的関数を提供しています。これらは内部で QFileDialog オブジェクトを作成し、exec() を呼び出し、結果を返します。最も手軽で簡潔な方法です。

主な静的関数

  • QFileDialog::getExistingDirectory(): 既存のディレクトリを選択するためのパスを取得。
  • QFileDialog::getSaveFileName(): ファイルを保存するためのパスを取得。
  • QFileDialog::getOpenFileNames(): 複数の既存のファイルを開くためのパスのリストを取得。
  • QFileDialog::getOpenFileName(): 既存のファイルを開くためのパスを取得。

特徴

  • カスタマイズの制限
    QFileDialog オブジェクトを直接操作しないため、詳細なカスタマイズ(例: ダイアログのオプション設定、カスタムプレビューペインの追加など)はできません。
  • ネイティブダイアログ優先
    デフォルトでネイティブダイアログが使用されます。
  • モーダル
    exec() と同様にモーダルで動作します。
  • 非常に簡潔
    1行のコードでファイル選択ダイアログを表示し、結果を取得できます。

コード例

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug>

class StaticDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    StaticDialogWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setWindowTitle("QFileDialog Static Functions Example");

        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *openFileButton = new QPushButton("単一ファイルを開く (静的)", this);
        QPushButton *openFilesButton = new QPushButton("複数ファイルを開く (静的)", this);
        QPushButton *saveFileButton = new QPushButton("ファイルを保存 (静的)", this);
        QPushButton *selectDirButton = new QPushButton("ディレクトリを選択 (静的)", this);

        layout->addWidget(openFileButton);
        layout->addWidget(openFilesButton);
        layout->addWidget(saveFileButton);
        layout->addWidget(selectDirButton);

        connect(openFileButton, &QPushButton::clicked, this, &StaticDialogWindow::onOpenFileClicked);
        connect(openFilesButton, &QPushButton::clicked, this, &StaticDialogWindow::onOpenFilesClicked);
        connect(saveFileButton, &QPushButton::clicked, this, &StaticDialogWindow::onSaveFileClicked);
        connect(selectDirButton, &QPushButton::clicked, this, &StaticDialogWindow::onSelectDirClicked);
    }

private slots:
    void onOpenFileClicked()
    {
        QString fileName = QFileDialog::getOpenFileName(this,
                                                        tr("ファイルを開く"),
                                                        QDir::homePath(), // 初期ディレクトリ
                                                        tr("画像ファイル (*.png *.jpg);;テキストファイル (*.txt);;すべてのファイル (*.*)"));
        if (!fileName.isEmpty()) {
            qDebug() << "選択されたファイル:" << fileName;
        } else {
            qDebug() << "ファイル選択がキャンセルされました。";
        }
    }

    void onOpenFilesClicked()
    {
        QStringList fileNames = QFileDialog::getOpenFileNames(this,
                                                             tr("複数のファイルを開く"),
                                                             QDir::homePath(),
                                                             tr("すべてのファイル (*.*)"));
        if (!fileNames.isEmpty()) {
            qDebug() << "選択されたファイル:" << fileNames;
        } else {
            qDebug() << "ファイル選択がキャンセルされました。";
        }
    }

    void onSaveFileClicked()
    {
        QString fileName = QFileDialog::getSaveFileName(this,
                                                        tr("ファイルを保存"),
                                                        QDir::homePath() + "/新しいファイル.txt", // 初期ファイル名
                                                        tr("テキストファイル (*.txt);;すべてのファイル (*.*)"));
        if (!fileName.isEmpty()) {
            qDebug() << "保存パス:" << fileName;
        } else {
            qDebug() << "ファイル保存がキャンセルされました。";
        }
    }

    void onSelectDirClicked()
    {
        QString dir = QFileDialog::getExistingDirectory(this,
                                                       tr("ディレクトリを選択"),
                                                       QDir::homePath(),
                                                       QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
        if (!dir.isEmpty()) {
            qDebug() << "選択されたディレクトリ:" << dir;
        } else {
            qDebug() << "ディレクトリ選択がキャンセルされました。";
        }
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc[0], argv); // Q_INIT_RESOURCE を使う場合は argc の型に注意
    StaticDialogWindow window;
    window.show();
    return app.exec();
}

#include "main.moc"

Qt 5 以降で導入された open() メソッドは、QDialog::open() を継承しており、非モーダルなダイアログ表示と結果の非同期処理を可能にします。これは、特に macOS でのシートダイアログ(親ウィンドウの上部にスライドして現れるダイアログ)として表示する際に推奨される方法です。GUIスレッドをブロックしないため、ユーザーはダイアログが開いている間も親ウィンドウを操作できます(ただし、通常ファイルダイアログではその必要性は低い)。

特徴

  • ネイティブダイアログ優先
    静的関数や exec() と同様にネイティブダイアログが優先されます。
  • シートダイアログ
    macOSではシートダイアログとして動作します。
  • finished() シグナルで結果取得
    ダイアログが閉じられたときに finished(int result) シグナルが発せられます。
  • 非同期
    呼び出し元のコードの実行をブロックしません。

コード例

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug>

class AsyncDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    AsyncDialogWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setWindowTitle("QFileDialog::open() Example (Async)");

        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *openFileButton = new QPushButton("ファイルを開く (open())", this);
        layout->addWidget(openFileButton);

        connect(openFileButton, &QPushButton::clicked, this, &AsyncDialogWindow::onOpenFileClicked);
    }

private slots:
    void onOpenFileClicked()
    {
        QFileDialog *fileDialog = new QFileDialog(this); // ヒープに作成し、後でdeleteLater()で破棄
        fileDialog->setFileMode(QFileDialog::ExistingFile);
        fileDialog->setWindowTitle("非同期ファイルダイアログ");
        fileDialog->setNameFilter("実行可能ファイル (*.exe *.app);;すべてのファイル (*.*)");

        // ダイアログが完了したら finished() シグナルで結果を受け取る
        // lambda Capture: [fileDialog] - シグナルが発せられた時に fileDialog が有効であることを保証
        // fileDialog->deleteLater(); で安全に削除
        connect(fileDialog, &QFileDialog::finished, this, [fileDialog](int result) {
            if (result == QDialog::Accepted) {
                QStringList selectedFiles = fileDialog->selectedFiles();
                if (!selectedFiles.isEmpty()) {
                    qDebug() << "選択されたファイル:" << selectedFiles.first();
                }
            } else {
                qDebug() << "ファイル選択がキャンセルされました。";
            }
            fileDialog->deleteLater(); // ダイアログオブジェクトを安全に破棄
        });

        fileDialog->open(); // ここで非同期でダイアログが表示される
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    AsyncDialogWindow window;
    window.show();

    return app.exec();
}

#include "main.moc"

QFileDialog::setVisible() は、QDialog 一般のウィジェット表示/非表示メカニズムを継承しているものの、QFileDialog の特殊な性質(特にネイティブダイアログの使用)と、ファイル選択というモーダルな操作の性質から、直接的に利用するケースは非常に稀です。

ほとんどの場合、以下のいずれかの方法を使用することが推奨されます。

  1. QFileDialog::exec(): 最も一般的で、シンプルかつ堅牢なモーダルダイアログ表示。
  2. 静的ヘルパー関数 (getOpenFileName() など): 最も手軽で簡潔なモーダルダイアログ表示。
  3. QFileDialog::open(): 非同期な表示が必要な場合や、macOSでのシートダイアログ表示に最適。