Python PyQt5 实现图片瀑布流(Flow Layout)布局详解
本文介绍如何在 PyQt5 中通过封装自定义 FlowLayout 类来实现图片的瀑布流布局。由于 PyQt5 原生控件不支持该布局,需手动实现布局逻辑以自动计算元素位置并适应窗口宽度。文章提供了完整的 FlowLayout 类代码及图片展示示例,帮助开发者掌握在 Python GUI 开发中处理不定高度图片排列的技巧。
流式布局概述
流式布局
流式布局,亦称瀑布流布局,是网页设计中常见的一种排版方式。其核心原理是保持图片高度固定,而让图片宽度根据容器自适应调整,从而使加载后的图片呈现出如瀑布般整齐下流的视觉效果。
PyQt5 中的流式布局实现
在 PyQt5 中实现流式布局
PyQt5 原生并未提供直接的流式布局控件,因此需要开发者自行封装实现。以下是流式布局类的封装代码示例。
FlowLayout 类封装代码
class FlowLayout(QLayout):
def __init__(self, parent=None, margin=0, spacing=-1):
super(FlowLayout, self).__init__(parent)
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 Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self.doLayout(QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
margin, _, _, _ = self.getContentsMargins()
size += QSize(2 * margin, 2 * margin)
return size
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
lineHeight = 0
for item in self.itemList:
wid = item.widget()
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton,
QSizePolicy.PushButton, Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton,
QSizePolicy.PushButton, Qt.Vertical)
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(QRect(QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
return y + lineHeight - rect.y()
布局类的使用说明
封装完成的流式布局类,只需将其传入相应的布局容器中,它即可自动计算页面元素的位置,并适应窗口的宽度变化。
图片瀑布流显示示例
接下来展示一段使用上述流式布局显示图片的完整代码示例。
完整示例代码
from PyQt5.QtCore import QPoint, QRect, QSize, Qt
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (
QApplication, QLayout, QPushButton, QSizePolicy, QWidget, QGridLayout)
class Window(QWidget):
def __init__(self):
self.imageheight = 100
super(Window, self).__init__()
self.resize(400, 300)
flowLayout = FlowLayout()
highlight_dir = "./"
self.files_it = iter([os.path.join(highlight_dir, file)
for file in os.listdir(highlight_dir)])
print()
for file in iter(self.files_it):
layout = QGridLayout()
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)
print(autoWidth)
label.setFixedWidth(autoWidth)
#label.setFixedSize(100, 50)
layout.addWidget(label)
widget = QWidget()
widget.setLayout(layout)
flowLayout.addWidget(widget)
self.setLayout(flowLayout)
self.setWindowTitle("Flow Layout")
class FlowLayout(QLayout):
def __init__(self, parent=None, margin=0, spacing=-1):
super(FlowLayout, self).__init__(parent)
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 Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self.doLayout(QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
margin, _, _, _ = self.getContentsMargins()
size += QSize(2 * margin, 2 * margin)
return size
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
lineHeight = 0
for item in self.itemList:
wid = item.widget()
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton,
QSizePolicy.PushButton, Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton,
QSizePolicy.PushButton, Qt.Vertical)
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(QRect(QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
return y + lineHeight - rect.y()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWin = Window()
mainWin.show()
sys.exit(app.exec_())
本文关于 Python GUI 框架 PyQt5 实现图片流式布局(瀑布流 FlowLayout)的介绍到此结束。如需了解更多 Python 与 PyQt5 图片布局相关内容,建议搜索本站过往文章或继续浏览后续系列教程,感谢大家的支持。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













