QFileDialog::getOpenFileUrls()の代替手段:Qtでカスタムファイル選択UIを実装

2025-05-27

QList<QUrl> QFileDialog::getOpenFileUrls() とは?

QFileDialog::getOpenFileUrls() は、Qtの QFileDialog クラスが提供する静的(static)関数の一つで、ユーザーが複数のファイルを選択するためのダイアログを表示するために使われます。この関数は、選択されたファイルのパスを QUrl オブジェクトのリスト(QList<QUrl>)として返します。

静的関数とは?

静的関数は、オブジェクトを生成せずにクラス名を使って直接呼び出すことができる関数です。つまり、QFileDialog dialog; のように QFileDialog のインスタンスを作る必要がなく、QFileDialog::getOpenFileUrls(...) のように呼び出すことができます。

QUrl とは?

QUrl は、Unified Resource Locator(統一資源ロケーター)の略で、ファイルパスだけでなく、ウェブ上のURL(http://...)やFTPパス(ftp://...)なども扱うことができる汎用的なパス表現です。ファイルパスの場合、file:///path/to/file.txt のような形式になります。

戻り値: QList<QUrl>

この関数が返すのは QList<QUrl> です。これは、ユーザーがファイルダイアログで選択したすべてのファイルのURL(パス)を格納したリストです。ユーザーが複数のファイルを選択した場合、そのすべてのファイルのURLがこのリストに含まれます。ユーザーが何も選択せずにダイアログをキャンセルした場合、空の QList が返されます。

関数の引数

QFileDialog::getOpenFileUrls() には、いくつかのオーバーロード(引数の異なるバージョン)がありますが、一般的な形式は以下のようになります。

QList<QUrl> QFileDialog::getOpenFileUrls(
    QWidget *parent = nullptr,
    const QString &caption = QString(),
    const QUrl &dir = QUrl(),
    const QString &filter = QString(),
    QString *selectedFilter = nullptr,
    Options options = Options()
)

それぞれの引数の意味は以下の通りです。

  1. QWidget *parent = nullptr:

    • このファイルダイアログの親となるウィジェットを指定します。親ウィジェットを指定すると、ダイアログはその親の中央に表示されたり、親が閉じられたときに一緒に閉じられたりするなど、親子の関係性が確立されます。通常は、ダイアログを呼び出すウィンドウ(例: this)を指定します。nullptr を指定することも可能ですが、その場合ダイアログは独立したウィンドウとして表示されます。
  2. const QString &caption = QString():

    • ファイルダイアログのタイトルバーに表示されるキャプション(タイトル)を指定します。例えば、「ファイルを開く」などの文字列を設定します。
  3. const QUrl &dir = QUrl():

    • ファイルダイアログが最初に開かれたときに表示されるディレクトリ(初期ディレクトリ)を指定します。QUrl 形式でパスを与えます。何も指定しない場合、アプリケーションの現在の作業ディレクトリなどがデフォルトで使われます。
  4. const QString &filter = QString():

    • 表示されるファイルの種類をフィルタリングするための文字列を指定します。例えば、「Images (*.png *.jpg);;Text files (*.txt)」のように指定すると、PNGとJPGの画像ファイル、またはテキストファイルのみが表示されるようになります。複数のフィルタは「;;」で区切ります。
  5. QString *selectedFilter = nullptr:

    • ユーザーがダイアログで最終的に選択したフィルタ文字列(例: *.png)を受け取るためのポインタです。必要なければ nullptr のままで構いません。
  6. Options options = Options():

    • ファイルダイアログの動作をカスタマイズするためのオプションフラグを指定します。QFileDialog::Options 列挙型で定義されている値を、| 演算子で組み合わせて指定します。
      • 例: QFileDialog::DontUseNativeDialog (ネイティブダイアログを使わず、Qt標準のダイアログを使う)
      • 例: QFileDialog::ReadOnly (ファイルを読み取り専用で開くことを示す)
      • 例: QFileDialog::ExistingFiles (既存のファイルのみ選択可能にする)

複数のファイルを選択する例:

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

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

    // ファイルダイアログを表示し、複数のファイルを選択させる
    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(
        nullptr, // 親ウィジェットなし
        "複数のファイルを開く", // ダイアログのタイトル
        QUrl::fromLocalFile("/home/user/documents"), // 初期ディレクトリ (例)
        "画像ファイル (*.png *.jpg *.jpeg);;テキストファイル (*.txt);;すべてのファイル (*.*)" // フィルタ
    );

    // ユーザーがファイルを選択し、OKをクリックした場合
    if (!selectedUrls.isEmpty()) {
        qDebug() << "選択されたファイル:";
        for (const QUrl &url : selectedUrls) {
            qDebug() << url.toLocalFile(); // ローカルファイルパスとして表示
        }
    } else {
        qDebug() << "ファイルは選択されませんでした。";
    }

    return 0; // ダイアログを表示するだけなので、通常はアプリケーションループは不要ですが、
              // 実際のQtアプリケーションでは a.exec() が必要です。
              // ここでは簡易的な例として省略しています。
}


QList<QUrl> QFileDialog::getOpenFileUrls() のよくあるエラーとトラブルシューティング

QFileDialog::getOpenFileUrls() は非常に便利な関数ですが、いくつかの一般的な落とし穴や予期せぬ挙動が発生することがあります。

空のリストが返ってくる(ファイルが選択されていない)

エラー/挙動
ユーザーがファイルダイアログを開いたものの、ファイルを選択せずに「キャンセル」ボタンを押すか、ダイアログを閉じた場合、QList<QUrl> は空になります。これはエラーではなく、想定された挙動です。

トラブルシューティング

  • 戻り値のチェック
    常に QList が空でないかを確認し、それに従って処理を分岐させることが重要です。

    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(...);
    if (selectedUrls.isEmpty()) {
        // ユーザーがキャンセルした、または何も選択しなかった場合の処理
        qDebug() << "ファイルが選択されませんでした。";
        return; // または適切なエラーハンドリング
    }
    
    // ファイルが選択された場合の処理
    qDebug() << "選択されたファイル数:" << selectedUrls.size();
    

ファイルパスの形式に関する問題 (QUrl とローカルパスの変換)

エラー/挙動
QFileDialog::getOpenFileUrls()QList<QUrl> を返しますが、多くのファイル操作関数(例: QFile::open()) は QString のローカルファイルパスを期待します。QUrl から QString への変換を誤ると、ファイルが見つからない、またはパスが無効なエラーが発生する可能性があります。

トラブルシューティング

  • Windowsのパス区切り文字
    Windowsでは通常\(バックスラッシュ)がパス区切り文字として使われますが、Qtの内部では/(スラッシュ)が推奨されます。QUrl::toLocalFile() はプラットフォームに適した形式に自動的に変換してくれます。手動でパスを構築する場合は注意が必要です。

  • QUrl::toLocalFile() の使用
    ローカルファイルシステム上のパスとして使用する場合は、必ず toLocalFile() メソッドを使用します。

    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(...);
    if (!selectedUrls.isEmpty()) {
        for (const QUrl &url : selectedUrls) {
            QString localPath = url.toLocalFile();
            qDebug() << "ローカルパス:" << localPath;
            // QDir, QFile などの Qt のファイルシステム操作に localPath を使用
            QFile file(localPath);
            if (file.open(QIODevice::ReadOnly)) {
                qDebug() << "ファイルを開けました:" << localPath;
                file.close();
            } else {
                qDebug() << "ファイルを開けませんでした:" << localPath << "エラー:" << file.errorString();
            }
        }
    }
    

ファイルダイアログが表示されない、またはクラッシュする

エラー/挙動
ファイルダイアログが全く表示されない、または表示直後にアプリケーションがクラッシュすることがあります。

トラブルシューティング

  • ネイティブダイアログの問題
    特定のOSバージョンや環境で、Qtがネイティブファイルダイアログを呼び出す際に問題が発生することがあります。

    // ネイティブダイアログの使用を無効にするオプションを試す
    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(
        parent,
        caption,
        dir,
        filter,
        selectedFilter,
        QFileDialog::DontUseNativeDialog // このオプションを追加
    );
    

    このオプションは、Qtが提供するウィジェットベースのダイアログを使用するように強制します。見た目はネイティブダイアログと異なる場合がありますが、問題解決に役立つことがあります。

  • 不正な引数
    特に parent ポインタが不正な(既に削除された)ウィジェットを指していたりすると、クラッシュの原因になります。

    // 親ウィジェットが有効であることを確認する
    // 例: 親が this ポインタの場合、そのオブジェクトが生存していることを確認
    QFileDialog::getOpenFileUrls(this, ...);
    

フィルタが正しく機能しない

エラー/挙動
filter 引数で指定したファイルの種類が正しくフィルタリングされない、または予期しないファイルが表示される。

トラブルシューティング

  • デフォルトフィルタ
    フィルタ文字列の最初のものがデフォルトで選択されます。

  • フィルタ文字列の構文
    フィルタ文字列の構文が間違っていると、正しく機能しません。

    • 複数のフィルタは「;;」(セミコロン2つ)で区切ります。
    • 各フィルタは「説明 (*.拡張子1 *.拡張子2)」の形式で記述します。
    • ワイルドカード(*)の前にスペースを入れないように注意してください。

    良い例
    Images (*.png *.jpg);;Text files (*.txt) 悪い例: Images (*. png *. jpg);Text files (*.txt) (スペースやセミコロン1つ)

初期ディレクトリが正しく設定されない

エラー/挙動
dir 引数で指定したディレクトリが、ファイルダイアログを開いたときに表示されない。

トラブルシューティング

  • 存在しないパス
    指定されたディレクトリが存在しない場合、ファイルダイアログはデフォルトの場所(アプリケーションの作業ディレクトリなど)を開くことがあります。初期ディレクトリを設定する前に、そのパスが存在するか確認することも有効です。

  • QUrl の形式
    dir 引数は QUrl を受け取ります。ローカルファイルパスを指定する場合は、QUrl::fromLocalFile() を使用して QUrl オブジェクトに変換する必要があります。

    // 正しい例
    QUrl initialDir = QUrl::fromLocalFile("/path/to/initial/directory");
    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(nullptr, "ファイルを開く", initialDir);
    
    // 誤った例 (QString を直接渡そうとするとコンパイルエラーまたは意図しない挙動)
    // QString initialDirString = "/path/to/initial/directory";
    // QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(nullptr, "ファイルを開く", initialDirString); // エラーまたは警告
    

パフォーマンスの問題(特にネットワークドライブや大きなディレクトリ)

エラー/挙動
ファイルダイアログの表示が非常に遅い、または反応が鈍い。

トラブルシューティング

  • 初期ディレクトリの最適化
    非常に大きなディレクトリを初期ディレクトリとして設定するのを避けるか、ユーザーがアクセスする可能性が高い、比較的小さなディレクトリを初期ディレクトリに設定します。

  • ネイティブダイアログの使用
    QFileDialog::DontUseNativeDialog オプションを設定している場合、Qtは独自のウィジェットベースのダイアログを使用します。これは、ネットワークドライブ上の多数のファイルや、非常に大きなディレクトリをスキャンする際にパフォーマンスが低下する原因となることがあります。可能な限りネイティブダイアログを使用することを検討してください。

    // ネイティブダイアログを使用する (デフォルト挙動)
    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(parent, caption, dir, filter);
    
  • エラーメッセージの確認
    コンソールに出力されるQtのエラーメッセージや警告メッセージに注意を払ってください。
  • 最小限の再現コード
    問題を切り分けるために、問題が発生する部分だけの最小限のコードを作成し、他の要素を排除してテストします。
  • Qt のドキュメントを参照
    QFileDialog クラスの公式ドキュメントには、詳細な情報、オプション、使用例が記載されています。
  • デバッガの使用
    問題が発生した場合は、デバッガを使用してステップ実行し、関数の戻り値や変数の値を確認することが最も効果的です。


QFileDialog::getOpenFileUrls() は、ユーザーが複数のファイルを選択できるようにするための静的関数です。ここでは、いくつかの一般的な使用シナリオに基づいたプログラミング例を挙げ、それぞれのコードと解説を行います。

例1: 最も基本的な使用法(複数の画像ファイルを選択)

この例では、親ウィジェットなしでファイルダイアログを表示し、ユーザーに複数の画像ファイルを選択させます。

#include <QApplication> // QApplicationはQt GUIアプリケーションに必須
#include <QFileDialog>  // QFileDialogクラスを使用するため
#include <QList>        // QListを使用するため
#include <QUrl>         // QUrlを使用するため
#include <QDebug>       // デバッグ出力のため

int main(int argc, char *argv[]) {
    QApplication a(argc, argv); // Qtアプリケーションオブジェクトの作成 (必須)

    // ファイルダイアログを表示し、複数のURLを取得
    // 引数:
    // 1. parent: nullptr (親なし)
    // 2. caption: "画像ファイルを選択してください" (ダイアログのタイトル)
    // 3. dir: QUrl() (初期ディレクトリはデフォルト)
    // 4. filter: "画像ファイル (*.png *.jpg *.jpeg);;すべてのファイル (*.*)" (フィルタ)
    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(
        nullptr,                                    // 親ウィジェットなし
        "画像ファイルを選択してください",               // ダイアログのタイトル
        QUrl(),                                     // 初期ディレクトリ (デフォルト)
        "画像ファイル (*.png *.jpg *.jpeg);;すべてのファイル (*.*)" // フィルタ
    );

    // ユーザーがファイルを選択したかチェック
    if (!selectedUrls.isEmpty()) {
        qDebug() << "選択されたファイル:";
        // 選択された各ファイルのURLをループで処理
        for (const QUrl &url : selectedUrls) {
            // QUrlをローカルファイルパス (QString) に変換して表示
            qDebug() << "  " << url.toLocalFile();

            // ここで、選択されたファイルに対する処理を行うことができます。
            // 例: 画像をロードする、ファイルの内容を読み込むなど。
            // QImage image(url.toLocalFile());
            // if (!image.isNull()) {
            //     qDebug() << "    画像が正常にロードされました。";
            // }
        }
    } else {
        qDebug() << "ファイルは選択されませんでした。";
    }

    // QApplicationのイベントループを開始 (GUIアプリケーションでは必須)
    // ただし、この例ではダイアログを表示して終了するだけなので、通常は main 関数内で a.exec() は必要ありません。
    // 実際のアプリケーションでは、ウィンドウが表示され続けるために必要です。
    // return a.exec();
    return 0;
}

解説

  • 取得した QUrl は、toLocalFile() メソッドを使って、オペレーティングシステムが認識するローカルパス(QString)に変換できます。
  • ユーザーがファイルを選択した場合、selectedUrls は空ではなくなります。
  • フィルタ文字列は「説明 (*.拡張子);;説明 (*.拡張子)」の形式で、複数のフィルタを「;;」で区切ります。
  • nullptr を最初の引数に渡すことで、ダイアログが親ウィジェットを持たない独立したウィンドウとして表示されます。
  • QFileDialog::getOpenFileUrls() は静的関数なので、QFileDialog オブジェクトを作成せずに直接呼び出します。
  • QApplication a(argc, argv); は、Qt GUIアプリケーションの実行に不可欠です。これがないと、GUIコンポーネントは正しく動作しません。

例2: 初期ディレクトリとオプションの指定

この例では、特定の初期ディレクトリを設定し、ネイティブダイアログではなくQt標準のダイアログを使用するオプションを指定します。

#include <QApplication>
#include <QFileDialog>
#include <QList>
#include <QUrl>
#include <QDebug>
#include <QStandardPaths> // 標準パスを取得するため

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

    // 初期ディレクトリを設定
    // QStandardPaths::HomeLocation はユーザーのホームディレクトリを返します
    QUrl initialDir = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
    qDebug() << "初期ディレクトリ:" << initialDir.toLocalFile();

    // ファイルダイアログを表示し、複数のURLを取得
    QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(
        nullptr,                                    // 親ウィジェットなし
        "プロジェクトファイルを選択してください",       // ダイアログのタイトル
        initialDir,                                 // 初期ディレクトリ
        "プロジェクトファイル (*.pro);;テキストファイル (*.txt)", // フィルタ
        nullptr,                                    // 選択されたフィルタを受け取らない
        QFileDialog::DontUseNativeDialog            // ネイティブダイアログを使用しないオプション
    );

    if (!selectedUrls.isEmpty()) {
        qDebug() << "選択されたファイル:";
        for (const QUrl &url : selectedUrls) {
            qDebug() << "  " << url.toLocalFile();
        }
    } else {
        qDebug() << "ファイルは選択されませんでした。";
    }

    return 0;
}

解説

  • QFileDialog::DontUseNativeDialog オプションを指定すると、QtはOS標準のファイルダイアログではなく、Qtが独自に実装したファイルダイアログを表示します。これは、特定のOS環境でのネイティブダイアログの挙動に問題がある場合や、UIの一貫性をQtアプリケーション全体で保ちたい場合に有用です。
  • QUrl::fromLocalFile(QString) を使用して、ローカルパスから QUrl オブジェクトを作成し、初期ディレクトリとして設定します。QStandardPaths::writableLocation() は、クロスプラットフォームで一般的なディレクトリ(ホーム、ドキュメントなど)のパスを取得するのに便利です。

例3: 親ウィジェットを持つダイアログと選択されたフィルタの取得

この例では、メインウィンドウクラス内でダイアログを呼び出し、選択されたフィルタ文字列も取得します。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QFileDialog>
#include <QList>
#include <QUrl>
#include <QDebug>
#include <QLabel> // 結果表示用

class MyMainWindow : public QMainWindow {
    Q_OBJECT // Q_OBJECT マクロはシグナル/スロットのために必須

public:
    MyMainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        setWindowTitle("ファイル選択アプリケーション");
        resize(400, 300);

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

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

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

        resultLabel = new QLabel("ここに選択結果が表示されます。", this);
        resultLabel->setWordWrap(true); // 長いテキストを折り返す
        layout->addWidget(resultLabel);

        // ボタンがクリックされたら openFiles() スロットを呼び出す
        connect(openFileButton, &QPushButton::clicked, this, &MyMainWindow::openFiles);
    }

private slots:
    void openFiles() {
        QString selectedFilter; // ユーザーが選択したフィルタを格納する変数

        QList<QUrl> selectedUrls = QFileDialog::getOpenFileUrls(
            this,                                   // 親ウィジェットを自身 (MyMainWindow) に設定
            "ドキュメントまたはコードファイルを選択してください", // タイトル
            QUrl(),                                 // 初期ディレクトリ (デフォルト)
            "ドキュメント (*.docx *.pdf);;C++ コード (*.cpp *.h);;すべてのファイル (*.*)", // フィルタ
            &selectedFilter                         // 選択されたフィルタをこの変数に格納
        );

        if (!selectedUrls.isEmpty()) {
            QString resultText = "選択されたファイル:\n";
            for (const QUrl &url : selectedUrls) {
                resultText += "  " + url.toLocalFile() + "\n";
            }
            resultText += "\n選択されたフィルタ: " + selectedFilter;
            resultLabel->setText(resultText); // QLabelに結果を表示
            qDebug() << resultText;
        } else {
            resultLabel->setText("ファイルは選択されませんでした。");
            qDebug() << "ファイルは選択されませんでした。";
        }
    }

private:
    QLabel *resultLabel;
};

// moc が必要なので、この部分を .cpp ファイルに含めるか、ビルドシステムで処理させる必要があります。
#include "main.moc" // このファイルをビルドする際には、moc_MyMainWindow.cpp が生成され、それに置き換わります。

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

    MyMainWindow mainWindow;
    mainWindow.show(); // メインウィンドウを表示

    return a.exec(); // アプリケーションのイベントループを開始
}
  • &selectedFilter を5番目の引数に渡すことで、ユーザーがファイルダイアログでどのフィルタ(例: "C++ コード (*.cpp *.h)")を選択したかを知ることができます。
  • QFileDialog::getOpenFileUrls(this, ...) のように this を親ウィジェットとして渡すことで、ダイアログはメインウィンドウに関連付けられます。これにより、ダイアログがメインウィンドウの上に表示されたり、メインウィンドウが閉じられたときにダイアログも自動的に閉じられたりするなどのメリットがあります。
  • QPushButton がクリックされたときに openFiles() スロットが呼び出されるように、シグナル-スロット接続を行っています。
  • この例では、QMainWindow を継承した独自の MyMainWindow クラスを作成しています。


QFileDialog クラスのインスタンス化と詳細な設定

getOpenFileUrls() のような静的関数ではなく、QFileDialog クラスのインスタンスを作成することで、より細かくダイアログの挙動を制御できます。これは、最も一般的な代替方法であり、getOpenFileUrls() が提供しない多くの設定が可能です。

できること

  • シグナル/スロットを使用した非同期なダイアログ処理
  • ダイアログの状態の保存と復元
  • カスタムのサイドバーURLの追加
  • ダイアログのレイアウトやウィジェットへのアクセス(Qt標準のダイアログを使用する場合に限る)

コード例

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QFileDialog>
#include <QList>
#include <QUrl>
#include <QDebug>
#include <QLabel>
#include <QMessageBox> // エラーメッセージ表示用

class MyMainWindow : public QMainWindow {
    Q_OBJECT

public:
    MyMainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        setWindowTitle("QFileDialog インスタンス");
        resize(500, 400);

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

        QVBoxLayout *layout = new QVBoxLayout(centralWidget);

        QPushButton *openFileButton = new QPushButton("詳細設定でファイルを開く", this);
        layout->addWidget(openFileButton);

        resultLabel = new QLabel("ここに選択結果が表示されます。", this);
        resultLabel->setWordWrap(true);
        layout->addWidget(resultLabel);

        connect(openFileButton, &QPushButton::clicked, this, &MyMainWindow::openFilesWithInstance);
    }

private slots:
    void openFilesWithInstance() {
        QFileDialog dialog(this); // QFileDialogのインスタンスを作成
        dialog.setWindowTitle("カスタムファイル選択");
        dialog.setFileMode(QFileDialog::ExistingFiles); // 複数既存ファイル選択モード
        dialog.setNameFilter("テキストファイル (*.txt);;ログファイル (*.log);;すべてのファイル (*.*)");
        dialog.setDirectory(QDir::homePath()); // 初期ディレクトリをホームパスに設定

        // カスタムサイドバーURLを追加 (例: デスクトップへのショートカット)
        QList<QUrl> sidebarUrls;
        sidebarUrls << QUrl::fromLocalFile(QDir::homePath() + "/Desktop");
        dialog.setSidebarUrls(sidebarUrls);

        // オプションの設定 (ネイティブダイアログを使用しない)
        dialog.setOption(QFileDialog::DontUseNativeDialog, true);

        // exec() はモーダルダイアログを表示し、ユーザーがOK/キャンセルするまでブロックします
        if (dialog.exec()) { // ダイアログが「OK」で閉じられた場合
            QList<QUrl> selectedUrls = dialog.selectedUrls(); // QList<QUrl> を取得
            if (!selectedUrls.isEmpty()) {
                QString resultText = "選択されたファイル:\n";
                for (const QUrl &url : selectedUrls) {
                    resultText += "  " + url.toLocalFile() + "\n";
                }
                resultLabel->setText(resultText);
                qDebug() << resultText;
            }
        } else {
            resultLabel->setText("ファイルは選択されませんでした。");
            qDebug() << "ファイルは選択されませんでした。";
        }
    }

private:
    QLabel *resultLabel;
};

#include "main.moc" // moc_MyMainWindow.cpp が生成され、ここに置き換わる

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

利点

  • exec() の代わりに open() メソッドとシグナル (filesSelected()urlSelected()) を使用することで、非同期にダイアログを処理し、メインスレッドをブロックしないようにすることも可能です(ただし、ファイル選択ダイアログは通常モーダルで問題ないことが多い)。
  • setSidebarUrls() で、ユーザーが頻繁にアクセスするディレクトリへのショートカットをサイドバーに追加できます。
  • setOption()DontUseNativeDialog などのオプションを個別に設定できます。
  • setNameFilters()selectNameFilter() でフィルタをより詳細に制御できます。
  • setFileMode(): QFileDialog::ExistingFiles (複数既存ファイル), QFileDialog::ExistingFile (単一既存ファイル), QFileDialog::AnyFile (新規保存など), QFileDialog::Directory (ディレクトリのみ) など、柔軟な選択モードを設定できます。

QListView や QTreeView を使用したカスタムファイルブラウザ

QFileDialog が提供する機能だけでは不十分で、独自のUIを持つファイル選択ダイアログをゼロから作りたい場合、QListViewQTreeViewQFileSystemModel を組み合わせて、完全にカスタムなファイルブラウザを作成できます。

できること

  • 単一のダイアログでファイルとディレクトリの両方を同時に選択できるようにする(QFileDialogでは直接できないことが多い)。
  • ドラッグ&ドロップなどの高度なインタラクションの追加。
  • 独自のフィルタリング、検索、ソートロジックの実装。
  • ファイル表示のカスタマイズ(アイコン、情報、コンテキストメニューなど)。
  • UIのレイアウト、デザイン、ウィジェットの配置を完全に制御。

コード例 (概念的なもの)

// これは完全な実行可能なコードではありませんが、概念を示します。

#include <QDialog>
#include <QVBoxLayout>
#include <QTreeView>
#include <QFileSystemModel>
#include <QPushButton>
#include <QMessageBox>
#include <QStringList>
#include <QItemSelectionModel> // 選択モデル用
#include <QUrl>

class CustomFileSelectorDialog : public QDialog {
    Q_OBJECT
public:
    CustomFileSelectorDialog(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("カスタムファイルブラウザ");
        QVBoxLayout *layout = new QVBoxLayout(this);

        fileSystemModel = new QFileSystemModel(this);
        fileSystemModel->setRootPath(QDir::homePath()); // ホームディレクトリをルートに設定
        fileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); // ディレクトリとファイルを表示

        treeView = new QTreeView(this);
        treeView->setModel(fileSystemModel);
        treeView->setRootIndex(fileSystemModel->index(QDir::homePath()));
        treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); // 複数選択を許可
        treeView->setHeaderHidden(true); // ヘッダを非表示にする例

        // 列を非表示にする(ファイル名だけを表示したい場合など)
        for (int i = 1; i < fileSystemModel->columnCount(); ++i) {
            treeView->hideColumn(i);
        }

        layout->addWidget(treeView);

        QPushButton *selectButton = new QPushButton("選択", this);
        layout->addWidget(selectButton);

        connect(selectButton, &QPushButton::clicked, this, &CustomFileSelectorDialog::onSelectButtonClicked);
    }

    QList<QUrl> selectedUrls() const {
        return m_selectedUrls;
    }

private slots:
    void onSelectButtonClicked() {
        m_selectedUrls.clear();
        QModelIndexList selectedIndexes = treeView->selectionModel()->selectedRows(); // 選択された行を取得

        for (const QModelIndex &index : selectedIndexes) {
            QString filePath = fileSystemModel->filePath(index);
            m_selectedUrls << QUrl::fromLocalFile(filePath);
        }

        if (m_selectedUrls.isEmpty()) {
            QMessageBox::warning(this, "選択なし", "ファイルまたはディレクトリが選択されていません。");
        } else {
            accept(); // ダイアログを「OK」で閉じる
        }
    }

private:
    QFileSystemModel *fileSystemModel;
    QTreeView *treeView;
    QList<QUrl> m_selectedUrls;
};

// 使用例 (MyMainWindow クラス内、または main 関数内)
/*
    // CustomFileSelectorDialog のインスタンスを作成し、表示
    CustomFileSelectorDialog dialog(this); // 親ウィジェットを指定
    if (dialog.exec() == QDialog::Accepted) {
        QList<QUrl> urls = dialog.selectedUrls();
        for (const QUrl &url : urls) {
            qDebug() << "カスタム選択:" << url.toLocalFile();
        }
    }
*/

利点

  • 特定のユースケースへの最適化
    アプリケーションの特定のニーズに合わせて、非常に特化したファイル選択体験を提供できます。
  • 究極の柔軟性
    UIとロジックを完全に制御できます。

欠点

  • OSのルック&フィール
    OSネイティブのファイルダイアログとは異なる外観になります。Qt標準のダイアログはQtアプリケーション内で一貫性がありますが、OS全体での一貫性は失われます。
  • 開発コスト
    QFileDialog を使用するよりもはるかに多くのコードを記述する必要があります。

プラットフォーム固有のAPIの直接呼び出し(非推奨だが可能性として)

ごく稀なケースで、Qtの QFileDialog が提供する機能が、特定のOSの非常に特殊なファイル選択要件を満たせない場合、そのOSのネイティブAPIを直接呼び出すことを検討するかもしれません。

できること

  • OSの最新のファイル選択機能や、Qtがまだサポートしていない特殊なオプションを利用できます。
  • Qtのシグナル/スロットシステムとの統合が複雑になる場合があります。
  • 保守性の低下
    OSのAPI変更に対応する必要があります。
  • クロスプラットフォーム性の喪失
    コードが特定のOSに強く依存し、他のOSでは動作しなくなります。