使用PySide2的Widget写的轮播图插件

使用PySide2 Widgets插件实现一个轮播图插件


当涉及到使用python和PySide2 Widgets来实现轮播图控件时,需要考虑如何实现一个可以响应手势(鼠标)左右滑动来切换轮播图的组件。以下我将分享如何使用PySide2创建一个简单的轮播图插件,并添加手势支持来实现左右滑动切换轮播图的功能。

轮播图
首先,安装PySide2。你可以使用pip来安装:

pip install PySide2

接如下时轮播图组件代码实现:

from PySide2 import QtWidgets, QtCore, QtGui
from PySide2.QtCore import QTimer, QRect, QEasingCurve
import math

class ThumbnailLabel(QtWidgets.QLabel):
    def __init__(self, parent=None):
        super(ThumbnailLabel, self).__init__(parent)
        self.margin = 5
        self.borderWidth = 1
        self.borderColor = QtGui.QColor('transparent')

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        image = self.pixmap()
        if not image:
            return
        x = self.margin
        y = self.margin
        width = self.width() - 2 * self.margin
        height = self.height() - 2 * self.margin
        painter.drawPixmap(x, y, width, height, image)
        painter.setPen(QtGui.QPen(self.borderColor, self.borderWidth))
        painter.drawRect(self.rect())


    def mousePressEvent(self, event):
        super(ThumbnailLabel, self).mousePressEvent(event)  # 调用父类的实现
        pass

class CarouselWidget(QtWidgets.QWidget):
    def __init__(self, images, parent=None,
                 width=600, hight=140,
                 thumbnail_width=140,
                 thumbnail_height=140,
                 sensitivity=5,
                 show_img_num=4):
        super(CarouselWidget, self).__init__(parent)
        self.setFixedSize(width, hight)
        self.images = images
        self.image_count = len(images)
        self.thumbnail_width = thumbnail_width
        self.thumbnail_height = thumbnail_height
        self.thumbnail_labels = []
        self.thumbnail_positions = []
        self.current_index = 0
        # self.scroll_speed = scroll_speed  # 滚动速度,数值越大滚动越快

        self.is_dragging = False  # 标识是否正在拖动
        self.drag_start_pos = 0  # 记录鼠标按下时的位置
        self.scroll_position = 0  # 定义 scroll_position 属性
        self.widget_total_length = 0
        self.scroll_current_time = None

        self.sensitivity = sensitivity  # 设置灵敏度系数
        self.is_mouseMove = False
        self.show_img_num = show_img_num
        self.setMouseTracking(True)  # 开启鼠标追踪
        self.setCursor(QtCore.Qt.OpenHandCursor)  # 设置鼠标样式为手形
        self.anima_duration = self.image_count * 2000
        self.init_ui()
        self.start_scrolling()

    # 使用 QtCore.Property 定义一个可动画的属性
    @QtCore.Property(int)
    def scrollable_position(self):
        return self.scroll_position

    @QtCore.Property(int)
    def get_widget_total_length(self):
        return self.widget_total_length

    def init_ui(self):
        self.layout = QtWidgets.QHBoxLayout(self)
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)
        for _ in range(self.image_count):  # 一次显示4张缩略图
            thumbnail_label = ThumbnailLabel(self)
            thumbnail_label.setAlignment(QtCore.Qt.AlignTop)
            self.thumbnail_labels.append(thumbnail_label)
            self.layout.addWidget(thumbnail_label, alignment=QtCore.Qt.AlignTop)

        self.widget_total_length = self.image_count * self.thumbnail_width

        self.setLayout(self.layout)
        self.update_thumbnails()

    def start_scrolling(self):
        self.animation = QtCore.QPropertyAnimation(self, b"scroll_position", self)
        self.animation.setKeyValueAt(0, 0)
        self.animation.setKeyValueAt(1, -self.thumbnail_width * self.image_count)  # 滚动4张缩略图的宽度
        self.animation.setEasingCurve(QtCore.QEasingCurve.Linear)
        self.animation.setDuration(self.anima_duration)  # 动画总时长
        self.animation.valueChanged.connect(self.update_widget_positions)
        self.animation.finished.connect(self.animationFinished)
        # self.animatio
        self.animation.setLoopCount(-1)  # 无限循环
        if self.image_count < self.show_img_num + 1:
            return
        self.animation.start()

        # 定义槽函数,在动画完成时调用
    def animationFinished(self):
        if self.animation.state() == QtCore.QPropertyAnimation.Stopped:
            self.animation.setStartValue(0)
            self.animation.setDuration(self.anima_duration)
            self.animation.setLoopCount(-1)
            self.animation.start()

    def update_widget_positions(self, value):
        # 计算当前显示区域的左右边界
        display_area_left = 0
        display_area_right = self.width()
        self.thumbnail_positions.clear()

        for i, label in enumerate(self.thumbnail_labels):
            if type(value) is type(None):
                value = self.scroll_position
                x = value
            else:
                x = value + i * self.thumbnail_width
            label.setGeometry(QRect(x, 0, self.thumbnail_width, self.thumbnail_height))

            thumb_left_edge = x
            thumb_right_edge = x + self.thumbnail_width
            label_pos = {}
            # 判断缩略图是否与显示区域有重叠
            if thumb_left_edge < display_area_right and thumb_right_edge > display_area_left:
                # 如果有重叠,获取对应的图片索引
                index = (self.current_index + i) % self.image_count
                # 添加对应的图片路径到显示列表
                label_pos['left_edge'] = thumb_left_edge
                label_pos['right_edge'] = thumb_right_edge
                label_pos['path'] = self.images[index]
                self.thumbnail_positions.append(label_pos)
        self.scroll_position = value

    def update_thumbnails(self):
        for i, label in enumerate(self.thumbnail_labels):
            index = (self.current_index + i) % self.image_count
            image_path = self.images[index]
            thumbnail = self.create_thumbnail(image_path)
            label.setPixmap(thumbnail)
            label.setGeometry(QRect(i * self.thumbnail_width, 0, self.thumbnail_width, self.thumbnail_height))

    def create_thumbnail(self, image_path):
        image = QtGui.QPixmap(image_path)
        scaled_image = image.scaled(self.thumbnail_width, self.thumbnail_height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
        return scaled_image

    def update_widgets(self):
        # print("new position:", self.scroll_position)
        for i, label in enumerate(self.thumbnail_labels):
            index = (self.current_index + i) % self.image_count
            image_path = self.images[i]
            thumbnail = self.create_thumbnail(image_path)
            label.setPixmap(thumbnail)
            x = i * self.thumbnail_width + self.scroll_position
            print(f"x:{x}, index:{i}")
            label.setGeometry(QRect(x, 0, self.thumbnail_width, self.thumbnail_height))

    def update_thumbnail_positions(self, drag_distance):
        for i, label in enumerate(self.thumbnail_labels):
            x = drag_distance + i * self.thumbnail_width + self.scroll_position
            label.setGeometry(QRect(x, 0, self.thumbnail_width, self.thumbnail_height))
        self.scroll_position = self.scroll_position + drag_distance
        if abs(self.scroll_position) > self.widget_total_length - (self.show_img_num - 2) * self.thumbnail_width:
            self.scroll_position = 0

    def mousePressEvent(self, event):
        # 只处理左键点击事件
        if event.button() == QtCore.Qt.LeftButton:
            # 暂停滚动动画
            if self.animation.state() == QtCore.QPropertyAnimation.Running:
                self.scroll_current_time = self.animation.currentTime()
                self.animation.pause()

        if self.is_mouseMove == False:
            # self.is_mouseMove = True
            # 获取点击位置的x坐标
            clicked_x = event.x()
            for i, label in enumerate(self.thumbnail_positions):
                left_edge = label['left_edge']
                right_edge = label['right_edge']
                path = label['path']
                if left_edge < clicked_x < right_edge:
                    print(f'current image index:{path}')

        # 如果需要处理拖动,可以在这里设置额外的逻辑
        self.is_dragging = True
        self.drag_start_pos = event.x()

    def mouseMoveEvent(self, event):
        self.is_mouseMove = True
        if self.is_dragging:
            current_pos = event.x()
            drag_distance = current_pos - self.drag_start_pos
            # 根据灵敏度调整拖动距离
            if drag_distance > self.thumbnail_width * self.show_img_num:
                drag_distance = drag_distance - self.thumbnail_width * self.show_img_num
            self.update_thumbnail_positions(drag_distance/self.sensitivity)

    def mouseReleaseEvent(self, event):
        if self.is_dragging:
            self.is_dragging = False
            current_pos = event.x()
            drag_distance = current_pos - self.drag_start_pos
            # 更新当前索引,考虑灵敏度
            # 注意:这里需要确保索引不会超出图片的总数
            self.current_index = max(0, min(self.image_count - self.show_img_num,
                                            self.current_index - (drag_distance // self.thumbnail_width)))
            # 暂停动画,更新动画的起始值,然后重新启动动画
            if self.animation.state() == QtCore.QPropertyAnimation.Paused:
                if self.is_mouseMove:
                    self.is_mouseMove = False
                    self.animation.setStartValue(self.scroll_position)
                    self.animation.setLoopCount(1)
                    x = self.anima_duration * (self.widget_total_length + self.scroll_position) / self.widget_total_length
                    if x <= 5:
                        x = 5
                    self.animation.setDuration(x)
                self.animation.resume()

    def closeEvent(self, event: QtGui.QCloseEvent) -> None:
        if self.animation:
            self.animation.stop()
        super().closeEvent(event)

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    import os
    root_dir = "./test" #换成你图片的目录
    img_path = os.listdir(root_dir)
    image_paths = []
    for img in img_path:
        image_paths.append(os.path.join(root_dir, img))
    slideshow = CarouselWidget(image_paths)
    slideshow.show()
    app.exec_()

在上面的代码中,添加了mousePressEventmouseMoveEventmouseReleaseEvent方法来处理鼠标点击、移动和释放事件。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值