文字特效贴片,可以设置一些列常规样式
支持多行文本,自动处理多行文字边距和排列布局

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from PySide6.QtCore import QPointF
from PySide6.QtGui import Qt, QFont, QColor, QPainterPath, QPolygonF, QBrush, QPixmap
from PySide6.QtWidgets import QGraphicsDropShadowEffect, QGraphicsPolygonItem
from PySide6.QtWidgets import QGraphicsRectItem, QGraphicsSimpleTextItem, QGraphicsPixmapItem


class TextInfo:
def __init__(self, params):
self.content = params.get('content', '默认文字')
self.size = params.get('size', 40)
self.gap = self.size // 10
self.ziti = params.get('ziti', '黑体')
self.border = params.get('border', '圆角')
self.align = params.get('align', '居中')
self.bold = params.get('bold', True)
self.italic = params.get('italic', True)
self.triangle = params.get('triangle', False)
self.color = params.get('color', '#000000')
self.shadow = params.get('shadow', '#ffffff')
self.background = params.get('background', '#00000000')
self.image = params.get('image', None)


class TextRect(QGraphicsRectItem):
def __init__(self, text, text_info):
super().__init__()
self.setPen(Qt.NoPen)
self.text_item = QGraphicsSimpleTextItem(text)
self.text_item.setBrush(QColor(text_info.color))
shadow_effect = QGraphicsDropShadowEffect()
shadow_effect.setBlurRadius(8)
shadow_effect.setColor(QColor(text_info.shadow))
shadow_effect.setOffset(3, 3)
self.text_item.setGraphicsEffect(shadow_effect)
font = QFont(text_info.ziti, text_info.size)
font.setBold(text_info.bold)
font.setItalic(text_info.italic)
self.text_item.setFont(font)
self.text_item.setParentItem(self)

def offset(self, x, y):
self.text_item.setPos(x, y)

def width(self):
return self.text_item.boundingRect().width()

def height(self):
return self.text_item.boundingRect().height()


class PatchText(QGraphicsRectItem):
def __init__(self, params):
super().__init__()
self.setPen(Qt.NoPen)
self.text_info = TextInfo(params)
self.setBrush(QColor(self.text_info.background))
self.text_line_list = []
w, h = 0, self.text_info.gap * 2
for line in self.text_info.content.split('\n'):
if not line:
line = ' ' * 4
text_line = TextRect(line, self.text_info)
w = max(w, text_line.width() + self.text_info.gap * 8)
h += text_line.height() + self.text_info.gap
self.text_line_list.append(text_line)
text_line.setParentItem(self)
x, y = self.text_info.gap * 4, self.text_info.gap * 2
for text_line in self.text_line_list:
if self.text_info.align == '居中':
x = (w - text_line.width()) // 2
if self.text_info.align == '右对齐':
x = w - text_line.width() - self.text_info.gap * 4
text_line.setRect(0, y, w, text_line.height())
text_line.offset(x, y)
y += text_line.height() + self.text_info.gap
h = h + self.text_info.gap * 2
self.setRect(0, 0, w, h)
# 画一个倒三角形模拟消息框
if self.text_info.triangle:
side = self.text_info.gap * 10
triangle = QGraphicsPolygonItem()
triangle.setPen(Qt.NoPen)
points = QPolygonF([
QPointF(w - side * 2, h),
QPointF(w - side, h),
QPointF(w - side * 1.5, h + side * 0.5)
])
triangle.setPolygon(points)
triangle.setBrush(QBrush(QColor(self.text_info.background)))
triangle.setParentItem(self)
# 居中添加一个图片
if self.text_info.image:
pixmap = QGraphicsPixmapItem()
pixmap.setZValue(-1)
pixmap.setPixmap(QPixmap(self.text_info.image))
pixmap.setParentItem(self)
x, y = (w - pixmap.boundingRect().width()) // 2, (h - pixmap.boundingRect().height()) // 2
pixmap.setPos(x, y)
xx = x * -1 if x < 0 else 0
yy = y * -1 if y < 0 else 0
self.setRect(0, 0, w + xx * 2, h + yy * 2)
self.setPos(xx, yy)

def paint(self, painter, option, widget=None):
if self.text_info.border != '圆角':
super().paint(painter, option, widget)
return
radius = self.text_info.gap * 4
painter.save()
path = QPainterPath()
path.addRoundedRect(self.rect(), radius, radius)
if self.brush().style() != Qt.NoBrush:
painter.fillPath(path, self.brush())
if self.pen().style() != Qt.NoPen:
painter.setPen(self.pen())
painter.drawPath(path)
painter.restore()