Using PyQt6, I am investigating using QFileDialog directly without the use of one of the static functions (i.e. don't use QFileDialog.getOpenFileName). The issue that I am running into is creating a filter list that uses a combination of MIME types and named types.
For example, say you want to set a filter for *.css and *.qss files. At there core, they are essentially the same file type, however MIME doesn't recognize *.qss. I really like the idea of using MIME types because it ensures that the many extension options of a file type are included (i.e. [*.jpg, *.jpeg, *.jpe]
or [*.md, *.mkd, *.markdown]
), but I also need to work with the files that are not included in MIME.
The code snippet that I am working with is as follows:
file_dialog = QFileDialog()
file_dialog.setFileMode(QFileDialog.FileMode.ExistingFiles)
file_dialog.setMimeTypeFilters(["text/css",
"application/octet-stream"])
file_dialog.setNameFilter("Qt Style Sheet (*.qss)")
if file_dialog.exec():
print(file_dialog.selectedFiles())
When this code executes, the .setNameFilter
function completely overwrites the filters set with the .setMimeTypeFilters
function. If I reverse the order of the filter setting, the same thing happens just in reverse.
I have also tried adding the name filter to the MIME type list, but the name filter is just ignored.
file_dialog.setMimeTypeFilters(["text/css",
"Qt Style Sheet (*.qss)",
"application/octet-stream"])
Anyone know of a way to have both filters without having to explicitly set all options and nix using .setMimeTypeFilters
?
Combining mime-filters and name-filters is quite easy to achieve using QMimeDatabase. Doing things this way will allow you to merge glob patterns (e.g. *.qss
with the css defaults), as well as getting full control over the final ordering of the filters. This won't normally be possible when using the QFileDialog methods.
Below is a simple function that demonstrates the idea. The function takes a dict
with mime-type/filter-name keys and glob-list values. If the function detects a mime-type, the glob-list will be merged with the built-in defaults. The return value is a list that can be passed to setNameFilters. The code logic is essentially the same as what QFileDialog itself uses, but tweaked slightly to allow greater flexibility:
def combine_filters(filters):
result = []
md = QMimeDatabase()
for name, patterns in filters.items():
if (mt := md.mimeTypeForName(name)).isValid():
if mt.isDefault():
result.append('All Files (*)')
continue
name = mt.comment()
patterns = sorted(mt.globPatterns() + list(patterns or ()))
result.append(f'{name} ({" ".join(patterns)})')
return result
Example with merge:
filters = combine_filters({
'text/css': ['*.qss'],
'application/octet-stream': None,
})
print('\n'.join(filters))
# CSS stylesheet (*.css *.qss)
# All Files (*)
Example without merge:
filters = combine_filters({
'text/css': None,
'Qt Style Sheet': ['*.qss'],
'application/octet-stream': None,
})
print('\n'.join(filters))
# CSS stylesheet (*.css)
# Qt Style Sheet (*.qss)
# All Files (*)