Python3使用PyQt5制作簡單的畫板/手寫板實(shí)例
1.前言
版本:Python3.6.1 + PyQt5
寫一個程序的時候需要用到畫板/手寫板,只需要最簡單的那種。原以為網(wǎng)上到處都是,結(jié)果找了好幾天,都沒有找到想要的結(jié)果。
網(wǎng)上的要么是非python版的qt程序(要知道qt版本之間差異巨大,還是非同一語言的),改寫難度太大。要么是PyQt4的老程序,很多都已經(jīng)不能在PyQt5上運(yùn)行了。要么是大神寫的特別復(fù)雜的程序,簡直是直接做出了一個Windows自帶的畫圖版,只能膜拜~
于是我只能在眾多代碼中慢慢尋找自己需要的那一小部分,然后不斷地拼湊,不斷地理解大神的代碼,最終做出這么一個簡單的畫板。望著這個簡單的畫板我真是淚流滿面,中間數(shù)十次拼不對拼不全導(dǎo)致程序無數(shù)次崩潰,差點(diǎn)就放棄了......
2.簡單的畫板1.0
在簡單的畫板1.0這里,實(shí)現(xiàn)的功能是:在定點(diǎn)和移動中的鼠標(biāo)所在處畫一條線
如圖所示:
鼠標(biāo)按住移動的話,線也會跟著移動,從這個簡單的程序開始理解PyQt5的運(yùn)行機(jī)制吧。
''' 簡單的畫板1.0 功能:在定點(diǎn)和移動中的鼠標(biāo)所在處畫一條線 作者: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設(shè)置寬高,move設(shè)置位置 self.resize(400, 300) self.move(100, 100) self.setWindowTitle("簡單的畫板1.0") #setMouseTracking設(shè)置為False,否則不按下鼠標(biāo)時也會跟蹤鼠標(biāo)事件 self.setMouseTracking(False) #設(shè)置兩個變量接收移動中的點(diǎn)的x、y坐標(biāo) 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) #定點(diǎn)(20, 20) 到 (self.pos_x, self.pos_y)之間畫線 painter.drawLine(20, 20, self.pos_x, self.pos_y) painter.end() def mouseMoveEvent(self, event): ''' 按住鼠標(biāo)移動事件:更新pos_x和pos_y的值 調(diào)用update()函數(shù)在這里相當(dāng)于調(diào)用paintEvent()函數(shù) 每次update()時,之前調(diào)用的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程序的運(yùn)行可以發(fā)現(xiàn),按住鼠標(biāo)移動的時候,線也會跟著移動,那如何讓之前的線留下痕跡,而不是消失呢?
在簡單的畫板2.0中,使用一個列表保存所有移動過的點(diǎn),然后要畫線的時候,循環(huán)遍歷列表,依次畫出列表中點(diǎn)到定點(diǎn)之間的線即可。
效果如圖所示:
''' 簡單的畫板2.0 功能: 在定點(diǎn)和移動中的鼠標(biāo)所在處畫一條線 并將畫過的線都保留在窗體上 作者: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設(shè)置寬高,move設(shè)置位置 self.resize(400, 300) self.move(100, 100) self.setWindowTitle("簡單的畫板2.0") #setMouseTracking設(shè)置為False,否則不按下鼠標(biāo)時也會跟蹤鼠標(biāo)事件 self.setMouseTracking(False) ''' 要想將畫過的線都保留在窗體上 需要一個列表來保存所有移動過的點(diǎn) ''' self.pos_xy = [] def paintEvent(self, event): painter = QPainter() painter.begin(self) pen = QPen(Qt.black, 2, Qt.SolidLine) painter.setPen(pen) #循環(huán)遍歷self.pos_xy中每個點(diǎn),然后畫點(diǎn)到定點(diǎn)之間的線 for pos_tmp in self.pos_xy: painter.drawLine(20, 20, pos_tmp[0], pos_tmp[1]) painter.end() def mouseMoveEvent(self, event): ''' 按住鼠標(biāo)移動事件:將當(dāng)前點(diǎn)添加到pos_xy列表中 調(diào)用update()函數(shù)在這里相當(dāng)于調(diào)用paintEvent()函數(shù) 每次update()時,之前調(diào)用的paintEvent()留下的痕跡都會清空 ''' #中間變量pos_tmp提取當(dāng)前點(diǎn) 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
好了,接下來進(jìn)入正題了。簡單的畫板2.0不過是畫鼠標(biāo)所在點(diǎn)到定點(diǎn)的線,那么如何將按住鼠標(biāo)后移動的軌跡保留在窗體上?
這個就需要一個列表來保存所有移動過的點(diǎn),然后把所有相鄰兩個點(diǎn)之間都畫一條線,就能斷斷續(xù)續(xù)連成鼠標(biāo)的痕跡了。
效果如圖所示:
是不是就畫出鼠標(biāo)移動的軌跡了!
不過這也是有缺點(diǎn)的,比如說寫個5看看:
硬生生變成了一個5不是5, 6不是6的數(shù)字。這是因?yàn)樵俅翁峁P畫時,5上面的那一橫跟之前畫的尾巴那里連起來了。好好想想,這個問題怎么解決呢?
''' 簡單的畫板3.0 功能:將按住鼠標(biāo)后移動的軌跡保留在窗體上 作者: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設(shè)置寬高,move設(shè)置位置 self.resize(400, 300) self.move(100, 100) self.setWindowTitle("簡單的畫板3.0") #setMouseTracking設(shè)置為False,否則不按下鼠標(biāo)時也會跟蹤鼠標(biāo)事件 self.setMouseTracking(False) ''' 要想將按住鼠標(biāo)后移動的軌跡保留在窗體上 需要一個列表來保存所有移動過的點(diǎn) ''' self.pos_xy = [] def paintEvent(self, event): painter = QPainter() painter.begin(self) pen = QPen(Qt.black, 2, Qt.SolidLine) painter.setPen(pen) ''' 首先判斷pos_xy列表中是不是至少有兩個點(diǎn)了 然后將pos_xy中第一個點(diǎn)賦值給point_start 利用中間變量pos_tmp遍歷整個pos_xy列表 point_end = pos_tmp 畫point_start到point_end之間的線 point_start = point_end 這樣,不斷地將相鄰兩個點(diǎn)之間畫線,就能留下鼠標(biāo)移動軌跡了 ''' 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): ''' 按住鼠標(biāo)移動事件:將當(dāng)前點(diǎn)添加到pos_xy列表中 調(diào)用update()函數(shù)在這里相當(dāng)于調(diào)用paintEvent()函數(shù) 每次update()時,之前調(diào)用的paintEvent()留下的痕跡都會清空 ''' #中間變量pos_tmp提取當(dāng)前點(diǎn) 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中有一個致命的問題,那就是連續(xù)的問題,比如說要寫一個三位數(shù)123:
很難看對不對?
解決這個問題的方法應(yīng)該是有很多種的,我也沒有深入想,就直接用了這個麻煩點(diǎn)的方法。
我的辦法是當(dāng)鼠標(biāo)按住移動然后松開的時候,往保存所有移動過的點(diǎn)的列表中添加一個斷點(diǎn)(-1, -1)。然后在每次畫線的時候,都判斷一下是不是斷點(diǎn),如果是斷點(diǎn)的話就想辦法跳過去,并且不連續(xù)的開始接著畫線。
效果如圖所示:
以下是具體實(shí)現(xiàn)代碼:
''' 簡單的畫板4.0 功能: 將按住鼠標(biāo)后移動的軌跡保留在窗體上 并解決二次作畫時與上次痕跡連續(xù)的問題 作者: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設(shè)置寬高,move設(shè)置位置 self.resize(400, 300) self.move(100, 100) self.setWindowTitle("簡單的畫板4.0") #setMouseTracking設(shè)置為False,否則不按下鼠標(biāo)時也會跟蹤鼠標(biāo)事件 self.setMouseTracking(False) ''' 要想將按住鼠標(biāo)后移動的軌跡保留在窗體上 需要一個列表來保存所有移動過的點(diǎn) ''' self.pos_xy = [] def paintEvent(self, event): painter = QPainter() painter.begin(self) pen = QPen(Qt.black, 2, Qt.SolidLine) painter.setPen(pen) ''' 首先判斷pos_xy列表中是不是至少有兩個點(diǎn)了 然后將pos_xy中第一個點(diǎn)賦值給point_start 利用中間變量pos_tmp遍歷整個pos_xy列表 point_end = pos_tmp 判斷point_end是否是斷點(diǎn),如果是 point_start賦值為斷點(diǎn) continue 判斷point_start是否是斷點(diǎn),如果是 point_start賦值為point_end continue 畫point_start到point_end之間的線 point_start = point_end 這樣,不斷地將相鄰兩個點(diǎn)之間畫線,就能留下鼠標(biāo)移動軌跡了 ''' 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): ''' 按住鼠標(biāo)移動事件:將當(dāng)前點(diǎn)添加到pos_xy列表中 調(diào)用update()函數(shù)在這里相當(dāng)于調(diào)用paintEvent()函數(shù) 每次update()時,之前調(diào)用的paintEvent()留下的痕跡都會清空 ''' #中間變量pos_tmp提取當(dāng)前點(diǎn) 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): ''' 重寫鼠標(biāo)按住后松開的事件 在每次松開后向pos_xy列表中添加一個斷點(diǎn)(-1, -1) 然后在繪畫時判斷一下是不是斷點(diǎn)就行了 是斷點(diǎn)的話就跳過去,不與之前的連續(xù) ''' 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_()
至此,終于完成了簡單的畫板程序的實(shí)現(xiàn)!
另外,如果在使用這個代碼的過程中有遇到什么問題,也歡迎向我反饋。
以上這篇Python3使用PyQt5制作簡單的畫板/手寫板實(shí)例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
在pytorch中對非葉節(jié)點(diǎn)的變量計算梯度實(shí)例
今天小編就為大家分享一篇在pytorch中對非葉節(jié)點(diǎn)的變量計算梯度實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01python中import,from……import的使用詳解
這篇文章主要介紹了python中import,from……import的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02解決pytorch下只打印tensor的數(shù)值不打印出device等信息的問題
這篇文章主要介紹了解決pytorch下只打印tensor的數(shù)值不打印出device等信息的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-05-05Python詳解文字轉(zhuǎn)語音的實(shí)現(xiàn)
在自然語言處理上,文字、音頻互轉(zhuǎn)是一個很關(guān)鍵的技術(shù)點(diǎn)。對于語音轉(zhuǎn)文字,個人實(shí)現(xiàn)較為困難,我們可以使用語音轉(zhuǎn)文字的軟件或借助各API(如科大訊飛等)進(jìn)行移植開發(fā)。不過文字轉(zhuǎn)語音就相對而言容易實(shí)現(xiàn)很多了2022-02-02Python ORM框架SQLAlchemy學(xué)習(xí)筆記之映射類使用實(shí)例和Session會話介紹
這篇文章主要介紹了Python ORM框架SQLAlchemy學(xué)習(xí)筆記之映射類使用實(shí)例和Session會話介紹,需要的朋友可以參考下2014-06-06Python中線程的MQ消息隊(duì)列實(shí)現(xiàn)以及消息隊(duì)列的優(yōu)點(diǎn)解析
消息隊(duì)列(MQ,Message Queue)在消息數(shù)據(jù)傳輸中的保存作用為數(shù)據(jù)通信提供了保障和實(shí)時處理上的便利,這里我們就來看一下Python中線程的MQ消息隊(duì)列實(shí)現(xiàn)以及消息隊(duì)列的優(yōu)點(diǎn)解析2016-06-06Python基于scapy實(shí)現(xiàn)修改IP發(fā)送請求的方法示例
這篇文章主要介紹了Python基于scapy實(shí)現(xiàn)修改IP發(fā)送請求的方法,涉及Python網(wǎng)絡(luò)編程中使用scapy操作IP的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-07-07python列表使用實(shí)現(xiàn)名字管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python列表使用實(shí)現(xiàn)名字管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01