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 相关文章推荐
Windows上配置Emacs来开发Python及用Python扩展Emacs
Nov 20 Python
实例讲解Python编程中@property装饰器的用法
Jun 20 Python
python中set常用操作汇总
Jun 30 Python
利用Python脚本实现ping百度和google的方法
Jan 24 Python
python 计算两个日期相差多少个月实例代码
May 24 Python
pycharm debug功能实现跳到循环末尾的方法
Nov 29 Python
django 使用全局搜索功能的实例详解
Jul 18 Python
对Python生成器、装饰器、递归的使用详解
Jul 19 Python
django表单的Widgets使用详解
Jul 22 Python
python爬虫 urllib模块url编码处理详解
Aug 20 Python
Jupyter Notebook输出矢量图实例
Apr 14 Python
python 录制系统声音的示例
Dec 21 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与javascript的两种交互方式
2006/10/09 PHP
smarty模板嵌套之include与fetch性能测试
2010/12/05 PHP
如何让搜索引擎抓取AJAX内容解决方案
2014/08/25 PHP
分享一则PHP定义函数代码
2015/02/26 PHP
js获取单选框或复选框值及操作
2012/12/18 Javascript
深入分析Javascript跨域问题
2015/04/17 Javascript
jquery实现通用的内容渐显Tab选项卡效果
2015/09/07 Javascript
AngularJS 指令的交互详解及实例代码
2016/09/14 Javascript
简单谈谈CommonsChunkPlugin抽取公共模块
2017/12/31 Javascript
用Node编写RESTful API接口的示例代码
2018/07/04 Javascript
bootstrap table实现合并单元格效果
2018/12/24 Javascript
Vue仿百度搜索功能
2020/12/28 Vue.js
在CentOS上配置Nginx+Gunicorn+Python+Flask环境的教程
2016/06/07 Python
判断网页编码的方法python版
2016/08/12 Python
通过Python 获取Android设备信息的轻量级框架
2017/12/18 Python
一道python走迷宫算法题
2018/01/22 Python
Python DataFrame 设置输出不显示index(索引)值的方法
2018/06/07 Python
python中单下划线_的常见用法总结
2018/07/10 Python
Python解析Excle文件中的数据方法
2018/10/23 Python
python在回调函数中获取返回值的方法
2019/02/22 Python
pytorch多进程加速及代码优化方法
2019/08/19 Python
python tkinter控件布局项目实例
2019/11/04 Python
python中有关时间日期格式转换问题
2019/12/25 Python
在ipython notebook中使用argparse方式
2020/04/20 Python
Keras自动下载的数据集/模型存放位置介绍
2020/06/19 Python
Python3爬虫里关于识别微博宫格验证码的知识点详解
2020/07/30 Python
python实现马丁策略的实例详解
2021/01/15 Python
说一下Linux下有关用户和组管理的命令
2014/08/18 面试题
如何安装ruby on rails
2014/02/09 面试题
大专生自我鉴定范文
2013/10/01 职场文书
电气专业应届生求职信
2013/11/01 职场文书
高中教师评语大全
2014/04/25 职场文书
团支部推优材料
2014/05/21 职场文书
工程部文员岗位职责
2015/02/04 职场文书
python3+PyQt5+Qt Designer实现界面可视化
2021/06/10 Python
Java工作中实用的代码优化技巧分享
2022/04/21 Java/Android