From 1bd978897334d7508aa432481efac0210018633d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 27 Jan 2021 08:46:56 +0100 Subject: Port the Qt Designer taskmenuextension example Show how to use QDesignerTaskMenuExtension for a custom widget. Task-number: PYSIDE-1455 Change-Id: Ia3a1a65890747a0d960702c25dfe20e75084a2b2 Reviewed-by: Qt CI Bot Reviewed-by: Cristian Maureira-Fredes --- examples/designer/taskmenuextension/main.py | 53 +++++++ .../taskmenuextension/registertictactoe.py | 60 ++++++++ .../taskmenuextension/taskmenuextension.pyproject | 4 + examples/designer/taskmenuextension/tictactoe.py | 170 +++++++++++++++++++++ .../designer/taskmenuextension/tictactoeplugin.py | 118 ++++++++++++++ .../taskmenuextension/tictactoetaskmenu.py | 115 ++++++++++++++ 6 files changed, 520 insertions(+) create mode 100644 examples/designer/taskmenuextension/main.py create mode 100644 examples/designer/taskmenuextension/registertictactoe.py create mode 100644 examples/designer/taskmenuextension/taskmenuextension.pyproject create mode 100644 examples/designer/taskmenuextension/tictactoe.py create mode 100644 examples/designer/taskmenuextension/tictactoeplugin.py create mode 100644 examples/designer/taskmenuextension/tictactoetaskmenu.py (limited to 'examples/designer') diff --git a/examples/designer/taskmenuextension/main.py b/examples/designer/taskmenuextension/main.py new file mode 100644 index 000000000..883070f77 --- /dev/null +++ b/examples/designer/taskmenuextension/main.py @@ -0,0 +1,53 @@ +############################################################################# +## +## Copyright (C) 2021 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$ +## +############################################################################# + +"""PySide6 port of the Qt Designer taskmenuextension example from Qt v6.x""" + +import sys +from PySide6.QtWidgets import QApplication + +from tictactoe import TicTacToe + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = TicTacToe() + window.state = "-X-XO----" + window.show() + sys.exit(app.exec_()) diff --git a/examples/designer/taskmenuextension/registertictactoe.py b/examples/designer/taskmenuextension/registertictactoe.py new file mode 100644 index 000000000..d34bb248d --- /dev/null +++ b/examples/designer/taskmenuextension/registertictactoe.py @@ -0,0 +1,60 @@ +############################################################################ +## +## 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$ +## +############################################################################ + +from tictactoe import TicTacToe +from tictactoeplugin import TicTacToePlugin + +from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection + +# Set PYSIDE_DESIGNER_PLUGINS to point to this directory and load the plugin + + +if __name__ == '__main__': + QPyDesignerCustomWidgetCollection.addCustomWidget(TicTacToePlugin()) diff --git a/examples/designer/taskmenuextension/taskmenuextension.pyproject b/examples/designer/taskmenuextension/taskmenuextension.pyproject new file mode 100644 index 000000000..29ec5245f --- /dev/null +++ b/examples/designer/taskmenuextension/taskmenuextension.pyproject @@ -0,0 +1,4 @@ +{ + "files": ["tictactoe.py", "main.py", "registertictactoe.py", "tictactoeplugin.py", + "tictactoetaskmenu.py"] +} diff --git a/examples/designer/taskmenuextension/tictactoe.py b/examples/designer/taskmenuextension/tictactoe.py new file mode 100644 index 000000000..539decce2 --- /dev/null +++ b/examples/designer/taskmenuextension/tictactoe.py @@ -0,0 +1,170 @@ +############################################################################# +## +## Copyright (C) 2021 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$ +## +############################################################################# + +from PySide6.QtCore import Qt, QPoint, QRect, QSize, Property, Slot +from PySide6.QtGui import QMouseEvent, QPainter, QPen +from PySide6.QtWidgets import QWidget + + +EMPTY = '-' +CROSS = 'X' +NOUGHT = 'O' +DEFAULT_STATE = "---------" + + +class TicTacToe(QWidget): + def __init__(self, parent=None): + super(TicTacToe, self).__init__(parent) + self._state = DEFAULT_STATE + self._turn_number = 0 + + def minimumSizeHint(self): + return QSize(200, 200) + + def sizeHint(self): + return QSize(200, 200) + + def setState(self, new_state): + self._turn_number = 0 + self._state = DEFAULT_STATE + for position in range(min(9, len(new_state))): + mark = new_state[position] + if mark == CROSS or mark == NOUGHT: + self._turn_number += 1 + self._change_state_at(position, mark) + position += 1 + self.update() + + def state(self): + return self._state + + @Slot() + def clear_board(self): + self._state = DEFAULT_STATE + self._turn_number = 0 + self.update() + + def _change_state_at(self, pos, new_state): + self._state = (self._state[:pos] + new_state + + self._state[pos + 1:]) + + def mousePressEvent(self, event): + if self._turn_number == 9: + self.clear_board() + return + for position in range(9): + cell = self._cell_rect(position) + if cell.contains(event.position().toPoint()): + if self._state[position] == EMPTY: + new_state = CROSS if self._turn_number % 2 == 0 else NOUGHT + self._change_state_at(position, new_state) + self._turn_number += 1 + self.update() + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + + painter.setPen(QPen(Qt.darkGreen, 1)) + painter.drawLine(self._cell_width(), 0, + self._cell_width(), self.height()) + painter.drawLine(2 * self._cell_width(), 0, + 2 * self._cell_width(), self.height()) + painter.drawLine(0, self._cell_height(), + self.width(), self._cell_height()) + painter.drawLine(0, 2 * self._cell_height(), + self.width(), 2 * self._cell_height()) + + painter.setPen(QPen(Qt.darkBlue, 2)) + + for position in range(9): + cell = self._cell_rect(position) + if self._state[position] == CROSS: + painter.drawLine(cell.topLeft(), cell.bottomRight()) + painter.drawLine(cell.topRight(), cell.bottomLeft()) + elif self._state[position] == NOUGHT: + painter.drawEllipse(cell) + + painter.setPen(QPen(Qt.yellow, 3)) + + for position in range(9): + if (self._state[position] != EMPTY + and self._state[position + 1] == self._state[position] + and self._state[position + 2] == self._state[position]): + y = self._cell_rect(position).center().y() + painter.drawLine(0, y, self.width(), y) + self._turn_number = 9 + + for position in range(3): + if (self._state[position] != EMPTY + and self._state[position + 3] == self._state[position] + and self._state[position + 6] == self._state[position]): + x = self._cell_rect(position).center().x() + painter.drawLine(x, 0, x, height()) + self._turn_number = 9 + + if (self._state[0] != EMPTY and self._state[4] == self._state[0] + and self._state[8] == self._state[0]): + painter.drawLine(0, 0, self.width(), self.height()) + self._turn_number = 9 + + if (self._state[2] != EMPTY and self._state[4] == self._state[2] + and self._state[6] == self._state[2]): + painter.drawLine(0, self.height(), self.width(), 0) + self._turn_number = 9 + + def _cell_rect(self, position): + h_margin = self.width() / 30 + v_margin = self.height() / 30 + row = int(position / 3) + column = position - 3 * row + pos = QPoint(column * self._cell_width() + h_margin, + row * self._cell_height() + v_margin) + size = QSize(self._cell_width() - 2 * h_margin, + self._cell_height() - 2 * v_margin) + return QRect(pos, size) + + def _cell_width(self): + return self.width() / 3 + + def _cell_height(self): + return self.height() / 3 + + state = Property(str, state, setState) diff --git a/examples/designer/taskmenuextension/tictactoeplugin.py b/examples/designer/taskmenuextension/tictactoeplugin.py new file mode 100644 index 000000000..9b9938479 --- /dev/null +++ b/examples/designer/taskmenuextension/tictactoeplugin.py @@ -0,0 +1,118 @@ +############################################################################ +## +## 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$ +## +############################################################################ + +from tictactoe import TicTacToe +from tictactoetaskmenu import TicTacToeTaskMenuFactory + +from PySide6.QtGui import QIcon +from PySide6.QtDesigner import (QExtensionManager, + QDesignerCustomWidgetInterface) + + +DOM_XML = """ + + + + + 0 + 0 + 200 + 200 + + + + -X-XO---- + + + +""" + + +class TicTacToePlugin(QDesignerCustomWidgetInterface): + def __init__(self): + super(TicTacToePlugin, self).__init__() + self._form_editor = None + + def createWidget(self, parent): + t = TicTacToe(parent) + return t + + def domXml(self): + return DOM_XML + + def group(self): + return '' + + def icon(self): + return QIcon() + + def includeFile(self): + return 'tictactoe' + + def initialize(self, form_editor): + self._form_editor = form_editor + manager = form_editor.extensionManager() + iid = TicTacToeTaskMenuFactory.task_menu_iid() + manager.registerExtensions(TicTacToeTaskMenuFactory(manager), iid) + + def isContainer(self): + return False + + def isInitialized(self): + return self._form_editor is not None + + def name(self): + return 'TicTacToe' + + def toolTip(self): + return 'Tic Tac Toe Example, demonstrating class QDesignerTaskMenuExtension (Python)' + + def whatsThis(self): + return self.toolTip() diff --git a/examples/designer/taskmenuextension/tictactoetaskmenu.py b/examples/designer/taskmenuextension/tictactoetaskmenu.py new file mode 100644 index 000000000..d8f805c2e --- /dev/null +++ b/examples/designer/taskmenuextension/tictactoetaskmenu.py @@ -0,0 +1,115 @@ +############################################################################ +## +## 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$ +## +############################################################################ + +from tictactoe import TicTacToe + +from PySide6.QtCore import QObject, Slot +from PySide6.QtGui import QAction +from PySide6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout +from PySide6.QtDesigner import (QExtensionFactory, QPyDesignerTaskMenuExtension) + + +class TicTacToeDialog(QDialog): + def __init__(self, parent): + super(TicTacToeDialog, self).__init__(parent) + layout = QVBoxLayout(self) + self._ticTacToe = TicTacToe(self) + layout.addWidget(self._ticTacToe) + button_box = QDialogButtonBox(QDialogButtonBox.Ok + | QDialogButtonBox.Cancel + | QDialogButtonBox.Reset) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + reset_button = button_box.button(QDialogButtonBox.Reset) + reset_button.clicked.connect(self._ticTacToe.clear_board) + layout.addWidget(button_box) + + def set_state(self, new_state): + self._ticTacToe.setState(new_state) + + def state(self): + return self._ticTacToe.state + + +class TicTacToeTaskMenu(QPyDesignerTaskMenuExtension): + def __init__(self, ticTacToe, parent): + super(TicTacToeTaskMenu, self).__init__(parent) + self._ticTacToe = ticTacToe + self._edit_state_action = QAction('Edit State...', None) + self._edit_state_action.triggered.connect(self._edit_state) + + def taskActions(self): + return [self._edit_state_action] + + def preferredEditAction(self): + return self._edit_state_action + + @Slot() + def _edit_state(self): + dialog = TicTacToeDialog(self._ticTacToe) + dialog.set_state(self._ticTacToe.state) + if dialog.exec_() == QDialog.Accepted: + self._ticTacToe.state = dialog.state() + + +class TicTacToeTaskMenuFactory(QExtensionFactory): + def __init__(self, extension_manager): + super(TicTacToeTaskMenuFactory, self).__init__(extension_manager) + + @staticmethod + def task_menu_iid(): + return 'org.qt-project.Qt.Designer.TaskMenu' + + def createExtension(self, object, iid, parent): + if iid != TicTacToeTaskMenuFactory.task_menu_iid(): + return None + if object.__class__.__name__ != 'TicTacToe': + return None + return TicTacToeTaskMenu(object, parent) -- cgit v1.2.3