亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解如何在PyQt5中實現(xiàn)平滑滾動的QScrollArea

 更新時間:2023年01月28日 10:07:14   作者:之一Yo  
Qt 自帶的 QScrollArea 滾動時只能在兩個像素節(jié)點之間跳變,看起來很突兀。所以本文將通過定時器,重寫 wheelEvent() 來實現(xiàn)平滑滾動,需要的可以參考一下

平滑滾動的視覺效果

Qt 自帶的 QScrollArea 滾動時只能在兩個像素節(jié)點之間跳變,看起來很突兀。剛開始試著用 QPropertyAnimation 來實現(xiàn)平滑滾動,但是效果不太理想。所以直接開了定時器,重寫 wheelEvent() 來實現(xiàn)平滑滾動。效果如下:

實現(xiàn)思路

定時器溢出是需要時間的,無法立馬處理完所有的滾輪事件,所以自己復制一個滾輪事件 lastWheelEvent,然后計算每一次滾動需要移動的距離和步數(shù),將這兩個參數(shù)綁定在一起放入隊列中。定時器溢出時就將所有未處理完的事件對應的距離累加得到 totalDelta,每個未處理事件的步數(shù)-1,將 totalDelta 和 lastWheelEvent 作為參數(shù)傳入 QWheelEvent的構(gòu)造函數(shù),構(gòu)建出真正需要的滾輪事件 e 并將其發(fā)送到 app 的事件處理隊列中,發(fā)生滾動。

具體代碼

from collections import deque
from enum import Enum
from math import cos, pi

from PyQt5.QtCore import QDateTime, Qt, QTimer, QPoint, pyqtSignal
from PyQt5.QtGui import QWheelEvent
from PyQt5.QtWidgets import QApplication, QScrollArea


class ScrollArea(QScrollArea):
    """ A scroll area which can scroll smoothly """

    def __init__(self, parent=None, orient=Qt.Vertical):
        """
        Parameters
        ----------
        parent: QWidget
            parent widget

        orient: Orientation
            scroll orientation
        """
        super().__init__(parent)
        self.orient = orient
        self.fps = 60
        self.duration = 400
        self.stepsTotal = 0
        self.stepRatio = 1.5
        self.acceleration = 1
        self.lastWheelEvent = None
        self.scrollStamps = deque()
        self.stepsLeftQueue = deque()
        self.smoothMoveTimer = QTimer(self)
        self.smoothMode = SmoothMode(SmoothMode.LINEAR)
        self.smoothMoveTimer.timeout.connect(self.__smoothMove)

    def setSmoothMode(self, smoothMode):
        """ set smooth mode """
        self.smoothMode = smoothMode

    def wheelEvent(self, e):
        if self.smoothMode == SmoothMode.NO_SMOOTH:
            super().wheelEvent(e)
            return

        # push current time to queque
        now = QDateTime.currentDateTime().toMSecsSinceEpoch()
        self.scrollStamps.append(now)
        while now - self.scrollStamps[0] > 500:
            self.scrollStamps.popleft()

        # adjust the acceration ratio based on unprocessed events
        accerationRatio = min(len(self.scrollStamps) / 15, 1)
        if not self.lastWheelEvent:
            self.lastWheelEvent = QWheelEvent(e)
        else:
            self.lastWheelEvent = e

        # get the number of steps
        self.stepsTotal = self.fps * self.duration / 1000

        # get the moving distance corresponding to each event
        delta = e.angleDelta().y() * self.stepRatio
        if self.acceleration > 0:
            delta += delta * self.acceleration * accerationRatio

        # form a list of moving distances and steps, and insert it into the queue for processing.
        self.stepsLeftQueue.append([delta, self.stepsTotal])

        # overflow time of timer: 1000ms/frames
        self.smoothMoveTimer.start(1000 / self.fps)

    def __smoothMove(self):
        """ scroll smoothly when timer time out """
        totalDelta = 0

        # Calculate the scrolling distance of all unprocessed events,
        # the timer will reduce the number of steps by 1 each time it overflows.
        for i in self.stepsLeftQueue:
            totalDelta += self.__subDelta(i[0], i[1])
            i[1] -= 1

        # If the event has been processed, move it out of the queue
        while self.stepsLeftQueue and self.stepsLeftQueue[0][1] == 0:
            self.stepsLeftQueue.popleft()

        # construct wheel event
        if self.orient == Qt.Vertical:
            p = QPoint(0, totalDelta)
            bar = self.verticalScrollBar()
        else:
            p = QPoint(totalDelta, 0)
            bar = self.horizontalScrollBar()

        e = QWheelEvent(
            self.lastWheelEvent.pos(),
            self.lastWheelEvent.globalPos(),
            QPoint(),
            p,
            round(totalDelta),
            self.orient,
            self.lastWheelEvent.buttons(),
            Qt.NoModifier
        )

        # send wheel event to app
        QApplication.sendEvent(bar, e)

        # stop scrolling if the queque is empty
        if not self.stepsLeftQueue:
            self.smoothMoveTimer.stop()

    def __subDelta(self, delta, stepsLeft):
        """ get the interpolation for each step """
        m = self.stepsTotal / 2
        x = abs(self.stepsTotal - stepsLeft - m)

        res = 0
        if self.smoothMode == SmoothMode.NO_SMOOTH:
            res = 0
        elif self.smoothMode == SmoothMode.CONSTANT:
            res = delta / self.stepsTotal
        elif self.smoothMode == SmoothMode.LINEAR:
            res = 2 * delta / self.stepsTotal * (m - x) / m
        elif self.smoothMode == SmoothMode.QUADRATI:
            res = 3 / 4 / m * (1 - x * x / m / m) * delta
        elif self.smoothMode == SmoothMode.COSINE:
            res = (cos(x * pi / m) + 1) / (2 * m) * delta

        return res


class SmoothMode(Enum):
    """ Smooth mode """
    NO_SMOOTH = 0
    CONSTANT = 1
    LINEAR = 2
    QUADRATI = 3
    COSINE = 4

最后

也許有人會發(fā)現(xiàn)動圖的界面和 Groove音樂 很像,實現(xiàn)代碼放在了github。

到此這篇關于詳解如何在PyQt5中實現(xiàn)平滑滾動的QScrollArea的文章就介紹到這了,更多相關PyQt5 QScrollArea內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論