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_())
我的困惑:系統默認會把單點觸控事件轉為鼠標事件,即一點觸控就是再用鼠標繪畫,這樣獲取不到鼠標的寬度,就不能自動設置筆畫粗細,采用各種方法嘗試之后仍無效!
上圖中黑板擦的實現加了一點作弊,原來的黑板擦會把識別為鼠標,所以在擦的過程中,我的另一只手指按住屏幕,這時候黑板擦再擦時就被認為是黑板擦(檢測觸摸點的寬度)
這種方法並不適用,用沒有其他方法能避免將單點觸控轉換為鼠標左鍵呢?