使用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_()
在上面的代码中,添加了mousePressEvent
、mouseMoveEvent
和mouseReleaseEvent
方法来处理鼠标点击、移动和释放事件。