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

来源:http://www.cnblogs.com/PyLearn/archive/2017/10/18/7689170.html
-Advertisement-
Play Games

0.目錄 1. "前言" 2. "簡單的畫板1.0" 在定點和移動中的滑鼠所在處畫一條線 3. "簡單的畫板2.0" 在定點和移動中的滑鼠所在處畫一條線 並將畫過的線都保留在窗體上 4. "簡單的畫板3.0" 將按住滑鼠後移動的軌跡保留在窗體上 5. "簡單的畫板4.0" 將按住滑鼠後移動的軌跡保留 ...


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

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在web.xml中配置載入spring時,發現項目無法運行;而去掉spring的配置時,項目可以被初始化。 此時應考慮到spring的配置文件中存在錯誤,以至於web容器無法對項目成功初始化,在web.xml中配置log4j, 根據列印的信息對spring的配置進行修改。 <context-para ...
  • 作者: 阿布 阿布量化版權所有 未經允許 禁止轉載 "abu量化系統github地址(歡迎+star,你的支持是我更新的動力!)" "本節ipython notebook" 上一節使用AbuFactorBuyBreak和AbuFactorSellBreak且混入基本止盈止損策略AbuFactorAt ...
  • 作者: 阿布 阿布量化版權所有 未經允許 禁止轉載 "abu量化系統github地址" (您的star是我的動力!) "本節ipython notebook" "本節界面操作教程視頻播放地址" 量化系統一般分為回測模塊、實盤模塊。 回測模塊:首先交易者編寫實現一個交易策略,它基於一段歷史的交易數據, ...
  • <settings/> settings的配置內容 <typeAliases/> 別名 表 系統定義的typeAliases 別名 映射的類型 _byte byte _long long _short short _int int _integer int _double double _float ...
  • 日誌的列印在軟體開發過程中必不可少,一般分為兩個大類: 操作日誌 系統日誌 操作日誌,主要針對的是用戶,例如在Photoshop軟體中會記錄自己操作的步驟,便於用戶自己查看。 系統日誌,主要針對的是軟體開發人員(包括測試、維護人員),也就是說這部分的日誌用戶是看不到的,也就是我們通常所說的debug ...
  • package com.swift; import com.rupeng.game.GameCore; public class BouncingBall implements Runnable { public static void main(String[] args) { GameCore.... ...
  • 前置知識 ssh工具 連接linux工具 顏色設置, "參考" 中文亂碼, "參考" Linux相關知識 防火牆 的基本使用, "參考" 啟動: 查看狀態: 停止: 禁用: 配置firewalld cmd 查看版本: firewall cmd version 查看幫助: firewall cmd h ...
  • 下麵的解釋中有一個databaseId屬性: 如果配置了 databaseIdProvider,MyBatis 會載入所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。新增,修改和刪除都有這個屬性 一、在configuration ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...