Qtファイルダイアログのアイコン表示を極める:QFileDialog::setIconProvider()の代替アプローチ

2025-05-27

QFileDialog::setIconProvider() とは?

QFileDialog::setIconProvider()は、Qtのファイルダイアログ(ファイルを保存したり開いたりする際に表示されるウィンドウ)において、ファイルやディレクトリのアイコンをカスタマイズするために使用される関数です。

通常、ファイルダイアログはオペレーティングシステム(Windows, macOS, Linuxなど)が提供する標準的なアイコンを使用します。しかし、特定の種類のファイルに独自のアイコンを表示させたい場合や、アプリケーションのテーマに合わせてアイコンを変更したい場合などには、この関数が非常に役立ちます。

使い方

setIconProvider()には、QFileIconProviderクラスのインスタンスを渡します。QFileIconProviderは、ファイルの種類に応じてアイコンを提供する役割を担う抽象クラスです。

独自のアイコンを設定するには、QFileIconProviderを継承したカスタムクラスを作成し、その中のicon()仮想関数をオーバーライドします。このicon()関数の中で、特定のファイルパスやファイル情報に基づいて、表示したいQIconオブジェクトを返します。


#include <QApplication>
#include <QFileDialog>
#include <QFileIconProvider>
#include <QIcon>
#include <QFile>
#include <QFileInfo>

// カスタムアイコンプロバイダの定義
class CustomIconProvider : public QFileIconProvider
{
public:
    virtual QIcon icon(const QFileInfo &info) const override
    {
        // 拡張子が ".myext" のファイルにカスタムアイコンを設定
        if (info.isFile() && info.suffix() == "myext") {
            return QIcon(":/icons/my_custom_icon.png"); // リソースファイルからアイコンを読み込む
        }
        // それ以外のファイルやディレクトリはデフォルトのアイコンを使用
        return QFileIconProvider::icon(info);
    }
};

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

    QFileDialog dialog;

    // カスタムアイコンプロバイダのインスタンスを作成し、設定
    CustomIconProvider *provider = new CustomIconProvider();
    dialog.setIconProvider(provider);

    dialog.setFileMode(QFileDialog::AnyFile);
    dialog.setNameFilter("My Extension Files (*.myext);;All Files (*)");

    if (dialog.exec()) {
        QString selectedFile = dialog.selectedFiles().first();
        qDebug() << "選択されたファイル: " << selectedFile;
    }

    // providerはQFileDialogが所有するので、deleteする必要はありません。
    // QFileDialogが破棄されるときに自動的に解放されます。

    return a.exec();
}
  1. CustomIconProviderというQFileIconProviderを継承したクラスを作成します。
  2. icon()関数をオーバーライドし、引数で渡されるQFileInfoオブジェクト(ファイルやディレクトリの情報)を基に、独自のロジックでアイコンを決定します。
  3. この例では、ファイルが通常のファイルであり、かつ拡張子が".myext"である場合に、:/icons/my_custom_icon.pngというリソースファイルから読み込んだアイコンを返します。
  4. それ以外のファイルやディレクトリについては、基底クラスのQFileIconProvider::icon(info)を呼び出すことで、Qtが提供するデフォルトのアイコンを使用させます。
  5. main関数内でQFileDialogのインスタンスを作成し、作成したCustomIconProviderのインスタンスをsetIconProvider()に渡して設定します。
  • パフォーマンス
    icon()関数は、ファイルダイアログが表示される際に多くのファイルやディレクトリに対して呼び出される可能性があるため、重い処理を行わないように注意が必要です。アイコンの読み込みなどを頻繁に行うとパフォーマンスに影響が出る可能性があります。
  • ネイティブダイアログとの互換性
    QFileDialogは、デフォルトでOSのネイティブなファイルダイアログを使用しようとします(QFileDialog::DontUseNativeDialogオプションを設定しない限り)。ネイティブダイアログを使用する場合、setIconProvider()の設定は無視される可能性があります。カスタムアイコンを確実に適用したい場合は、dialog.setOption(QFileDialog::DontUseNativeDialog);を設定して、Qtウィジェットベースのダイアログを強制的に使用させる必要があります。
  • リソースファイル
    例で示した:/icons/my_custom_icon.pngのように、カスタムアイコンは通常、Qtのリソースシステムに組み込まれた画像ファイルを使用すると便利です。


QFileDialog::setIconProvider()は非常に強力な機能ですが、いくつかの一般的な落とし穴があります。

アイコンが全く表示されない、またはデフォルトのアイコンのまま表示される

これは最もよくある問題です。

原因とトラブルシューティング

  • リソースファイルが見つからない/正しくないパス

    • 原因
      カスタムアイコンをQtのリソースシステムから読み込もうとしているが、リソースファイル(.qrc)が正しく設定されていない、またはアイコンのパスが間違っている。
    • 解決策
      • .qrcファイルがプロジェクトに正しく追加され、ビルドされていることを確認します。
      • .qrcファイル内でアイコンのエイリアス(例: <file alias="my_custom_icon.png">images/my_custom_icon.png</file>)が正しく設定されているか確認し、コード内のパス(例: ":/icons/my_custom_icon.png")と一致しているか確認します。
      • QFile::exists(":/icons/my_custom_icon.png")などで実行時にアイコンファイルが存在するか確認できます。
  • QFileIconProvider::icon() の実装ミス

    • 原因
      カスタムQFileIconProvidericon()関数内で、条件分岐が正しくない、または常にデフォルトのアイコンを返している、もしくは無効なQIconを返している。
    • 解決策
      • icon()関数内で、デバッグ出力(qDebug()など)を使用して、どのファイルやディレクトリに対してicon()が呼び出されているか、そしてどのようなQIconが返されているかを確認します。
      • 特定の条件に合致した場合にのみカスタムアイコンを返し、それ以外の場合は必ずQFileIconProvider::icon(info);を呼び出して、デフォルトのアイコンを返してください。これを怠ると、他のファイルタイプやディレクトリのアイコンが表示されなくなる可能性があります。
  • QFileIconProviderのインスタンスのライフサイクル

    • 原因
      setIconProvider()に渡したQFileIconProviderのインスタンスが、QFileDialogが使用する前に解放されてしまうと、正しく機能しません。

    • 解決策
      QFileDialogQFileIconProviderのポインタを渡すと、QFileDialogがそのオブジェクトの所有権を引き継ぎ、適切に破棄します。したがって、手動でdeleteする必要はありません。もしスタックにオブジェクトを作成してsetIconProvider()に渡す(例: dialog.setIconProvider(&myProvider);)場合、myProviderがスコープを抜けて破棄される前にdialogがアイコンを使用できるように、ライフサイクルを慎重に管理する必要があります。通常はヒープに作成し、QFileDialogに所有権を渡す方法が推奨されます。

      // 推奨される方法: ヒープに作成し、QFileDialogに所有権を渡す
      CustomIconProvider *provider = new CustomIconProvider();
      dialog.setIconProvider(provider); 
      // providerはdialogが破棄されるときに自動的に解放される
      
    • 原因
      QtのQFileDialogは、デフォルトでOSのネイティブなファイルダイアログ(Windowsならエクスプローラー風のダイアログ、macOSならFinder風のダイアログ)を使用しようとします。ネイティブダイアログは、QtのQFileIconProviderによるカスタムアイコンの描画をサポートしていないため、設定したアイコンが表示されません。

    • 解決策
      QFileDialog::DontUseNativeDialogオプションを設定して、Qtウィジェットベースのダイアログを強制的に使用させます。

      QFileDialog dialog;
      // カスタムアイコンプロバイダを設定
      dialog.setIconProvider(new CustomIconProvider()); // CustomIconProviderは前回の説明のカスタムクラス
      // ネイティブダイアログの使用を無効にする
      dialog.setOption(QFileDialog::DontUseNativeDialog);
      
      // ダイアログを表示
      if (dialog.exec()) {
          // ...
      }
      
    • 注意点
      DontUseNativeDialogを使用すると、ダイアログの外観や挙動がOSの標準とは異なるものになります。アプリケーションの統一感を重視する場合は考慮が必要です。

パフォーマンスの問題

ファイルダイアログの表示が遅い、または反応が鈍いと感じる場合があります。

原因とトラブルシューティング

  • ネットワークドライブやリムーバブルメディアでの動作

    • 原因
      ネットワークドライブやリムーバブルメディア上のファイルに対してicon()が呼び出されると、I/Oの遅延によりパフォーマンスが大幅に低下することがあります。
    • 解決策
      QFileDialog::DontUseCustomDirectoryIconsオプションを設定することで、カスタムのディレクトリアイコンの使用を無効にし、パフォーマンスへの影響を軽減できます。これにより、QFileIconProvider::DontUseCustomDirectoryIconsオプションが内部的に設定されます。
  • icon() 関数の重い処理

    • 原因
      QFileIconProvider::icon()関数は、ダイアログに表示されるすべてのファイルやディレクトリに対して呼び出される可能性があります。この関数内で、ディスクI/Oが伴うような重い処理(例: 大量の画像ファイルの解析、ネットワークアクセスなど)を行うと、パフォーマンスが著しく低下します。
    • 解決策
      • icon()関数は可能な限り軽量に保ちます。
      • アイコンのロードは一度だけ行い、キャッシュするなどの工夫をします。QIcon自体が内部でキャッシュ機構を持っていることが多いですが、カスタムアイコンの場合、自分でキャッシュを管理することを検討してください。
      • 必要であれば、別のスレッドでアイコンの読み込みを行い、完了後にメインスレッドに通知して更新するなどの非同期処理を検討しますが、QFileIconProviderの設計上、これはより複雑になります。一般的には、icon()関数内でのファイル読み込みは避けるべきです。リソースファイルから読み込むのが最も高速です。

クラッシュや未定義動作

まれに、アプリケーションがクラッシュしたり、予期しない動作をしたりする場合があります。

原因とトラブルシューティング

  • QFileInfoの誤った使用

    • 原因
      QFileInfoオブジェクトが参照しているファイルやディレクトリが存在しない、またはアクセス権がない場合に、その情報に基づいて処理を行うと問題が発生する可能性があります。
    • 解決策
      QFileInfo::exists()QFileInfo::isReadable()などで事前に存在やアクセス権を確認することを検討してください。
  • 無効なQIconの返却

    • 原因
      icon()関数から無効なQIconオブジェクト(例えば、ロードに失敗した画像など)を返すと、描画エンジンがクラッシュする可能性があります。

    • 解決策
      QIconが有効であることを確認し、ロードに失敗した場合は、フォールバックとしてデフォルトのアイコンを返すようにします。

      QIcon myIcon(":/icons/my_custom_icon.png");
      if (myIcon.isNull()) {
          qWarning() << "カスタムアイコンのロードに失敗しました!";
          return QFileIconProvider::icon(info); // デフォルトアイコンを返す
      }
      return myIcon;
      

デバッグのヒント

QFileDialog::setIconProvider()関連の問題をデバッグする際に役立つヒントです。

  • ソースコードの確認

    • Qtのソースコード(qfiledialog.cpp, qfileiconprovider.cppなど)を確認することも、内部的な動作を理解し、問題を特定するのに役立ちます。
  • 単純なテストケースの作成

    • 問題が複雑な場合は、QFileDialog::setIconProvider()の機能を分離し、最小限のコードで動作確認できる単純なテストアプリケーションを作成します。これにより、問題の範囲を絞り込みやすくなります。
  • Qtのデバッグ出力の監視

    • Qt自体が内部エラーや警告をqWarning()qCritical()で出力することがあります。これらのメッセージも注意深く監視してください。
  • qDebug() の活用

    • QFileIconProvider::icon()関数内にqDebug()文を挿入し、どのQFileInfoが渡され、どのQIconが返されているかをログに出力します。これにより、予期しないファイルに対してicon()が呼び出されているか、あるいは意図したアイコンが返されていないかなどを特定できます。

      virtual QIcon icon(const QFileInfo &info) const override
      {
          qDebug() << "icon() called for:" << info.filePath();
          if (info.isFile() && info.suffix() == "myext") {
              QIcon customIcon(":/icons/my_custom_icon.png");
              qDebug() << "  -> Returning custom icon (null:" << customIcon.isNull() << ")";
              return customIcon;
          }
          qDebug() << "  -> Returning default icon";
          return QFileIconProvider::icon(info);
      }
      


事前準備:リソースファイルの作成

カスタムアイコンを使用する場合、通常はQtのリソースシステムを使用します。これにより、アイコン画像が実行ファイルに埋め込まれ、配布が容易になります。

  1. プロジェクトにリソースファイルを追加: Qt Creatorでプロジェクトを右クリックし、「新規追加」 -> 「Qt」 -> 「Qt リソースファイル」を選択し、例えば icons.qrc という名前で作成します。

  2. リソースファイルにアイコンを追加: icons.qrc を開いて「追加」ボタンをクリックし、Prefixprefix のまま(または任意のパス、例: /icons)にして、アイコン画像をプロジェクトに追加します。例えば、my_custom_icon.pngmy_folder_icon.png などの画像ファイルを追加します。

これで、コード内で ":/prefix/my_custom_icon.png" のようにしてアイコンを参照できるようになります。

例1: 特定の拡張子のファイルにカスタムアイコンを設定する

最も基本的な使用例です。.myext という拡張子を持つファイルにカスタムアイコンを設定します。

main.cpp

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

// カスタムアイコンプロバイダの定義
class MyCustomIconProvider : public QFileIconProvider
{
public:
    MyCustomIconProvider() {}

    // icon() 仮想関数をオーバーライド
    virtual QIcon icon(const QFileInfo &info) const override
    {
        // デバッグ情報
        // qDebug() << "Checking file:" << info.filePath() << " (suffix:" << info.suffix() << ")";

        // ".myext" 拡張子のファイルにカスタムアイコンを設定
        if (info.isFile() && info.suffix() == "myext") {
            QIcon customIcon(":/icons/my_custom_icon.png"); // リソースファイルから読み込み
            if (customIcon.isNull()) {
                qWarning() << "Warning: Custom icon ':/icons/my_custom_icon.png' could not be loaded!";
            }
            return customIcon;
        }

        // それ以外のファイルやディレクトリはデフォルトのアイコンを使用
        return QFileIconProvider::icon(info);
    }
};

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

    QFileDialog dialog;

    // IMPORTANT: ネイティブダイアログを無効にする
    // これをしないと、カスタムアイコンは表示されません。
    dialog.setOption(QFileDialog::DontUseNativeDialog);

    // カスタムアイコンプロバイダのインスタンスを作成し、設定
    // QFileDialogが所有権を持つため、new で作成し、delete は不要
    MyCustomIconProvider *provider = new MyCustomIconProvider();
    dialog.setIconProvider(provider);

    // ダイアログの設定
    dialog.setFileMode(QFileDialog::ExistingFile); // 既存のファイルを選択
    dialog.setNameFilter("My Extension Files (*.myext);;All Files (*)");
    dialog.setWindowTitle("カスタムアイコンのテスト");

    // ダイアログを表示
    if (dialog.exec()) {
        QString selectedFile = dialog.selectedFiles().first();
        qDebug() << "選択されたファイル: " << selectedFile;
    }

    return a.exec();
}

icons.qrc (例)

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/icons">
    <file>my_custom_icon.png</file>
</qresource>
</RCC>

説明

  • 重要な点
    dialog.setOption(QFileDialog::DontUseNativeDialog); を設定して、Qtウィジェットベースのダイアログを使用するように強制しています。これをしないと、カスタムアイコンは表示されません。
  • それ以外の場合、QFileIconProvider::icon(info) を呼び出して、Qtのデフォルトアイコンを返します。
  • 条件に合致した場合、QIcon(":/icons/my_custom_icon.png") でリソースからカスタムアイコンを読み込み、返します。
  • info.isFile() でそれがファイルであるか、info.suffix() == "myext" で拡張子が.myextであるかをチェックします。
  • icon(const QFileInfo &info) 関数をオーバーライドし、ファイル情報 (info) を基にアイコンを決定します。
  • MyCustomIconProvider クラスは QFileIconProvider を継承しています。

例2: 特定のフォルダにカスタムアイコンを設定する

ファイルだけでなく、特定のパスのフォルダにもカスタムアイコンを設定できます。

main.cpp (一部変更)

// ... (MyCustomIconProviderの定義は例1と同じ)

class MyCustomIconProvider2 : public QFileIconProvider
{
public:
    virtual QIcon icon(const QFileInfo &info) const override
    {
        // "MyProject" という名前のフォルダにカスタムアイコンを設定
        if (info.isDir() && info.fileName() == "MyProject") {
            QIcon folderIcon(":/icons/my_folder_icon.png"); // リソースからフォルダアイコンを読み込み
            if (folderIcon.isNull()) {
                qWarning() << "Warning: Custom folder icon ':/icons/my_folder_icon.png' could not be loaded!";
            }
            return folderIcon;
        }
        
        // ".myext" 拡張子のファイルにカスタムアイコンを設定 (例1のロジックを再利用)
        if (info.isFile() && info.suffix() == "myext") {
            QIcon customFileIcon(":/icons/my_custom_icon.png");
            if (customFileIcon.isNull()) {
                qWarning() << "Warning: Custom file icon ':/icons/my_custom_icon.png' could not be loaded!";
            }
            return customFileIcon;
        }

        // それ以外はデフォルトのアイコンを使用
        return QFileIconProvider::icon(info);
    }
};

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

    QFileDialog dialog;
    dialog.setOption(QFileDialog::DontUseNativeDialog);

    MyCustomIconProvider2 *provider = new MyCustomIconProvider2();
    dialog.setIconProvider(provider);

    dialog.setFileMode(QFileDialog::Directory); // ディレクトリを選択
    dialog.setWindowTitle("カスタムフォルダアイコンのテスト");

    if (dialog.exec()) {
        QString selectedDir = dialog.selectedFiles().first();
        qDebug() << "選択されたディレクトリ: " << selectedDir;
    }

    return a.exec();
}

icons.qrc (例)

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/icons">
    <file>my_custom_icon.png</file>
    <file>my_folder_icon.png</file> </qresource>
</RCC>

説明

  • この例では、ファイルとフォルダの両方にカスタムアイコンを設定するロジックが含まれています。
  • info.isDir() でそれがディレクトリであるか、info.fileName() == "MyProject" でフォルダ名が"MyProject"であるかをチェックしています。
#include <QApplication>
#include <QFileDialog>
#include <QFileIconProvider>
#include <QIcon>
#include <QFileInfo>
#include <QMimeDatabase> // MIMEタイプ情報用
#include <QDebug>

class AdvancedIconProvider : public QFileIconProvider
{
public:
    AdvancedIconProvider() {}

    virtual QIcon icon(const QFileInfo &info) const override
    {
        // ディレクトリの場合
        if (info.isDir()) {
            if (info.fileName().startsWith("temp_")) {
                // "temp_" で始まるフォルダには一時フォルダアイコン
                return QIcon(":/icons/temp_folder.png");
            }
            // その他のフォルダはデフォルト
            return QFileIconProvider::icon(info);
        }

        // ファイルの場合
        if (info.isFile()) {
            // サイズが非常に大きいファイルに特別なアイコン
            if (info.size() > 1024 * 1024 * 50) { // 50MB以上
                return QIcon(":/icons/large_file.png");
            }

            // MIMEタイプに基づくアイコン
            QMimeDatabase mimeDb;
            QMimeType mimeType = mimeDb.mimeTypeForFile(info);
            
            if (mimeType.isValid()) {
                // テキストファイルにカスタムアイコン
                if (mimeType.name().startsWith("text/")) {
                    return QIcon(":/icons/text_file.png");
                }
                // 画像ファイルにカスタムアイコン
                else if (mimeType.name().startsWith("image/")) {
                    return QIcon(":/icons/image_file.png");
                }
                // PDFファイルにカスタムアイコン
                else if (mimeType.name() == "application/pdf") {
                    return QIcon(":/icons/pdf_file.png");
                }
            }
        }

        // 上記のどの条件にも合致しない場合はデフォルトのアイコンを使用
        return QFileIconProvider::icon(info);
    }
};

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

    QFileDialog dialog;
    dialog.setOption(QFileDialog::DontUseNativeDialog); // 重要!

    AdvancedIconProvider *provider = new AdvancedIconProvider();
    dialog.setIconProvider(provider);

    dialog.setFileMode(QFileDialog::AnyFile); // ファイルまたはディレクトリを選択可能
    dialog.setWindowTitle("高度なカスタムアイコンのテスト");

    if (dialog.exec()) {
        QStringList selectedItems = dialog.selectedFiles();
        for (const QString &item : selectedItems) {
            qDebug() << "選択されたアイテム: " << item;
        }
    }

    return a.exec();
}

icons.qrc (例)

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/icons">
    <file>temp_folder.png</file>
    <file>large_file.png</file>
    <file>text_file.png</file>
    <file>image_file.png</file>
    <file>pdf_file.png</file>
    </qresource>
</RCC>

説明

  • 複数の条件を組み合わせて、より柔軟なアイコン表示を実現しています。
  • info.size() を使ってファイルサイズをチェックし、大きいファイルに特別なアイコンを設定しています。
  • QMimeDatabase を使用してファイルのMIMEタイプを取得し、それに基づいてアイコンを決定しています。


QFileSystemModel を独自に拡張し、setIconProvider() を使用する

QFileDialog は内部で QFileSystemModel を使用してファイルシステムの内容を表示しています。QFileDialog::setModel() を使用して独自の QFileSystemModel のサブクラスを設定し、その QFileSystemModel インスタンスに setIconProvider() を設定することで、アイコンのカスタマイズが可能です。これは、QFileDialog が内部で使用するモデルに直接介入する形になります。

利点

  • QFileDialog::setIconProvider() と同様に、アイコン提供ロジックを QFileIconProvider に集約できるため、整理しやすいです。
  • QFileSystemModelQTreeViewQListView など、他のビューウィジェットでも使用できるため、一貫したアイコン表示ロジックを再利用できます。
  • QFileDialog の内部モデルに直接アクセスするため、より詳細なカスタマイズが可能になる場合があります。

欠点

  • やはり QFileDialog::DontUseNativeDialog が必要になる場合が多いです。
  • QFileDialog の内部構造をある程度理解する必要があり、設定が少し複雑になります。

コード例

#include <QApplication>
#include <QFileDialog>
#include <QFileSystemModel>
#include <QFileIconProvider>
#include <QIcon>
#include <QFileInfo>
#include <QDebug>

// カスタムアイコンプロバイダ (setIconProvider() の説明と同じ)
class MyCustomIconProvider : public QFileIconProvider
{
public:
    virtual QIcon icon(const QFileInfo &info) const override
    {
        if (info.isFile() && info.suffix() == "myext") {
            return QIcon(":/icons/my_custom_icon.png");
        }
        return QFileIconProvider::icon(info);
    }
};

// QFileSystemModel を継承したカスタムモデル
class CustomFileSystemModel : public QFileSystemModel
{
public:
    CustomFileSystemModel(QObject *parent = nullptr) : QFileSystemModel(parent)
    {
        // カスタムアイコンプロバイダを設定
        setIconProvider(new MyCustomIconProvider());
    }
};

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

    QFileDialog dialog;
    dialog.setOption(QFileDialog::DontUseNativeDialog); // 重要!

    CustomFileSystemModel *model = new CustomFileSystemModel(&dialog); // dialogが所有権を持つ
    dialog.setModel(model); // カスタムモデルを設定

    dialog.setFileMode(QFileDialog::ExistingFile);
    dialog.setNameFilter("My Extension Files (*.myext);;All Files (*)");
    dialog.setWindowTitle("カスタムモデルを使ったアイコンテスト");

    if (dialog.exec()) {
        QString selectedFile = dialog.selectedFiles().first();
        qDebug() << "選択されたファイル: " << selectedFile;
    }

    return a.exec();
}

QFileDialog を完全にサブクラス化し、独自のビューとモデルを作成する

これは最も柔軟な方法ですが、最も手間がかかります。QFileDialog を継承し、内部で QTreeViewQListView などのカスタムビューを使用し、それに独自の QAbstractItemModel のサブクラス(例えば QFileSystemModel を継承したもの)を設定します。このアプローチでは、ファイルダイアログのレイアウトや挙動全体を完全に制御できます。

利点

  • ネイティブダイアログの制約に縛られません。
  • アイコンの表示だけでなく、列の追加、データのフィルタリング、並べ替えなど、モデルが提供する情報を自由に操作できます。
  • ファイルダイアログのUIとロジックを完全にカスタマイズできます。標準の QFileDialog が提供しない独自のウィジェットを追加したり、独自のナビゲーションロジックを実装したりできます。

欠点

  • Qtのバージョンアップに伴う互換性の問題が発生する可能性が高まります。
  • 複雑なモデル-ビュープログラミングの知識が必要です。
  • 開発コストが非常に高いです。ファイルシステムの走査、フィルタリング、表示など、多くの機能をゼロから(または既存のQtクラスを深く利用して)実装する必要があります。

いつこの方法を検討するか

  • ファイル選択ダイアログが、単なるファイル選択だけでなく、追加情報や機能(例: 特定のプロジェクト構造の表示、クラウドストレージとの連携など)を持つ必要がある場合。
  • 標準の QFileDialog では実現できない、非常に特殊な要件がある場合。

コード例 (概念のみ - 実際の完全な実装は複雑です)

#include <QApplication>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTreeView>
#include <QPushButton>
#include <QLineEdit>
#include <QFileSystemModel>
#include <QFileIconProvider> // ここでも使用可能

// カスタムアイコンプロバイダ (必要に応じて)
class MyIconProviderForCustomDialog : public QFileIconProvider
{
public:
    virtual QIcon icon(const QFileInfo &info) const override
    {
        if (info.isFile() && info.suffix() == "mydata") {
            return QIcon(":/icons/my_data_file.png");
        }
        return QFileIconProvider::icon(info);
    }
};

class CustomFileDialog : public QDialog
{
    Q_OBJECT
public:
    explicit CustomFileDialog(QWidget *parent = nullptr) : QDialog(parent)
    {
        setWindowTitle("カスタムファイルダイアログ");
        QVBoxLayout *mainLayout = new QVBoxLayout(this);

        // ファイルシステムモデルとビューの設定
        fileSystemModel = new QFileSystemModel(this);
        fileSystemModel->setRootPath(QDir::homePath()); // 初期パス
        fileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);

        // カスタムアイコンプロバイダをモデルに設定
        fileSystemModel->setIconProvider(new MyIconProviderForCustomDialog());

        treeView = new QTreeView(this);
        treeView->setModel(fileSystemModel);
        treeView->setRootIndex(fileSystemModel->index(QDir::homePath()));
        treeView->setColumnHidden(1, true); // サイズ列を非表示
        treeView->setColumnHidden(2, true); // タイプ列を非表示
        treeView->setColumnHidden(3, true); // 最終更新日時列を非表示

        mainLayout->addWidget(treeView);

        // ファイル名入力欄
        fileNameEdit = new QLineEdit(this);
        fileNameEdit->setPlaceholderText("ファイル名を入力...");
        mainLayout->addWidget(fileNameEdit);

        // OK/Cancel ボタン
        QHBoxLayout *buttonLayout = new QHBoxLayout();
        QPushButton *okButton = new QPushButton("開く", this);
        QPushButton *cancelButton = new QPushButton("キャンセル", this);
        buttonLayout->addStretch();
        buttonLayout->addWidget(okButton);
        buttonLayout->addWidget(cancelButton);
        mainLayout->addLayout(buttonLayout);

        // シグナルとスロットの接続
        connect(okButton, &QPushButton::clicked, this, &QDialog::accept);
        connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
        connect(treeView, &QTreeView::doubleClicked, this, &CustomFileDialog::handleDoubleClick);
    }

    QString selectedFile() const
    {
        return fileNameEdit->text();
    }

private slots:
    void handleDoubleClick(const QModelIndex &index)
    {
        if (fileSystemModel->isDir(index)) {
            treeView->setRootIndex(index); // ディレクトリをダブルクリックしたらそのディレクトリに入る
        } else {
            fileNameEdit->setText(fileSystemModel->filePath(index)); // ファイルをダブルクリックしたらファイル名を入力欄に設定
            accept(); // ダイアログを閉じる
        }
    }

private:
    QFileSystemModel *fileSystemModel;
    QTreeView *treeView;
    QLineEdit *fileNameEdit;
};

// main 関数
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    // リソースの読み込み(必要であれば)
    // Q_INIT_RESOURCE(icons); // icons.qrc があれば

    CustomFileDialog dialog;
    if (dialog.exec() == QDialog::Accepted) {
        qDebug() << "選択されたファイル: " << dialog.selectedFile();
    }

    return a.exec();
}

#include "main.moc" // mocファイルを生成するために必要

プラットフォームネイティブのファイルダイアログの利用(アイコンカスタマイズ不可)

これは代替手段というよりは、setIconProvider() を使用しない場合のデフォルトの挙動です。Qtは、QFileDialog::DontUseNativeDialog オプションが設定されていない限り、可能な限りOSのネイティブファイルダイアログを使用します。

利点

  • QFileDialog のウィジェットベースの実装よりも、ネイティブダイアログの方が安定している、または特定のOS機能(例: クラウドストレージ連携、最近のファイル履歴など)にアクセスできる場合があります。
  • 実装が非常に簡単です。静的関数を呼び出すだけです。
  • OSのルック&フィールに完全に一致し、ユーザーは慣れた操作性で利用できます。

欠点

  • UIをQtアプリケーションのテーマに合わせたい場合、ネイティブダイアログは浮いてしまう可能性があります。
  • アイコンのカスタマイズは一切できません。 これは setIconProvider() の目的と真っ向から対立します。
#include <QApplication>
#include <QFileDialog>
#include <QDebug>

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

    // ネイティブダイアログを使用 (setDefault)
    QString fileName = QFileDialog::getOpenFileName(nullptr,
                                                    "ファイルを開く",
                                                    QDir::homePath(),
                                                    "Text Files (*.txt);;All Files (*)");

    if (!fileName.isEmpty()) {
        qDebug() << "選択されたファイル (ネイティブ): " << fileName;
    }

    return a.exec();
}
  • OSのネイティブダイアログを優先し、アイコンのカスタマイズは不要な場合は、setIconProvider() を使用せず、デフォルトの挙動(または静的関数)を利用します。
  • より深いカスタマイズ(アイコンだけでなく、列の追加やカスタムウィジェットなど)が必要な場合は、QFileSystemModel を独自に拡張するか、QFileDialog を完全にサブクラス化して独自のモデルとビューを使用することを検討します。
  • QFileDialog::setIconProvider() は、Qtウィジェットベースのファイルダイアログでアイコンをカスタマイズするための最も直接的な方法です。QFileDialog::DontUseNativeDialog が必須であることを理解しておくことが重要です。