PyQt5 PySide2 触摸测试功能的实现

注:2023.11.1更新:见第四大点
更新内容:1. 增加画线轨迹 2. 支持鼠标事件和触摸事件获取坐标 3. 删去style.py文件,仅需main.py文件即可运行

一、前言

该测试功能是Linux产测软件的一个子功能,主要涉及:
140行代码
PySide2/PyQt5 的Event、信号和槽、QLabel,QWidget。
QLabel实现每个小框,QWidget作为主界面

PySide2和Pyqt5只要把import的包改好,代码是可以通用的。

手指滑动,手指坐标所在方块的颜色发生改变,如果手指划出方块区域,则所有已染色方块清空颜色,松开事件同理

实现效果:请添加图片描述

请添加图片描述

二、实现思路:

  1. 使用GridLayout,绘制四周方格,方格初始化为黄色,点击到则为红色

  2. 重写Label,对每个label定义x,y表示所在gridlayout中的位置
    如x = 1,y = 3表示label位于第一行第三列,left,right,top,bottom表示方格四条边的实际坐标

  3. 重写moveEvent事件和ReleaseEvent事件,
    moveEvnet事件:
    每次鼠标移动即触发moveEvent,则发出已经定义的信号move_signal
    move_singal.connect(self.manager_touch)
    manager_touch是判断当前鼠标是否位于方格内,如果在方格内则染色,如果鼠标划入空白部分,则清空所有方块颜色
    另对染色label进行计数,如果达到绘制的label的总数量则发出信号返回测试通过

    ReleaseEvent事件:
    鼠标左键松开(对应手指离开屏幕)即触发ReleaseEvent,则清空已染色方块,另把已染色方块数量清零

  4. clear_sources: 在退出页面前应将保存label的列表即self.touch_labels清空,否则重启该界面会报错

三、实现代码:

style.py

COLOR_RED = "color: rgb(255, 255, 255); background-color: #FF0000;"
COLOR_YELLOW = "color: rgb(255, 255, 255); background-color: #FFFF00;"
COLOR_WHITE = "color: rgb(255, 255, 255); background-color: #FFFFFF;"
COLOR_BLACK = "color: rgb(255, 255, 255); background-color: #000000;"
COLOR_GREEN = "color: rgb(255, 255, 255); background-color: #00FF00;"
COLOR_BLUE = "color: rgb(255, 255, 255); background-color:  #0000FF;"
COLOR_PURPLE = "color: rgb(255, 255, 255); background-color: #871F78;"

main.py

import sys
from functools import partial
from PySide2 import QtWidgets
from PySide2.QtCore import Qt, Signal, QEvent
from PySide2.QtGui import QCursor, QFont
from PySide2.QtWidgets import QGridLayout, QPushButton, QWidget, QApplication
import style


class TouchLabel(QtWidgets.QLabel):
    def __init__(self, i, j, top, bottom, left, right, target, parent=None):
        super(TouchLabel, self).__init__(parent)
        self.setStyleSheet(style.COLOR_YELLOW)
        self.flag = False  # 如果被滑过则置为True
        self.target = target
        self.x = i  # x,y 代表在gridLayout中的位置
        self.y = j

        self.flag = False
        self.left = left
        self.right = right
        self.top = top
        self.bottom = bottom


class TouchWidget(QWidget):
    touch_labels = []  # 存储label的列表
    sum_moved_labels = 0
    move_signal = Signal(QEvent)
    out_signal = Signal()
    release_signal = Signal()

    def __init__(self):
        super().__init__()
        self.setAttribute(Qt.WA_AcceptTouchEvents, True)
        self.sum_labels = 0  # 统计染色的方块个数
        self.target = 0
        print("start TouchWidget")
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.showFullScreen()
        self.init_parameters()  # 初始化gridlayout的参数
        print("绘制的按钮个数为:" + str(self.target))

    def init_parameters(self):
        self.layout = QGridLayout(self)
        self.layout.setMargin(0)  # 设置widget离窗口的距离
        self.layout.setSpacing(0)  # 设置控件间距
        self.row = 20  # 纵向的按钮数量
        self.column = 20  # 横向的按钮数量
        desktop = QtWidgets.QApplication.desktop()
        global label_width, label_height
        label_width = desktop.width() / self.column  # 列宽
        label_height = desktop.height() / self.row  # 行高
        self.target = self.row * 2 + self.column * 2 - 4

        self.touch_labels.clear()
        self.init_touch_label(self.row, self.column, label_height, label_width)
        self.add_touch_label(label_width, label_height)

    def init_touch_label(self, row, column, label_height, label_width):
        desktop = QtWidgets.QApplication.desktop()
        height = desktop.height()
        width = desktop.width()
        for i in range(row):
            for j in range(column):
                if i == 0 and j == 0:
                    label = TouchLabel(i, j, 0, label_height, 0, label_width, self.target)
                    self.touch_labels.append(label)
                elif i == 0 and j != 0:
                    label = TouchLabel(i, j, 0, label_height, j * label_width, (j + 1) * label_width, self.target)
                    self.touch_labels.append(label)
                elif i != 0 and j == 0:
                    label = TouchLabel(i, j, i * label_height, (i + 1) * label_height, 0, label_width, self.target)
                    self.touch_labels.append(label)
                elif i == row - 1 and j != 0:
                    label = TouchLabel(i, j, height - label_height, height, j * label_width, (j + 1) * label_width,
                                       self.target)
                    self.touch_labels.append(label)
                elif j == column - 1 and i != 0:
                    label = TouchLabel(i, j, i * label_height, (i + 1) * label_height, width - label_width, width,
                                       self.target)
                    self.touch_labels.append(label)

    def add_touch_label(self, label_width, label_height):
        for label in self.touch_labels:
            self.layout.addWidget(label, label.x, label.y, 1, 1)
        nopass_quit_btn = QPushButton("如果测试不通过,点击此按钮退出")
        nopass_quit_btn.setFont(QFont('Arial', 16))
        nopass_quit_btn.setMinimumSize(label_width, label_height)
        self.layout.addWidget(nopass_quit_btn, self.row / 2 - 1, self.column / 2 - 1, 3, 3)
        nopass_quit_btn.clicked.connect(partial(self.on_result, "TEST NOT PASS"))

        self.move_signal.connect(self.manager_touch)
        self.out_signal.connect(partial(self.on_result, "TEST PASS"))
        self.release_signal.connect(self.clear_label_status)

    def manager_touch(self, event):
        desktop = QtWidgets.QApplication.desktop()
        frontier_top = label_height
        frontier_bottom = desktop.height() - label_height
        frontier_left = label_width
        frontier_right = desktop.width() - label_width

        x = event.pos().x()
        y = event.pos().y()
        print("inner  x: " + str(x) + "  y: " + str(y) + '\n')

        if frontier_left <= x <= frontier_right and frontier_top <= y <= frontier_bottom:
            self.clear_label_status()
            print("手指划出边界,清空所有方块颜色")

        for label in self.touch_labels:
            if label.left <= x <= label.right and label.top <= y <= label.bottom:
                if label.flag:
                    continue
                else:
                    label.setStyleSheet(style.COLOR_RED)
                    TouchWidget.sum_moved_labels += 1
                    label.flag = True
            if TouchWidget.sum_moved_labels == self.target:
                self.out_signal.emit()

    def clear_label_status(self):
        for label in self.touch_labels:
            label.setStyleSheet(style.COLOR_YELLOW)
            label.flag = False
        TouchWidget.sum_moved_labels = 0

    def clear_sources(self):
        TouchWidget.sum_moved_labels = 0
        self.touch_labels.clear()
        self.sum_moved_labels = 0
        self.target = 0

    def mouseClickEvent(self,event):
        self.move_signal.emit(event)

    def mouseMoveEvent(self, event):
        print("outer:  x: " + str(event.pos().x()) + "  y: " + str(event.pos().y()) + '\n')
        self.move_signal.emit(event)

    def mouseReleaseEvent(self, event):
        self.release_signal.emit()
        print("释放焦点,清空所有方块颜色")

    def on_result(self, ret):
        self.clear_sources()
        print(ret)
        self.close()


if __name__ == '__main__':
    app = QApplication()
    w = TouchWidget()
    w.show()
    app.exec_()

四、更新

2023.11.1 更新代码:
使用python3 main.py即可运行 注意设置屏幕大小,比如我的显示器是1920*1080,那么就对应41、42行设置

self.setMinimumWidth(1920)
self.setMinimumHeight(1080)

更新后效果图:
在这里插入图片描述

main.py:

import sys
from functools import partial

from PySide2 import QtWidgets
from PySide2.QtCore import Qt, Signal, QEvent, QLineF, QPointF
from PySide2.QtGui import QFont, QPixmap, QPainter, QPen, QColor, QTouchEvent
from PySide2.QtWidgets import QGridLayout, QPushButton, QWidget, QApplication


class TouchLabel(QtWidgets.QLabel):
    def __init__(self, i, j, top, bottom, left, right, target, parent=None):
        super(TouchLabel, self).__init__(parent)
        self.setStyleSheet("background-color: rgba(255, 255, 0, 128);")  # 将背景色设置为半透明的黄色
        self.flag = False  # 如果被滑过则置为True
        self.target = target
        self.x = i  # x,y 代表在gridLayout中的位置
        self.y = j

        self.flag = False
        self.left = left
        self.right = right
        self.top = top
        self.bottom = bottom


class TouchWidget(QWidget):
    touch_labels = []  # 存储label的列表
    sum_moved_labels = 0
    move_signal = Signal(QEvent)
    out_signal = Signal()
    release_signal = Signal()

    def __init__(self, parent=None):
        super(TouchWidget, self).__init__(parent)
        self.over_border_flag = True
        self.desktop = None
        self.setAttribute(Qt.WA_AcceptTouchEvents, True)
        self.sum_labels = 0  # 统计染色的方块个数
        self.target = 0
        self.showFullScreen()
        self.setMinimumWidth(1920)
        self.setMinimumHeight(1080)
        self.init_parameters()  # 初始化gridlayout的参数
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.lastPoint_t = QPointF()  # 触屏点前一点
        self.endPoint_t = QPointF()  # 触屏点后一点
        self.lastPoint_m = QPointF()  # 鼠标点前一点
        self.endPoint_m = QPointF()  # 鼠标点后一点
        self.pix = QPixmap()  # 画布
        self.penWidth = 5

        desktop = QtWidgets.QApplication.desktop()

        self.frontier_top = label_height
        self.frontier_bottom = desktop.height() - label_height
        self.frontier_left = label_width
        self.frontier_right = desktop.width() - label_width

        self.pen = QPen(Qt.black, 6, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)  # 画笔
        self.pen.setColor(QColor(0, 0, 0))  # 设置初始颜色
        self.init()

    def init(self):
        self.setAttribute(Qt.WA_AcceptTouchEvents, True)
        self.setWindowOpacity(1)  # 设置透明度
        self.desktop = QtWidgets.QApplication.desktop()  # 分辨率
        self.pix = QPixmap(self.desktop.width(), self.desktop.height())
        self.pix.fill(Qt.white)
        self.pp = QPainter(self.pix)
        self.pp.setPen(self.pen)
        self.lines = []

    def init_parameters(self):
        self.layout = QGridLayout(self)
        self.layout.setMargin(0)  # 设置widget离窗口的距离
        self.layout.setSpacing(0)  # 设置控件间距
        self.row = 20  # 纵向的按钮数量
        self.column = 20  # 横向的按钮数量
        desktop = QtWidgets.QApplication.desktop()
        global label_width, label_height
        label_width = desktop.width() / self.column  # 列宽
        label_height = desktop.height() / self.row  # 行高
        self.target = self.row * 2 + self.column * 2 - 4

        self.touch_labels.clear()
        self.init_touch_label(self.row, self.column, label_height, label_width)
        self.add_touch_label(label_width, label_height)

    def init_touch_label(self, row, column, label_height, label_width):
        desktop = QtWidgets.QApplication.desktop()
        height = desktop.height()
        width = desktop.width()
        for i in range(row):
            for j in range(column):
                if i == 0 and j == 0:
                    label = TouchLabel(i, j, 0, label_height, 0, label_width, self.target)
                    self.touch_labels.append(label)
                elif i == 0 and j != 0:
                    label = TouchLabel(i, j, 0, label_height, j * label_width, (j + 1) * label_width, self.target)
                    self.touch_labels.append(label)
                elif i != 0 and j == 0:
                    label = TouchLabel(i, j, i * label_height, (i + 1) * label_height, 0, label_width, self.target)
                    self.touch_labels.append(label)
                elif i == row - 1 and j != 0:
                    label = TouchLabel(i, j, height - label_height, height, j * label_width, (j + 1) * label_width,
                                       self.target)
                    self.touch_labels.append(label)
                elif j == column - 1 and i != 0:
                    label = TouchLabel(i, j, i * label_height, (i + 1) * label_height, width - label_width, width,
                                       self.target)
                    self.touch_labels.append(label)

    def add_touch_label(self, label_width, label_height):
        for label in self.touch_labels:
            self.layout.addWidget(label, label.x, label.y, 1, 1)
        nopass_quit_btn = QPushButton("如果测试不通过,点击此按钮退出")
        nopass_quit_btn.setFont(QFont('Arial', 16))
        nopass_quit_btn.setMinimumSize(label_width, label_height)
        self.layout.addWidget(nopass_quit_btn, self.row / 2 - 1, self.column / 2 - 1, 3, 3)
        nopass_quit_btn.clicked.connect(partial(self.on_result, "TEST NOT PASS"))

        self.move_signal.connect(self.manager_touch)
        self.out_signal.connect(partial(self.on_result, "TEST PASS"))
        self.release_signal.connect(self.clear_label_status)

    def manager_touch(self, event):
        x = event.pos().x()
        y = event.pos().y()
        if self.frontier_left <= x <= self.frontier_right and self.frontier_top <= y <= self.frontier_bottom:
            if self.over_border_flag:
                return
            self.over_border_flag = True
            self.clear_label_status()
            print('hello')
            return

        self.over_border_flag = False
        for label in self.touch_labels:
            if label.left <= x <= label.right and label.top <= y <= label.bottom:
                if label.flag:
                    continue
                else:
                    label.setStyleSheet("background-color: rgba(255, 0, 0, 128);")  # 将背景色设置为半透明的红色
                    TouchWidget.sum_moved_labels += 1
                    label.flag = True
            if TouchWidget.sum_moved_labels == self.target:
                self.out_signal.emit()

    def clear_label_status(self):
        for label in self.touch_labels:
            label.setStyleSheet("background-color: rgba(255, 255, 0, 128);")  # 将背景色设置为半透明的黄色
            label.flag = False
        TouchWidget.sum_moved_labels = 0

    def clear_sources(self):
        TouchWidget.sum_moved_labels = 0
        self.touch_labels.clear()
        self.sum_moved_labels = 0
        self.target = 0

    def on_result(self, ret):
        self.clear_sources()
        self.close()

    def paintEvent(self):
        self.pen.setWidthF(self.penWidth)  # 采用触摸点大小作为笔宽
        self.pp.setPen(self.pen)
        self.pp.drawLine(self.lastPoint_m, self.endPoint_m)
        self.lastPoint_m = self.endPoint_m

        painter = QPainter(self)
        painter.setPen(self.pen)
        painter.drawPixmap(0, 0, self.pix)

    def paintTouchEvent(self):
        self.pen.setWidthF(self.penWidth)  # 采用触摸点大小作为笔宽
        self.pp.setPen(self.pen)
        self.pp.drawLine(self.lastPoint_t, self.endPoint_t)

        painter = QPainter(self)
        painter.setPen(self.pen)
        painter.drawPixmap(0, 0, self.pix)

    def mousePressEvent(self, event):
        self.pix.fill(Qt.white)
        if event.button() == Qt.LeftButton:
            self.lastPoint_m = event.pos()
            self.endPoint_m = self.lastPoint_m

    def mouseMoveEvent(self, event):
        if event.buttons() and Qt.LeftButton:
            self.endPoint_m = event.pos()
            self.update()
        # if not self.frontier_left <= event.pos().x() <= self.frontier_right or not self.frontier_top <= event.pos().y() <= self.frontier_bottom:
        self.move_signal.emit(event)

    def mouseReleaseEvent(self, event):
        self.release_signal.emit()
        if event.button() == Qt.LeftButton:
            self.endPoint_m = event.pos()
            self.update()

    def eventFilter(self, watched, event):
        if watched == form:
            if event.type() == QEvent.TouchBegin:
                pass
            if event.type() == QEvent.TouchUpdate or event.type() == QEvent.TouchEnd:
                self.addline(QTouchEvent(event))
                return True
            if event.type() == QEvent.Paint:
                self.paintEvent()
                return True
        return QWidget.eventFilter(self, watched, event)  # 其他情况会返回系统默认的事件处理方法。

    def addline(self, event):
        touchPoints = event.touchPoints()
        for point in touchPoints:
            self.penWidth = point.ellipseDiameters().width()
            self.lastPoint_t = point.lastPos()
            self.endPoint_t = point.pos()
            line = QLineF()
            line.setP1(self.lastPoint_t)
            line.setP2(self.lastPoint_t)
            self.lines.append(line)
            self.paintTouchEvent()  # 手动调绘图事件


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = TouchWidget()
    app.installEventFilter(form)  # 监听form的所有事件
    form.show()
    sys.exit(app.exec_())

五、参考文档

实现触摸轨迹追踪:https://www.cnblogs.com/Alier/p/8125524.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值