Qt QFileDialog::getOpenFileContent 解説:非同期ファイル読み込みの基本と応用

2025-05-27

主な特徴は以下の通りです。

  • エラー処理
    ファイルの読み込み中にエラーが発生した場合、エラー情報もコールバック関数に渡されます。
  • コールバック
    ファイルの読み込みが完了すると、指定されたコールバック関数(Qtのシグナルとスロットの仕組みを利用)が呼び出され、読み込まれたファイルの内容を受け取ることができます。
  • ファイル選択ダイアログ
    ユーザーに対して標準のファイル選択ダイアログを表示し、読み込むファイルを選択させます。
  • 非同期処理
    ファイルの読み込みはメインスレッドとは別のスレッドで行われるため、アプリケーションの応答性を維持できます。

基本的な使い方

QFileDialog::getOpenFileContent() は静的関数なので、QFileDialog クラスのインスタンスを作成する必要はありません。以下のように呼び出します。

QFileDialog::getOpenFileContent(
    const QString &caption, // ダイアログのタイトル
    const QString &filter,  // ファイルフィルタ (例: "Text files (*.txt);;All files (*)")
    const QUrl &startLocation, // ダイアログを開く初期ディレクトリ (省略可能)
    std::function<void(const QByteArray &, const QUrl &)> slot // 成功時のコールバック
);

または、エラー処理のための別のコールバックを指定することもできます。

QFileDialog::getOpenFileContent(
    const QString &caption,
    const QString &filter,
    const QUrl &startLocation,
    std::function<void(const QByteArray &, const QUrl &)> successSlot,
    std::function<void(QFileDevice::FileError)> errorSlot // エラー時のコールバック
);

引数の説明

  • errorSlot: ファイルの読み込み中にエラーが発生した場合に呼び出される関数オブジェクトです。この関数は、発生したエラーの種類を示す QFileDevice::FileError 型の引数を受け取ります。
  • successSlot: ファイルの読み込みが成功したときに呼び出される関数オブジェクト(ラムダ式や関数ポインタなど)です。この関数は、読み込まれたファイルの内容 (QByteArray) と、選択されたファイルの QUrl を引数として受け取ります。
  • startLocation: ファイル選択ダイアログが最初に開くディレクトリを指定する QUrl オブジェクトです。省略すると、通常は最後に使用したディレクトリが開きます。
  • filter: ユーザーが選択できるファイルの種類を制限するためのフィルタです。複数のフィルタを ";;" で区切って指定できます。例えば、"画像ファイル (*.png *.jpg);;テキストファイル (*.txt);;すべてのファイル (*)" のように指定します。
  • caption: ファイル選択ダイアログのタイトルバーに表示される文字列です。


以下は、テキストファイルを選択してその内容をコンソールに出力する簡単な例です。

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

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

    QFileDialog::getOpenFileContent(
        "テキストファイルを開く",
        "テキストファイル (*.txt);;すべてのファイル (*)",
        QUrl(), // デフォルトの開始ディレクトリ
        [](const QByteArray &fileContent, const QUrl &fileUrl) {
            qDebug() << "読み込んだファイル:" << fileUrl;
            qDebug() << "内容:\n" << fileContent;
        },
        [](QFileDevice::FileError error) {
            qDebug() << "エラーが発生しました:" << error;
        }
    );

    return a.exec(); // イベントループを開始 (ダイアログが表示されるために必要)
}

このコードを実行すると、ファイル選択ダイアログが表示されます。ユーザーがテキストファイルを選択して「開く」ボタンを押すと、成功時のラムダ式が呼び出され、ファイルの内容と URL がデバッグ出力に表示されます。もしエラーが発生した場合は、エラー時のラムダ式が呼び出され、エラー内容が表示されます。



一般的なエラーとトラブルシューティング

    • 原因
      アプリケーションのイベントループが適切に実行されていない可能性があります。QApplication::exec() が呼び出されていることを確認してください。getOpenFileContent() はダイアログを表示し、ユーザーの操作を待つためにイベントループに依存します。
    • トラブルシューティング
      main() 関数内で QApplication a(argc, argv); を作成し、最後に return a.exec(); を記述しているか確認してください。GUIアプリケーションでない場合は、適切なイベントループの処理が必要になる場合があります。
  1. ファイルの内容が空 (QByteArray が空) で返ってくる

    • 原因
      • ユーザーがファイルを選択しなかった(ダイアログで「キャンセル」を押したなど)。
      • 選択されたファイルが存在しないか、パスが間違っている。
      • アプリケーションにファイルの読み取り権限がない。
      • ファイルが非常に大きく、読み込みに時間がかかっている(ただし、getOpenFileContent() は全体をメモリに読み込むため、巨大ファイルには不向きです)。
    • トラブルシューティング
      • 成功時のコールバック関数内で、QByteArray が空でないことを確認する処理を追加してください。
      • 選択されたファイルの QUrl をログ出力するなどして、正しいファイルが選択されているか確認してください。
      • ファイルシステムの権限を確認してください。
      • 非常に大きなファイルを扱う場合は、QFileQDataStreamQTextStream などを組み合わせて、少しずつ読み込む方法を検討してください。
  2. エラーコールバック関数が呼び出される

    • 原因
      ファイルの読み込み中に何らかのエラーが発生しました。エラーの種類は QFileDevice::FileError 型の値で通知されます。
    • トラブルシューティング
      • エラーコールバック関数内で、渡された QFileDevice::FileError の値を調べて、具体的なエラー内容を特定してください。一般的なエラーとしては、QFileDevice::FileNotFoundError(ファイルが見つからない)、QFileDevice::PermissionError(アクセス権がない)、QFileDevice::OpenError(ファイルを開けない)などがあります。
      • エラーメッセージをログ出力したり、ユーザーに通知したりする処理を追加してください。
  3. ファイルフィルタが期待通りに動作しない

    • 原因
      ファイルフィルタの記述が間違っている可能性があります。
    • トラブルシューティング
      • ファイルフィルタの構文 ("説明 (*.拡張子1 *.拡張子2);;他の説明 (*.拡張子3)") を確認してください。複数の拡張子を同じフィルタで指定する場合は、スペースで区切ります。複数のフィルタは ";;" で区切ります。
      • ワイルドカード (*) が正しく使用されているか確認してください。
  4. 初期ディレクトリ (startLocation) が無視される

    • 原因
      QUrl の形式が正しくないか、指定されたディレクトリが存在しない可能性があります。
    • トラブルシューティング
      • QUrl::fromLocalFile() などを使用して、ローカルファイルのパスから QUrl を作成しているか確認してください。
      • 指定したパスが存在することを確認してください。
  5. GUI スレッドに関する問題 (Qt のスレッド規則)

    • 原因
      getOpenFileContent() 自体は非同期にファイル読み込みを行いますが、その結果を処理するコールバック関数は通常、GUI スレッドで実行されます。コールバック関数内で時間のかかる処理を行うと、GUI がフリーズする可能性があります。
    • トラブルシューティング
      • コールバック関数内では、GUI の更新など、GUI スレッドで行うべき処理のみを行い、時間のかかる処理は別のスレッドに移動することを検討してください(例: QtConcurrent::run()QThread)。
  6. ダイアログの表示位置やスタイルに関する問題

    • 原因
      プラットフォームの設定や、アプリケーションのスタイルシートなどが影響している可能性があります。
    • トラブルシューティング
      • 異なるプラットフォームで挙動を確認してみてください。
      • アプリケーションのスタイルシートを確認し、意図しないスタイルが適用されていないか確認してください。
      • 親ウィジェットを指定することで、ダイアログの表示位置を制御できる場合があります(ただし、getOpenFileContent() は静的関数なので、直接的な親ウィジェットの指定はできません)。

トラブルシューティングのヒント

  • Qt のドキュメント
    Qt の公式ドキュメントは非常に充実しています。QFileDialog::getOpenFileContent() のページや、関連するクラス(QUrl, QFileDevice::FileError など)のドキュメントを参照すると、より詳細な情報が得られます。
  • シンプルなテストケース
    問題が発生しているコードをできるだけ小さく切り出したテストケースを作成し、そこで問題が再現するかどうかを確認することで、原因の特定が容易になります。
  • ログ出力
    エラーメッセージや、コールバック関数に渡される値(QByteArray のサイズ、QUrl など)をログ出力するようにすると、問題の原因を特定しやすくなります。qDebug() などのQtのデバッグ出力機能を利用すると便利です。


例1: テキストファイルの内容を読み込んで表示する (基本的な使用例)

この例では、ユーザーにテキストファイルを選択させ、その内容を QTextEdit ウィジェットに表示します。

#include <QApplication>
#include <QMainWindow>
#include <QTextEdit>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenuBar>
#include <QAction>
#include <QDebug>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        textEdit = new QTextEdit(this);
        setCentralWidget(textEdit);

        QMenuBar *menuBar = new QMenuBar(this);
        QMenu *fileMenu = menuBar->addMenu("ファイル(&F)");
        QAction *openAction = new QAction("開く(&O)", this);
        fileMenu->addAction(openAction);
        setMenuBar(menuBar);

        connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
    }

private slots:
    void openFile() {
        QFileDialog::getOpenFileContent(
            "テキストファイルを開く",
            "テキストファイル (*.txt);;すべてのファイル (*)",
            this, // 親ウィジェットを指定 (省略可能)
            [this](const QByteArray &fileContent, const QUrl &fileUrl) {
                if (!fileContent.isEmpty()) {
                    textEdit->setText(QString::fromUtf8(fileContent));
                    statusBar()->showMessage(QString("ファイルを開きました: %1").arg(fileUrl.fileName()));
                } else {
                    statusBar()->showMessage("ファイルの読み込みに失敗しました。", 5000);
                }
            },
            [this](QFileDevice::FileError error) {
                statusBar()->showMessage(QString("エラーが発生しました: %1").arg(error), 5000);
                qDebug() << "ファイル読み込みエラー:" << error;
            }
        );
    }

private:
    QTextEdit *textEdit;
};

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

この例のポイント

  • this を親ウィジェットとして渡すことで、ダイアログがメインウィンドウの中央に表示されるようになります(プラットフォームによって挙動が異なる場合があります)。
  • エラー時には、ステータスバーにエラーメッセージを表示し、デバッグ出力にエラー内容を出力します。
  • 成功時には、読み込んだ内容を QTextEdit に表示し、ステータスバーにメッセージを表示します。
  • ラムダ式を使用して、成功時とエラー時のコールバック関数をインラインで定義しています。
  • openFile() スロット内で QFileDialog::getOpenFileContent() を呼び出しています。
  • 「ファイル」メニューの「開く」アクションに openFile() スロットを接続しています。
  • QMainWindow を使用して、メニューバーとステータスバーを持つ基本的なウィンドウを作成しています。

例2: 画像ファイルの内容を読み込んで QLabel に表示する (バイナリデータの処理)

この例では、ユーザーに画像ファイルを選択させ、その内容を QLabel に表示します。

#include <QApplication>
#include <QMainWindow>
#include <QLabel>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenuBar>
#include <QAction>
#include <QImage>
#include <QPixmap>
#include <QDebug>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        imageLabel = new QLabel(this);
        imageLabel->setAlignment(Qt::AlignCenter);
        setCentralWidget(imageLabel);

        QMenuBar *menuBar = new QMenuBar(this);
        QMenu *fileMenu = menuBar->addMenu("ファイル(&F)");
        QAction *openAction = new QAction("画像を開く(&O)", this);
        fileMenu->addAction(openAction);
        setMenuBar(menuBar);

        connect(openAction, &QAction::triggered, this, &MainWindow::openImage);
    }

private slots:
    void openImage() {
        QFileDialog::getOpenFileContent(
            "画像ファイルを開く",
            "画像ファイル (*.png *.jpg *.jpeg *.gif);;すべてのファイル (*)",
            this,
            [this](const QByteArray &fileContent, const QUrl &fileUrl) {
                QImage image;
                if (image.loadFromData(fileContent)) {
                    imageLabel->setPixmap(QPixmap::fromImage(image).scaled(imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
                    statusBar()->showMessage(QString("画像を開きました: %1").arg(fileUrl.fileName()));
                } else {
                    statusBar()->showMessage("画像の読み込みに失敗しました。", 5000);
                }
            },
            [this](QFileDevice::FileError error) {
                statusBar()->showMessage(QString("エラーが発生しました: %1").arg(error), 5000);
                qDebug() << "画像読み込みエラー:" << error;
            }
        );
    }

private:
    QLabel *imageLabel;
};

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

この例のポイント

  • エラー処理はテキストファイルの例と同様に行っています。
  • QImage のロードに成功したら、QPixmap に変換し、QLabel に設定して表示します。画像のサイズに合わせてスケーリングしています。
  • 成功時のコールバック関数内で、QByteArray の内容を QImage にロードしようとしています。
  • ファイルフィルタで一般的な画像ファイル形式を指定しています。
  • QLabel を使用して画像を表示します。

例3: 複数のファイルを選択する ( getOpenFileNames の使用)

QFileDialog::getOpenFileContent() は単一のファイルの内容を読み込むためのものですが、複数のファイルを選択したい場合は QFileDialog::getOpenFileNames() を使用します。その後、選択された各ファイルに対して個別に読み込み処理を行う必要があります。

#include <QApplication>
#include <QMainWindow>
#include <QListWidget>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenuBar>
#include <QAction>
#include <QFile>
#include <QTextStream>
#include <QDebug>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        fileListWidget = new QListWidget(this);
        setCentralWidget(fileListWidget);

        QMenuBar *menuBar = new QMenuBar(this);
        QMenu *fileMenu = menuBar->addMenu("ファイル(&F)");
        QAction *openMultipleAction = new QAction("複数のファイルを開く(&M)", this);
        fileMenu->addAction(openMultipleAction);
        setMenuBar(menuBar);

        connect(openMultipleAction, &QAction::triggered, this, &MainWindow::openMultipleFiles);
    }

private slots:
    void openMultipleFiles() {
        QFileDialog dialog(this);
        dialog.setFileMode(QFileDialog::ExistingFiles);
        dialog.setNameFilter("テキストファイル (*.txt);;すべてのファイル (*)");
        if (dialog.exec()) {
            QStringList selectedFiles = dialog.selectedFiles();
            fileListWidget->clear();
            for (const QString &filePath : selectedFiles) {
                QFile file(filePath);
                if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                    QTextStream in(&file);
                    QString fileContent = in.readAll();
                    file.close();
                    fileListWidget->addItem(QString("%1:\n%2...").arg(QFileInfo(filePath).fileName()).arg(fileContent.left(50))); // 先頭50文字を表示
                } else {
                    qDebug() << "ファイルのオープンに失敗:" << filePath;
                }
            }
        }
    }

private:
    QListWidget *fileListWidget;
};

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

この例のポイント

  • 取得した各ファイルパスに対して QFile を使用してファイルを開き、内容を読み込んで QListWidget に表示しています。
  • selectedFiles() メソッドで選択されたファイルのパスのリスト (QStringList) を取得します。
  • exec() メソッドでダイアログを表示し、ユーザーがファイルを選択して「開く」ボタンを押した場合に true が返ります。
  • QFileDialog のインスタンスを作成し、setFileMode(QFileDialog::ExistingFiles) を設定することで、複数のファイルを選択できるようにしています。
  • 非同期処理の結果を GUI に反映する場合は、コールバック関数が GUI スレッドで実行されることを意識し、時間のかかる処理は別のスレッドで行うことを検討してください。
  • エラー処理は、ユーザーエクスペリエンス向上のために重要です。ファイルが存在しない、読み取り権限がないなどのエラーを適切に処理し、ユーザーに分かりやすいメッセージを表示するように心がけましょう。
  • QFileDialog::getOpenFileContent() はファイル全体をメモリに読み込むため、非常に大きなファイルに対してはメモリ使用量に注意が必要です。大きなファイルを扱う場合は、QFile とストリーム (QDataStreamQTextStream) を組み合わせて、少しずつ読み込む方法を検討してください。


QFileDialog::getOpenFileName() と QFile を組み合わせて同期的に読み込む

最も基本的な代替方法は、QFileDialog::getOpenFileName() を使用してユーザーにファイルを選択させ、そのパスを取得した後、QFile クラスを使って同期的にファイルの内容を読み込む方法です。

#include <QApplication>
#include <QMainWindow>
#include <QTextEdit>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenuBar>
#include <QAction>
#include <QDebug>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        textEdit = new QTextEdit(this);
        setCentralWidget(textEdit);

        QMenuBar *menuBar = new QMenuBar(this);
        QMenu *fileMenu = menuBar->addMenu("ファイル(&F)");
        QAction *openAction = new QAction("開く(&O)", this);
        fileMenu->addAction(openAction);
        setMenuBar(menuBar);

        connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
    }

private slots:
    void openFile() {
        QString filePath = QFileDialog::getOpenFileName(
            this,
            "テキストファイルを開く",
            "",
            "テキストファイル (*.txt);;すべてのファイル (*)"
        );

        if (!filePath.isEmpty()) {
            QFile file(filePath);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream in(&file);
                QString fileContent = in.readAll();
                file.close();
                textEdit->setText(fileContent);
                statusBar()->showMessage(QString("ファイルを開きました: %1").arg(QFileInfo(filePath).fileName()));
            } else {
                statusBar()->showMessage(QString("ファイルのオープンに失敗しました: %1").arg(file.errorString()), 5000);
                qDebug() << "ファイルのオープンに失敗:" << file.errorString();
            }
        } else {
            statusBar()->showMessage("ファイル選択がキャンセルされました。", 5000);
        }
    }

private:
    QTextEdit *textEdit;
};

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

この方法の利点

  • 大きなファイルへの対応
    必要に応じて、少しずつデータを読み込むことで、メモリ使用量を抑えられます。
  • 柔軟な読み込み
    QFile は様々な読み込みモードや操作を提供します(行ごとの読み込み、特定の部分のみ読み込みなど)。
  • 同期処理
    コードの流れが直感的で、ファイルの内容をすぐに利用できます。

この方法の欠点

  • GUI のブロッキング
    ファイルの読み込みに時間がかかると、GUI スレッドがブロックされ、アプリケーションがフリーズしたように見える可能性があります。大きなファイルを扱う場合は、別スレッドで読み込み処理を行う必要があります。

QFileDialog::getOpenFileName() と QFile、そして QtConcurrent::run() を組み合わせて非同期に読み込む

上記の同期的な読み込みの欠点を解消するために、QtConcurrent::run() を使用してファイル読み込みを別スレッドで行うことができます。

#include <QApplication>
#include <QMainWindow>
#include <QTextEdit>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenuBar>
#include <QAction>
#include <QDebug>
#include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent), watcher(new QFutureWatcher<QString>(this)) {
        textEdit = new QTextEdit(this);
        setCentralWidget(textEdit);

        QMenuBar *menuBar = new QMenuBar(this);
        QMenu *fileMenu = menuBar->addMenu("ファイル(&F)");
        QAction *openAction = new QAction("開く(&O)", this);
        fileMenu->addAction(openAction);
        setMenuBar(menuBar);

        connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
        connect(watcher, &QFutureWatcher<QString>::finished, this, &MainWindow::fileReadFinished);
        connect(watcher, &QFutureWatcher<QString>::progressValueChanged, this, &MainWindow::fileReadProgress);
    }

private slots:
    void openFile() {
        QString filePath = QFileDialog::getOpenFileName(
            this,
            "テキストファイルを開く",
            "",
            "テキストファイル (*.txt);;すべてのファイル (*)"
        );

        if (!filePath.isEmpty()) {
            statusBar()->showMessage("ファイル読み込み中...", 0);
            QFuture<QString> future = QtConcurrent::run(this, &MainWindow::readFileContent, filePath);
            watcher->setFuture(future);
        } else {
            statusBar()->showMessage("ファイル選択がキャンセルされました。", 5000);
        }
    }

    QString readFileContent(const QString &filePath) {
        QFile file(filePath);
        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            QTextStream in(&file);
            QString content = in.readAll();
            file.close();
            return content;
        } else {
            qDebug() << "ファイルのオープンに失敗:" << file.errorString();
            return QString();
        }
    }

    void fileReadFinished() {
        QString content = watcher->result();
        if (!content.isEmpty()) {
            textEdit->setText(content);
            statusBar()->showMessage(QString("ファイルを開きました: %1").arg(QFileInfo(watcher->future().property("filePath").toString())));
        } else {
            statusBar()->showMessage("ファイルの読み込みに失敗しました。", 5000);
        }
    }

    void fileReadProgress(int progress) {
        // 必要に応じてプログレスバーなどを更新
        qDebug() << "読み込み進捗:" << progress;
    }

private:
    QTextEdit *textEdit;
    QFutureWatcher<QString> *watcher;
};

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

この方法の利点

  • 進捗状況の追跡
    QFutureWatcher を使用することで、読み込みの完了や進捗状況を監視できます。
  • 大きなファイルへの対応
    readFileContent 関数内で、必要に応じて少しずつデータを読み込むように変更できます。
  • 非同期処理
    GUI スレッドをブロックせずにファイルの読み込みを行えます。

この方法の欠点

  • コードがやや複雑になる
    スレッド管理やシグナルとスロットの接続が必要になります。

QFileDialog を直接使用してファイルを開き、QFile で処理する

QFileDialog のインスタンスを作成し、exec() メソッドでダイアログを表示した後、selectedFiles() メソッドで選択されたファイルのパスを取得し、QFile で処理する方法です。これは複数のファイルを選択する場合にも適しています。

#include <QApplication>
#include <QMainWindow>
#include <QListWidget>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenuBar>
#include <QAction>
#include <QDebug>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent), fileListWidget(new QListWidget(this)) {
        setCentralWidget(fileListWidget);

        QMenuBar *menuBar = new QMenuBar(this);
        QMenu *fileMenu = menuBar->addMenu("ファイル(&F)");
        QAction *openMultipleAction = new QAction("複数のファイルを開く(&M)", this);
        fileMenu->addAction(openMultipleAction);
        setMenuBar(menuBar);

        connect(openMultipleAction, &QAction::triggered, this, &MainWindow::openMultipleFiles);
    }

private slots:
    void openMultipleFiles() {
        QFileDialog dialog(this);
        dialog.setFileMode(QFileDialog::ExistingFiles);
        dialog.setNameFilter("テキストファイル (*.txt);;すべてのファイル (*)");
        if (dialog.exec()) {
            QStringList selectedFiles = dialog.selectedFiles();
            fileListWidget->clear();
            for (const QString &filePath : selectedFiles) {
                QFile file(filePath);
                if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                    QTextStream in(&file);
                    QString fileContent = in.readAll();
                    file.close();
                    fileListWidget->addItem(QString("%1:\n%2...").arg(QFileInfo(filePath).fileName()).arg(fileContent.left(50)));
                } else {
                    qDebug() << "ファイルのオープンに失敗:" << file.errorString();
                }
            }
        }
    }

private:
    QListWidget *fileListWidget;
};

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

この方法の利点

  • 柔軟なファイル処理
    選択された各ファイルに対して、QFile を使って個別に読み込みや処理を行えます。
  • 複数のファイル選択
    QFileDialog::ExistingFiles モードを使用すると、複数のファイルを選択できます。

この方法の欠点

  • 同期処理
    ファイルの読み込みは同期的に行われるため、大きなファイルを扱う場合は GUI がブロックされる可能性があります。非同期処理が必要な場合は、QtConcurrent::run() などと組み合わせる必要があります。

カスタムのファイル選択 UI を作成する

より高度な要件がある場合は、QFileDialog を使用せずに、独自のファイル選択 UI を作成することも可能です。QFileSystemModelQTreeViewQListView などを組み合わせて、独自のファイルブラウザを作成し、ファイルの選択と読み込みを完全に制御できます。

この方法の利点

  • 特定のニーズへの対応
    特定のファイル操作やフィルタリング要件に対応できます。
  • 完全なカスタマイズ
    UI の外観や動作を完全に制御できます。
  • 標準的なファイルダイアログの利便性がない
    ユーザーは慣れない操作が必要になる可能性があります。
  • 開発コストが高い
    ゼロからファイル選択 UI を作成する必要があるため、開発に時間と労力がかかります。