QFileDialog::done()の深掘り:内部動作とカスタマイズのヒント

2025-05-27

まず、QFileDialog はQtにおける標準的なファイルダイアログを提供するクラスです。これは、ユーザーがファイルを開いたり、保存したり、ディレクトリを選択したりする際に使用されるGUIコンポーネントです。

done() メソッドは、QDialog クラス(QFileDialog が継承しているクラス)から継承された保護された(protected)スロットです。このメソッドは、ダイアログが閉じられる際に呼び出され、ダイアログの結果コード(Accepted または Rejected)を設定するために使用されます。

通常、QFileDialog を使用する際には、次のような静的関数を使うことがほとんどです。

  • QFileDialog::getExistingDirectory()
  • QFileDialog::getSaveFileName()
  • QFileDialog::getOpenFileName()

これらの静的関数は、内部的に QFileDialog オブジェクトを作成し、exec() メソッドを呼び出してモーダルダイアログとして表示し、ユーザーが選択を完了すると結果を返します。この場合、開発者が明示的に done() を呼び出す必要はありません。

しかし、もしあなたが QFileDialog をカスタマイズして、通常のダイアログとは異なる方法で動作させたい場合(例えば、独自のウィジェットを追加したり、ダイアログをモーダルではなく非モーダルで表示したりする場合)、自分で QFileDialog のインスタンスを作成し、そのライフサイクルを管理する必要があります。

このようなシナリオで done() を使う目的は以下の通りです。

  1. ダイアログの終了と結果の設定
    done() メソッドは、ダイアログの表示を終了させ、その結果(ユーザーが「OK」を押した場合は QDialog::Accepted、キャンセルした場合は QDialog::Rejected)を返します。例えば、独自に作成した「OK」ボタンのシグナルを QFileDialog::accept() スロットに接続したり、独自の「キャンセル」ボタンのシグナルを QFileDialog::reject() スロットに接続したりできます。これらのスロットは内部的に done() を適切な引数(QDialog::Accepted または QDialog::Rejected)で呼び出します。

  2. プログラマティックなダイアログの終了
    テスト目的や特定の自動化されたシナリオで、ユーザーの操作なしにプログラムからダイアログを閉じたい場合に done() を呼び出すことがあります。ただし、これは QFileDialog がネイティブダイアログを使用している場合にはうまく機能しないことがあります。ネイティブダイアログはOSのコードによって制御されるため、Qtの done() を呼び出しても、必ずしもネイティブダイアログが期待通りに閉じるとは限りません。



ここでは、QFileDialog::done() が関与する可能性のある、またはQFileDialog 全体の使用時に遭遇しがちなエラーとその対処法について説明します。

ダイアログが期待通りに閉じない、または結果が取得できない

  • トラブルシューティング
    • QFileDialog::getOpenFileName()QFileDialog::getSaveFileName() のような静的関数を使用している場合、通常はこの問題は発生しません。もし発生する場合は、Qtのインストールや環境に問題がある可能性があります。
    • 独自の QFileDialog インスタンスを作成して表示している場合、必ず dialog.exec() を呼び出してモーダルダイアログとして表示し、その戻り値を確認するようにしてください。
    • カスタムUIで QFileDialog を使用している場合、OKボタンは QFileDialog::accept() (または QDialog::Accepted) に、キャンセルボタンは QFileDialog::reject() (または QDialog::Rejected) に接続されていることを確認してください。
  • 原因
    • exec() などのモーダルダイアログ表示メソッドを使用せず、非モーダルで表示しようとしている(show() など)。QFileDialog は通常、モーダルダイアログとして使用されることが想定されています。
    • カスタムダイアログを作成している場合、OK/Cancelボタンのシグナルが QDialog::accept()QDialog::reject() に正しく接続されていない。これらのメソッドが最終的に done() を呼び出します。
  • 問題
    QFileDialog を表示した後、ユーザーが操作してもダイアログが閉じない、または selectedFiles() などで結果が取得できない。

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

  • トラブルシューティング
    • QFileDialog::DontUseNativeDialog フラグを使用する
      QFileDialog::setOption(QFileDialog::DontUseNativeDialog) を設定して、Qtが提供する非ネイティブのファイルダイアログを使用するように試します。これで問題が解決する場合、OSのネイティブダイアログに問題がある可能性が高いです。
    • GUI操作はメインスレッドで
      QFileDialog の呼び出しは常にメインスレッドから行うように徹底してください。
    • 初期ディレクトリの確認
      QFileDialog::setDirectory()getOpenFileName の引数で指定する初期ディレクトリが、常に有効でアクセス可能なパスであることを確認してください。特に、ユーザーのホームディレクトリなど、存在が保証される場所から開始することを検討してください。
    • Qtのアップデート
      可能であれば、Qtの最新の安定版にアップデートすることを検討してください。バグ修正が含まれている可能性があります。
    • 最小限のコードで再現性を確認
      問題が発生している部分だけを切り出し、最小限のQtアプリケーションで再現するかどうかを確認します。これにより、問題の範囲を絞り込むことができます。
  • 原因
    • ネイティブダイアログの問題 (Windows/macOS/Linux)
      Qtは可能な限りOSのネイティブファイルダイアログを使用しようとします。しかし、ネイティブダイアログの挙動が不安定な場合や、特定のファイル形式(例:特定の拡張子のファイル)を操作しようとした際に、OSレベルで問題が発生し、アプリケーションがクラッシュすることがあります。
    • スレッドの問題
      GUI操作はメインスレッドで行われる必要があります。別スレッドから QFileDialog を開こうとすると、クラッシュや予期せぬ動作を引き起こす可能性があります。
    • 無効なパスや初期ディレクトリ
      存在しない、またはアクセス権のないパスを初期ディレクトリとして設定しようとすると問題が発生することがあります。
    • 古いQtバージョンや環境の問題
      使用しているQtのバージョンが古い、またはシステム環境との相性が悪い場合。
  • 問題
    QFileDialog を開く際にアプリケーションがクラッシュしたり、フリーズしたりする。

ダイアログの表示がおかしい、スタイルが崩れる

  • トラブルシューティング
    • QFileDialog::DontUseNativeDialog フラグを試す
      ネイティブダイアログのスタイリング問題であれば、Qtの組み込みダイアログを使用することで解決する場合があります。
    • 環境変数の設定 (Linux)
      QT_QPA_PLATFORMTHEME 環境変数を設定することで、Qtが使用するプラットフォームテーマを明示的に指定できる場合があります(例: export QT_QPA_PLATFORMTHEME=gtk2 または gtk3)。
    • システムライブラリの確認
      OSのパッケージマネージャーを使用して、Qtが依存する関連ライブラリがすべてインストールされ、最新の状態であることを確認します。
    • QSSの影響調査
      一時的にアプリケーションのスタイルシートを無効にして、QFileDialog の表示が改善されるか確認します。もし改善されるなら、スタイルシートの記述に問題がある可能性があります。
  • 原因
    • Qtのプラットフォームテーマの問題 (Linux)
      Linux環境では、QtがGTK+やKDEなどのデスクトップ環境のテーマをうまく統合できない場合に、ダイアログの見た目が崩れることがあります。
    • 古いQtバージョンやシステムライブラリの欠如
      必要なシステムライブラリが不足している、またはバージョンが古い場合に発生することがあります。
    • カスタムスタイルシートの影響
      アプリケーション全体にカスタムのQSS(Qt Style Sheets)を適用している場合、それが QFileDialog の表示に悪影響を与えている可能性があります。
  • 問題
    QFileDialog の見た目が期待通りでなかったり、UI要素が正しく表示されない。

ファイルパスのエンコーディング問題

  • トラブルシューティング
    • Qtは内部的にUnicodeを使用するため、通常はエンコーディング問題は発生しにくいですが、QStringstd::string の変換時や、C++の標準ライブラリ関数(fopenなど)と連携する際に問題が発生することがあります。
    • QFile などQtのI/Oクラスを使用する場合は、QString でパスを扱うことでエンコーディングの問題を回避できます。
    • 外部のC++ライブラリと連携する場合は、QString::toLocal8Bit()QString::toUtf8() などを使用して、適切なエンコーディングに変換してから渡すようにします。
  • 原因
    • ファイルパスのエンコーディングとシステムの期待するエンコーディングが一致しない。

QFileDialog::done() メソッド自体が直接的なエラーの原因となることは稀ですが、それは QFileDialog の動作の根幹に関わる部分です。そのため、QFileDialog 全体の問題、特に「ダイアログが正しく閉じない」「結果が取得できない」「クラッシュする」といった問題に遭遇した場合は、done() が適切に呼び出されていない、またはその前段階でネイティブダイアログとの連携に問題があることを示唆している可能性があります。



しかし、QFileDialog を継承して完全にカスタマイズされたファイルダイアログを作成する場合や、その内部動作を理解する上で done() の役割を知っておくことは重要です。

ここでは、QFileDialog::done() がどのように関与するかを示すための例コードをいくつかご紹介します。

例1: 静的関数を用いた最も一般的な使用法

この例では、done() を明示的に呼び出すことはありません。QFileDialog::getOpenFileName() が内部でダイアログの生成、表示、結果の取得、そして done() を含む終了処理を行ってくれます。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QFileDialog>
#include <QDebug> // for qDebug()

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        QPushButton *openFileButton = new QPushButton("ファイルを開く", this);
        setCentralWidget(openFileButton);

        connect(openFileButton, &QPushButton::clicked, this, &MainWindow::openFileDialog);
    }

private slots:
    void openFileDialog()
    {
        QString fileName = QFileDialog::getOpenFileName(this,
                                                        tr("ファイルを開く"),
                                                        QDir::homePath(), // 初期ディレクトリ
                                                        tr("テキストファイル (*.txt);;すべてのファイル (*.*)"));

        if (!fileName.isEmpty()) {
            qDebug() << "選択されたファイル: " << fileName;
            // ここで選択されたファイルを処理する
        } else {
            qDebug() << "ファイル選択がキャンセルされました。";
        }
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

#include "main.moc" // mocファイルを含める(Qtのビルドシステムが生成)

解説
このコードでは、QFileDialog::getOpenFileName() が呼び出されると、ファイルダイアログが表示され、ユーザーがファイルを選択して「開く」ボタンをクリックするとダイアログが閉じ、選択されたファイルパスが fileName に格納されます。ユーザーが「キャンセル」ボタンをクリックすると、fileName は空になり、ダイアログは QDialog::Rejected の結果で終了します。この裏側で、QFileDialog 内部で done() が適切な引数(QDialog::Accepted または QDialog::Rejected)で呼び出されています。

例2: QFileDialog インスタンスを直接作成し、accept() / reject() を使用する

この例では、QFileDialog のインスタンスを明示的に作成し、exec() メソッドでモーダルに表示します。ユーザーの操作に応じて accept() または reject() が呼び出され、これらが間接的に done() を呼び出します。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QFileDialog>
#include <QDebug>
#include <QVBoxLayout> // レイアウト用
#include <QWidget>     // 中央ウィジェット用

class CustomFileDialogExample : public QMainWindow
{
    Q_OBJECT

public:
    CustomFileDialogExample(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        QPushButton *selectFileButton = new QPushButton("ファイルを保存", this);
        setCentralWidget(selectFileButton);

        connect(selectFileButton, &QPushButton::clicked, this, &CustomFileDialogExample::saveFileWithDialog);
    }

private slots:
    void saveFileWithDialog()
    {
        QFileDialog dialog(this);
        dialog.setWindowTitle(tr("ファイルを保存する場所を選択"));
        dialog.setFileMode(QFileDialog::AnyFile); // 既存のファイルでなくてもOK
        dialog.setAcceptMode(QFileDialog::AcceptSave); // 保存モード
        dialog.setNameFilter(tr("テキストファイル (*.txt);;ログファイル (*.log);;すべてのファイル (*.*)"));
        dialog.setDirectory(QDir::homePath());

        // ダイアログを表示し、結果を待つ
        if (dialog.exec() == QDialog::Accepted) {
            QStringList selectedFiles = dialog.selectedFiles();
            if (!selectedFiles.isEmpty()) {
                QString savePath = selectedFiles.first();
                qDebug() << "保存パス: " << savePath;
                // ここでファイルにデータを保存する
            }
        } else {
            qDebug() << "ファイル保存がキャンセルされました。";
        }
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    CustomFileDialogExample w;
    w.show();
    return a.exec();
}

#include "main.moc"

解説
dialog.exec() が呼び出されると、Qtはダイアログのイベントループを開始し、ユーザーの操作を待ちます。ユーザーが「保存」ボタン(またはプラットフォームによっては「OK」など)をクリックすると、QFileDialog 内部のロジックが accept() スロットを呼び出し、それが最終的に done(QDialog::Accepted) を呼び出します。同様に、「キャンセル」ボタンがクリックされると、reject() スロットが呼び出され、それが done(QDialog::Rejected) を呼び出します。exec() は、done() が呼び出されてダイアログが終了するまでブロックし、done() に渡された result コードを返します。

これは done() メソッドの直接的なオーバーライドを示す例です。通常、ここまで深くカスタマイズする必要はありませんが、done() がどのように機能するかを示すのに役立ちます。

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

// MyCustomFileDialog は QFileDialog を継承
class MyCustomFileDialog : public QFileDialog
{
    Q_OBJECT

public:
    MyCustomFileDialog(QWidget *parent = nullptr)
        : QFileDialog(parent)
    {
        setWindowTitle("カスタムファイルダイアログ");
        setFileMode(QFileDialog::ExistingFile);
        setNameFilter(tr("イメージファイル (*.png *.jpg);;すべてのファイル (*.*)"));
        setDirectory(QDir::homePath());
        setOption(QFileDialog::DontUseNativeDialog, true); // ネイティブダイアログを使用しない

        // QFileDialogのレイアウトを取得(非ネイティブダイアログの場合のみ可能)
        QGridLayout *layout = qobject_cast<QGridLayout*>(this->layout());
        if (layout) {
            // 例として、追加のQLineEditをファイル名入力欄の下に追加
            QLabel *extraLabel = new QLabel("追加情報:", this);
            extraLineEdit = new QLineEdit(this);
            extraLineEdit->setPlaceholderText("ここに何か入力してください");

            // 既存の要素の配置を考慮して追加
            // QFileDialogのレイアウト構造は複雑なので、注意が必要です
            // 通常、ファイル名入力欄は行3、列1あたりにあることが多い
            // 正確な位置はQtのバージョンやスタイルによって異なる可能性がある
            layout->addWidget(extraLabel, layout->rowCount(), 0); // 新しい行にラベルを追加
            layout->addWidget(extraLineEdit, layout->rowCount() -1, 1); // 新しい行の2列目に入力欄を追加

            // OKボタンの接続はQFileDialogが内部で処理するので不要
            // ただし、OKボタンがクリックされたときにextraLineEditの内容を検証するなどの
            // 追加処理を行いたい場合は、accept()をオーバーライドするか、
            // accepted()シグナルに接続することができます。
        }
    }

    // 追加情報の取得
    QString getExtraInfo() const {
        return extraLineEdit->text();
    }

protected:
    // done() メソッドをオーバーライド
    // 注意: 通常は直接オーバーライドすることは推奨されません。
    // 代わりに accept() や reject() をオーバーライドするか、
    // accepted() / rejected() シグナルを処理する方が安全です。
    void done(int result) override
    {
        if (result == QDialog::Accepted) {
            // ダイアログが「OK」で閉じられた場合のカスタムロジック
            qDebug() << "MyCustomFileDialog::done(Accepted) が呼び出されました。";
            qDebug() << "追加情報: " << extraLineEdit->text();
            // ここで extraLineEdit の内容を検証するなど
        } else {
            // ダイアログが「キャンセル」で閉じられた場合のカスタムロジック
            qDebug() << "MyCustomFileDialog::done(Rejected) が呼び出されました。";
        }

        // 最後に基底クラスの done() を呼び出すことが重要
        // これを忘れるとダイアログが正しく終了しません。
        QFileDialog::done(result);
    }

private:
    QLineEdit *extraLineEdit = nullptr;
};

class MainAppWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainAppWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        QPushButton *openCustomDialogButton = new QPushButton("カスタムファイルダイアログを開く", this);
        setCentralWidget(openCustomDialogButton);

        connect(openCustomDialogButton, &QPushButton::clicked, this, &MainAppWindow::openCustomFileDialog);
    }

private slots:
    void openCustomFileDialog()
    {
        MyCustomFileDialog dialog(this);
        if (dialog.exec() == QDialog::Accepted) {
            QStringList selectedFiles = dialog.selectedFiles();
            if (!selectedFiles.isEmpty()) {
                qDebug() << "選択されたファイル: " << selectedFiles.first();
                qDebug() << "取得された追加情報: " << dialog.getExtraInfo();
            }
        } else {
            qDebug() << "カスタムファイルダイアログがキャンセルされました。";
        }
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainAppWindow w;
    w.show();
    return a.exec();
}

#include "main.moc"
  • 実際のアプリケーションでは、done() を直接オーバーライドするよりも、accept()reject() をオーバーライドするか、accepted()rejected() シグナルにスロットを接続する方が一般的で安全です。なぜなら、accept()reject() はダイアログが受け入れられたか拒否されたかを明確に示し、かつそれらが内部的に done() を呼び出すため、done() の複雑な呼び出しロジックを自分で管理する必要がないからです。
  • 重要な注意点
    オーバーライドした done() の中で、必ず基底クラスの QFileDialog::done(result); を呼び出す必要があります。これを怠ると、ダイアログが正しく終了しなかったり、メモリリークが発生したりする可能性があります。
  • done(int result) メソッドを override しています。この中で、ダイアログが閉じられる直前のカスタム処理を記述できます。
  • コンストラクタで setOption(QFileDialog::DontUseNativeDialog, true) を設定することで、Qtの組み込みダイアログを使用するように強制しています。これにより、ダイアログの内部レイアウトにアクセスしてウィジェットを追加できるようになります。ネイティブダイアログの場合、OSがGUIを制御するため、レイアウトのカスタマイズはできません。
  • MyCustomFileDialog クラスは QFileDialog を継承しています。


これらの代替方法は、より高レベルで安全であり、QFileDialog を適切に利用するための標準的なアプローチです。

静的関数を使用する (最も一般的で推奨される方法)

QFileDialog は、ファイルを開く、保存する、ディレクトリを選択するといった一般的なタスクのために、便利な静的関数を提供しています。これらの関数は、内部的に QFileDialog オブジェクトを作成し、モーダルダイアログとして表示し、ユーザーが操作を完了すると結果を返します。開発者が done() を意識する必要は全くありません。

  • QString QFileDialog::getExistingDirectory(...): 既存のディレクトリを選択させるダイアログを開きます。
  • QString QFileDialog::getSaveFileName(...): ファイルの保存場所と名前を選択させるダイアログを開きます。
  • QStringList QFileDialog::getOpenFileNames(...): 複数のファイルを選択させるダイアログを開きます。
  • QString QFileDialog::getOpenFileName(...): 既存のファイルを選択させるダイアログを開きます。

利点

  • プラットフォームネイティブなダイアログを自動的に使用しようとするため、ユーザー体験が良い。
  • ダイアログのライフサイクル管理が不要。
  • 最もシンプルでコード量が少ない。

欠点

  • ダイアログがモーダルであるため、ダイアログが閉じられるまでアプリケーションの他の部分がブロックされる。
  • ダイアログの表示前に細かいカスタマイズ(例:ウィジェットの追加)がほとんどできない。

コード例

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

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

    QPushButton button("ファイルを選択");
    QObject::connect(&button, &QPushButton::clicked, [&]() {
        QString fileName = QFileDialog::getOpenFileName(nullptr,
                                                        "ファイルを開く",
                                                        QDir::homePath(),
                                                        "テキストファイル (*.txt);;すべてのファイル (*.*)");
        if (!fileName.isEmpty()) {
            qDebug() << "選択されたファイル: " << fileName;
        } else {
            qDebug() << "ファイル選択がキャンセルされました。";
        }
    });

    button.show();
    return a.exec();
}

QFileDialog インスタンスを作成し、exec() の戻り値を確認する

ダイアログのプロパティ(タイトル、フィルタ、初期ディレクトリなど)をより細かく設定したいが、複雑なカスタムUIは必要ない場合にこの方法を使用します。

  • exec() メソッドを呼び出し、その戻り値 (QDialog::Accepted または QDialog::Rejected) を確認してユーザーの選択を判断する。
  • setFileMode(), setNameFilter(), setDirectory(), setAcceptMode() などのメソッドでダイアログを設定する。
  • QFileDialog のインスタンスを作成する。

利点

  • 引き続きプラットフォームネイティブなダイアログを使用しようとする。
  • 静的関数よりも柔軟にダイアログの設定ができる。

欠点

  • 静的関数と同様に、ダイアログがモーダルであり、細かなUIカスタマイズは難しい。

コード例

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

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

    QPushButton button("ファイルを保存");
    QObject::connect(&button, &QPushButton::clicked, [&]() {
        QFileDialog dialog(nullptr); // 親ウィジェットを指定しない場合はnullptr
        dialog.setWindowTitle("ファイルを保存する");
        dialog.setFileMode(QFileDialog::AnyFile); // 存在しないファイル名も許容
        dialog.setAcceptMode(QFileDialog::AcceptSave); // 保存モード
        dialog.setNameFilter("画像ファイル (*.png *.jpg);;すべてのファイル (*.*)");
        dialog.setDirectory(QDir::homePath());

        if (dialog.exec() == QDialog::Accepted) { // ダイアログが「OK」で閉じられた場合
            QStringList selectedFiles = dialog.selectedFiles();
            if (!selectedFiles.isEmpty()) {
                qDebug() << "保存パス: " << selectedFiles.first();
            }
        } else { // ダイアログが「キャンセル」で閉じられた場合
            qDebug() << "ファイル保存がキャンセルされました。";
        }
    });

    button.show();
    return a.exec();
}

QDialog::accepted() / QDialog::rejected() シグナルを接続する

これは、QFileDialog を非モーダルで使用したい場合や、ダイアログが閉じられたときに特定のカスタムアクションを実行したい場合に有用です。

  • これらのシグナルは、それぞれ内部的に done(QDialog::Accepted) および done(QDialog::Rejected) が呼び出された後に発行されます。
  • accepted() シグナル(ユーザーが「OK」を押したとき)と rejected() シグナル(ユーザーが「キャンセル」を押したとき)にカスタムスロットを接続する。
  • QFileDialog インスタンスを作成し、show() を呼び出して非モーダルで表示する。

利点

  • シグナル/スロットメカニズムにより、イベントドリブンなプログラミングが可能。
  • ダイアログを非モーダルで表示できるため、ダイアログ表示中にアプリケーションの他の部分を操作できる。

欠点

  • 非モーダルダイアログは、ユーザーが複数のダイアログを開いたり、意図しないタイミングで閉じたりする可能性があるため、注意深い設計が必要。
  • ダイアログのライフサイクル管理(メモリ解放など)を開発者が行う必要がある。

コード例

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

class MyWindow : public QWidget
{
    Q_OBJECT
public:
    MyWindow(QWidget *parent = nullptr) : QWidget(parent)
    {
        QVBoxLayout *layout = new QVBoxLayout(this);
        QPushButton *openButton = new QPushButton("非モーダルでファイルを開く");
        layout->addWidget(openButton);

        connect(openButton, &QPushButton::clicked, this, &MyWindow::openNonModalFileDialog);
    }

private slots:
    void openNonModalFileDialog()
    {
        QFileDialog *fileDialog = new QFileDialog(this); // new で作成し、後でdeleteする必要がある
        fileDialog->setWindowTitle("非モーダルファイル選択");
        fileDialog->setFileMode(QFileDialog::ExistingFile);
        fileDialog->setNameFilter("ドキュメント (*.doc *.pdf);;すべてのファイル (*.*)");
        fileDialog->setDirectory(QDir::homePath());
        fileDialog->setOption(QFileDialog::DontUseNativeDialog); // 非モーダルではネイティブダイアログを避けることが多い

        // accepted() シグナルが発火したら呼ばれるスロット
        connect(fileDialog, &QFileDialog::accepted, [fileDialog]() {
            QStringList selectedFiles = fileDialog->selectedFiles();
            if (!selectedFiles.isEmpty()) {
                qDebug() << "非モーダル: 選択されたファイル: " << selectedFiles.first();
            }
            fileDialog->deleteLater(); // ダイアログが閉じられたら自動的に削除
        });

        // rejected() シグナルが発火したら呼ばれるスロット
        connect(fileDialog, &QFileDialog::rejected, [fileDialog]() {
            qDebug() << "非モーダル: ファイル選択がキャンセルされました。";
            fileDialog->deleteLater(); // ダイアログが閉じられたら自動的に削除
        });

        fileDialog->show(); // 非モーダルで表示
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyWindow w;
    w.show();
    return a.exec();
}

#include "main.moc"

QDialog::result() メソッド (モーダルダイアログの表示後に使用)

この方法は、主にカスタムダイアログを実装する際に、exec() の戻り値以外でダイアログの結果コードを取得したい場合に使われます。QFileDialog を直接使う場合は exec() の戻り値で十分なので、あまり使いません。

  • exec() の呼び出し後、ダイアログオブジェクトがまだ有効な場合に result() を呼び出して、そのダイアログが Accepted で閉じられたか Rejected で閉じられたかを取得できます。
// 例2のコードのexec()呼び出し後に追加するイメージ
QFileDialog dialog(nullptr);
// ... ダイアログの設定 ...

int dialogResult = dialog.exec(); // exec()自体が結果を返す

// しかし、もしdialog.result()を使いたいなら
// if (dialog.result() == QDialog::Accepted) {
//     // 処理
// }
// とすることもできるが、exec()の戻り値を使うのが一般的