zhiyiYo / PyQt-Fluent-Widgets

A fluent design widgets library based on C++ Qt/PyQt/PySide. Make Qt Great Again.
https://qfluentwidgets.com
GNU General Public License v3.0
4.99k stars 464 forks source link

[Bug]: 在FlowLayout中启用QTimer刷新时,最后一个元素的形状会异常 #863

Closed lmhHydropower closed 2 months ago

lmhHydropower commented 2 months ago

What happened?

在FlowLayout中启用QTimer刷新时,最后一个元素的形状会异常,即使加上self.layout_flow.setGeometry(self.layout_flow.geometry())也没有作用。在这个代码中仅是在FlowLayout套了一个SmoothScrollArea。QTimer刷新频率为1s。

Operation System

Windows 11 家庭中文版 23H2 22631.3447

Python Version

3.8.18

PyQt/PySide Version

PyQt 5.15.9

PyQt/PySide-Fluent-Widgets Version

1.3.5

How to Reproduce?

运行下面代码即可复现

https://github.com/zhiyiYo/PyQt-Fluent-Widgets/assets/48676885/158eaa17-3ae3-4539-9e11-59701e814cba

Minimum code

# coding:utf-8
import sys
from PyQt5.QtCore import QEasingCurve, Qt, QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton ,QHBoxLayout
from PyQt5.QtGui import QPalette
from qfluentwidgets import FlowLayout, SmoothScrollArea

class Demo(QWidget):

    def __init__(self):
        super().__init__()
        self.scrollArea = SmoothScrollArea(self)
        self.scrollWidget = QWidget(self.scrollArea)
        self.layout_flow = FlowLayout(self.scrollWidget, isTight=True)
        self.hBoxLayout = QHBoxLayout(self)

        self.scrollArea.setWidget(self.scrollWidget)
        self.scrollArea.setViewportMargins(0, 5, 0, 5)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        pa : QPalette = self.scrollWidget.palette()
        pa.setBrush(QPalette.ColorRole.Window, Qt.GlobalColor.transparent)
        self.scrollWidget.setPalette(pa)

        self.hBoxLayout.setSpacing(0)
        self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
        self.hBoxLayout.addWidget(self.scrollArea)

        # customize animation
        self.layout_flow.setAnimation(250, QEasingCurve.OutQuad)

        self.layout_flow.setContentsMargins(30, 30, 30, 30)
        self.layout_flow.setVerticalSpacing(20)
        self.layout_flow.setHorizontalSpacing(10)

        self.check = QTimer(self)
        self.check.timeout.connect(self.refresh)
        self.check.start(500)

        self.resize(250, 300)
        self.setStyleSheet('Demo{background: white} QPushButton{padding: 5px 10px; font:15px "Microsoft YaHei"}')

    def refresh(self):
        # self.scrollArea.viewport().update()
        self.layout_flow.takeAllWidgets()
        self.layout_flow.addWidget(QPushButton('柳井爱子'))
        self.layout_flow.addWidget(QPushButton('aiko 赛高'))
        self.layout_flow.addWidget(QPushButton('aiko 太爱啦😘'))
        self.layout_flow.addWidget(QPushButton('aiko'))
        self.layout_flow.addWidget(QPushButton('刘静爱'))
        self.layout_flow.addWidget(QPushButton('柳井爱子'))
        self.layout_flow.addWidget(QPushButton('aiko 赛高'))
        self.layout_flow.addWidget(QPushButton('aiko 太爱啦😘'))
        self.layout_flow.addWidget(QPushButton('aiko'))
        self.layout_flow.addWidget(QPushButton('刘静爱'))
        self.layout_flow.addWidget(QPushButton('柳井爱子'))
        self.layout_flow.addWidget(QPushButton('aiko 赛高'))
        self.layout_flow.addWidget(QPushButton('aiko 太爱啦😘'))
        self.layout_flow.addWidget(QPushButton('aiko'))
        self.layout_flow.addWidget(QPushButton('刘静爱'))
        self.layout_flow.addWidget(QPushButton('柳井爱子'))
        self.layout_flow.addWidget(QPushButton('aiko 赛高'))
        self.layout_flow.addWidget(QPushButton('aiko 太爱啦😘'))
        self.layout_flow.addWidget(QPushButton('aiko'))
        self.layout_flow.addWidget(QPushButton('刘静爱'))
        self.layout_flow.addWidget(QPushButton('柳井爱子'))
        self.layout_flow.addWidget(QPushButton('aiko 赛高'))
        # self.layout_flow.addWidget(QPushButton('aiko 太爱啦😘'))

        self.layout_flow.setGeometry(self.layout_flow.geometry())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Demo()
    w.show()
    app.exec_()
rainzee commented 2 months ago

无法复现,我的代码

import random
import sys

from PyQt5.QtCore import QEasingCurve, QTimer
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
from qfluentwidgets import FlowLayout

class Demo(QWidget):
    def __init__(self):
        super().__init__()
        self.refreshTimer = QTimer(self)
        self.refreshTimer.timeout.connect(self.refresh)
        self.refreshTimer.start(1000)
        self.mainLyoaut = FlowLayout(self, needAni=True)

        # customize animation
        self.mainLyoaut.setAnimation(250, QEasingCurve.OutQuad)

        self.mainLyoaut.setContentsMargins(30, 30, 30, 30)
        self.mainLyoaut.setVerticalSpacing(20)
        self.mainLyoaut.setHorizontalSpacing(10)

        self.mainLyoaut.addWidget(QPushButton("aiko"))
        self.mainLyoaut.addWidget(QPushButton("刘静爱"))
        self.mainLyoaut.addWidget(QPushButton("柳井爱子"))
        self.mainLyoaut.addWidget(QPushButton("aiko 赛高"))
        self.mainLyoaut.addWidget(QPushButton("aiko 太爱啦😘"))
        self.mainLyoaut.addWidget(QPushButton("aiko"))
        self.mainLyoaut.addWidget(QPushButton("刘静爱"))
        self.mainLyoaut.addWidget(QPushButton("柳井爱子"))
        self.mainLyoaut.addWidget(QPushButton("aiko 赛高"))
        self.mainLyoaut.addWidget(QPushButton("aiko 太爱啦😘"))
        self.mainLyoaut.addWidget(QPushButton("aiko"))
        self.mainLyoaut.addWidget(QPushButton("刘静爱"))
        self.mainLyoaut.addWidget(QPushButton("柳井爱子"))
        self.mainLyoaut.addWidget(QPushButton("aiko 赛高"))
        self.mainLyoaut.addWidget(QPushButton("aiko 太爱啦😘"))
        self.mainLyoaut.addWidget(QPushButton("aiko"))
        self.mainLyoaut.addWidget(QPushButton("刘静爱"))
        self.mainLyoaut.addWidget(QPushButton("柳井爱子"))
        self.mainLyoaut.addWidget(QPushButton("aiko 赛高"))
        self.mainLyoaut.addWidget(QPushButton("aiko 太爱啦😘"))

        self.resize(250, 300)
        self.setStyleSheet('Demo{background: white} QPushButton{padding: 5px 10px; font:15px "Microsoft YaHei"}')

    def refresh(self):
        self.mainLyoaut.takeAllWidgets()
        for i in range(10):
            self.mainLyoaut.addWidget(QPushButton(str(random.randint(0, 100))))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = Demo()
    w.show()
    app.exec()
lmhHydropower commented 2 months ago

您的代码,我也运行了,确实没有异常问题,但加上滚动区域self.scrollArea = SmoothScrollArea(self) self.scrollWidget = QWidget(self.scrollArea) 即可复现出现问题

rainzee commented 2 months ago

你的写法是错误的,按照我这个写,这里没问题,issue可以关了

import random
from typing import Optional

from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QApplication, QWidget
from qfluentwidgets import FlowLayout, PushButton, SmoothScrollArea

class SegmentedView(SmoothScrollArea):
    def __init__(self, parent: Optional[QWidget] = None) -> None:
        super().__init__(parent=parent)
        self.refreshTimer = QTimer(self)
        self.refreshTimer.timeout.connect(self.refresh)
        self.refreshTimer.start(1000)
        # instant widget
        self.viewContainer = QWidget()
        # instant layout
        self.viewContainerLayout = FlowLayout(self.viewContainer, needAni=True)
        # init
        self._initWidget()
        self._initLayout()

    def _initWidget(self) -> None:
        self.setWidget(self.viewContainer)
        self.setWidgetResizable(True)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setViewportMargins(0, 5, 0, 5)
        self.setStyleSheet("border: none;")
        self.resize(400, 300)

    def _initLayout(self) -> None:
        for _ in range(100):
            self.viewContainerLayout.addWidget(PushButton(str(random.randint(0, 100))))

    def refresh(self):
        self.viewContainerLayout.takeAllWidgets()
        self._initLayout()

if __name__ == "__main__":
    app = QApplication([])
    w = SegmentedView()
    w.show()
    app.exec()
lmhHydropower commented 2 months ago

厉害,👍👍👍。问题解决了,但是不知道为什么,可以点播一下吗😀

lmhHydropower commented 2 months ago

关键在于实例化FlowLayout(self.viewContainer, needAni=True, isTight=True)的时候,去掉isTight=True