PyQt5多点触控写字板实现及困惑

Qt支持程序多点触控,就想使用PyQt5做一个触控画板,经过几番周折,查阅了TouchEvent官方文档,又参考了一篇QT for Android的例子,采用eventfilter过滤器来识别触屏事件和鼠标事件,分别作出处理;

其中鼠标事件(用鼠标绘画)固定宽度,触摸事件呢(触摸屏上绘画)采用ellipseDiameters()获取触摸点的宽度,作为笔触宽度。

 

具体代码见:GitHub

import sys
import math
from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget
from PyQt5.QtGui import QPainter, QPixmap, QPen, QTouchEvent, QColor, QMouseEvent
from PyQt5.QtCore import Qt, QPoint, QEvent, QCoreApplication, QPointF, QLineF
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)


class Winform(QWidget):
    def __init__(self, parent=None):
        super(Winform, self).__init__(parent)
        # self.setWindowTitle("绘图例子")
        self.lastPoint_t = QPointF()  # 触屏点前一点
        self.endPoint_t = QPointF()  # 触屏点后一点
        self.lastPoint_m = QPointF()  # 鼠标点前一点
        self.endPoint_m = QPointF()  # 鼠标点后一点

        self.eraser_width = 30  # 黑板擦默认宽度

        self.pix = QPixmap()  # 画布
        self.penWidth_t = 1;  # 触摸笔画的原始粗细,会随触点粗细变化
        self.penWidth_m = 5;  # 鼠标点的粗细固定

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

    def init(self):

        self.setEnabled(True)
        # 设置接受触摸屏
        self.setAttribute(Qt.WA_AcceptTouchEvents, True)
        QCoreApplication.setAttribute(Qt.AA_SynthesizeTouchForUnhandledMouseEvents, True)  # 禁用将触摸事件转为鼠标事件
        QCoreApplication.setAttribute(Qt.AA_SynthesizeMouseForUnhandledTouchEvents, True)
        # 窗口大小设置为800*600
        self.resize(self.cp.width() * 0.9, self.cp.height() * 0.9)
        self.setWindowOpacity(0.7)  # 设置透明度

        # 画布大小为400*400,背景为白色
        self.pix = QPixmap(self.cp.width(), self.cp.height())

        self.pix.fill(Qt.white)

        self.pp = QPainter(self.pix)
        self.pp.setPen(self.pen)

        self.lines = []

    def paintEvent(self):
        # logger.debug('开始绘制')
        # 根据鼠标指针前后两个位置绘制直线
        distance = int(
            math.sqrt(
                (self.lastPoint_m.x() - self.endPoint_m.x()) ** 2 + (
                    self.lastPoint_m.y() - self.endPoint_m.y()) ** 2)) + 1

        # print('distance', distance)
        distance = math.sqrt(distance)
        if distance > 6:
            distance = 6
        elif distance < 4:
            distance = 4;
        # self.pen.setWidthF(25 / distance) # 笔宽与距离成反比
        self.pen.setWidthF(self.penWidth_m)  # 采用触摸点大小作为笔宽
        self.pp.setPen(self.pen)
        self.pp.drawLine(self.lastPoint_m, self.endPoint_m)
        # self.pix.save('1.png') # 保存图片

        # self.pp.drawPoints(self.lastPoint,self.endPoint)
        self.lastPoint_m = self.endPoint_m

        painter = QPainter(self)
        painter.setPen(self.pen)
        painter.drawPixmap(0, 0, self.pix)
        # print('画在窗体上')

    def paintTouchEvent(self):

        # 根据鼠标指针前后两个位置绘制直线
        distance = int(
            math.sqrt(
                (self.lastPoint_t.x() - self.endPoint_t.x()) ** 2 + (
                    self.lastPoint_t.y() - self.endPoint_t.y()) ** 2)) + 1

        print('distance', distance)
        distance = math.sqrt(distance)
        if distance > 6:
            distance = 6
        elif distance < 4:
            distance = 4;
        # self.pen.setWidthF(18 / distance)
        self.pen.setWidthF(self.penWidth_t)  # 采用触摸点大小作为笔宽
        if self.penWidth_t > self.eraser_width:
            self.pen.setColor(Qt.white)
        else:
            self.pen.setColor(Qt.black)
        self.pp.setPen(self.pen)

        self.pp.drawLine(self.lastPoint_t, self.endPoint_t)
        # self.pix.save('1.png') # 保存图片

        # self.pp.drawPoints(self.lastPoint,self.endPoint)

        painter = QPainter(self)
        painter.setPen(self.pen)
        painter.drawPixmap(0, 0, self.pix)
        # print('画在窗体上')

    def mousePressEvent(self, event):
        # 鼠标左键按下
        if event.button() == Qt.LeftButton:
            # print('左键按下')
            self.lastPoint_m = event.pos()
            self.endPoint_m = self.lastPoint_m

    def mouseMoveEvent(self, event):
        # 鼠标左键按下的同时移动鼠标
        # print(QEvent.TouchBegin)

        if event.buttons() and Qt.LeftButton:
            self.endPoint_m = event.pos()
            # 进行重新绘制
            print('鼠标事件源', event.source())
            self.update()

    def mouseReleaseEvent(self, event):
        # 鼠标左键释放
        if event.button() == Qt.LeftButton:
            self.endPoint_m = event.pos()
            # 进行重新绘制
            self.update()

    def eventFilter(self, watched, event):
        '''
        事件过滤器
        :param watched: 监听到的对象
        :param event: 事件
        :return: 符合条件的事件单独处理,否则交给父类处理
        '''
        # print(type(watched))
        if watched == form:

            if event.type() == QEvent.TouchBegin:
                pass
            if event.type() == QEvent.TouchUpdate or event.type() == QEvent.TouchEnd:
                # print('触点开始', QTouchEvent(event).touchPoints())
                # logger.debug('TouchEvent,触摸点个数:')
                self.addline(QTouchEvent(event))
                return True
            if event.type() == QEvent.MouseButtonDblClick or event.type() == QEvent.MouseMove or event.type() == QEvent.MouseButtonRelease or event.type() == QEvent.MouseButtonPress:

                mouse_event = QMouseEvent(event)
                # logger.debug('鼠标事件!event类型:', event.type())
                if mouse_event != None and mouse_event.source() == Qt.MouseEventSynthesizedBySystem:
                    # 鼠标事件存在并且是有系统将触摸事件整合过来的
                    # logger.debug('触屏事件合成鼠标事件!event类型:', event.type())
                    #
                    # self.addline(QTouchEvent(event))
                    # mouse_event.ignore()
                    # return True
                    pass
            if event.type() == QEvent.Paint:
                # logger.debug('PaintEvent')
                self.paintEvent()
                # self.update()
                return True
        return QWidget.eventFilter(self, watched, event)  # 其他情况会返回系统默认的事件处理方法。

    def addline(self, event):
        '''
        获取触摸点
        :param event: 触摸事件对象
        :return:
        '''
        logger.debug('获取触摸点!')
        touchPoints = event.touchPoints()
        for point in touchPoints:
            # print(point.ellipseDiameters().width())
            self.penWidth_t = point.ellipseDiameters().width()
            # logger.debug('触摸点的直径:', self.penWidth_t)

            self.lastPoint_t = point.lastPos()
            self.endPoint_t = point.pos()
            self.paintTouchEvent()  # 手动调绘图事件
        self.update()  #

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_C:
            self.pix.fill(Qt.white)
            self.update()
        elif event.key() == Qt.Key_Escape:
            self.close()


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

  

我的困惑:系统默认会把单点触控事件转为鼠标事件,即一点触控就是再用鼠标绘画,这样获取不到鼠标的宽度,就不能自动设置笔画粗细,采用各种方法尝试之后仍无效!

上图中黑板擦的实现加了一点作弊,原来的黑板擦会把识别为鼠标,所以在擦的过程中,我的另一只手指按住屏幕,这时候黑板擦再擦时就被认为是黑板擦(检测触摸点的宽度)

这种方法并不适用,用没有其他方法能避免将单点触控转换为鼠标左键呢?

posted @ 2017-12-27 14:24  不见x的心  阅读(2589)  评论(3编辑  收藏  举报