Qtプログラミング入門: QFileDialog::getSaveFileName()でファイルを保存する方法

2025-05-27

QString QFileDialog::getSaveFileName()とは?

QFileDialog::getSaveFileName()は、Qtが提供する静的(static)関数の一つで、ユーザーに「ファイルを保存する場所とファイル名」を選択させるための標準的なダイアログを表示するために使用されます。この関数を呼び出すと、「名前を付けて保存」のようなダイアログボックスが画面に表示され、ユーザーは既存のディレクトリを参照したり、新しいファイル名を入力したりすることができます。

ユーザーがダイアログで「保存」ボタンをクリックすると、選択されたファイルの完全なパス(ディレクトリパス+ファイル名)がQString型で返されます。ユーザーが「キャンセル」ボタンをクリックした場合、またはファイルを何も選択せずにダイアログを閉じた場合は、空のQStringが返されます。

QFileDialog::getSaveFileName()にはいくつかのオーバーロードがありますが、よく使われる一般的なシグネチャは以下のようになります。

QString QFileDialog::getSaveFileName(QWidget *parent = nullptr,
                                     const QString &caption = QString(),
                                     const QString &dir = QString(),
                                     const QString &filter = QString(),
                                     QString *selectedFilter = nullptr,
                                     Options options = Options());

各引数の意味は以下の通りです。



QFileDialog::getSaveFileName()は便利ですが、特定の状況下で問題が発生することがあります。

ダイアログが表示されない、またはハングする(フリーズする)

これは比較的よく報告される問題で、特にWindows環境でネイティブダイアログを使用している場合に発生することがあります。

  • トラブルシューティング

    • QFileDialog::DontUseNativeDialogオプションの使用
      QFileDialog::getSaveFileName(..., QFileDialog::DontUseNativeDialog) このオプションを使用すると、OSネイティブのダイアログではなく、Qtが独自に描画するダイアログが使用されます。見た目が若干異なる場合がありますが、シェル拡張機能による問題を回避できる可能性が高いです。
    • 初期ディレクトリの確認
      dir引数には、確実に存在する、かつアプリケーションがアクセス権を持つパスを指定してください。QDir::homePath()QDir::currentPath()など、安全なパスを使用することをお勧めします。
    • メインスレッドでの呼び出し
      QFileDialog::getSaveFileName()は、常にメイン(GUI)スレッドから呼び出すようにしてください。バックグラウンドスレッドから呼び出すと、予期せぬ動作やクラッシュの原因になります。
    • 必要なDLLの確認(Windows)
      windeployqtツールを使用して、リリースビルドに必要なDLLがすべて揃っているか確認してください。
    • サードパーティ製ソフトウェアの無効化/アンインストール
      競合が疑われるソフトウェア(特にバックアップ、同期、セキュリティ関連)を一時的に無効化またはアンインストールして、問題が解消するかどうか確認します。これは原因特定に役立ちます。
    • シェル拡張機能の競合
      特定のサードパーティ製ソフトウェア(例: クラウドストレージの同期ツール、バックアップソフトウェア、セキュリティソフトなど)がインストールしているシェル拡張機能が、Qtのファイルダイアログと競合することがあります。これらはOSのファイルダイアログの動作に影響を与える可能性があります。
    • マルチスレッドの問題
      メインスレッド以外からQFileDialog::getSaveFileName()を呼び出そうとしている場合、UIスレッドがブロックされたり、ダイアログが正しく表示されなかったりすることがあります。QtのGUI操作は基本的にメインスレッドで行う必要があります。
    • DLLの不足(リリースビルドの場合)
      リリースビルドでアプリケーションを配布する際、必要なQtのDLL(特にGUIやプラットフォーム関連のDLL)が不足していると、ダイアログの表示に失敗することがあります。
    • 無効な初期ディレクトリ
      dir引数に存在しない、またはアクセス権のないディレクトリを指定した場合、ダイアログの表示に問題が生じることがあります。

ファイル拡張子が自動で付加されない、または意図しない拡張子になる

ユーザーがファイル名を入力した際に、指定したフィルタに基づいて拡張子が自動で付加されない、または間違った拡張子が付加されることがあります。

  • トラブルシューティング

    • ユーザーが選択したフィルタからの拡張子取得
      QFileDialog::getSaveFileName()selectedFilter引数を使用し、ユーザーがどのファイルタイプを選択したかを取得します。
      QString selectedFilter;
      QString fileName = QFileDialog::getSaveFileName(this,
                                                      tr("ファイルを保存"),
                                                      QDir::homePath(),
                                                      tr("テキストファイル (*.txt);;CSVファイル (*.csv);;すべてのファイル (*.*)"),
                                                      &selectedFilter); // ここで選択されたフィルタを受け取る
      
      if (!fileName.isEmpty()) {
          // ユーザーが拡張子を入力していない場合、選択されたフィルタに基づいて拡張子を付加
          QFileInfo fileInfo(fileName);
          if (fileInfo.suffix().isEmpty()) {
              if (selectedFilter.contains("*.txt")) {
                  fileName += ".txt";
              } else if (selectedFilter.contains("*.csv")) {
                  fileName += ".csv";
              }
              // 必要に応じて他のフィルタにも対応
          }
          qDebug() << "最終的なファイル名: " << fileName;
          // ... ファイル書き込み処理
      }
      
    • QFileDialog::setDefaultSuffix()の使用(ただし静的関数では注意)
      QFileDialogのインスタンスを生成して使用する場合、setDefaultSuffix()を呼び出すことでデフォルトの拡張子を設定できます。しかし、QFileDialog::getSaveFileName()は静的関数なので、この方法は直接適用できません。 静的関数で拡張子の自動付加を確実に行いたい場合は、上記のselectedFilterを使った方法で、取得したファイル名に不足している拡張子をプログラムで付加する処理を実装するのが一般的です。
    • QFileDialog::DontUseNativeDialogの使用
      これにより、Qtが拡張子をより一貫して処理するようになる場合があります。
  • 考えられる原因

    • ネイティブダイアログと非ネイティブダイアログの挙動の違い
      OSネイティブのダイアログとQtの非ネイティブダイアログでは、拡張子の自動付加の挙動が異なる場合があります。特に、ネイティブダイアログはOSの仕様に従うため、Qt側の制御が及ばないことがあります。
    • フィルタの指定方法
      フィルタ文字列の記述が正確でないと、期待通りに動作しないことがあります。
    • selectedFilter引数の誤解
      selectedFilterは、ユーザーが選択したフィルタの文字列を返すものであり、それ自体がファイル名に拡張子を付加するわけではありません。

空の文字列が返される(ユーザーがキャンセルしていないのに)

ユーザーが「保存」ボタンをクリックしたにもかかわらず、空の文字列が返されることがあります。

  • トラブルシューティング

    • 返されたファイル名の確認
      まず、!fileName.isEmpty()で、ユーザーがキャンセルしたかどうかを確実にチェックしてください。
    • ログ出力
      開発中にqDebug()などを利用して、QFileDialog::getSaveFileName()が返した生の文字列を確認し、何が問題なのかを特定します。
    • パスの検証
      QDir::exists()QFile::exists()などを用いて、返されたパスが有効かどうか、またそのパスへの書き込みが可能かどうかをアプリケーション側で確認します。
  • 考えられる原因

    • 無効なファイル名
      ユーザーがOSで許可されていない文字をファイル名に使用した場合や、ファイル名が空の場合。
    • アクセス権の問題
      指定されたディレクトリへの書き込み権限がない場合。ただし、この場合通常はエラーメッセージがダイアログから表示されるはずです。

クラッシュする

アプリケーションがQFileDialog::getSaveFileName()の呼び出し時にクラッシュする場合があります。

  • トラブルシューティング

    • parentポインタの確認
      parent引数には、有効なウィジェットのポインタを渡していることを確認してください。メインウィンドウなど、アプリケーションのライフサイクルを通じて存在し続けるオブジェクトを推奨します。
    • デバッガの使用
      クラッシュが発生した場合、デバッガ(GDB, Visual Studio Debuggerなど)を使用してコールスタックを確認し、クラッシュの原因となっているコードの正確な場所を特定します。
    • 最小限の再現コード
      問題が再現する最小限のコードを作成し、そのコードで同じ問題が発生するか確認します。これにより、問題の範囲を絞り込むことができます。
    • Qtのバージョンアップ/ダウングレード
      特定のQtバージョンで問題が発生している場合、別のバージョンを試すことで解決する可能性があります。
    • メモリデバッグツール
      メモリ破損が疑われる場合、Valgrind (Linux) や Application Verifier (Windows) などのメモリデバッグツールを使用して、アプリケーションのメモリ使用状況を監視します。
  • 考えられる原因

    • 無効なparentポインタ
      parent引数に無効なQWidgetポインタを渡した場合。特に、親ウィンドウがすでに破棄されている場合など。
    • メモリ破損
      アプリケーションの他の部分でメモリ破損が発生しており、それがダイアログの呼び出しに影響を与えている可能性。
    • Qtバージョンのバグ
      ごく稀に、使用しているQtの特定のバージョンにファイルダイアログ関連のバグが存在することがあります。


例1: 最も基本的な使用法

これは、最小限の引数でダイアログを表示する例です。

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

class BasicSaveDialogWindow : public QMainWindow
{
    Q_OBJECT // Qtのメタオブジェクトシステムを使用するために必要

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

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

private slots:
    void saveFileBasic()
    {
        // 親ウィンドウ (this), ダイアログのタイトル, 初期ディレクトリ, ファイルフィルタ
        QString fileName = QFileDialog::getSaveFileName(this,
                                                        tr("ファイルを保存"),
                                                        "", // 初期ディレクトリ: 空文字列で現在の作業ディレクトリ
                                                        ""); // ファイルフィルタ: 空文字列ですべてのファイルを表示

        if (!fileName.isEmpty()) {
            qDebug() << "選択されたファイル名 (基本): " << fileName;
            // ここでファイルにデータを書き込む処理を実装
        } else {
            qDebug() << "ファイル保存がキャンセルされました (基本)。";
        }
    }
};

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

#include "main.moc" // mocファイルをインクルード(Q_OBJECTを使用する場合に必要)

解説

  • "" (ファイルフィルタ): 空文字列を指定すると、ファイルの種類でフィルタリングされず、すべてのファイルが表示されます。
  • "" (初期ディレクトリ): 空文字列を指定すると、通常はアプリケーションの現在の作業ディレクトリが開かれます。
  • this: 親ウィジェットを指定します。これにより、ダイアログは親ウィンドウに対してモーダルに(親ウィンドウを操作できなくする)表示され、親ウィンドウの中央に配置される傾向があります。

例2: ファイルフィルタと初期ディレクトリの指定

特定の種類のファイルのみを表示し、特定のディレクトリからダイアログを開始する例です。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QFileDialog>
#include <QDebug>
#include <QDir> // QDir::homePath() を使用するため

class FilteredSaveDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    FilteredSaveDialogWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        QPushButton *saveButton = new QPushButton("ファイルを保存 (フィルタ)", this);
        setCentralWidget(saveButton);
        connect(saveButton, &QPushButton::clicked, this, &FilteredSaveDialogWindow::saveFileFiltered);
    }

private slots:
    void saveFileFiltered()
    {
        QString initialDir = QDir::homePath(); // ユーザーのホームディレクトリ
        QString filter = tr("テキストファイル (*.txt);;CSVファイル (*.csv);;すべてのファイル (*.*)");

        QString fileName = QFileDialog::getSaveFileName(this,
                                                        tr("データを保存"),
                                                        initialDir,
                                                        filter);

        if (!fileName.isEmpty()) {
            qDebug() << "選択されたファイル名 (フィルタ): " << fileName;
            // ここでファイルにデータを書き込む処理
            // 例えば、選択されたフィルタに基づいて拡張子をチェックする
            if (!fileName.endsWith(".txt") && !fileName.endsWith(".csv")) {
                // ユーザーが拡張子を入力しなかった場合、または誤った拡張子の場合
                // 簡易的にデフォルトの拡張子を付加する処理を追加することもできます
                // fileName += ".txt"; // 例
                qDebug() << "警告: 選択されたファイルに適切な拡張子がない可能性があります。";
            }
        } else {
            qDebug() << "ファイル保存がキャンセルされました (フィルタ)。";
        }
    }
};

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

#include "main.moc"

解説

  • filter文字列:
    • "テキストファイル (*.txt)": 拡張子が.txtのテキストファイルのみを表示します。括弧内の記述がダイアログのプルダウンメニューに表示されます。
    • ;;: 複数のフィルタを区切るために使用します。
    • "CSVファイル (*.csv)": .csvファイル用のフィルタ。
    • "すべてのファイル (*.*)": すべてのファイルを表示する一般的なフィルタ。これは通常、リストの最後に含めます。
  • QDir::homePath(): 現在のユーザーのホームディレクトリのパスを取得します。これにより、ダイアログはユーザーにとって馴染みのある場所から開かれます。

例3: 選択されたフィルタの取得とネイティブダイアログの制御

ユーザーがどのファイルフィルタを選択したかを取得し、必要に応じてOSネイティブのダイアログの使用を無効にする例です。

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QFileDialog>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>

class AdvancedSaveDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    AdvancedSaveDialogWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        QPushButton *saveButton = new QPushButton("ファイルを保存 (詳細)", this);
        setCentralWidget(saveButton);
        connect(saveButton, &QPushButton::clicked, this, &AdvancedSaveDialogWindow::saveFileAdvanced);
    }

private slots:
    void saveFileAdvanced()
    {
        QString initialFileName = QDir::homePath() + "/untitled.txt"; // 初期ファイル名
        QString filter = tr("テキストファイル (*.txt);;XMLファイル (*.xml);;すべてのファイル (*.*)");
        QString selectedFilter; // ユーザーが選択したフィルタがここに格納される

        // QFileDialog::DontUseNativeDialog を追加して、Qt独自のダイアログを使用する
        QString fileName = QFileDialog::getSaveFileName(this,
                                                        tr("文書を保存"),
                                                        initialFileName,
                                                        filter,
                                                        &selectedFilter, // ここで選択されたフィルタを受け取る
                                                        QFileDialog::DontUseNativeDialog); // ネイティブダイアログを強制的に無効化

        if (!fileName.isEmpty()) {
            qDebug() << "選択されたファイル名 (詳細): " << fileName;
            qDebug() << "選択されたフィルタ: " << selectedFilter;

            // ユーザーが拡張子を入力しなかった場合、選択されたフィルタに基づいて拡張子を付加
            QFileInfo fileInfo(fileName);
            if (fileInfo.suffix().isEmpty()) {
                if (selectedFilter.contains("*.txt")) {
                    fileName += ".txt";
                } else if (selectedFilter.contains("*.xml")) {
                    fileName += ".xml";
                }
                // もし「すべてのファイル (*.*)」が選択された場合は、
                // デフォルトの拡張子を付加するなど、別のロジックを検討
                qDebug() << "拡張子を付加しました: " << fileName;
            }

            // 実際にファイルに書き込む例
            QFile file(fileName);
            if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                QTextStream out(&file);
                out << "これは保存されるサンプルデータです。\n";
                out << "選択されたフィルタ: " << selectedFilter << "\n";
                file.close();
                QMessageBox::information(this, tr("保存完了"), tr("ファイルが正常に保存されました。"));
            } else {
                QMessageBox::warning(this, tr("保存エラー"), tr("ファイルを保存できませんでした: ") + file.errorString());
            }

        } else {
            qDebug() << "ファイル保存がキャンセルされました (詳細)。";
        }
    }
};

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

#include "main.moc"
  • ファイル書き込み処理: 実際に取得したファイルパスを使ってQFileQTextStreamを用いてファイルにデータを書き込む具体的な例を示しています。エラーハンドリングとしてQMessageBoxを使用しています。
  • QFileDialog::DontUseNativeDialog: このオプションを渡すと、QtはOS標準のファイルダイアログではなく、Qtが独自に実装したファイルダイアログを表示します。これにより、OSのバージョンや設定による表示の問題や、一部のサードパーティ製ソフトウェアとの競合を避けることができます。見た目はOS標準と若干異なる場合があります。
  • &selectedFilter: QString型のポインタを渡すことで、ユーザーがダイアログで最終的に選択したフィルタの文字列(例: "テキストファイル (*.txt)")を取得できます。これを利用して、ファイル名の拡張子が正しいか検証したり、必要に応じて自動付加したりするロジックを実装できます。
  • initialFileName: ダイアログを開いたときにファイル名の入力欄に表示される初期値を設定できます。パス全体を指定することも、ファイル名のみを指定することも可能です。


QFileDialogクラスのインスタンスを生成して使用する

QFileDialog::getSaveFileName()は静的関数であり、手軽に使える反面、一部の細かいカスタマイズ(例:プレビューウィジェットの追加、特定のUI要素の変更)には限界があります。より詳細な制御が必要な場合は、QFileDialogのオブジェクトを直接生成して使用します。

特徴

  • open()スロットを使用して、非同期でダイアログを表示し、結果をシグナルで受け取ることができる(非同期処理が必要な場合)。
  • setViewMode()で、ファイルリストの表示形式(リスト形式、詳細形式)を設定できる。
  • setDefaultSuffix()で、ユーザーが拡張子を入力しなかった場合のデフォルト拡張子を設定できる(ただし、これはQt独自のダイアログを使用する場合にのみ有効なことが多い)。
  • setFileMode()で、ファイルの選択モード(既存ファイル、任意のファイル、ディレクトリのみなど)を明示的に設定できる。
  • より柔軟なカスタマイズが可能。

コード例

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QFileDialog>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>

class InstanceSaveDialogWindow : public QMainWindow
{
    Q_OBJECT

public:
    InstanceSaveDialogWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        QPushButton *saveButton = new QPushButton("ファイルを保存 (インスタンス)", this);
        setCentralWidget(saveButton);
        connect(saveButton, &QPushButton::clicked, this, &InstanceSaveDialogWindow::saveFileUsingInstance);
    }

private slots:
    void saveFileUsingInstance()
    {
        QFileDialog dialog(this); // QFileDialogのインスタンスを生成
        dialog.setWindowTitle(tr("ドキュメントを保存"));
        dialog.setFileMode(QFileDialog::AnyFile); // どんなファイル名でも受け付ける(保存ダイアログ向け)
        dialog.setAcceptMode(QFileDialog::AcceptSave); // 保存モードであることを明示
        dialog.setNameFilter(tr("テキストファイル (*.txt);;すべてのファイル (*.*)"));
        dialog.setDefaultSuffix("txt"); // ユーザーが拡張子を入力しない場合のデフォルト
        dialog.setDirectory(QDir::homePath()); // 初期ディレクトリを設定
        dialog.setOption(QFileDialog::DontUseNativeDialog, false); // オプションの例: ネイティブダイアログを強制する/しない

        // ダイアログを表示し、結果を取得
        if (dialog.exec() == QDialog::Accepted) { // exec() はモーダルダイアログを表示し、結果を返す
            QStringList selectedFiles = dialog.selectedFiles(); // 選択されたファイルのリスト
            if (!selectedFiles.isEmpty()) {
                QString fileName = selectedFiles.at(0); // 保存ダイアログでは通常1つ

                qDebug() << "選択されたファイル名 (インスタンス): " << fileName;

                // ここでファイルにデータを書き込む処理を実装
                QFile file(fileName);
                if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                    QTextStream out(&file);
                    out << "これはインスタンスから保存されたデータです。\n";
                    file.close();
                    QMessageBox::information(this, tr("保存完了"), tr("ファイルが正常に保存されました。"));
                } else {
                    QMessageBox::warning(this, tr("保存エラー"), tr("ファイルを保存できませんでした: ") + file.errorString());
                }
            }
        } else {
            qDebug() << "ファイル保存がキャンセルされました (インスタンス)。";
        }
    }
};

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

#include "main.moc"

QFileDialogインスタンスを使用するメリット

  • 非同期操作
    open()スロットとシグナル(例: fileSelected)を組み合わせることで、ダイアログを非同期で表示し、UIスレッドをブロックしないようにすることも可能です。
  • イベント処理
    fileSelected()directoryEntered()などのシグナルに接続して、ユーザーの操作に応じて動的に処理を行うことができます。
  • 詳細な設定
    setFileMode(), setDefaultSuffix(), setViewMode()など、静的関数では提供されないメソッドでダイアログの挙動をより細かく制御できます。

完全にカスタムなファイル保存ダイアログを実装する

QFileDialogの機能では不十分な場合や、アプリケーションのデザインガイドラインに完全に合致する独自のUIが必要な場合は、QDialogを継承してゼロからファイル保存ダイアログを実装する方法があります。

特徴

  • プレビュー機能、メタデータ表示、独自のフィルタリングロジックなど、高度な機能を組み込める。
  • ファイルシステムへのアクセスにはQDirクラスなどを活用する。
  • UIのレイアウト、見た目、機能のすべてを完全に制御できる。

デメリット

  • OSのファイルダイアログが提供するアクセシビリティ機能や、ユーザーが慣れている操作性を損なう可能性がある。
  • ファイルシステム操作(ディレクトリの移動、ファイルの一覧表示、権限チェックなど)のロジックをすべて自前で実装する必要がある。
  • 開発コストが非常に高い。

基本的な実装のアイデア(コード例は省略、概念的な説明)

  1. QDialogのサブクラスを作成
    class CustomSaveDialog : public QDialog
    {
        Q_OBJECT
    public:
        CustomSaveDialog(QWidget *parent = nullptr);
        QString selectedFileName() const { return m_selectedFileName; }
    
    private slots:
        void browseButtonClicked();
        void saveButtonClicked();
        void cancelButtonClicked();
    
    private:
        QLineEdit *m_fileNameEdit;
        QLineEdit *m_directoryEdit;
        QTreeView *m_fileView; // ファイルリスト表示用
        QFileSystemModel *m_fileSystemModel; // ファイルシステムを操作するモデル
        QPushButton *m_browseButton;
        QPushButton *m_saveButton;
        QPushButton *m_cancelButton;
        QString m_selectedFileName;
    };
    
  2. UIコンポーネントの配置
    QLineEdit(ファイル名入力、ディレクトリパス表示)、QTreeView(ファイル・ディレクトリリスト)、QPushButton(保存、キャンセル、参照)などを配置し、適切なレイアウトマネージャーで整理します。
  3. ファイルシステムモデルの利用
    • QFileSystemModelを使用して、QTreeViewにファイルシステムの内容を効率的に表示します。
    • setRootPath()で表示するルートディレクトリを設定し、setNameFilters()などで表示するファイルをフィルタリングします。
  4. ロジックの実装
    • 「参照」ボタンでディレクトリ選択ダイアログ(QFileDialog::getExistingDirectory())を表示し、選択されたディレクトリをm_directoryEditに設定します。
    • 「保存」ボタンがクリックされたら、m_directoryEditm_fileNameEditからパスを構築し、m_selectedFileNameに格納してaccept()を呼び出します。
    • ファイル名やディレクトリの存在、書き込み権限などの検証ロジックを実装します。

カスタムダイアログを実装するべきケース

  • アプリケーション全体のデザインと完璧に一致させたい場合。
  • ファイル保存以外の、特定のメタデータ入力や、保存前後の独自のワークフローをダイアログ内に統合したい場合。
  • 標準のQFileDialogでは実現できない、非常に特殊なUI要件がある場合。

設定ファイルや最近使用したファイルリストを利用する

ユーザーが毎回ファイルダイアログを開く必要がないように、アプリケーションが過去に保存したパスを記憶したり、「最近使用したファイル」リストを提供したりすることも、代替手段として考えられます。

特徴

  • ファイルのパスを直接入力するQLineEditと「保存」ボタンのみで済む場合がある。
  • ユーザーエクスペリエンスの向上。

コード例(概念的)

// アプリケーション設定に最後の保存パスを記憶
QSettings settings("MyCompany", "MyApp");
QString lastSavePath = settings.value("LastSavePath", QDir::homePath()).toString();

// ファイルパス入力用のQLineEdit
QLineEdit *fileNameLineEdit = new QLineEdit(lastSavePath + "/MyDocument.txt");

// 保存ボタン
QPushButton *saveButton = new QPushButton("保存");
connect(saveButton, &QPushButton::clicked, [=]() {
    QString fileName = fileNameLineEdit->text();
    if (!fileName.isEmpty()) {
        // ファイルに書き込む処理
        // ...
        // 保存成功後、パスを設定に保存
        QFileInfo fileInfo(fileName);
        settings.setValue("LastSavePath", fileInfo.dir().path());
        QMessageBox::information(this, tr("保存"), tr("ファイルを保存しました: ") + fileName);
    } else {
        QMessageBox::warning(this, tr("エラー"), tr("ファイル名を入力してください。"));
    }
});

// QAction を使って「最近使用したファイル」メニューを作成
QMenu *recentFilesMenu = new QMenu(tr("最近のファイル"), this);
// 設定などから最近のファイルリストを読み込み、QAction を作成してメニューに追加
// connect(action, &QAction::triggered, this, &MyWindow::openRecentFile);
  • QFileInfo: ファイルのパスからファイル名、拡張子、ディレクトリなどの情報を取得するのに便利。
  • QSettings: アプリケーションの設定を保存・読み込みするためのクラス。
  • ユーザーの利便性を高めるために
    アプリケーションの設定で最近使用したパスを記憶したり、最近のファイルリストを提供したりするのも有効な代替手段です。
  • UIを完全にカスタマイズしたい、または標準ダイアログでは不可能な機能が必要な場合
    QDialogを継承して独自のダイアログを実装することを検討します。ただし、これは高い開発コストと労力を伴います。
  • より細かい設定(デフォルト拡張子、プレビュー、特定のオプションなど)が必要な場合
    QFileDialogのインスタンスを生成してメソッドを呼び出す方法が適しています。
  • 簡単なケースや標準的な挙動で十分な場合
    QString QFileDialog::getSaveFileName() (静的関数) が最も手軽で推奨されます。