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通过urllib2爬网页上种子下载示例
Feb 24 Python
python实现百度关键词排名查询
Mar 30 Python
python获取本地计算机名字的方法
Apr 29 Python
Python 描述符(Descriptor)入门
Nov 20 Python
Python实现批量读取图片并存入mongodb数据库的方法示例
Apr 02 Python
python数据批量写入ScrolledText的优化方法
Oct 11 Python
python实现中文文本分句的例子
Jul 15 Python
tensorflow通过模型文件,使用tensorboard查看其模型图Graph方式
Jan 23 Python
解决django框架model中外键不落实到数据库问题
May 20 Python
Python进行统计建模
Aug 10 Python
Python中with上下文管理协议的作用及用法
Mar 18 Python
Python 一键获取电脑浏览器的账号密码
May 11 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将回调函数作用到给定数组单元的方法
2014/08/19 PHP
php中array_slice和array_splice函数解析
2016/10/18 PHP
htm调用JS代码
2007/03/15 Javascript
js replace 与replaceall实例用法详解
2013/08/03 Javascript
jquery鼠标停止移动事件
2013/12/21 Javascript
javascript对象的使用和属性操作示例详解
2014/03/02 Javascript
jquery实现鼠标滑过显示提示框的方法
2015/02/05 Javascript
jQuery实现跨域iframe接口方法调用
2015/03/14 Javascript
JavaScript面向对象程序设计教程
2016/03/29 Javascript
jQuery实现右键菜单、遮罩等效果代码
2016/09/27 Javascript
关于JavaScript和jQuery的类型判断详解
2016/10/08 Javascript
自动适应iframe右边的高度
2016/12/22 Javascript
基于Two.js实现星球环绕动画效果的示例
2017/11/06 Javascript
vue 下列表侧滑操作实例代码详解
2018/07/24 Javascript
仿iPhone通讯录制作小程序自定义选择组件的实现
2019/05/23 Javascript
vue-cli3中配置alias和打包加hash值操作
2020/09/04 Javascript
[01:15:15]VG VS EG Supermajor小组赛B组胜者组第一轮 BO3第二场 6.2
2018/06/03 DOTA
利用Python开发实现简单的记事本
2016/11/15 Python
NetworkX之Prim算法(实例讲解)
2017/12/22 Python
Python SqlAlchemy动态添加数据表字段实例解析
2018/02/07 Python
Python实现的列表排序、反转操作示例
2019/03/13 Python
对Python获取屏幕截图的4种方法详解
2019/08/27 Python
django 简单实现登录验证给你
2019/11/06 Python
TensorFlow基本的常量、变量和运算操作详解
2020/02/03 Python
将不规则的Python多维数组拉平到一维的方法实现
2021/01/11 Python
美国最大点评网站:Yelp
2018/02/14 全球购物
Under Armour西班牙官网:美国知名的高端功能性运动品牌
2018/12/12 全球购物
什么是继承
2013/12/07 面试题
Servlet如何得到客户端机器的信息
2014/10/17 面试题
水产养殖学应届生求职信
2013/09/29 职场文书
经贸日语专业个人求职信
2013/12/13 职场文书
气象学专业个人求职信
2014/03/15 职场文书
营销部内勤岗位职责
2014/04/30 职场文书
教师节倡议书
2014/08/30 职场文书
python调试工具Birdseye的使用教程
2021/05/25 Python
Go timer如何调度
2021/06/09 Golang