Pyqt5 关于流式布局和滚动条的综合使用

2023-03-07 13:28:14 浏览数 (2)

流式布局

所谓流式布局指的是容器中的元素像流水一样,是可以浮动的,当元素一行或者一列占满的时候,它会自动流入到下一行或者下一列。

pyqt5流式布局

pyqt中采用流式布局的方法原理是,通过contentsMargins获取到子元素距离布局的上下左右宽度,然后我们将所有子元素进行遍历,如果它加上边距可以在一行放入的话,那么就放在一行内,如果不能,就放入到下一行,具体代码如下:

代码语言:javascript复制
m = self.contentsMargins()
effective_rect = rect.adjusted( m.left(),  m.top(), -m.right(), -m.bottom())
        x = effective_rect.x()
        y = effective_rect.y()
        line_height = 0

        for item in self._item_list:
            wid = item.widget()

            space_x = self.spacing()
            space_y = self.spacing()
            if wid is not None:
                space_x  = wid.style().layoutSpacing(
                    QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
                space_y  = wid.style().layoutSpacing(
                    QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)

            next_x = x   item.sizeHint().width()   space_x
            if next_x - space_x > effective_rect.right() and line_height > 0:
                x = effective_rect.x()
                y = y   line_height   space_y
                next_x = x   item.sizeHint().width()   space_x
                line_height = 0

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

            x = next_x
            line_height = max(line_height, item.sizeHint().height())

滚动条的设置

pyqt中有专门的滚动条组件QScrollBar,这个组件需要配合其他组件使用,我们这里使用QScrollArea这个组件进行滚动条的设置。

滚动条的使用方法

  • 首先,我们需要声明QScrollArea
  • 然后,我们需要设置QScrollArea的位置大小
  • 最后,我们将需要产生滚动条的元素放入它的内部。
代码语言:javascript复制
q  = QWidget()
qscrollarea = QtWidgets.QScrollArea(q)     qscrollarea.setGeometry(QRect(50,100,600,500))
qscrollarea.setWidgetResizable(True)
listWidget = QtWidgets.QListWidget()
qscrollarea.setWidget(listWidget)

流式布局和滚动条的结合案例:

在文件当前目录创建一个images文件夹,然后放入想要展示的多张图片,然后执行当前程序,就会看到带有滚动条的流式布局界面。 运行程序,需要安装pyqt5

代码语言:javascript复制
from PyQt5.QtCore import QPoint, QRect, QSize, Qt,pyqtSignal
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (
    QApplication, QLayout, QPushButton, QSizePolicy, QWidget, QGridLayout)
import sys
class Window(QWidget):
    def __init__(self):
        self.imageheight = 100
        super(Window, self).__init__()
        self.resize(800, 600)

        #self.listwidget = QtWidgets.QListWidget(self)
        #self.listwidget.resize(400,300)
        #self.listwidget.setGeometry(QtCore.QRect(0, 0, 300, 200))
        #self.listwidget.addItem("test")

        highlight_dir = r"./images"
        self.files_it = iter([os.path.join(highlight_dir, file)
                              for file in os.listdir(highlight_dir)])

        # self.centralwidget = QtWidgets.QWidget(MainWindow)
        # self.gongzuomoshi = QtWidgets.QGroupBox(self.centralwidget)
        self.listWidget = QtWidgets.QListWidget(self)
        #self.listWidget.setFixedWidth(600)
        container_layout = QtWidgets.QVBoxLayout()
        g = QtWidgets.QGroupBox('')
        l = FlowLayout()
        g.setLayout(l)
        for file in iter(self.files_it):
            pixmap = QtGui.QPixmap(file)
            if not pixmap.isNull():
                autoWidth = pixmap.width()*self.imageheight/pixmap.height()
                label = QtWidgets.QLabel(pixmap=pixmap)
                label.setScaledContents(True)
                label.setFixedHeight(self.imageheight)
                label.setFixedWidth(autoWidth)
                l.addWidget(label)

        container_layout.addWidget(g)
        container_layout.addStretch()
        self.listWidget.setLayout(container_layout)

        self.qscrollarea = QtWidgets.QScrollArea(self)
        self.qscrollarea.setGeometry(QRect(50,100,600,500))
        self.qscrollarea.setWidgetResizable(True)
        self.qscrollarea.setWidget(self.listWidget)
        self.setWindowTitle("Flow Layout Scroll")

class FlowLayout(QLayout):
    """流式布局,使用说明
    1.声明流式布局 layout = FlowLayout
    2.将元素放入流式布局中
    3.将QGroupBox应用流式布局
    4.如果期望水平流式,将QGroupBox放入到QHBoxLayout,如果期望垂直布局,将QGroupBox放入到QVBoxLayout
    """
    heightChanged = pyqtSignal(int)

    def __init__(self, parent=None, margin=0, spacing=-1):
        super().__init__(parent)
        if parent is not None:
            self.setContentsMargins(margin, margin, margin, margin)
        self.setSpacing(spacing)

        self._item_list = []

    def __del__(self):
        while self.count():
            self.takeAt(0)

    def addItem(self, item):  # pylint: disable=invalid-name
        self._item_list.append(item)

    def addSpacing(self, size):  # pylint: disable=invalid-name
        self.addItem(QSpacerItem(size, 0, QSizePolicy.Fixed, QSizePolicy.Minimum))

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

    def itemAt(self, index):  # pylint: disable=invalid-name
        if 0 <= index < len(self._item_list):
            return self._item_list[index]
        return None

    def takeAt(self, index):  # pylint: disable=invalid-name
        if 0 <= index < len(self._item_list):
            return self._item_list.pop(index)
        return None

    def expandingDirections(self):  # pylint: disable=invalid-name,no-self-use
        return Qt.Orientations(Qt.Orientation(0))

    def hasHeightForWidth(self):  # pylint: disable=invalid-name,no-self-use
        return True

    def heightForWidth(self, width):  # pylint: disable=invalid-name
        height = self._do_layout(QRect(0, 0, width, 0), True)
        return height

    def setGeometry(self, rect):  # pylint: disable=invalid-name
        super().setGeometry(rect)
        self._do_layout(rect, False)

    def sizeHint(self):  # pylint: disable=invalid-name
        return self.minimumSize()

    def minimumSize(self):  # pylint: disable=invalid-name
        size = QSize()

        for item in self._item_list:
            minsize = item.minimumSize()
            extent = item.geometry().bottomRight()
            size = size.expandedTo(QSize(minsize.width(), extent.y()))

        margin = self.contentsMargins().left()
        size  = QSize(2 * margin, 2 * margin)
        return size

    def _do_layout(self, rect, test_only=False):
        m = self.contentsMargins()
        effective_rect = rect.adjusted( m.left(),  m.top(), -m.right(), -m.bottom())
        x = effective_rect.x()
        y = effective_rect.y()
        line_height = 0

        for item in self._item_list:
            wid = item.widget()

            space_x = self.spacing()
            space_y = self.spacing()
            if wid is not None:
                space_x  = wid.style().layoutSpacing(
                    QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
                space_y  = wid.style().layoutSpacing(
                    QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)

            next_x = x   item.sizeHint().width()   space_x
            if next_x - space_x > effective_rect.right() and line_height > 0:
                x = effective_rect.x()
                y = y   line_height   space_y
                next_x = x   item.sizeHint().width()   space_x
                line_height = 0

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

            x = next_x
            line_height = max(line_height, item.sizeHint().height())

        new_height = y   line_height - rect.y()
        self.heightChanged.emit(new_height)
        return new_height

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = Window()

    mainWin.show()
    sys.exit(app.exec_())

0 人点赞