You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让PyQt5应用支持QScrollArea触摸拖动滚动功能?

问题描述

我用PyQt5开发了一款仅支持触摸屏操作的应用(无键盘、鼠标,用户完全通过触摸完成操作)。应用内的QScrollArea包含多个选项,需上下滚动查看全部内容,但用户无法像在Chrome中那样通过触摸拖动实现滚动,只能使用滚动条,操作不便,现寻求解决方案。测试代码如下:

from PyQt5 import QtWidgets
import sys


app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
window.resize(500,500)
window_layout = QtWidgets.QGridLayout()


scrollarea = QtWidgets.QScrollArea()



frame = QtWidgets.QFrame()
frame_layout = QtWidgets.QGridLayout()

button1 = QtWidgets.QPushButton()
button1.setStyleSheet("background-color:rgb(150,0,0);")
button1.setFixedSize(200,200)

button2 = QtWidgets.QPushButton()
button2.setStyleSheet("background-color:rgb(150,0,0);")
button2.setFixedSize(200,200)

button3 = QtWidgets.QPushButton()
button3.setStyleSheet("background-color:rgb(150,0,0);")
button3.setFixedSize(200,200)

button4 = QtWidgets.QPushButton()
button4.setStyleSheet("background-color:rgb(150,0,0);")
button4.setFixedSize(200,200)

frame_layout.addWidget(button1)
frame_layout.addWidget(button2)
frame_layout.addWidget(button3)
frame_layout.addWidget(button4)
frame.setLayout(frame_layout)



scrollarea.setWidget(frame)


window_layout.addWidget(scrollarea)
window.setLayout(window_layout)
window.show()
app.exec()
解决方案

要实现QScrollArea的触摸拖动滚动,可通过自定义QScrollArea子类监听触摸事件,计算拖动偏移量来控制滚动条位置,以下是具体实现:

实现思路

  1. 自定义TouchScrollArea类继承自QScrollArea
  2. 重写touchEvent方法,记录触摸起始位置与滚动条初始状态
  3. 触摸移动时计算位移差,同步更新滚动条垂直位置
  4. 添加简单惯性滚动效果,优化触摸屏操作体验

修改后的完整代码

from PyQt5 import QtWidgets, QtCore
import sys


class TouchScrollArea(QtWidgets.QScrollArea):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.touch_start_pos = None
        self.scroll_start_value = None
        # 惯性滚动相关参数
        self.last_move_pos = None
        self.inactive_timer = QtCore.QTimer(self)
        self.inactive_timer.setInterval(16)  # 约60帧刷新率
        self.inactive_timer.timeout.connect(self._inertia_scroll)
        self.speed = 0

    def touchEvent(self, event):
        if event.type() == QtCore.QEvent.TouchBegin:
            touch_point = event.touchPoints()[0]
            self.touch_start_pos = touch_point.pos().y()
            self.scroll_start_value = self.verticalScrollBar().value()
            self.last_move_pos = self.touch_start_pos
            self.speed = 0
            self.inactive_timer.stop()
        elif event.type() == QtCore.QEvent.TouchUpdate:
            touch_point = event.touchPoints()[0]
            current_pos = touch_point.pos().y()
            delta = current_pos - self.touch_start_pos
            self.verticalScrollBar().setValue(self.scroll_start_value - delta)
            # 计算移动速度用于惯性滚动
            self.speed = current_pos - self.last_move_pos
            self.last_move_pos = current_pos
        elif event.type() == QtCore.QEvent.TouchEnd:
            self.inactive_timer.start()
        event.accept()

    def _inertia_scroll(self):
        # 逐渐减速的惯性滚动逻辑
        if abs(self.speed) < 0.5:
            self.inactive_timer.stop()
            return
        self.verticalScrollBar().setValue(self.verticalScrollBar().value() - self.speed)
        self.speed *= 0.9  # 减速系数,可按需调整


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = QtWidgets.QWidget()
    window.resize(500,500)
    window_layout = QtWidgets.QGridLayout()

    # 使用自定义触摸滚动区域
    scrollarea = TouchScrollArea()
    scrollarea.setWidgetResizable(True)  # 自适应内容大小

    frame = QtWidgets.QFrame()
    frame_layout = QtWidgets.QVBoxLayout()  # 垂直布局让按钮纵向排列,便于滚动

    button1 = QtWidgets.QPushButton()
    button1.setStyleSheet("background-color:rgb(150,0,0);")
    button1.setFixedSize(200,200)

    button2 = QtWidgets.QPushButton()
    button2.setStyleSheet("background-color:rgb(150,0,0);")
    button2.setFixedSize(200,200)

    button3 = QtWidgets.QPushButton()
    button3.setStyleSheet("background-color:rgb(150,0,0);")
    button3.setFixedSize(200,200)

    button4 = QtWidgets.QPushButton()
    button4.setStyleSheet("background-color:rgb(150,0,0);")
    button4.setFixedSize(200,200)

    frame_layout.addWidget(button1)
    frame_layout.addWidget(button2)
    frame_layout.addWidget(button3)
    frame_layout.addWidget(button4)
    frame.setLayout(frame_layout)

    scrollarea.setWidget(frame)

    window_layout.addWidget(scrollarea)
    window.setLayout(window_layout)
    window.show()
    app.exec()

关键说明

  • 自定义的TouchScrollArea类处理了触摸全周期事件,通过位移计算直接控制滚动条位置
  • 惯性滚动效果让触摸拖动体验更贴近浏览器的流畅感
  • 将原代码的QGridLayout改为QVBoxLayout,确保按钮纵向排列,滚动逻辑更直观(如需横向滚动,可修改为监听水平方向位移)
  • setWidgetResizable(True)确保滚动区域能根据内容自动调整尺寸

内容的提问来源于stack exchange,提问作者Mohammed almalki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:16:02