diff options
Diffstat (limited to 'examples/corelib/mimetypesbrowser')
-rw-r--r-- | examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.png | bin | 0 -> 22569 bytes | |||
-rw-r--r-- | examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.rst | 10 | ||||
-rw-r--r-- | examples/corelib/mimetypesbrowser/mainwindow.py | 161 | ||||
-rw-r--r-- | examples/corelib/mimetypesbrowser/mimetypemodel.py | 136 | ||||
-rw-r--r-- | examples/corelib/mimetypesbrowser/mimetypesbrowser.py | 24 | ||||
-rw-r--r-- | examples/corelib/mimetypesbrowser/mimetypesbrowser.pyproject | 3 |
6 files changed, 334 insertions, 0 deletions
diff --git a/examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.png b/examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.png Binary files differnew file mode 100644 index 000000000..3c4a476b3 --- /dev/null +++ b/examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.png diff --git a/examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.rst b/examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.rst new file mode 100644 index 000000000..e8e4d1ecd --- /dev/null +++ b/examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.rst @@ -0,0 +1,10 @@ +MIME Type Browser Example +========================= + +A Python application that demonstrates the analogous example in C++ +`MIME Type Browser Example <https://doc.qt.io/qt-6/qtcore-mimetypes-mimetypebrowser-example.html>`_ + +.. image:: mimetypesbrowser.png + :width: 400 + :alt: mimetypebrowser screenshot + diff --git a/examples/corelib/mimetypesbrowser/mainwindow.py b/examples/corelib/mimetypesbrowser/mainwindow.py new file mode 100644 index 000000000..76404eeb7 --- /dev/null +++ b/examples/corelib/mimetypesbrowser/mainwindow.py @@ -0,0 +1,161 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from mimetypemodel import MimeTypeModel +from PySide6.QtCore import (QDir, QFileInfo, QMimeDatabase, QModelIndex, Qt, + Slot) +from PySide6.QtGui import QAction, QKeySequence +from PySide6.QtWidgets import (QAbstractItemView, QApplication, QDialog, + QFileDialog, QInputDialog, QMainWindow, + QMessageBox, QSplitter, QTextEdit, QTreeView, + QWidget) + + +class MainWindow(QMainWindow): + def __init__(self, parent: QWidget = None) -> None: + super().__init__(parent=parent) + self.m_find_index: int = 0 + self.m_model = MimeTypeModel(self) + self.m_tree_view = QTreeView(self) + self.m_details_text = QTextEdit(self) + self.m_find_matches = [] + + self.setWindowTitle("Qt Mime Database Browser") + + # create actions + self.detect_file_action = QAction( + "&Detect File Type...", self, shortcut="Ctrl+O", triggered=self.detect_file + ) + self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close) + self.m_find_action = QAction("&Find...", self, shortcut="Ctrl+F", triggered=self.find) + self.m_find_next_action = QAction( + "Find &Next", self, shortcut="Ctrl+G", triggered=self.find_next + ) + self.m_find_previous_action = QAction( + "Find &Previous", + self, + shortcut="Ctrl+Shift+G", + triggered=self.find_previous, + ) + self.about_action = QAction( + "About Qt", + self, + shortcut=QKeySequence(QKeySequence.HelpContents), + triggered=QApplication.aboutQt, + ) + + # add action to menu + self.file_menu = self.menuBar().addMenu("&File") + self.file_menu.addAction(self.detect_file_action) + self.file_menu.addAction(self.exit_action) + self.find_menu = self.menuBar().addMenu("&Edit") + self.find_menu.addAction(self.m_find_action) + self.find_menu.addAction(self.m_find_next_action) + self.find_menu.addAction(self.m_find_previous_action) + self.about_menu = self.menuBar().addMenu("&About") + self.about_menu.addAction(self.about_action) + + self.central_splitter = QSplitter(self) + self.setCentralWidget(self.central_splitter) + + self.m_tree_view.setUniformRowHeights(True) + self.m_tree_view.setModel(self.m_model) + + self.items = self.m_model.findItems( + "application/octet-stream", + Qt.MatchContains | Qt.MatchFixedString | Qt.MatchRecursive, + ) + + if self.items: + self.m_tree_view.expand(self.m_model.indexFromItem(self.items[0])) + + self.m_tree_view.selectionModel().currentChanged.connect(self.current_changed) + self.central_splitter.addWidget(self.m_tree_view) + self.m_details_text.setReadOnly(True) + self.central_splitter.addWidget(self.m_details_text) + + self.update_find_actions() + + @Slot() + def detect_file(self): + file_name = QFileDialog.getOpenFileName(self, "Choose File") + if not file_name: + return + + mime_database = QMimeDatabase() + fi = QFileInfo(file_name[0]) + mime_type = mime_database.mimeTypeForFile(fi) + index = ( + self.m_model.indexForMimeType(mime_type.name()) + if mime_type.isValid() + else QModelIndex() + ) + + if index.isValid(): + self.statusBar().showMessage(f'{fi.fileName()}" is of type "{mime_type.name()}"') + self._select_and_goto(index) + else: + QMessageBox.information( + self, + "Unknown File Type", + f"The type of {QDir.toNativeSeparators(file_name)} could not be determined.", + ) + + @Slot() + def find(self): + input_dialog = QInputDialog(self) + input_dialog.setWindowTitle("Find") + input_dialog.setLabelText("Text") + if input_dialog.exec() != QDialog.Accepted: + return + + value = input_dialog.textValue().strip() + if not value: + return + + self.m_find_matches.clear() + self.m_find_index = 0 + items = self.m_model.findItems( + value, Qt.MatchContains | Qt.MatchFixedString | Qt.MatchRecursive + ) + + for item in items: + self.m_find_matches.append(self.m_model.indexFromItem(item)) + + self.statusBar().showMessage(f'{len(self.m_find_matches)} mime types match "{value}".') + self.update_find_actions() + + if self.m_find_matches: + self._select_and_goto(self.m_find_matches[0]) + + @Slot() + def find_next(self): + self.m_find_index = self.m_find_index + 1 + if self.m_find_index >= len(self.m_find_matches): + self.m_find_index = 0 + if self.m_find_index < len(self.m_find_matches): + self._select_and_goto(self.m_find_matches[self.m_find_index]) + + @Slot() + def find_previous(self): + self.m_find_index = self.m_find_index - 1 + if self.m_find_index < 0: + self.m_find_index = len(self.m_find_matches) - 1 + if self.m_find_index >= 0: + self._select_and_goto(self.m_find_matches[self.m_find_index]) + + @Slot(QModelIndex) + def current_changed(self, index: QModelIndex): + if index.isValid(): + self.m_details_text.setText( + MimeTypeModel.formatMimeTypeInfo(self.m_model.mimeType(index)) + ) + + def update_find_actions(self): + self.find_next_previous_enabled = len(self.m_find_matches) > 1 + self.m_find_next_action.setEnabled(self.find_next_previous_enabled) + self.m_find_previous_action.setEnabled(self.find_next_previous_enabled) + + def _select_and_goto(self, index: QModelIndex): + self.m_tree_view.scrollTo(index, QAbstractItemView.PositionAtCenter) + self.m_tree_view.setCurrentIndex(index) diff --git a/examples/corelib/mimetypesbrowser/mimetypemodel.py b/examples/corelib/mimetypesbrowser/mimetypemodel.py new file mode 100644 index 000000000..5243f110e --- /dev/null +++ b/examples/corelib/mimetypesbrowser/mimetypemodel.py @@ -0,0 +1,136 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from typing import List + +from PySide6.QtCore import QMimeDatabase, QMimeType, QModelIndex, QObject, Qt, qWarning +from PySide6.QtGui import QStandardItem, QStandardItemModel + +mimeTypeRole = Qt.UserRole + 1 +iconQueriedRole = Qt.UserRole + 2 + + +def createRow(t: QMimeType): + name_item = QStandardItem(t.name()) + flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled + name_item.setData(t, mimeTypeRole) + name_item.setData(False, iconQueriedRole) + name_item.setFlags(flags) + name_item.setToolTip(t.comment()) + return [name_item] + + +class MimeTypeModel(QStandardItemModel): + def __init__(self, parent: QObject = None): + super().__init__(0, 1, parent) + self.setHorizontalHeaderLabels(["Name"]) + self.m_name_index_hash = {} + self.populate() + + def populate(self): + mime_database = QMimeDatabase() + all_types: List[QMimeType] = mime_database.allMimeTypes() + + # Move top level types to rear end of list, sort this partition, + # create top level items and truncate the list. + with_parent_mimetypes, without_parent_mimetypes = [], [] + + for mime_type in all_types: + if mime_type.parentMimeTypes(): + with_parent_mimetypes.append(mime_type) + else: + without_parent_mimetypes.append(mime_type) + + without_parent_mimetypes.sort(key=lambda x: x.name()) + + for top_level_type in without_parent_mimetypes: + row = createRow(top_level_type) + self.appendRow(row) + self.m_name_index_hash[top_level_type.name()] = self.indexFromItem(row[0]) + + all_types = with_parent_mimetypes + + while all_types: + # Find a type inheriting one that is already in the model. + name_index_value: QModelIndex = None + name_index_key = "" + for mime_type in all_types: + name_index_value = self.m_name_index_hash.get( + mime_type.parentMimeTypes()[0] + ) + if name_index_value: + name_index_key = mime_type.parentMimeTypes()[0] + break + + if not name_index_value: + orphaned_mime_types = ", ".join( + [mime_type.name() for mime_type in all_types] + ) + qWarning(f"Orphaned mime types: {orphaned_mime_types}") + break + + # Move types inheriting the parent type to rear end of list, sort this partition, + # append the items to parent and truncate the list. + parent_name = name_index_key + with_parent_name, without_parent_name = [], [] + + for mime_type in all_types: + if parent_name in mime_type.parentMimeTypes(): + with_parent_name.append(mime_type) + else: + without_parent_name.append(mime_type) + + without_parent_name.sort(key=lambda x: x.name()) + parent_item = self.itemFromIndex(name_index_value) + + for mime_type in with_parent_name: + row = createRow(mime_type) + parent_item.appendRow(row) + self.m_name_index_hash[mime_type.name()] = self.indexFromItem(row[0]) + + all_types = without_parent_name + + def mimeType(self, index: QModelIndex): + return index.data(mimeTypeRole) + + def indexForMimeType(self, name): + return self.m_name_index_hash[name] + + @staticmethod + def formatMimeTypeInfo(t: QMimeType): + out = f"<html><head/><body><h3><center>{t.name()}</center></h3><br><table>" + aliases_str = ", ".join(t.aliases()) + if aliases_str: + out += f"<tr><td>Aliases:</td><td> ({aliases_str})" + + out += ( + f"</td></tr><tr><td>Comment:</td><td>{t.comment()}" + f"</td></tr><tr><td>Icon name:</td><td>{t.iconName()}</td></tr>" + f"<tr><td>Generic icon name</td><td>{t.genericIconName()}</td></tr>" + ) + + filter_str = t.filterString() + if filter_str: + out += f"<tr><td>Filter:</td><td>{filter_str}</td></tr>" + + patterns_str = ", ".join(t.globPatterns()) + if patterns_str: + out += f"<tr><td>Glob patterns:</td><td>{patterns_str}</td></tr>" + + parentMimeTypes_str = ", ".join(t.parentMimeTypes()) + if parentMimeTypes_str: + out += f"<tr><td>Parent types:</td><td>{parentMimeTypes_str}</td></tr>" + + suffixes = t.suffixes() + if suffixes: + out += "<tr><td>Suffixes:</td><td>" + preferredSuffix = t.preferredSuffix() + if preferredSuffix: + suffixes.remove(preferredSuffix) + out += f"<b>{preferredSuffix}</b> " + suffixes_str = ", ".join(suffixes) + out += f"{suffixes_str}</td></tr>" + + out += "</table></body></html>" + + return out diff --git a/examples/corelib/mimetypesbrowser/mimetypesbrowser.py b/examples/corelib/mimetypesbrowser/mimetypesbrowser.py new file mode 100644 index 000000000..4742a31b8 --- /dev/null +++ b/examples/corelib/mimetypesbrowser/mimetypesbrowser.py @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +"""PySide6 port of the corelib/mimetypes/mimetypebrowser example from from Qt""" + +import argparse +import sys + +from mainwindow import MainWindow +from PySide6.QtWidgets import QApplication + +if __name__ == "__main__": + app = QApplication(sys.argv) + + parser = argparse.ArgumentParser(description="MimeTypesBrowser Example") + parser.add_argument("-v", "--version", action="version", version="%(prog)s 1.0") + args = parser.parse_args() + + mainWindow = MainWindow() + availableGeometry = mainWindow.screen().availableGeometry() + mainWindow.resize(availableGeometry.width() / 3, availableGeometry.height() / 2) + mainWindow.show() + + sys.exit(app.exec()) diff --git a/examples/corelib/mimetypesbrowser/mimetypesbrowser.pyproject b/examples/corelib/mimetypesbrowser/mimetypesbrowser.pyproject new file mode 100644 index 000000000..ada4252da --- /dev/null +++ b/examples/corelib/mimetypesbrowser/mimetypesbrowser.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["mimetypesbrowser.py"] +} |