Python3使用PyQt5制作简单的画板/手写板实例


Posted in Python onOctober 19, 2017

1.前言

版本:Python3.6.1 + PyQt5

写一个程序的时候需要用到画板/手写板,只需要最简单的那种。原以为网上到处都是,结果找了好几天,都没有找到想要的结果。

网上的要么是非python版的qt程序(要知道qt版本之间差异巨大,还是非同一语言的),改写难度太大。要么是PyQt4的老程序,很多都已经不能在PyQt5上运行了。要么是大神写的特别复杂的程序,简直是直接做出了一个Windows自带的画图版,只能膜拜~

于是我只能在众多代码中慢慢寻找自己需要的那一小部分,然后不断地拼凑,不断地理解大神的代码,最终做出这么一个简单的画板。望着这个简单的画板我真是泪流满面,中间数十次拼不对拼不全导致程序无数次崩溃,差点就放弃了......

2.简单的画板1.0

在简单的画板1.0这里,实现的功能是:在定点和移动中的鼠标所在处画一条线
如图所示:
Python3使用PyQt5制作简单的画板/手写板实例
鼠标按住移动的话,线也会跟着移动,从这个简单的程序开始理解PyQt5的运行机制吧。

'''
 简单的画板1.0
 功能:在定点和移动中的鼠标所在处画一条线
 作者: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中,使用一个列表保存所有移动过的点,然后要画线的时候,循环遍历列表,依次画出列表中点到定点之间的线即可。

效果如图所示:
Python3使用PyQt5制作简单的画板/手写板实例

'''
 简单的画板2.0
 功能:
  在定点和移动中的鼠标所在处画一条线
  并将画过的线都保留在窗体上
 作者: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不过是画鼠标所在点到定点的线,那么如何将按住鼠标后移动的轨迹保留在窗体上?

这个就需要一个列表来保存所有移动过的点,然后把所有相邻两个点之间都画一条线,就能断断续续连成鼠标的痕迹了。
效果如图所示:
Python3使用PyQt5制作简单的画板/手写板实例

是不是就画出鼠标移动的轨迹了!

不过这也是有缺点的,比如说写个5看看:

Python3使用PyQt5制作简单的画板/手写板实例

硬生生变成了一个5不是5, 6不是6的数字。这是因为再次提笔画时,5上面的那一横跟之前画的尾巴那里连起来了。好好想想,这个问题怎么解决呢?

'''
 简单的画板3.0
 功能:将按住鼠标后移动的轨迹保留在窗体上
 作者: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:

Python3使用PyQt5制作简单的画板/手写板实例

很难看对不对?

解决这个问题的方法应该是有很多种的,我也没有深入想,就直接用了这个麻烦点的方法。

我的办法是当鼠标按住移动然后松开的时候,往保存所有移动过的点的列表中添加一个断点(-1, -1)。然后在每次画线的时候,都判断一下是不是断点,如果是断点的话就想办法跳过去,并且不连续的开始接着画线。

效果如图所示:

Python3使用PyQt5制作简单的画板/手写板实例

以下是具体实现代码:

'''
 简单的画板4.0
 功能:
  将按住鼠标后移动的轨迹保留在窗体上
  并解决二次作画时与上次痕迹连续的问题
 作者: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_()

至此,终于完成了简单的画板程序的实现!

另外,如果在使用这个代码的过程中有遇到什么问题,也欢迎向我反馈。

以上这篇Python3使用PyQt5制作简单的画板/手写板实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python通过websocket与js客户端通信示例分析
Jun 25 Python
python 简单的多线程链接实现代码
Aug 28 Python
python Flask实现restful api service
Dec 04 Python
详解python中的Turtle函数库
Nov 19 Python
搞定这套Python爬虫面试题(面试会so easy)
Apr 03 Python
详解用Python实现自动化监控远程服务器
May 18 Python
远程部署工具Fabric详解(支持Python3)
Jul 04 Python
Python 切分数组实例解析
Nov 07 Python
简单了解python字符串前面加r,u的含义
Dec 26 Python
解决pyinstaller打包运行程序时出现缺少plotly库问题
Jun 02 Python
python爬虫---requests库的用法详解
Sep 28 Python
Python函数对象与闭包函数
Apr 13 Python
python里使用正则的findall函数的实例详解
Oct 19 #Python
详解python里使用正则表达式的全匹配功能
Oct 19 #Python
python中logging库的使用总结
Oct 18 #Python
R vs. Python 数据分析中谁与争锋?
Oct 18 #Python
Ubuntu安装Jupyter Notebook教程
Oct 18 #Python
python 中的divmod数字处理函数浅析
Oct 17 #Python
Python中的id()函数指的什么
Oct 17 #Python
You might like
php empty,isset,is_null判断比较(差异与异同)
2010/10/19 PHP
php技术实现加载字体并保存成图片
2015/07/27 PHP
php使用event扩展的io复用测试的示例
2020/10/20 PHP
Jquery css函数用法(判断标签是否拥有某属性)
2011/05/28 Javascript
js+css 实现遮罩居中弹出层(随浏览器窗口滚动条滚动)
2013/12/11 Javascript
jquery教程限制文本框只能输入数字和小数点示例分享
2014/01/13 Javascript
Javascript中对象继承的实现小例
2014/05/12 Javascript
使用JavaScript实现网页版Pongo设计思路及源代码分享
2014/06/16 Javascript
通用无限极下拉菜单的实现代码
2016/05/31 Javascript
浅谈DOCTYPE对$(window).height()取值的影响
2016/07/21 Javascript
提高Web性能的前端优化技巧总结
2017/02/27 Javascript
Ajax实现不刷新取最新商品
2017/03/01 Javascript
vue项目常用组件和框架结构介绍
2017/12/24 Javascript
JS中DOM元素的attribute与property属性示例详解
2018/09/04 Javascript
详解Webpack如何引入CDN链接来优化编译后的体积
2019/06/21 Javascript
浅谈layer的Icon样式以及一些常用的layer窗口使用方法
2019/09/11 Javascript
JavaScript中如何对多维数组(矩阵)去重的实现
2019/12/04 Javascript
js实现3D粒子酷炫动态旋转特效
2020/09/13 Javascript
Python中操作MySQL入门实例
2015/02/08 Python
Python提取Linux内核源代码的目录结构实现方法
2016/06/24 Python
Python自然语言处理之词干,词形与最大匹配算法代码详解
2017/11/16 Python
python交互式图形编程实例(三)
2017/11/17 Python
Python笔记之观察者模式
2019/11/20 Python
python+opencv3生成一个自定义纯色图教程
2020/02/19 Python
Django Form常用功能及代码示例
2020/10/13 Python
瑞士香水购物网站:Parfumcity.ch
2017/01/14 全球购物
澳大利亚百货公司:David Jones
2018/02/08 全球购物
AJAX的全称是什么
2012/11/06 面试题
厨房工作人员岗位职责
2013/11/15 职场文书
预备党员的自我评价
2014/03/12 职场文书
员工安全承诺书
2014/05/22 职场文书
企业活动策划方案
2014/06/02 职场文书
高考标语大全
2014/06/05 职场文书
大学生实习证明范文(5篇)
2014/09/18 职场文书
2016暑期师德培训心得体会
2016/01/09 职场文书
看完这篇文章获得一些java if优化技巧
2021/07/15 Java/Android