Qt学习笔记:QPainter


前言

QPainter类负责绘画

简单来说,就是你想要将你的app设计成什么样子,都需要重写QPainter来实现


QPainter

简单的绘图,我们通常用QPainter,举个例子:

# -*- coding: utf-8 -*-
# 简单的QPainter程序

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setFixedSize(400, 400)
        self.show()

    def paintEvent(self, event):
	    # 在QMainWindow上绘图
        qp = QPainter(self)
        # 蓝色,粗细为2,实线
        pen = QPen(Qt.blue, 2, Qt.SolidLine)
        qp.setPen(pen)
        # 画一个直径400的圆
        qp.drawEllipse(0, 0, 400, 400)

app = QApplication(sys.argv)
win = MyWindow()
sys.exit(app.exec_())

在这里插入图片描述

这就是一个简单的Qt程序,效果就是建立一个400*400大小的窗口,并在中心化一个直径400的圆。

三种方法自定义paintEvent()函数

但这个程序其实很有问题,因为我们自己做开发的时候,不可能直接在主窗口上绘图,通常来讲,我们会给主窗口一个或则多个container,然后container里面再放入各种widget,而我们需要绘制的内容,往往是在这些widget当中的。比如我们将上面的code改改,添加一个Qwidget:

    def __init__(self):
        super(MyWindow, self).__init__()
        self.setFixedSize(400, 400)
        self.widget = QWidget(self)
        self.widget.setFixedSize(300, 300)
        self.widget.setStyleSheet("background-color:white")
        self.show()

在这里插入图片描述

这里我就遇到一个问题,QPainter说明文档里面倒是提到了可以在别的QPaintDevice上作图,按照我本来的理解是,只要把paintEvent里面的QPainter参数变一下,应该就可以在对应的device上绘图。

    def paintEvent(self, event):
    
	    # 在QWidget上绘图
        qp = QPainter(self.widget)
        
        # 蓝色,粗细为2,实线
        pen = QPen(Qt.blue, 2, Qt.SolidLine)
        qp.setPen(pen)
        # 画一个直径400的圆
        qp.drawEllipse(0, 0, 400, 400)

结果总是提示

QPainter::begin: Widget painting can only begin as a result of a paintEvent

那么如何能够将圆画入self.widget里面呢?

经过探索,以下几种方法可以解决这个问题:

1、使用Designer的promote功能

这个方法最简单,但是却是我最晚知道的。。。。。

哎,自学者的悲哀。。

过程很简单

1、 进入Designer界面
2、右键点击你需要重写paintEvent方法的部件
3、promote to…
4、new Promoted Class里面,填写你自定义类的类名,头文件名,在python中头文件名,就是你自定义类所在的文件
5、点Promote

这个时候你所promote的插件,就变成了你所自定义的类的一个实例了,当然现在这个所谓的自定义类除了名字什么都没有。

你现在所需要做的就是按照前面的方法自定义类,并重写paintEvent方法

只要保证自定义的名字和promote的名字一致就行了。

唯一需要注意的是,必须要有主函数,也就是这个样子

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    window.show()
    sys.exit(app.exec_())

而不能偷懒写成

app = QApplication(sys.argv)
window = MyWidget()
window.show()
sys.exit(app.exec_())

否则会报各种错误,切记!

2、自己定义一个类,并继承QWidget,在自己的类里面重写paintEvent()

# -*- coding: utf-8 -*-
# 简单的QPainter程序

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setFixedSize(400, 400)
        # self.widget = QWidget(self)
        self.widget = MyWidget(self)
        self.widget.setFixedSize(300, 300)
        self.widget.setStyleSheet("background-color:white")
        self.show()

    def paintEvent(self, event):
        # 在QMainWindow上绘图
        qp = QPainter(self)
        # 蓝色,粗细为2,实线
        pen = QPen(Qt.blue, 2, Qt.SolidLine)
        qp.setPen(pen)
        # 画一个直径400的圆
        qp.drawEllipse(0, 0, 400, 400)

class MyWidget(QWidget):
    def __init__(self,parent):
        super(MyWidget, self).__init__(parent)

    def paintEvent(self, event):
        # 在QWidget上绘图
        qp = QPainter(self)
        # 黑色,粗细为2,实线
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        qp.setPen(pen)
        # 画一个直径400的圆
        qp.drawEllipse(0, 0, 400, 400)

app = QApplication(sys.argv)
win = MyWindow()
sys.exit(app.exec_())

在这里插入图片描述


现在的问题是,我的PyQt4配置文件其实是通过pyuic从.ui文件转换而来的。得到的文件可以说是自成一体,如果我直接在此文件上更改,添加,甚至重写函数,那么如果万一我在designer上调整了ui,并重新更新这个文件,那么我在上面的任何修改都将付诸流水。

既然我不可能修改配置文件,那么我有没有可能从外部去修改或者重写Widget里的paintEvent方法呢?

答案是有!

3、动态的添加方法

你还可以自定义一个函数 paintEvent,然后动态添加到已经存在的实例当中,这样就相当于重写paintEvent 方法,举个例子:

>>> def foo():# 这就是函数
...     print "foo"
...
>>> class A:
...     def act_a(self):# 而这里是方法
...         print "actA"

那么怎样将函数 foo 添加到 class A里面呢?

讲之前要说明的是,在python当中,方法分为bound和unbound两大类。

unbound对于class而言,他的方法都是"未绑定"的状态,所以如果我们直接给class添加方法,那么该方法就会自动"绑定"到所有的对象当中去,例如:

>>> A.foo = foo # 给class添加方法的步骤只有这一步,简不简单?~~~~^o^
>>> a1 = A()
>>> a2 = A()
>>> a1.foo() # 所有的对象都可以访问foo方法
foo
>>> a2.foo()
foo

我们可以看到,无论是a1还是a2,他们都可以访问foo()方法

bound所谓"绑定"的方法,通常只存在与具体的对象当中,如果我们将一个方法添加到一个对象当中去,那么这个方法会绑定到这个对象身上,很显然,这个方法就变成了该对象独有的方法。

>>> a = A()
>>> import types
>>> a.foo = types.MethodType( foo, a )
>>> a.foo()
foo
>>> a2 = A()
>>> a2.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'foo'

此时a和a2虽然都是class A的对象,但是只有a才能访问foo()方法。

网上搜到两篇文章,讲得非常好,可以看看: 英文版 中文版

再来看我自己的代码:

# -*- coding: utf-8 -*-
# 简单的QPainter程序

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.initUI()
        self.show()

    def initUI(self):
        centralwidget = QWidget()
        self.setCentralWidget(centralwidget)
        # 添加一个有两个Tab的QTabWidget
        self.widget = QTabWidget(centralwidget)
        tab_1 = QWidget()
        tab_2 = QWidget()
        self.widget.addTab(tab_1,"tab1")
        self.widget.addTab(tab_2,"tab2")

        vbox = QVBoxLayout(centralwidget)
        vbox.addWidget(self.widget)
        self.setFixedSize(400, 400)
		
		# 关键步骤:给QWidget添加非绑定方法paintEvent
        QWidget.paintEvent = paintEvent

def paintEvent(self, event):
    # 在QMainWindow上绘图
    qp = QPainter(self)
    # 蓝色,粗细为2,实线
    pen = QPen(Qt.blue, 2, Qt.SolidLine)
    qp.setPen(pen)
    # 画一个直径400的圆
    qp.drawEllipse(0, 0, 400, 400)


app = QApplication(sys.argv)
win = MyWindow()
sys.exit(app.exec_())

在这里插入图片描述

呵呵,是不是和想想的不一样?

不要慌,这是因为我们现在添加的是非绑定的方法,所以所有QWidget及其子类的对象都会运行paintEvent方法,这就造成了混乱,而我们只是想在特定对象tab_1上画圆。

这时候,只需要为tab_1添加绑定方法:

		# 关键步骤:给QWidget添加非绑定方法paintEvent
        # QWidget.paintEvent = paintEvent
        
        # 给QWidget的对象tab_1添加绑定的方法
		import types
        tab_1.paintEvent = types.MethodType(paintEvent,tab_1)

在这里插入图片描述

至此,问题解决。

这个方法真是折磨了我好几天,所幸解决了,不过这个方法也有局限性,虽然比方法1要简单,至少不用定义类,但是paint函数仍然需要大量定义。

发布了45 篇原创文章 · 获赞 46 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/founderznd/article/details/52603791
今日推荐