PyQt5多點觸控寫字板實現及困惑

Qt支持程序多點觸控,就想使用PyQt5作一個觸控畫板,通過幾番周折,查閱了TouchEvent官方文檔,又參考了一篇QT for Android的例子,採用eventfilter過濾器來識別觸屏事件和鼠標事件,分別做出處理;html

其中鼠標事件(用鼠標繪畫)固定寬度,觸摸事件呢(觸摸屏上繪畫)採用ellipseDiameters()獲取觸摸點的寬度,做爲筆觸寬度。python

 

具體代碼見:GitHubgit

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_())

  

個人困惑:系統默認會把單點觸控事件轉爲鼠標事件,即一點觸控就是再用鼠標繪畫,這樣獲取不到鼠標的寬度,就不能自動設置筆畫粗細,採用各類方法嘗試以後仍無效!github

上圖中黑板擦的實現加了一點做弊,原來的黑板擦會把識別爲鼠標,因此在擦的過程當中,個人另外一隻手指按住屏幕,這時候黑板擦再擦時就被認爲是黑板擦(檢測觸摸點的寬度)app

這種方法並不適用,用沒有其餘方法能避免將單點觸控轉換爲鼠標左鍵呢?ui

相關文章
相關標籤/搜索