1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45from PyQt5.QtCore import QRect, QSize, Qt 46from PyQt5.QtWidgets import (QApplication, QFrame, QLabel, QLayout, 47 QTextBrowser, QWidget, QWidgetItem) 48 49 50class ItemWrapper(object): 51 def __init__(self, i, p): 52 self.item = i 53 self.position = p 54 55 56class BorderLayout(QLayout): 57 West, North, South, East, Center = range(5) 58 MinimumSize, SizeHint = range(2) 59 60 def __init__(self, parent=None, margin=None, spacing=-1): 61 super(BorderLayout, self).__init__(parent) 62 63 if margin is not None: 64 self.setContentsMargins(margin, margin, margin, margin) 65 66 self.setSpacing(spacing) 67 self.list = [] 68 69 def __del__(self): 70 l = self.takeAt(0) 71 while l is not None: 72 l = self.takeAt(0) 73 74 def addItem(self, item): 75 self.add(item, self.West) 76 77 def addWidget(self, widget, position): 78 self.add(QWidgetItem(widget), position) 79 80 def expandingDirections(self): 81 return Qt.Horizontal | Qt.Vertical 82 83 def hasHeightForWidth(self): 84 return False 85 86 def count(self): 87 return len(self.list) 88 89 def itemAt(self, index): 90 if index < len(self.list): 91 return self.list[index].item 92 93 return None 94 95 def minimumSize(self): 96 return self.calculateSize(self.MinimumSize) 97 98 def setGeometry(self, rect): 99 center = None 100 eastWidth = 0 101 westWidth = 0 102 northHeight = 0 103 southHeight = 0 104 centerHeight = 0 105 106 super(BorderLayout, self).setGeometry(rect) 107 108 for wrapper in self.list: 109 item = wrapper.item 110 position = wrapper.position 111 112 if position == self.North: 113 item.setGeometry(QRect(rect.x(), northHeight, 114 rect.width(), item.sizeHint().height())) 115 116 northHeight += item.geometry().height() + self.spacing() 117 118 elif position == self.South: 119 item.setGeometry(QRect(item.geometry().x(), 120 item.geometry().y(), rect.width(), 121 item.sizeHint().height())) 122 123 southHeight += item.geometry().height() + self.spacing() 124 125 item.setGeometry(QRect(rect.x(), 126 rect.y() + rect.height() - southHeight + self.spacing(), 127 item.geometry().width(), item.geometry().height())) 128 129 elif position == self.Center: 130 center = wrapper 131 132 centerHeight = rect.height() - northHeight - southHeight 133 134 for wrapper in self.list: 135 item = wrapper.item 136 position = wrapper.position 137 138 if position == self.West: 139 item.setGeometry(QRect(rect.x() + westWidth, 140 northHeight, item.sizeHint().width(), centerHeight)) 141 142 westWidth += item.geometry().width() + self.spacing() 143 144 elif position == self.East: 145 item.setGeometry(QRect(item.geometry().x(), 146 item.geometry().y(), item.sizeHint().width(), 147 centerHeight)) 148 149 eastWidth += item.geometry().width() + self.spacing() 150 151 item.setGeometry(QRect(rect.x() + rect.width() - eastWidth + self.spacing(), 152 northHeight, item.geometry().width(), 153 item.geometry().height())) 154 155 if center: 156 center.item.setGeometry(QRect(westWidth, northHeight, 157 rect.width() - eastWidth - westWidth, centerHeight)) 158 159 def sizeHint(self): 160 return self.calculateSize(self.SizeHint) 161 162 def takeAt(self, index): 163 if index >= 0 and index < len(self.list): 164 layoutStruct = self.list.pop(index) 165 return layoutStruct.item 166 167 return None 168 169 def add(self, item, position): 170 self.list.append(ItemWrapper(item, position)) 171 172 def calculateSize(self, sizeType): 173 totalSize = QSize() 174 175 for wrapper in self.list: 176 position = wrapper.position 177 itemSize = QSize() 178 179 if sizeType == self.MinimumSize: 180 itemSize = wrapper.item.minimumSize() 181 else: # sizeType == self.SizeHint 182 itemSize = wrapper.item.sizeHint() 183 184 if position in (self.North, self.South, self.Center): 185 totalSize.setHeight(totalSize.height() + itemSize.height()) 186 187 if position in (self.West, self.East, self.Center): 188 totalSize.setWidth(totalSize.width() + itemSize.width()) 189 190 return totalSize 191 192 193class Window(QWidget): 194 def __init__(self): 195 super(Window, self).__init__() 196 197 centralWidget = QTextBrowser() 198 centralWidget.setPlainText("Central widget") 199 200 layout = BorderLayout() 201 layout.addWidget(centralWidget, BorderLayout.Center) 202 203 # Because BorderLayout doesn't call its super-class addWidget() it 204 # doesn't take ownership of the widgets until setLayout() is called. 205 # Therefore we keep a local reference to each label to prevent it being 206 # garbage collected too soon. 207 label_n = self.createLabel("North") 208 layout.addWidget(label_n, BorderLayout.North) 209 210 label_w = self.createLabel("West") 211 layout.addWidget(label_w, BorderLayout.West) 212 213 label_e1 = self.createLabel("East 1") 214 layout.addWidget(label_e1, BorderLayout.East) 215 216 label_e2 = self.createLabel("East 2") 217 layout.addWidget(label_e2, BorderLayout.East) 218 219 label_s = self.createLabel("South") 220 layout.addWidget(label_s, BorderLayout.South) 221 222 self.setLayout(layout) 223 224 self.setWindowTitle("Border Layout") 225 226 def createLabel(self, text): 227 label = QLabel(text) 228 label.setFrameStyle(QFrame.Box | QFrame.Raised) 229 230 return label 231 232 233if __name__ == '__main__': 234 235 import sys 236 237 app = QApplication(sys.argv) 238 window = Window() 239 window.show() 240 sys.exit(app.exec_()) 241