반응형

PyQt5에서 사용할 수 있는 FlowLayout 예제 코드입니다.



2022. 7. 23  최초작성



우선 실행 결과입니다. 창의 크기가 변경됨에 따라 FlowLayout에 추가된 자식 위젯의 배치가 달라지게 됩니다. 

FlowLayout 생성시  orientation의 아규먼트에 따라 배치 방법이 다르니 아래 캡처 사진으로 확인하세요.



flowLayout = FlowLayout(orientation=QtCore.Qt.Horizontal)

 

 



flowLayout = FlowLayout(orientation=QtCore.Qt.Vertical)

 





전체 소스 코드입니다. 소스코드가 있는 곳에 이미지 파일을 하나 저장하고 다음 부분을 수정하세요. 

 

        pixmap = QtGui.QPixmap('apple.jpeg')



from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5 import QtGui


class Window(QtWidgets.QWidget):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(1024, 768)
        image_viewer1 = QtWidgets.QLabel()
        image_viewer2 = QtWidgets.QLabel()
        image_viewer3 = QtWidgets.QLabel()
        image_viewer4 = QtWidgets.QLabel()
        image_viewer5 = QtWidgets.QLabel()

        flowLayout = FlowLayout(orientation=QtCore.Qt.Horizontal)
        # flowLayout = FlowLayout(orientation=QtCore.Qt.Vertical)
        flowLayout.addWidget(image_viewer1)
        flowLayout.addWidget(image_viewer2)
        flowLayout.addWidget(image_viewer3)
        flowLayout.addWidget(image_viewer4)
        flowLayout.addWidget(image_viewer5)
        self.setLayout(flowLayout)

        pixmap = QtGui.QPixmap('apple.jpeg')
        pixmap = pixmap.scaledToWidth(160)
        image_viewer1.setPixmap(pixmap)
        image_viewer2.setPixmap(pixmap)
        image_viewer3.setPixmap(pixmap)
        image_viewer4.setPixmap(pixmap)
        image_viewer5.setPixmap(pixmap)

        self.setWindowTitle("Flow Layout")

class FlowLayout(QtWidgets.QLayout):
    def __init__(self, orientation=QtCore.Qt.Horizontal, parent=None, margin=0, spacing=-1):
        super().__init__(parent)
        self.orientation = orientation

        if parent is not None:
            self.setContentsMargins(margin, margin, margin, margin)

        self.setSpacing(spacing)

        self.itemList = []

    def __del__(self):
        item = self.takeAt(0)
        while item:
            item = self.takeAt(0)

    def addItem(self, item):
        self.itemList.append(item)

    def count(self):
        return len(self.itemList)

    def itemAt(self, index):
        if index >= 0 and index < len(self.itemList):
            return self.itemList[index]

        return None

    def takeAt(self, index):
        if index >= 0 and index < len(self.itemList):
            return self.itemList.pop(index)

        return None

    def expandingDirections(self):
        return QtCore.Qt.Orientations(QtCore.Qt.Orientation(0))

    def hasHeightForWidth(self):
        return self.orientation == QtCore.Qt.Horizontal

    def heightForWidth(self, width):
        return self.doLayout(QtCore.QRect(0, 0, width, 0), True)

    def hasWidthForHeight(self):
        return self.orientation == QtCore.Qt.Vertical

    def widthForHeight(self, height):
        return self.doLayout(QtCore.QRect(0, 0, 0, height), True)

    def setGeometry(self, rect):
        super().setGeometry(rect)
        self.doLayout(rect, False)

    def sizeHint(self):
        return self.minimumSize()

    def minimumSize(self):
        size = QtCore.QSize()

        for item in self.itemList:
            size = size.expandedTo(item.minimumSize())

        margin, _, _, _ = self.getContentsMargins()

        size += QtCore.QSize(2 * margin, 2 * margin)
        return size

    def doLayout(self, rect, testOnly):
        x = rect.x()
        y = rect.y()
        lineHeight = columnWidth = heightForWidth = 0

        for item in self.itemList:
            wid = item.widget()
            spaceX = self.spacing() + wid.style().layoutSpacing(QtWidgets.QSizePolicy.PushButton, QtWidgets.QSizePolicy.PushButton, QtCore.Qt.Horizontal)
            spaceY = self.spacing() + wid.style().layoutSpacing(QtWidgets.QSizePolicy.PushButton, QtWidgets.QSizePolicy.PushButton, QtCore.Qt.Vertical)
            if self.orientation == QtCore.Qt.Horizontal:
                nextX = x + item.sizeHint().width() + spaceX
                if nextX - spaceX > rect.right() and lineHeight > 0:
                    x = rect.x()
                    y = y + lineHeight + spaceY
                    nextX = x + item.sizeHint().width() + spaceX
                    lineHeight = 0

                if not testOnly:
                    item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))

                x = nextX
                lineHeight = max(lineHeight, item.sizeHint().height())
            else:
                nextY = y + item.sizeHint().height() + spaceY
                if nextY - spaceY > rect.bottom() and columnWidth > 0:
                    x = x + columnWidth + spaceX
                    y = rect.y()
                    nextY = y + item.sizeHint().height() + spaceY
                    columnWidth = 0

                heightForWidth += item.sizeHint().height() + spaceY
                if not testOnly:
                    item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))

                y = nextY
                columnWidth = max(columnWidth, item.sizeHint().width())

        if self.orientation == QtCore.Qt.Horizontal:
            return y + lineHeight - rect.y()
        else:
            return heightForWidth - rect.y()


if __name__ == '__main__':

    import sys

    app = QtWidgets.QApplication(sys.argv)
    mainWin = Window()
    mainWin.show()
    sys.exit(app.exec_())




참고

https://github.com/baoboa/pyqt5/blob/master/examples/layouts/flowlayout.py

https://www.pythonfixing.com/2022/04/fixed-pyqt-oriented-flow-layout.html



반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts