Python3使用PyQt5制作簡單的畫板/手寫板


0.目錄

1.前言

2.簡單的畫板1.0

  • 在定點和移動中的鼠標所在處畫一條線

3.簡單的畫板2.0

  • 在定點和移動中的鼠標所在處畫一條線
  • 並將畫過的線都保留在窗體上

4.簡單的畫板3.0

  • 將按住鼠標后移動的軌跡保留在窗體上

5.簡單的畫板4.0

  • 將按住鼠標后移動的軌跡保留在窗體上
  • 並解決二次作畫時與上次痕跡連續的問題

1.前言

版本:Python3.6.1 + PyQt5

寫一個程序的時候需要用到畫板/手寫板,只需要最簡單的那種。原以為網上到處都是,結果找了好幾天,都沒有找到想要的結果。
網上的要么是非python版的qt程序(要知道qt版本之間差異巨大,還是非同一語言的),改寫難度太大。要么是PyQt4的老程序,很多都已經不能在PyQt5上運行了。要么是大神寫的特別復雜的程序,簡直是直接做出了一個Windows自帶的畫圖版,只能膜拜~

於是我只能在眾多代碼中慢慢尋找自己需要的那一小部分,然后不斷地拼湊,不斷地理解大神的代碼,最終做出這么一個簡單的畫板。望着這個簡單的畫板我真是淚流滿面,中間數十次拼不對拼不全導致程序無數次崩潰,差點就放棄了......

以下是參考網站的名單(名單不分先后順序):
1.PyQt5教程(九)——繪圖https://my.oschina.net/wisedream/blog/549989
2.Qt入門 小程序之畫圖板http://blog.csdn.net/doraemon___/article/details/53026890
3.使用Python編寫簡單的畫圖板程序的示例教程http://www.jb51.net/article/76067.htm
4.qt中函數paintEvent(QPaintEvent*)是不是被系統自動調用的https://zhidao.baidu.com/question/1509518984399472660.html
5.從Qt到PyQthttp://www.cnblogs.com/Finley/p/5268861.html
6.python3+PyQt5 重新實現QT事件處理程序http://blog.sina.com.cn/s/blog_c22e36090102wzxq.html
7.PyQt5入門http://www.docin.com/p-1560265564.html
8.PyQt5教程(一)——第一個PyQt5程序https://my.oschina.net/wisedream/blog/536052
9.PyQt之布局&無邊框&信號http://www.cnblogs.com/codeAB/p/5019439.html
10.QT界面簡單的圖形移動和鼠標繪圖http://blog.csdn.net/K54387/article/details/77926313
11.qt鼠標事件總結(轉)http://blog.sina.com.cn/s/blog_8b97b05e0100v6kk.html
12.一個簡單的畫圖程序http://blog.csdn.net/cutter_point/article/details/43087497
13.Qt雙緩沖機制:實現一個簡單的繪圖工具(純代碼實現)http://blog.csdn.net/rl529014/article/details/51658350

2.簡單的畫板1.0

在簡單的畫板1.0這里,實現的功能是:在定點和移動中的鼠標所在處畫一條線
如圖所示:

鼠標按住移動的話,線也會跟着移動,從這個簡單的程序開始理解PyQt5的運行機制吧。

'''
    簡單的畫板1.0
    功能:在定點和移動中的鼠標所在處畫一條線
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最后修改日期: 2017/10/18
'''
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.QtGui import (QPainter, QPen)
from PyQt5.QtCore import Qt

class Example(QWidget):

    def __init__(self):
        super(Example, self).__init__()

        #resize設置寬高,move設置位置
        self.resize(400, 300)
        self.move(100, 100)
        self.setWindowTitle("簡單的畫板1.0")

        #setMouseTracking設置為False,否則不按下鼠標時也會跟蹤鼠標事件
        self.setMouseTracking(False)

        #設置兩個變量接收移動中的點的x、y坐標
        self.pos_x = 20
        self.pos_y = 20

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)

        #定點(20, 20) 到 (self.pos_x, self.pos_y)之間畫線
        painter.drawLine(20, 20, self.pos_x, self.pos_y)

        painter.end()

    def mouseMoveEvent(self, event):
        '''
            按住鼠標移動事件:更新pos_x和pos_y的值
            調用update()函數在這里相當於調用paintEvent()函數
            每次update()時,之前調用的paintEvent()留下的痕跡都會清空
        '''
        self.pos_x = event.pos().x()
        self.pos_y = event.pos().y()

        self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    pyqt_learn = Example()
    pyqt_learn.show()
    app.exec_()

3.簡單的畫板2.0

從以上的簡單的畫板1.0程序的運行可以發現,按住鼠標移動的時候,線也會跟着移動,那如何讓之前的線留下痕跡,而不是消失呢?
在簡單的畫板2.0中,使用一個列表保存所有移動過的點,然后要畫線的時候,循環遍歷列表,依次畫出列表中點到定點之間的線即可。
效果如圖所示:

'''
    簡單的畫板2.0
    功能:
        在定點和移動中的鼠標所在處畫一條線
        並將畫過的線都保留在窗體上
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最后修改日期: 2017/10/18
'''
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.QtGui import (QPainter, QPen)
from PyQt5.QtCore import Qt

class Example(QWidget):

    def __init__(self):
        super(Example, self).__init__()

        #resize設置寬高,move設置位置
        self.resize(400, 300)
        self.move(100, 100)
        self.setWindowTitle("簡單的畫板2.0")

        #setMouseTracking設置為False,否則不按下鼠標時也會跟蹤鼠標事件
        self.setMouseTracking(False)

        '''
            要想將畫過的線都保留在窗體上
            需要一個列表來保存所有移動過的點
        '''
        self.pos_xy = []

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)

        #循環遍歷self.pos_xy中每個點,然后畫點到定點之間的線
        for pos_tmp in self.pos_xy:
            painter.drawLine(20, 20, pos_tmp[0], pos_tmp[1])

        painter.end()

    def mouseMoveEvent(self, event):
        '''
            按住鼠標移動事件:將當前點添加到pos_xy列表中
            調用update()函數在這里相當於調用paintEvent()函數
            每次update()時,之前調用的paintEvent()留下的痕跡都會清空
        '''
        #中間變量pos_tmp提取當前點
        pos_tmp = (event.pos().x(), event.pos().y())
        #pos_tmp添加到self.pos_xy中
        self.pos_xy.append(pos_tmp)

        self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    pyqt_learn = Example()
    pyqt_learn.show()
    app.exec_()

4.簡單的畫板3.0

好了,接下來進入正題了。簡單的畫板2.0不過是畫鼠標所在點到定點的線,那么如何將按住鼠標后移動的軌跡保留在窗體上?
這個就需要一個列表來保存所有移動過的點,然后把所有相鄰兩個點之間都畫一條線,就能斷斷續續連成鼠標的痕跡了。
效果如圖所示:

是不是就畫出鼠標移動的軌跡了!
不過這也是有缺點的,比如說寫個5看看:

硬生生變成了一個5不是5, 6不是6的數字。這是因為再次提筆畫時,5上面的那一橫跟之前畫的尾巴那里連起來了。好好想想,這個問題怎么解決呢?

'''
    簡單的畫板3.0
    功能:將按住鼠標后移動的軌跡保留在窗體上
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最后修改日期: 2017/10/18
'''
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.QtGui import (QPainter, QPen)
from PyQt5.QtCore import Qt

class Example(QWidget):

    def __init__(self):
        super(Example, self).__init__()

        #resize設置寬高,move設置位置
        self.resize(400, 300)
        self.move(100, 100)
        self.setWindowTitle("簡單的畫板3.0")

        #setMouseTracking設置為False,否則不按下鼠標時也會跟蹤鼠標事件
        self.setMouseTracking(False)

        '''
            要想將按住鼠標后移動的軌跡保留在窗體上
            需要一個列表來保存所有移動過的點
        '''
        self.pos_xy = []

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)

        '''
            首先判斷pos_xy列表中是不是至少有兩個點了
            然后將pos_xy中第一個點賦值給point_start
            利用中間變量pos_tmp遍歷整個pos_xy列表
                point_end = pos_tmp
                畫point_start到point_end之間的線
                point_start = point_end
            這樣,不斷地將相鄰兩個點之間畫線,就能留下鼠標移動軌跡了
        '''
        if len(self.pos_xy) > 1:
            point_start = self.pos_xy[0]
            for pos_tmp in self.pos_xy:
                point_end = pos_tmp
                painter.drawLine(point_start[0], point_start[1], point_end[0], point_end[1])
                point_start = point_end

        painter.end()

    def mouseMoveEvent(self, event):
        '''
            按住鼠標移動事件:將當前點添加到pos_xy列表中
            調用update()函數在這里相當於調用paintEvent()函數
            每次update()時,之前調用的paintEvent()留下的痕跡都會清空
        '''
        #中間變量pos_tmp提取當前點
        pos_tmp = (event.pos().x(), event.pos().y())
        #pos_tmp添加到self.pos_xy中
        self.pos_xy.append(pos_tmp)

        self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    pyqt_learn = Example()
    pyqt_learn.show()
    app.exec_()

5.簡單的畫板4.0

簡單的畫板3.0中有一個致命的問題,那就是連續的問題,比如說要寫一個三位數123:

很難看對不對?

解決這個問題的方法應該是有很多種的,我也沒有深入想,就直接用了這個麻煩點的方法。

我的辦法是當鼠標按住移動然后松開的時候,往保存所有移動過的點的列表中添加一個斷點(-1, -1)。然后在每次畫線的時候,都判斷一下是不是斷點,如果是斷點的話就想辦法跳過去,並且不連續的開始接着畫線。
效果如圖所示:

以下是具體實現代碼:

'''
    簡單的畫板4.0
    功能:
        將按住鼠標后移動的軌跡保留在窗體上
        並解決二次作畫時與上次痕跡連續的問題
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最后修改日期: 2017/10/18
'''
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.QtGui import (QPainter, QPen)
from PyQt5.QtCore import Qt

class Example(QWidget):

    def __init__(self):
        super(Example, self).__init__()

        #resize設置寬高,move設置位置
        self.resize(400, 300)
        self.move(100, 100)
        self.setWindowTitle("簡單的畫板4.0")

        #setMouseTracking設置為False,否則不按下鼠標時也會跟蹤鼠標事件
        self.setMouseTracking(False)

        '''
            要想將按住鼠標后移動的軌跡保留在窗體上
            需要一個列表來保存所有移動過的點
        '''
        self.pos_xy = []

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)

        '''
            首先判斷pos_xy列表中是不是至少有兩個點了
            然后將pos_xy中第一個點賦值給point_start
            利用中間變量pos_tmp遍歷整個pos_xy列表
                point_end = pos_tmp

                判斷point_end是否是斷點,如果是
                    point_start賦值為斷點
                    continue
                判斷point_start是否是斷點,如果是
                    point_start賦值為point_end
                    continue

                畫point_start到point_end之間的線
                point_start = point_end
            這樣,不斷地將相鄰兩個點之間畫線,就能留下鼠標移動軌跡了
        '''
        if len(self.pos_xy) > 1:
            point_start = self.pos_xy[0]
            for pos_tmp in self.pos_xy:
                point_end = pos_tmp

                if point_end == (-1, -1):
                    point_start = (-1, -1)
                    continue
                if point_start == (-1, -1):
                    point_start = point_end
                    continue

                painter.drawLine(point_start[0], point_start[1], point_end[0], point_end[1])
                point_start = point_end
        painter.end()

    def mouseMoveEvent(self, event):
        '''
            按住鼠標移動事件:將當前點添加到pos_xy列表中
            調用update()函數在這里相當於調用paintEvent()函數
            每次update()時,之前調用的paintEvent()留下的痕跡都會清空
        '''
        #中間變量pos_tmp提取當前點
        pos_tmp = (event.pos().x(), event.pos().y())
        #pos_tmp添加到self.pos_xy中
        self.pos_xy.append(pos_tmp)

        self.update()

    def mouseReleaseEvent(self, event):
        '''
            重寫鼠標按住后松開的事件
            在每次松開后向pos_xy列表中添加一個斷點(-1, -1)
            然后在繪畫時判斷一下是不是斷點就行了
            是斷點的話就跳過去,不與之前的連續
        '''
        pos_test = (-1, -1)
        self.pos_xy.append(pos_test)

        self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    pyqt_learn = Example()
    pyqt_learn.show()
    app.exec_()

至此,終於完成了簡單的畫板程序的實現!
另外,如果在使用這個代碼的過程中有遇到什么問題,也歡迎向我反饋。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM