QFileDialog::done()の深掘り:内部動作とカスタマイズのヒント
まず、QFileDialog
はQtにおける標準的なファイルダイアログを提供するクラスです。これは、ユーザーがファイルを開いたり、保存したり、ディレクトリを選択したりする際に使用されるGUIコンポーネントです。
done()
メソッドは、QDialog
クラス(QFileDialog
が継承しているクラス)から継承された保護された(protected)スロットです。このメソッドは、ダイアログが閉じられる際に呼び出され、ダイアログの結果コード(Accepted
または Rejected
)を設定するために使用されます。
通常、QFileDialog
を使用する際には、次のような静的関数を使うことがほとんどです。
QFileDialog::getExistingDirectory()
QFileDialog::getSaveFileName()
QFileDialog::getOpenFileName()
これらの静的関数は、内部的に QFileDialog
オブジェクトを作成し、exec()
メソッドを呼び出してモーダルダイアログとして表示し、ユーザーが選択を完了すると結果を返します。この場合、開発者が明示的に done()
を呼び出す必要はありません。
しかし、もしあなたが QFileDialog
をカスタマイズして、通常のダイアログとは異なる方法で動作させたい場合(例えば、独自のウィジェットを追加したり、ダイアログをモーダルではなく非モーダルで表示したりする場合)、自分で QFileDialog
のインスタンスを作成し、そのライフサイクルを管理する必要があります。
このようなシナリオで done()
を使う目的は以下の通りです。
-
ダイアログの終了と結果の設定
done()
メソッドは、ダイアログの表示を終了させ、その結果(ユーザーが「OK」を押した場合はQDialog::Accepted
、キャンセルした場合はQDialog::Rejected
)を返します。例えば、独自に作成した「OK」ボタンのシグナルをQFileDialog::accept()
スロットに接続したり、独自の「キャンセル」ボタンのシグナルをQFileDialog::reject()
スロットに接続したりできます。これらのスロットは内部的にdone()
を適切な引数(QDialog::Accepted
またはQDialog::Rejected
)で呼び出します。 -
プログラマティックなダイアログの終了
テスト目的や特定の自動化されたシナリオで、ユーザーの操作なしにプログラムからダイアログを閉じたい場合に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アプリケーションで再現するかどうかを確認します。これにより、問題の範囲を絞り込むことができます。
- QFileDialog::DontUseNativeDialog フラグを使用する
- 原因
- ネイティブダイアログの問題 (Windows/macOS/Linux)
Qtは可能な限りOSのネイティブファイルダイアログを使用しようとします。しかし、ネイティブダイアログの挙動が不安定な場合や、特定のファイル形式(例:特定の拡張子のファイル)を操作しようとした際に、OSレベルで問題が発生し、アプリケーションがクラッシュすることがあります。 - スレッドの問題
GUI操作はメインスレッドで行われる必要があります。別スレッドからQFileDialog
を開こうとすると、クラッシュや予期せぬ動作を引き起こす可能性があります。 - 無効なパスや初期ディレクトリ
存在しない、またはアクセス権のないパスを初期ディレクトリとして設定しようとすると問題が発生することがあります。 - 古いQtバージョンや環境の問題
使用しているQtのバージョンが古い、またはシステム環境との相性が悪い場合。
- ネイティブダイアログの問題 (Windows/macOS/Linux)
- 問題
QFileDialog
を開く際にアプリケーションがクラッシュしたり、フリーズしたりする。
ダイアログの表示がおかしい、スタイルが崩れる
- トラブルシューティング
- QFileDialog::DontUseNativeDialog フラグを試す
ネイティブダイアログのスタイリング問題であれば、Qtの組み込みダイアログを使用することで解決する場合があります。 - 環境変数の設定 (Linux)
QT_QPA_PLATFORMTHEME
環境変数を設定することで、Qtが使用するプラットフォームテーマを明示的に指定できる場合があります(例:export QT_QPA_PLATFORMTHEME=gtk2
またはgtk3
)。 - システムライブラリの確認
OSのパッケージマネージャーを使用して、Qtが依存する関連ライブラリがすべてインストールされ、最新の状態であることを確認します。 - QSSの影響調査
一時的にアプリケーションのスタイルシートを無効にして、QFileDialog
の表示が改善されるか確認します。もし改善されるなら、スタイルシートの記述に問題がある可能性があります。
- QFileDialog::DontUseNativeDialog フラグを試す
- 原因
- Qtのプラットフォームテーマの問題 (Linux)
Linux環境では、QtがGTK+やKDEなどのデスクトップ環境のテーマをうまく統合できない場合に、ダイアログの見た目が崩れることがあります。 - 古いQtバージョンやシステムライブラリの欠如
必要なシステムライブラリが不足している、またはバージョンが古い場合に発生することがあります。 - カスタムスタイルシートの影響
アプリケーション全体にカスタムのQSS(Qt Style Sheets)を適用している場合、それがQFileDialog
の表示に悪影響を与えている可能性があります。
- Qtのプラットフォームテーマの問題 (Linux)
- 問題
QFileDialog
の見た目が期待通りでなかったり、UI要素が正しく表示されない。
ファイルパスのエンコーディング問題
- トラブルシューティング
- Qtは内部的にUnicodeを使用するため、通常はエンコーディング問題は発生しにくいですが、
QString
とstd::string
の変換時や、C++の標準ライブラリ関数(fopen
など)と連携する際に問題が発生することがあります。 QFile
などQtのI/Oクラスを使用する場合は、QString
でパスを扱うことでエンコーディングの問題を回避できます。- 外部のC++ライブラリと連携する場合は、
QString::toLocal8Bit()
やQString::toUtf8()
などを使用して、適切なエンコーディングに変換してから渡すようにします。
- Qtは内部的にUnicodeを使用するため、通常はエンコーディング問題は発生しにくいですが、
- 原因
- ファイルパスのエンコーディングとシステムの期待するエンコーディングが一致しない。
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()の戻り値を使うのが一般的