Qt提供了比较丰富的内置控件,一些常规的开发通常够用。

但是有时,还是需要我们自己开发特殊用途的自定义控件。

比如, 在我们的项目实战3中,要求开发一个模拟充电桩、洗车机等设备的测试工具。

模拟这些设备的控件,就需要我们自己定制开发。

QPainter

我们前面学过的内置控件,比如按钮、输入框、选择框,它们的视觉界面是怎么呈现出来的呢?

其实都是 Qt 画出来的。

Qt是通过 QPainter 类实现画出各种控件的。

QPainter 类对象可以在 任何继承自 QPaintDevice (比如QWidget, QImage, QPixmap )的实例上 绘制 图形、文字

控件的视觉界面,其实就是通过画出各种 线条、矩形、圆形,等这些形状组合出来的。

画线

比如,下面的代码调用 QPainter 对象的 drawLine 方法 画一条线,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from PySide2 import  QtGui, QtWidgets 

# 自定义一个QWidget的子类
class MyWidget(QtWidgets.QWidget):

# QWidget类的 paintEvent方法会被Qt调用画出控件自身的形象
def paintEvent(self, e):
# 参数 是绘制图形 目标对象
painter = QtGui.QPainter(self)
# 画一条线,起点坐标为 50,80 ; 终点坐标为 200,300
painter.drawLine(50, 80, 200, 300)

painter.end() # 结束绘制

app = QtWidgets.QApplication()
window = QtWidgets.QMainWindow()
window.resize(500, 500)

my = MyWidget(window)
my.move(0,0)
my.resize(500,500)

window.show()
app.exec_()

画笔指定粗度、颜色

上面的示例中,画线的宽度比较细, 颜色是黑色

如果我们想让画出的线有指定的宽度和颜色,该怎么办?

这是由 QPainter 的画笔属性(pen)决定的

缺省的画笔属性 的宽度为1个像素,颜色为黑色。

我们可以设置 为另外的画笔对象(QPen) 作为 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyWidget(QtWidgets.QWidget):

def paintEvent(self, e):
painter = QtGui.QPainter(self)

# 创建画笔对象
pen = QtGui.QPen()
pen.setWidth(3) # 宽度为3个像素
pen.setColor(QtGui.QColor('red')) #颜色为红色

# 设置 painter 的画笔属性对象
painter.setPen(pen)

painter.drawLine(50, 80, 200, 300)
painter.end()

画点

调用 QPainter 对象的 drawPoint 方法可以画一个点,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyWidget(QtWidgets.QWidget):

def paintEvent(self, e):
painter = QtGui.QPainter(self)

# 创建画笔对象
pen = QtGui.QPen()
pen.setWidth(10) # 宽度为10个像素
pen.setColor(QtGui.QColor('red'))
painter.setPen(pen)


# 画一个点坐标为 200, 150
painter.drawPoint(200, 150)

painter.end()

画矩形

调用 QPainter 对象的 drawRect 方法可以画一个矩形,如下

1
2
3
4
5
6
7
8
9
10
class MyWidget(QtWidgets.QWidget):

def paintEvent(self, e):
painter = QtGui.QPainter(self)

# 画一个矩形,左上角坐标为 50, 50
# 宽度为200像素,高度为100像素
painter.drawRect(50, 50, 300,300)

painter.end()

画椭圆

调用 QPainter 对象的 drawEllipse 方法可以画一个椭圆,如下

1
2
3
4
5
6
7
8
9
10
11
class MyWidget(QtWidgets.QWidget):

def paintEvent(self, e):
painter = QtGui.QPainter(self)

painter.drawRect(50, 50, 300,200)

# 画一个椭圆,左上角坐标为 50, 50
# 水平方向直径为300像素,垂直方向直径为200像素
painter.drawEllipse(50, 50, 300,200)
painter.end()

可以想象,如果水平垂直方向直径相同,那就是一个正圆形了。

画刷填充

前面我们画的矩形和椭圆,这些图形内部的颜色没有设置的,和底色一致

如果我们想设定矩形椭圆等图形内部填充的颜色,就要通过 QPainter 的画刷属性(brush) 来设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from PySide2 import  QtGui, QtWidgets 
from PySide2.QtCore import Qt

class MyWidget(QtWidgets.QWidget):

def paintEvent(self, e):
painter = QtGui.QPainter(self)

# 创建画刷QBrush对象
brush = QtGui.QBrush()
# 设置画刷的颜色
brush.setColor(QtGui.QColor("yellow"))
# 设置画刷的填充样式
brush.setStyle(Qt.SolidPattern)

# 指定使用这个画刷对象
painter.setBrush(brush)

# 画图
painter.drawRect(50, 50, 100,100)
painter.drawEllipse(200, 200, 100,100)

painter.end()


app = QtWidgets.QApplication()

window = QtWidgets.QMainWindow()
window.resize(500, 500)

my = MyWidget(window)
my.move(0,0)
my.resize(500,500)

window.show()
app.exec_()

其中

1
brush.setStyle(Qt.SolidPattern)

设定了图形内部的填充样式, SolidPattern 是最常见的填满的风格,还有其它的比如

1
2
3
4
5
6
7
Dense1Pattern

HorPattern

VerPattern

CrossPattern

等等,具体可以参考这里

画文字

我们也可以在控件上绘制文字,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from PySide2 import  QtGui, QtWidgets 

class MyWidget(QtWidgets.QWidget):

def paintEvent(self, e):
painter = QtGui.QPainter(self)

# 文字的粗细颜色,由画笔属性决定
pen = QtGui.QPen()
pen.setWidth(1)
pen.setColor(QtGui.QColor('red'))
painter.setPen(pen) # 设置画笔

# 字体
font = QtGui.QFont()
font.setFamily('黑体') #字体类型
font.setPointSize(30) # 字体大小
painter.setFont(font) # 设置字体

painter.drawText(100, 100, 'hello,白月黑羽!')

painter.end()


app = QtWidgets.QApplication()

window = QtWidgets.QMainWindow()
window.resize(500, 500)

my = MyWidget(window)
my.move(0,0)
my.resize(500,500)

window.show()
app.exec_()

自定义控件示例

现在我们可以创建一个真正需要用到的自定义控件了。

在黑羽Elife项目中,要求开发一个模拟充电桩、洗车机设备的测试工具。

下面的示例代码来实现 充电口控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from PySide2 import  QtGui, QtWidgets 

class QOutlet(QtWidgets.QWidget):
"""
插座控件
"""

def __init__(self,parent, color) -> None:
super().__init__(parent)

self.color = color
# 控件设置为固定大小
self.setMinimumSize(70, 70)
self.setMaximumSize(70, 70)

def paintEvent(self, e):
painter = QtGui.QPainter(self)

# 画插座边框,细线条
pen = QtGui.QPen()
pen.setWidth(1)

pen.setColor(QtGui.QColor(self.color))
painter.setPen(pen)
painter.drawRect(0, 0, 60, 60)

# 画插孔线条,组线条
pen = QtGui.QPen()
pen.setWidth(3)
pen.setColor(QtGui.QColor(self.color))
painter.setPen(pen)

painter.drawLine(12,10,12,25)
painter.drawLine(30,10,30,25)
painter.drawLine(48,10,48,25)

painter.drawLine(10,35,20,50)
painter.drawLine(50,35,40,50)

painter.end()


app = QtWidgets.QApplication()

window = QtWidgets.QMainWindow()
window.resize(500, 500)

# 创建第1个插座实例
my = QOutlet(window,'green')
my.move(0,0)

# 创建第2个插座实例
my = QOutlet(window,'red')
my.move(100,0)

# 创建第3个插座实例
my = QOutlet(window,'blue')
my.move(200,0)

window.show()
app.exec_()