Qt QFileDialog::setNameFilter() 徹底解説:ファイル選択ダイアログを使いこなす
QFileDialog::setNameFilter()
とは
QFileDialog
は、ユーザーがファイルシステムを操作してファイルを選択したり、保存場所を指定したりするためのダイアログボックスを提供するクラスです。setNameFilter()
メソッドは、このファイルダイアログに表示されるファイルのリストを、特定のファイル拡張子やファイルタイプに限定するために使用されます。
使い方
setNameFilter()
メソッドには、フィルターを表す文字列を引数として渡します。この文字列は、表示したいファイルのタイプとその表示名を指定する形式になっています。
基本的な書式
dialog.setNameFilter("表示名 (*.拡張子1 *.拡張子2 ...)");
例
例えば、画像ファイル(PNG、JPG、BMP)のみを表示したい場合は、次のように指定します。
QFileDialog dialog;
dialog.setNameFilter("画像ファイル (*.png *.jpg *.bmp)");
// dialog.exec() などでダイアログを表示
このコードを実行すると、ファイルダイアログの下部にある「ファイルの種類」のようなドロップダウンリストに「画像ファイル」という項目が表示され、選択すると、指定された拡張子(.png, .jpg, .bmp)を持つファイルのみがファイルリストに表示されるようになります。
複数のフィルターを指定する場合
複数の異なる種類のフィルターを提供したい場合は、セミコロン ;;
で区切って複数のフィルター文字列を指定します。
QFileDialog dialog;
dialog.setNameFilter("画像ファイル (*.png *.jpg *.bmp);;テキストファイル (*.txt);;すべてのファイル (*.*)");
// dialog.exec() などでダイアログを表示
この場合、ファイルダイアログには「画像ファイル」「テキストファイル」「すべてのファイル」という3つのフィルターが選択可能になります。
setNameFilters()
との違い
QStringList filters;
filters << "画像ファイル (*.png *.jpg)"
<< "テキストファイル (*.txt)"
<< "すべてのファイル (*.*)";
QFileDialog dialog;
dialog.setNameFilters(filters);
- ネイティブダイアログとの関係
QtのQFileDialog
は、プラットフォームネイティブなファイルダイアログを使用するか、Qtウィジェットベースのダイアログを使用するかを選択できます。一部の高度なフィルタリングやカスタマイズ(例:プロキシモデルの使用)は、Qtウィジェットベースのダイアログを使用しないと機能しない場合があります。その場合はdialog.setOption(QFileDialog::DontUseNativeDialog);
を設定する必要があります。 - フィルターは表示の絞り込みのみ
setNameFilter()
はあくまでファイルダイアログに表示されるファイルを絞り込むためのものであり、ユーザーが手動でファイル名を入力する際に、指定された拡張子と異なるファイル名を保存することを強制するものではありません。例えば、画像ファイルフィルターが設定されていても、ユーザーが「my_document.doc」と入力して保存しようとすれば、通常は保存できてしまいます。
フィルター文字列の書式間違い
これが最も一般的なエラーです。setNameFilter()
に渡す文字列の書式が間違っていると、フィルターが正しく機能しないか、まったく表示されないことがあります。
よくある間違い
- 不適切なパスセパレータ
フィルター文字列内にパス区切り文字 (/
や\
) を含めてしまう。フィルターはファイル名と拡張子のみに適用されます。 - セミコロン (;;) の使い方間違い
複数のフィルターを区切る際に、セミコロンが1つしかなかったり、余分なスペースが入ったりする。- 誤り例
"Images (*.png); Text (*.txt)"
(正しいのは"Images (*.png);;Text (*.txt)"
)
- 誤り例
- ワイルドカード (*) の欠落
拡張子を指定する際にワイルドカードを忘れる。- 誤り例
"Documents(.doc)"
(正しいのは"Documents (*.doc)"
)
- 誤り例
- スペースの欠落
(*.txt)
のようなワイルドカードの前後にスペースがない。- 誤り例
"TextFiles(*.txt)"
(正しいのは"Text Files (*.txt)"
または"TextFiles (*.txt)"
)
- 誤り例
トラブルシューティング
- デバッグ出力
qDebug()
を使って、setNameFilter()
に渡している文字列が期待通りのものであるかを確認してください。 - シンプルなフィルターでテスト
まずは"All Files (*.*)"
のような最もシンプルなフィルターで正しく表示されるかを確認し、そこから徐々に複雑なフィルターを追加していくと、どこで書式が間違ったか特定しやすくなります。 - Qt公式ドキュメントの確認
QFileDialog::setNameFilter()
のドキュメントを再確認し、正しい書式に従っているかを確認してください。
ネイティブダイアログとの非互換性
Qtは、可能な限りプラットフォームネイティブのファイルダイアログを使用しようとします。しかし、ネイティブダイアログはQtのすべてのフィルターオプションやカスタマイズに対応しているわけではありません。
よくある問題
- プロキシモデルのフィルタリングが機能しない
QFileDialog
でより高度なフィルタリング(例えば、ファイル名の一部でフィルタリングしたり、ファイルの作成日でフィルタリングしたりする)を行うためにQAbstractProxyModel
を使用している場合、ネイティブダイアログではその機能がサポートされません。 - フィルターが適用されない
setNameFilter()
で設定したフィルターが、ネイティブダイアログでは期待通りに機能しない(例えば、ドロップダウンリストにフィルターが表示されない、または表示されても機能しない)。
トラブルシューティング
-
動作の違いを理解する
ネイティブダイアログの挙動はOSによって異なるため、異なるプラットフォームでテストする際に、その違いを認識しておくことが重要です。 -
ネイティブダイアログの使用を無効にする
QFileDialog::DontUseNativeDialog
オプションを設定することで、Qtは常にQtウィジェットベースのファイルダイアログを使用するようになります。これにより、Qtのすべてのフィルタリング機能が確実に動作するようになります。QFileDialog dialog; dialog.setOption(QFileDialog::DontUseNativeDialog); // これを追加 dialog.setNameFilter("画像ファイル (*.png *.jpg)"); dialog.exec();
フィルターが多すぎる・複雑すぎる
非常に多くのフィルターや、複雑な正規表現のようなフィルター文字列を指定すると、ダイアログの動作が遅くなったり、ユーザーインターフェースが乱れたりする可能性があります。
よくある問題
- UIの乱れ
フィルターの表示名が長すぎたり、多くのフィルターが存在したりすると、ダイアログのレイアウトが崩れることがあります。 - パフォーマンスの低下
多数の拡張子や非常に長いフィルター文字列は、特にファイル数が多いディレクトリでダイアログの応答性を低下させることがあります。
トラブルシューティング
- フィルター名の簡潔化
フィルターの表示名を短く、分かりやすくすることを心がけてください。 - フィルターの数を制限する
ユーザーにとって本当に必要なフィルターに絞り込むことを検討してください。
QFileDialog
は、ファイルを「開く」ためのダイアログと「保存する」ためのダイアログの両方として使用できます。setNameFilter()
は両方のモードで機能しますが、ユーザーの期待値が異なる場合があります。
よくある問題
- 保存時の拡張子自動付加
QFileDialog
は、ユーザーがファイル名を指定する際に、選択されたフィルターの拡張子を自動的に付加しようとする場合がありますが、これはプラットフォームやQtのバージョンによって挙動が異なります。ユーザーが意図しない拡張子が付加されることを防ぐために、追加のロジックが必要になることがあります。
トラブルシューティング
-
setDefaultSuffix() の利用
QFileDialog::setDefaultSuffix()
を使用して、特定の拡張子をデフォルトとして設定することもできます。これは特に保存ダイアログで便利です。QFileDialog dialog; dialog.setAcceptMode(QFileDialog::AcceptSave); // 保存ダイアログとして設定 dialog.setNameFilter("テキストファイル (*.txt)"); dialog.setDefaultSuffix("txt"); // デフォルトの拡張子を設定 dialog.exec();
-
selectedNameFilter() の利用
保存ダイアログの場合、ユーザーがどのフィルターを選択したかを知るためにQFileDialog::selectedNameFilter()
を使用し、それに基づいてアプリケーションで拡張子を付与するなどの追加処理を検討してください。
- 空のフィルター文字列
setNameFilter("")
のように空の文字列を渡すと、すべてのファイルが表示されるか、フィルターがまったく機能しない可能性があります。すべてのファイルを表示したい場合は、明示的に"All Files (*.*)"
のように指定するのが良いプラクティスです。 - 大文字・小文字の区別
ファイルシステムの特性(大文字・小文字を区別するかどうか)によっては、フィルターも影響を受けることがあります。通常、setNameFilter()
は大文字・小文字を区別しないマッチングを試みますが、OSやファイルシステムによっては異なる挙動を示す可能性も考慮に入れてください。
例1:基本的なファイルオープンダイアログ(単一フィルター)
最も基本的な使い方で、特定の種類のファイルのみを開くためのダイアログを表示します。
#include <QApplication>
#include <QFileDialog>
#include <iostream> // デバッグ出力用
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// QFileDialogのインスタンスを作成
QFileDialog dialog;
// ウィンドウタイトルを設定(任意)
dialog.setWindowTitle("画像ファイルを開く");
// フィルターを設定:PNGとJPGファイルのみを表示
dialog.setNameFilter("画像ファイル (*.png *.jpg)");
// ダイアログを表示し、ユーザーがOKボタンを押したかどうかをチェック
if (dialog.exec() == QDialog::Accepted) {
// 選択されたファイルのパスを取得
QString selectedFile = dialog.selectedFiles().first();
std::cout << "選択されたファイル: " << selectedFile.toStdString() << std::endl;
} else {
std::cout << "ファイル選択がキャンセルされました。" << std::endl;
}
return a.exec();
}
解説
dialog.exec() == QDialog::Accepted
:ユーザーが「開く」または「OK」ボタンを押した場合にQDialog::Accepted
が返されます。dialog.setNameFilter("画像ファイル (*.png *.jpg)");
:ここでフィルターを設定しています。「画像ファイル」がドロップダウンリストに表示されるフィルター名で、「(*.png *.jpg)
」が実際にフィルタリングする拡張子です。複数の拡張子はスペースで区切ります。dialog.setWindowTitle("画像ファイルを開く");
:ダイアログのタイトルバーに表示されるテキストを設定します。
例2:複数のフィルターを設定するファイルオープンダイアログ
異なる種類のファイルを複数選択できるように、複数のフィルターを設定します。
#include <QApplication>
#include <QFileDialog>
#include <QStringList>
#include <iostream>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QFileDialog dialog;
dialog.setWindowTitle("ファイルを開く");
// 複数のフィルターをセミコロン (;;) で区切って設定
dialog.setNameFilter("画像ファイル (*.png *.jpg *.bmp);;テキストファイル (*.txt);;すべてのファイル (*.*)");
// または QStringList を使って設定する(推奨)
/*
QStringList filters;
filters << "画像ファイル (*.png *.jpg *.bmp)"
<< "テキストファイル (*.txt)"
<< "すべてのファイル (*.*)";
dialog.setNameFilters(filters);
*/
if (dialog.exec() == QDialog::Accepted) {
QString selectedFile = dialog.selectedFiles().first();
std::cout << "選択されたファイル: " << selectedFile.toStdString() << std::endl;
// どのフィルターが選択されたかを取得することもできる
QString selectedFilter = dialog.selectedNameFilter();
std::cout << "選択されたフィルター: " << selectedFilter.toStdString() << std::endl;
} else {
std::cout << "ファイル選択がキャンセルされました。" << std::endl;
}
return a.exec();
}
解説
dialog.selectedNameFilter()
:ユーザーがダイアログでどのフィルターを選択したかを取得できます。これは、保存ダイアログでデフォルトの拡張子を決定する際などに役立ちます。- コメントアウトされた部分では
setNameFilters(QStringList)
を使用する方法も示しています。この方法は、特に多くのフィルターを設定する場合や、動的にフィルターを生成する場合にコードが読みやすくなります。 dialog.setNameFilter("画像ファイル (*.png *.jpg *.bmp);;テキストファイル (*.txt);;すべてのファイル (*.*)");
:複数のフィルターを設定する場合は、各フィルター文字列をセミコロン2つ;;
で区切ります。
例3:ファイル保存ダイアログでの使用とデフォルト拡張子の設定
ファイルを保存するためのダイアログでフィルターを使用し、さらにデフォルトの拡張子を設定します。
#include <QApplication>
#include <QFileDialog>
#include <iostream>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QFileDialog dialog;
dialog.setWindowTitle("名前を付けて保存");
// ダイアログを「保存」モードに設定
dialog.setAcceptMode(QFileDialog::AcceptSave);
// フィルターを設定
dialog.setNameFilter("テキストドキュメント (*.txt)");
dialog.setNameFilter("CSVファイル (*.csv);;すべてのファイル (*.*)"); // 複数のフィルターを設定
// デフォルトの拡張子を設定(ユーザーが拡張子を入力しなかった場合に自動で付加される)
dialog.setDefaultSuffix("txt");
if (dialog.exec() == QDialog::Accepted) {
QString selectedFile = dialog.selectedFiles().first();
std::cout << "保存ファイル名: " << selectedFile.toStdString() << std::endl;
// 選択されたフィルターに基づいてファイルの拡張子を最終確認するロジックをここに記述することもできる
// 例えば、ユーザーがフィルターを変更した場合に適切に処理するなど
QString selectedFilter = dialog.selectedNameFilter();
if (selectedFilter.startsWith("テキストドキュメント") && !selectedFile.endsWith(".txt", Qt::CaseInsensitive)) {
selectedFile += ".txt";
std::cout << "拡張子を補完しました: " << selectedFile.toStdString() << std::endl;
}
} else {
std::cout << "ファイル保存がキャンセルされました。" << std::endl;
}
return a.exec();
}
解説
- 選択されたフィルターに基づいて拡張子を補完するロジックの例も示しています。これは
setDefaultSuffix()
が期待通りに機能しない場合や、より厳密な制御が必要な場合に役立ちます。 dialog.setDefaultSuffix("txt");
:ユーザーがファイル名だけを入力した場合(例:「新しいドキュメント」)に、自動的に.txt
拡張子が付加されるようになります(例:「新しいドキュメント.txt」)。これはネイティブダイアログの挙動に依存する場合があります。dialog.setAcceptMode(QFileDialog::AcceptSave);
:ダイアログを「保存」モードに切り替えます。「開く」モードの場合はQFileDialog::AcceptOpen
(デフォルト) です。
一部のOSでは、setNameFilter()
で設定したフィルターがネイティブダイアログで期待通りに機能しない場合があります。その場合は、Qtウィジェットベースのダイアログを強制的に使用するように設定できます。
#include <QApplication>
#include <QFileDialog>
#include <iostream>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QFileDialog dialog;
dialog.setWindowTitle("画像ファイルを開く(ネイティブダイアログOFF)");
// ネイティブダイアログの使用を無効にするオプションを設定
// これにより、Qt独自のウィジェットベースのダイアログが強制的に使用される
dialog.setOption(QFileDialog::DontUseNativeDialog, true);
dialog.setNameFilter("SVGファイル (*.svg);;JPEGファイル (*.jpeg *.jpg);;すべてのファイル (*.*)");
if (dialog.exec() == QDialog::Accepted) {
QString selectedFile = dialog.selectedFiles().first();
std::cout << "選択されたファイル: " << selectedFile.toStdString() << std::endl;
} else {
std::cout << "ファイル選択がキャンセルされました。" << std::endl;
}
return a.exec();
}
dialog.setOption(QFileDialog::DontUseNativeDialog, true);
:この行が重要です。これを設定することで、QtはOS標準のファイルダイアログではなく、Qtが提供する独自のファイルダイアログを表示します。これにより、setNameFilter()
を含むQtのすべてのオプションや機能が確実に動作するようになります。
QFileDialog::setProxyModel() を使用した高度なフィルタリング
setNameFilter()
はファイル拡張子ベースのシンプルなフィルタリングには優れていますが、ファイル名に特定の文字列が含まれているか、ファイルの最終更新日が新しいか、ファイルサイズが特定の値以上か、といったより複雑な条件でフィルタリングしたい場合には限界があります。
このような場合、QFileDialog
が内部で使用するモデル(通常は QFileSystemModel
)に対して、カスタムの プロキシモデル を設定することができます。QSortFilterProxyModel
を継承した独自のクラスを作成し、filterAcceptsRow()
メソッドをオーバーライドすることで、非常に柔軟なフィルタリングロジックを実装できます。
利用ケース
- 独自のカスタムルールに基づいてファイルをフィルタリング
- 特定のユーザーが所有するファイルのみ表示
- 特定の期間に作成されたファイルのみ表示
- ファイル名に特定のキーワードが含まれるもののみ表示
-
#include <QSortFilterProxyModel> #include <QFileInfo> #include <QDateTime> // 必要に応じて class CustomFileFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit CustomFileFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {} protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); QFileInfo fileInfo = sourceModel()->data(index, QFileSystemModel::FilePathRole).toString(); // ここにカスタムフィルタリングロジックを記述 // 例: ファイル名に "report" が含まれるファイルのみ表示 if (fileInfo.isFile() && !fileInfo.fileName().contains("report", Qt::CaseInsensitive)) { return false; } // 例: 過去7日以内に更新されたファイルのみ表示 // if (fileInfo.lastModified().daysTo(QDateTime::currentDateTime()) > 7) { // return false; // } // 親ディレクトリも表示されるように、ディレクトリは常にtrueを返す if (fileInfo.isDir()) { return true; } // setNameFilter() のフィルターも適用したい場合は、親クラスのフィルターを呼び出す return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } };
-
QFileDialog にプロキシモデルを設定
#include <QApplication> #include <QFileDialog> #include <QDebug> // デバッグ出力用 // ... (CustomFileFilterProxyModel の定義をここに含めるか、別のヘッダーファイルからインクルード) int main(int argc, char *argv[]) { QApplication a(argc, argv); QFileDialog dialog; dialog.setWindowTitle("高度なファイルフィルター"); // !!! 重要 !!! ネイティブダイアログの使用を無効にする // setProxyModel() はQtウィジェットベースのダイアログでのみ機能する dialog.setOption(QFileDialog::DontUseNativeDialog, true); // カスタムプロキシモデルを設定 CustomFileFilterProxyModel *proxyModel = new CustomFileFilterProxyModel(&dialog); // dialogを親に設定 dialog.setProxyModel(proxyModel); // setNameFilter() も併用可能だが、proxyModelのfilterAcceptsRow()が優先される dialog.setNameFilter("すべてのファイル (*.*)"); // 表示上のフィルター名 if (dialog.exec() == QDialog::Accepted) { QString selectedFile = dialog.selectedFiles().first(); qDebug() << "選択されたファイル:" << selectedFile; } else { qDebug() << "ファイル選択がキャンセルされました。"; } return a.exec(); }
注意点
setNameFilter()
も引き続き設定できますが、それはダイアログの「ファイルの種類」ドロップダウンに表示される名前として機能し、実際のフィルタリングはプロキシモデルのfilterAcceptsRow()
が行います。- ディレクトリも表示されるように、
isDir()
の場合はtrue
を返すようにしています。これをしないと、フィルターに合致しないディレクトリ内のファイルに到達できなくなります。 filterAcceptsRow()
内でQFileSystemModel::FilePathRole
を使用してファイル情報を取得しています。他にもFileNameRole
,FileSizeRole
,LastModifiedRole
など、さまざまな役割(Role)があります。- QFileDialog::DontUseNativeDialog
非常に重要です。カスタムプロキシモデルは、Qtウィジェットベースのファイルダイアログでのみ機能します。ネイティブダイアログではプロキシモデルが無視されるため、必ずこのオプションをtrue
に設定してください。
QFileSystemModel::setNameFilters() の利用(ファイルシステムツリービューなど)
QFileDialog
はファイルダイアログを表示するための高レベルなコンポーネントですが、もしアプリケーション内で独自のファイルブラウザ(QTreeView
などでファイルシステムを表示する)を実装している場合、その表示モデルとして QFileSystemModel
を使用できます。
QFileSystemModel
自体にも setNameFilters()
メソッドがあり、QFileDialog::setNameFilter()
と同様のワイルドカードベースのフィルタリングを適用できます。
利用ケース
- ファイルシステムの内容を独自のビューで表示し、そのビューにフィルタリング機能を付けたい場合
- カスタムのファイルブラウザウィジェットを構築している場合
#include <QApplication>
#include <QTreeView>
#include <QFileSystemModel>
#include <QVBoxLayout>
#include <QWidget>
#include <QLineEdit>
#include <QLabel>
#include <QStringList>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget window;
QVBoxLayout *layout = new QVBoxLayout(&window);
QLabel *filterLabel = new QLabel("フィルター (例: *.txt *.log):");
QLineEdit *filterLineEdit = new QLineEdit(&window);
QTreeView *treeView = new QTreeView(&window);
QFileSystemModel *model = new QFileSystemModel(&window);
model->setRootPath(QDir::homePath()); // ホームディレクトリをルートに設定
// デフォルトのフィルターを設定(ディレクトリは常に表示、隠しファイルは非表示)
model->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files);
// model->setNameFilterDisables(false); // フィルターに合致しないファイルを隠すか、無効にするか (true=隠す, false=無効化)
treeView->setModel(model);
treeView->setRootIndex(model->index(QDir::homePath()));
treeView->hideColumn(1); // サイズ列を非表示
treeView->hideColumn(2); // タイプ列を非表示
treeView->hideColumn(3); // 最終更新日時列を非表示
layout->addWidget(filterLabel);
layout->addWidget(filterLineEdit);
layout->addWidget(treeView);
// QLineEdit のテキスト変更シグナルと QFileSystemModel の setNameFilters スロットを接続
QObject::connect(filterLineEdit, &QLineEdit::textChanged, [&](const QString &text) {
QStringList filters;
if (!text.isEmpty()) {
filters = text.split(" ", Qt::SkipEmptyParts); // スペースで区切ってフィルターリストを作成
}
model->setNameFilters(filters);
// フィルターが設定されていなくても、ディレクトリは表示されるようにするため、
// 常に QDir::AllDirs を含める必要がある場合があります。
// model->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files);
});
window.resize(600, 400);
window.show();
return a.exec();
}
解説
QDir::Filter
もQFileSystemModel
に設定できる別の種類のフィルターです。これはディレクトリのエントリの種類(例:QDir::Files
はファイルのみ、QDir::Dirs
はディレクトリのみ)を制御します。setNameFilters()
とは独立して機能します。setNameFilterDisables(true)
(デフォルト) の場合、フィルターに合致しないファイルは完全にツリービューから消えます。false
にすると、グレーアウトされて表示されます。filterLineEdit
にユーザーが*.txt *.log
のようなフィルター文字列を入力すると、QFileSystemModel::setNameFilters()
が呼び出され、ツリービューの表示がリアルタイムでフィルタリングされます。QFileSystemModel
をQTreeView
に設定し、ファイルシステムを表示します。
-
QFileSystemModel::setNameFilters()
- アプリケーション内に独自のファイルブラウザやツリービューを実装している場合。
- ユーザーがフィルターを動的に変更できるようなカスタムUIを提供したい場合。
-
QFileDialog::setProxyModel()
setNameFilter()
では実現できない、より複雑なカスタムフィルタリングロジックが必要な場合。- ファイルの属性(サイズ、日付、カスタムタグなど)に基づくフィルタリング。
- ただし、ネイティブダイアログが利用できないため、Qtウィジェットベースのダイアログの外観と動作に限定されます。
-
QFileDialog::setNameFilter()
(またはsetNameFilters()
):- 単一のファイル選択・保存ダイアログで、単純な拡張子ベースのフィルタリングが必要な場合。
- 最も簡単で、Qtの標準的なファイルダイアログの機能として利用できます。