diff options
Diffstat (limited to 'examples/corelib')
-rw-r--r-- | examples/corelib/ipc/sharedmemory/dialog.py | 53 | ||||
-rw-r--r-- | examples/corelib/ipc/sharedmemory/main.py | 51 | ||||
-rw-r--r-- | examples/corelib/ipc/sharedmemory/ui_dialog.py | 4 | ||||
-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 | ||||
-rw-r--r-- | examples/corelib/settingseditor/settingseditor.py | 126 | ||||
-rw-r--r-- | examples/corelib/threads/mandelbrot.py | 163 |
11 files changed, 450 insertions, 281 deletions
diff --git a/examples/corelib/ipc/sharedmemory/dialog.py b/examples/corelib/ipc/sharedmemory/dialog.py index 8045330e2..134900047 100644 --- a/examples/corelib/ipc/sharedmemory/dialog.py +++ b/examples/corelib/ipc/sharedmemory/dialog.py @@ -1,52 +1,5 @@ -############################################################################ -## -## Copyright (C) 2021 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## BSD License Usage -## Alternatively, you may use this file under the terms of the BSD license -## as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import os from pathlib import Path @@ -137,4 +90,4 @@ class Dialog(QDialog): def detach(self): if not self._shared_memory.detach(): - self.ui.label.setText(tr("Unable to detach from shared memory.")) + self.ui.label.setText(self.tr("Unable to detach from shared memory.")) # noqa: F821 diff --git a/examples/corelib/ipc/sharedmemory/main.py b/examples/corelib/ipc/sharedmemory/main.py index 13e8f9dff..e497c8de6 100644 --- a/examples/corelib/ipc/sharedmemory/main.py +++ b/examples/corelib/ipc/sharedmemory/main.py @@ -1,52 +1,5 @@ -############################################################################ -## -## Copyright (C) 2021 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## BSD License Usage -## Alternatively, you may use this file under the terms of the BSD license -## as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the ipc/sharedmemory example from Qt v6.x""" diff --git a/examples/corelib/ipc/sharedmemory/ui_dialog.py b/examples/corelib/ipc/sharedmemory/ui_dialog.py index 891c7b847..2cd544f40 100644 --- a/examples/corelib/ipc/sharedmemory/ui_dialog.py +++ b/examples/corelib/ipc/sharedmemory/ui_dialog.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'dialog.ui' ## -## Created by: Qt User Interface Compiler version 6.2.0 +## Created by: Qt User Interface Compiler version 6.7.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -16,7 +16,7 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) from PySide6.QtWidgets import (QApplication, QDialog, QGridLayout, QLabel, - QPushButton, QSizePolicy) + QPushButton, QSizePolicy, QWidget) class Ui_Dialog(object): def setupUi(self, Dialog): 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"] +} diff --git a/examples/corelib/settingseditor/settingseditor.py b/examples/corelib/settingseditor/settingseditor.py index 2c6a0703b..f87a2f4b5 100644 --- a/examples/corelib/settingseditor/settingseditor.py +++ b/examples/corelib/settingseditor/settingseditor.py @@ -1,59 +1,26 @@ -# -*- coding: utf-8 -*- - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2020 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the widgets/tools/settingseditor example from Qt v5.x""" import sys from PySide6.QtCore import (QByteArray, QDate, QDateTime, QDir, QEvent, QPoint, - QRect, QRegularExpression, QSettings, QSize, QTime, QTimer, Qt) + QRect, QRegularExpression, QSettings, QSize, QTime, + QTimer, Qt, Slot) from PySide6.QtGui import (QAction, QColor, QIcon, QIntValidator, - QDoubleValidator, QRegularExpressionValidator, QValidator) + QDoubleValidator, QRegularExpressionValidator, + QValidator) from PySide6.QtWidgets import (QAbstractItemView, QApplication, - QCheckBox, QComboBox, QFileDialog, QDialog, QDialogButtonBox, QGridLayout, - QGroupBox, QHeaderView, QInputDialog, QItemDelegate, QLabel, QLineEdit, - QMainWindow, QMessageBox, QStyle, QSpinBox, QStyleOptionViewItem, - QTableWidget, QTableWidgetItem, QTreeWidget, QTreeWidgetItem, QVBoxLayout) + QCheckBox, QComboBox, QFileDialog, QDialog, + QDialogButtonBox, QGridLayout, + QGroupBox, QHeaderView, QInputDialog, + QItemDelegate, QLabel, QLineEdit, + QMainWindow, QMessageBox, QStyle, QSpinBox, + QStyleOptionViewItem, QTableWidget, + QTableWidgetItem, QTreeWidget, QTreeWidgetItem, + QVBoxLayout) class TypeChecker: @@ -183,6 +150,7 @@ class MainWindow(QMainWindow): self.setWindowTitle("Settings Editor") self.resize(500, 600) + @Slot() def open_settings(self): if self.location_dialog is None: self.location_dialog = LocationDialog(self) @@ -195,9 +163,10 @@ class MainWindow(QMainWindow): self.set_settings_object(settings) self.fallbacks_action.setEnabled(True) + @Slot() def open_inifile(self): file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File", - '', "INI Files (*.ini *.conf)") + '', "INI Files (*.ini *.conf)") if file_name: self.load_ini_file(file_name) @@ -209,77 +178,71 @@ class MainWindow(QMainWindow): self.set_settings_object(settings) self.fallbacks_action.setEnabled(False) + @Slot() def open_property_list(self): file_name, _ = QFileDialog.getOpenFileName(self, - "Open Property List", '', "Property List Files (*.plist)") + "Open Property List", '', + "Property List Files (*.plist)") if file_name: settings = QSettings(file_name, QSettings.NativeFormat) self.set_settings_object(settings) self.fallbacks_action.setEnabled(False) + @Slot() def open_registry_path(self): path, ok = QInputDialog.getText(self, "Open Registry Path", - "Enter the path in the Windows registry:", - QLineEdit.Normal, 'HKEY_CURRENT_USER\\') + "Enter the path in the Windows registry:", + QLineEdit.Normal, 'HKEY_CURRENT_USER\\') if ok and path != '': settings = QSettings(path, QSettings.NativeFormat) self.set_settings_object(settings) self.fallbacks_action.setEnabled(False) + @Slot() def about(self): QMessageBox.about(self, "About Settings Editor", - "The <b>Settings Editor</b> example shows how to access " - "application settings using Qt.") - - def create_actions(self): - self._open_settings_act = QtGui.QAction("&Open Application Settings...", - self, shortcut="Ctrl+O", triggered=self.openSettings) - - self._open_ini_file_act = QtGui.QAction("Open I&NI File...", self, - shortcut="Ctrl+N", triggered=self.openIniFile) - - self._open_property_list_act = QtGui.QAction("Open macOS &Property List...", - self, shortcut="Ctrl+P", triggered=self.openPropertyList) + "The <b>Settings Editor</b> example shows how to access " + "application settings using Qt.") def create_actions(self): self.open_settings_action = QAction("&Open Application Settings...", - self, shortcut="Ctrl+O", triggered=self.open_settings) + self, shortcut="Ctrl+O", triggered=self.open_settings) self.open_ini_file_action = QAction("Open I&NI File...", self, - shortcut="Ctrl+N", triggered=self.open_inifile) + shortcut="Ctrl+N", triggered=self.open_inifile) - self.open_property_list_action = QAction("Open macOS &Property List...", - self, shortcut="Ctrl+P", triggered=self.open_property_list) + self.open_property_list_action = QAction("Open macOS &Property List...", self, + shortcut="Ctrl+P", + triggered=self.open_property_list) if sys.platform != 'darwin': self.open_property_list_action.setEnabled(False) self.open_registry_path_action = QAction( - "Open Windows &Registry Path...", self, shortcut="Ctrl+G", - triggered=self.open_registry_path) + "Open Windows &Registry Path...", self, shortcut="Ctrl+G", + triggered=self.open_registry_path) if sys.platform != 'win32': self.open_registry_path_action.setEnabled(False) self.refresh_action = QAction("&Refresh", self, shortcut="Ctrl+R", - enabled=False, triggered=self.settings_tree.refresh) + enabled=False, triggered=self.settings_tree.refresh) - self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q", - triggered=self.close) + self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close) self.auto_refresh_action = QAction("&Auto-Refresh", self, - shortcut="Ctrl+A", checkable=True, enabled=False) + shortcut="Ctrl+A", checkable=True, enabled=False) self.auto_refresh_action.triggered[bool].connect(self.settings_tree.set_auto_refresh) self.auto_refresh_action.triggered[bool].connect(self.refresh_action.setDisabled) self.fallbacks_action = QAction("&Fallbacks", self, - shortcut="Ctrl+F", checkable=True, enabled=False) + shortcut="Ctrl+F", checkable=True, enabled=False) self.fallbacks_action.triggered[bool].connect(self.settings_tree.set_fallbacks_enabled) self.about_action = QAction("&About", self, triggered=self.about) self.about_Qt_action = QAction("About &Qt", self, - triggered=qApp.aboutQt) + triggered=qApp.aboutQt) # noqa: F821 def create_menus(self): self.file_menu = self.menuBar().addMenu("&File") @@ -512,6 +475,7 @@ class SettingsTree(QTreeWidget): def sizeHint(self): return QSize(800, 600) + @Slot(bool) def set_auto_refresh(self, autoRefresh): self.auto_refresh = autoRefresh @@ -522,15 +486,18 @@ class SettingsTree(QTreeWidget): else: self.refresh_timer.stop() + @Slot(bool) def set_fallbacks_enabled(self, enabled): if self.settings is not None: self.settings.setFallbacksEnabled(enabled) self.refresh() + @Slot() def maybe_refresh(self): if self.state() != QAbstractItemView.EditingState: self.refresh() + @Slot() def refresh(self): if self.settings is None: return @@ -538,7 +505,7 @@ class SettingsTree(QTreeWidget): # The signal might not be connected. try: self.itemChanged.disconnect(self.update_setting) - except: + except Exception: pass self.settings.sync() @@ -561,7 +528,6 @@ class SettingsTree(QTreeWidget): key = ancestor.text(0) + '/' + key ancestor = ancestor.parent() - d = item.data(2, Qt.UserRole) self.settings.setValue(key, item.data(2, Qt.UserRole)) if self.auto_refresh: @@ -733,7 +699,7 @@ class VariantDelegate(QItemDelegate): value = editor.value() else: value = self.value_from_lineedit(editor, model, index) - if not value is None: + if value is not None: model.setData(index, value, Qt.UserRole) model.setData(index, self.display_text(value), Qt.DisplayRole) @@ -771,7 +737,7 @@ class VariantDelegate(QItemDelegate): h = value.height() return f'({w},{h})' if isinstance(value, list): - return ','.join(value) + return ','.join(map(repr, value)) if value is None: return '<Invalid>' diff --git a/examples/corelib/threads/mandelbrot.py b/examples/corelib/threads/mandelbrot.py index c95966119..4689813d4 100644 --- a/examples/corelib/threads/mandelbrot.py +++ b/examples/corelib/threads/mandelbrot.py @@ -1,44 +1,6 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the corelib/threads/mandelbrot example from Qt v5.x, originating from PyQt""" @@ -47,7 +9,7 @@ import sys from PySide6.QtCore import (Signal, QMutex, QElapsedTimer, QMutexLocker, QPoint, QPointF, QSize, Qt, QThread, - QWaitCondition) + QWaitCondition, Slot) from PySide6.QtGui import QColor, QImage, QPainter, QPixmap, qRgb from PySide6.QtWidgets import QApplication, QWidget @@ -68,7 +30,7 @@ INFO_KEY = 'info' HELP = ("Use mouse wheel or the '+' and '-' keys to zoom. Press and " - "hold left mouse button to scroll.") + "hold left mouse button to scroll.") class RenderThread(QThread): @@ -91,7 +53,8 @@ class RenderThread(QThread): self.abort = False for i in range(RenderThread.colormap_size): - self.colormap.append(self.rgb_from_wave_length(380.0 + (i * 400.0 / RenderThread.colormap_size))) + self.colormap.append( + self.rgb_from_wave_length(380.0 + (i * 400.0 / RenderThread.colormap_size))) def stop(self): self.mutex.lock() @@ -102,18 +65,17 @@ class RenderThread(QThread): self.wait(2000) def render(self, centerX, centerY, scale_factor, resultSize): - locker = QMutexLocker(self.mutex) - - self._center_x = centerX - self._center_y = centerY - self._scale_factor = scale_factor - self._result_size = resultSize - - if not self.isRunning(): - self.start(QThread.LowPriority) - else: - self.restart = True - self.condition.wakeOne() + with QMutexLocker(self.mutex): + self._center_x = centerX + self._center_y = centerY + self._scale_factor = scale_factor + self._result_size = resultSize + + if not self.isRunning(): + self.start(QThread.LowPriority) + else: + self.restart = True + self.condition.wakeOne() def run(self): timer = QElapsedTimer() @@ -171,7 +133,8 @@ class RenderThread(QThread): if num_iterations < max_iterations: image.setPixel(x + half_width, y + half_height, - self.colormap[num_iterations % RenderThread.colormap_size]) + self.colormap[ + num_iterations % RenderThread.colormap_size]) all_black = False else: image.setPixel(x + half_width, y + half_height, qRgb(0, 0, 0)) @@ -185,7 +148,8 @@ class RenderThread(QThread): if elapsed > 2000: elapsed /= 1000 unit = 's' - text = f"Pass {curpass+1}/{NUM_PASSES}, max iterations: {max_iterations}, time: {elapsed}{unit}" + text = (f"Pass {curpass + 1}/{NUM_PASSES}, " + f"max iterations: {max_iterations}, time: {elapsed}{unit}") image.setText(INFO_KEY, text) self.rendered_image.emit(image, scale_factor) curpass += 1 @@ -253,45 +217,45 @@ class MandelbrotWidget(QWidget): self._info = '' def paintEvent(self, event): - painter = QPainter(self) - painter.fillRect(self.rect(), Qt.black) - - if self.pixmap.isNull(): + with QPainter(self) as painter: + painter.fillRect(self.rect(), Qt.black) + + if self.pixmap.isNull(): + painter.setPen(Qt.white) + painter.drawText(self.rect(), Qt.AlignCenter, + "Rendering initial image, please wait...") + return + + if self._cur_scale == self._pixmap_scale: + painter.drawPixmap(self._pixmap_offset, self.pixmap) + else: + scale_factor = self._pixmap_scale / self._cur_scale + new_width = int(self.pixmap.width() * scale_factor) + new_height = int(self.pixmap.height() * scale_factor) + new_x = self._pixmap_offset.x() + (self.pixmap.width() - new_width) / 2 + new_y = self._pixmap_offset.y() + (self.pixmap.height() - new_height) / 2 + + painter.save() + painter.translate(new_x, new_y) + painter.scale(scale_factor, scale_factor) + exposed, _ = painter.transform().inverted() + exposed = exposed.mapRect(self.rect()).adjusted(-1, -1, 1, 1) + painter.drawPixmap(exposed, self.pixmap, exposed) + painter.restore() + + text = HELP + if self._info: + text += ' ' + self._info + metrics = painter.fontMetrics() + text_width = metrics.horizontalAdvance(text) + + painter.setPen(Qt.NoPen) + painter.setBrush(QColor(0, 0, 0, 127)) + painter.drawRect((self.width() - text_width) / 2 - 5, 0, text_width + 10, + metrics.lineSpacing() + 5) painter.setPen(Qt.white) - painter.drawText(self.rect(), Qt.AlignCenter, - "Rendering initial image, please wait...") - return - - if self._cur_scale == self._pixmap_scale: - painter.drawPixmap(self._pixmap_offset, self.pixmap) - else: - scale_factor = self._pixmap_scale / self._cur_scale - new_width = int(self.pixmap.width() * scale_factor) - new_height = int(self.pixmap.height() * scale_factor) - new_x = self._pixmap_offset.x() + (self.pixmap.width() - new_width) / 2 - new_y = self._pixmap_offset.y() + (self.pixmap.height() - new_height) / 2 - - painter.save() - painter.translate(new_x, new_y) - painter.scale(scale_factor, scale_factor) - exposed, _ = painter.transform().inverted() - exposed = exposed.mapRect(self.rect()).adjusted(-1, -1, 1, 1) - painter.drawPixmap(exposed, self.pixmap, exposed) - painter.restore() - - text = HELP - if self._info: - text += ' ' + self._info - metrics = painter.fontMetrics() - text_width = metrics.horizontalAdvance(text) - - painter.setPen(Qt.NoPen) - painter.setBrush(QColor(0, 0, 0, 127)) - painter.drawRect((self.width() - text_width) / 2 - 5, 0, text_width + 10, - metrics.lineSpacing() + 5) - painter.setPen(Qt.white) - painter.drawText((self.width() - text_width) / 2, - metrics.leading() + metrics.ascent(), text) + painter.drawText((self.width() - text_width) / 2, + metrics.leading() + metrics.ascent(), text) def resizeEvent(self, event): self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size()) @@ -340,6 +304,7 @@ class MandelbrotWidget(QWidget): delta_y = (self.height() - self.pixmap.height()) / 2 - self._pixmap_offset.y() self.scroll(delta_x, delta_y) + @Slot(QImage, float) def update_pixmap(self, image, scale_factor): if not self._last_drag_pos.isNull(): return @@ -354,15 +319,13 @@ class MandelbrotWidget(QWidget): def zoom(self, zoomFactor): self._cur_scale *= zoomFactor self.update() - self.thread.render(self._center_x, self._center_y, self._cur_scale, - self.size()) + self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size()) def scroll(self, deltaX, deltaY): self._center_x += deltaX * self._cur_scale self._center_y += deltaY * self._cur_scale self.update() - self.thread.render(self._center_x, self._center_y, self._cur_scale, - self.size()) + self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size()) if __name__ == '__main__': |