From 14a03a7a55307b41d8664f05216ca9ea1f3c9be7 Mon Sep 17 00:00:00 2001 From: Jaime Resano Date: Tue, 5 Oct 2021 13:10:28 +0200 Subject: Add borderlayout example Border layout example ported from C++ to Python Pick-to: 6.2 Task-number: PYSIDE-841 Change-Id: I66df4041183ae1eced6fe75d52c21ce381c3d3eb Reviewed-by: Christian Tismer --- .../widgets/layouts/borderlayout/borderlayout.py | 285 +++++++++++++++++++++ .../layouts/borderlayout/borderlayout.pyproject | 3 + .../layouts/borderlayout/doc/borderlayout.png | Bin 0 -> 5450 bytes .../layouts/borderlayout/doc/borderlayout.rst | 10 + 4 files changed, 298 insertions(+) create mode 100644 examples/widgets/layouts/borderlayout/borderlayout.py create mode 100644 examples/widgets/layouts/borderlayout/borderlayout.pyproject create mode 100644 examples/widgets/layouts/borderlayout/doc/borderlayout.png create mode 100644 examples/widgets/layouts/borderlayout/doc/borderlayout.rst (limited to 'examples/widgets') diff --git a/examples/widgets/layouts/borderlayout/borderlayout.py b/examples/widgets/layouts/borderlayout/borderlayout.py new file mode 100644 index 000000000..74d5524e4 --- /dev/null +++ b/examples/widgets/layouts/borderlayout/borderlayout.py @@ -0,0 +1,285 @@ +############################################################################ +## +## 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 widgets/layouts/borderlayout example from Qt v5.x""" + +from dataclasses import dataclass +from enum import IntEnum, auto + +from PySide6.QtCore import QRect, QSize, Qt +from PySide6.QtWidgets import ( + QApplication, + QFrame, + QLabel, + QLayout, + QLayoutItem, + QTextBrowser, + QWidget, + QWidgetItem, +) +import sys + + +class Position(IntEnum): + West = auto() + North = auto() + South = auto() + East = auto() + Center = auto() + + +class SizeType(IntEnum): + MinimumSize = auto() + SizeHint = auto() + + +@dataclass +class ItemWrapper: + item: QLayoutItem + position: Position + + +class BorderLayout(QLayout): + def __init__(self, parent=None, spacing: int = -1): + super().__init__(parent) + + self._list: list[ItemWrapper] = [] + + self.setSpacing(spacing) + + if parent is not None: + self.setParent(parent) + + def __del__(self): + item = self.takeAt(0) + while item: + item = self.takeAt(0) + + def addItem(self, item: QLayoutItem): + self.add(item, Position.West) + + def addWidget(self, widget: QWidget, position: Position): + self.add(QWidgetItem(widget), position) + + def expandingDirections(self) -> Qt.Orientations: + return Qt.Horizontal | Qt.Vertical + + def hasHeightForWidth(self) -> bool: + return False + + def count(self) -> int: + return len(self._list) + + def itemAt(self, index: int) -> QLayoutItem: + if index < len(self._list): + wrapper: ItemWrapper = self._list[index] + return wrapper.item + return None + + def minimumSize(self) -> QSize: + return self.calculate_size(SizeType.MinimumSize) + + def setGeometry(self, rect: QRect): + center: ItemWrapper = None + east_width = 0 + west_width = 0 + north_height = 0 + south_height = 0 + + super().setGeometry(rect) + + for wrapper in self._list: + item: QLayoutItem = wrapper.item + position: Position = wrapper.position + + if position == Position.North: + item.setGeometry( + QRect( + rect.x(), north_height, rect.width(), item.sizeHint().height() + ) + ) + + north_height += item.geometry().height() + self.spacing() + + elif position == Position.South: + item.setGeometry( + QRect( + item.geometry().x(), + item.geometry().y(), + rect.width(), + item.sizeHint().height(), + ) + ) + + south_height += item.geometry().height() + self.spacing() + + item.setGeometry( + QRect( + rect.x(), + rect.y() + rect.height() - south_height + self.spacing(), + item.geometry().width(), + item.geometry().height(), + ) + ) + elif position == Position.Center: + center = wrapper + + center_height = rect.height() - north_height - south_height + + for wrapper in self._list: + item: QLayoutItem = wrapper.item + position: Position = wrapper.position + + if position == Position.West: + item.setGeometry( + QRect( + rect.x() + west_width, + north_height, + item.sizeHint().width(), + center_height, + ) + ) + + west_width += item.geometry().width() + self.spacing() + + elif position == Position.East: + item.setGeometry( + QRect( + item.geometry().x(), + item.geometry().y(), + item.sizeHint().width(), + center_height, + ) + ) + + east_width += item.geometry().width() + self.spacing() + + item.setGeometry( + QRect( + rect.x() + rect.width() - east_width + self.spacing(), + north_height, + item.geometry().width(), + item.geometry().height(), + ) + ) + + if center: + center.item.setGeometry( + QRect( + west_width, + north_height, + rect.width() - east_width - west_width, + center_height, + ) + ) + + def sizeHint(self) -> QSize: + return self.calculate_size(SizeType.SizeHint) + + def takeAt(self, index: int): + if 0 <= index < len(self._list): + layout_struct: ItemWrapper = self._list.pop(index) + return layout_struct.item + return None + + def add(self, item: QLayoutItem, position: Position): + self._list.append(ItemWrapper(item, position)) + + def calculate_size(self, size_type: SizeType): + total_size = QSize() + + for wrapper in self._list: + position = wrapper.position + + item_size: QSize + if size_type == SizeType.MinimumSize: + item_size = wrapper.item.minimumSize() + else: + item_size = wrapper.item.sizeHint() + + if position in (Position.North, Position.South, Position.Center): + total_size.setHeight(total_size.height() + item_size.height()) + + if position in (Position.West, Position.East, Position.Center): + total_size.setWidth(total_size.width() + item_size.width()) + + return total_size + + +class Window(QWidget): + def __init__(self): + super().__init__() + self.central_widget = QTextBrowser() + self.central_widget.setPlainText("Central widget") + + border_layout = BorderLayout() + border_layout.addWidget(self.central_widget, Position.Center) + + label_north = self.create_label("North") + border_layout.addWidget(label_north, Position.North) + + label_west = self.create_label("West") + border_layout.addWidget(label_west, Position.West) + + label_east1 = self.create_label("East 1") + border_layout.addWidget(label_east1, Position.East) + + label_east2 = self.create_label("East 2") + border_layout.addWidget(label_east2, Position.East) + + label_south = self.create_label("South") + border_layout.addWidget(label_south, Position.South) + + self.setLayout(border_layout) + + self.setWindowTitle("Border Layout") + + @staticmethod + def create_label(text: str): + label = QLabel(text) + label.setFrameStyle(QFrame.Box | QFrame.Raised) + return label + + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) diff --git a/examples/widgets/layouts/borderlayout/borderlayout.pyproject b/examples/widgets/layouts/borderlayout/borderlayout.pyproject new file mode 100644 index 000000000..fc0280348 --- /dev/null +++ b/examples/widgets/layouts/borderlayout/borderlayout.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["borderlayout.py"] +} diff --git a/examples/widgets/layouts/borderlayout/doc/borderlayout.png b/examples/widgets/layouts/borderlayout/doc/borderlayout.png new file mode 100644 index 000000000..8599b9d1c Binary files /dev/null and b/examples/widgets/layouts/borderlayout/doc/borderlayout.png differ diff --git a/examples/widgets/layouts/borderlayout/doc/borderlayout.rst b/examples/widgets/layouts/borderlayout/doc/borderlayout.rst new file mode 100644 index 000000000..652f84daa --- /dev/null +++ b/examples/widgets/layouts/borderlayout/doc/borderlayout.rst @@ -0,0 +1,10 @@ +Border Layout Example +======================= + +Shows how to arrange child widgets along a border. + +Border Layout implements a layout that arranges child widgets to surround the main area. + +.. image:: borderlayout.png + :width: 473 + :alt: border layout Screenshot -- cgit v1.2.3