如何制作带有三角形尖端的 Qlabel
How to make Qlabel with a triagular tip
我正在尝试使 Qlabel 看起来像 Messenger 中的现代聊天气泡(带有三角形尖端的圆形矩形),如下图所示:
我设法让 qlabel 有一个锋利的边缘,但不知道如何制作尖端。问题是在拐角处插入一个三角形路径,qlabel round rect 和文本应该向相反的方向移动,但是这样做会导致文本超出标签区域
这是一个子类标签,带有覆盖的绘画事件和调整大小事件(调整大小用于文字换行,这超出了我的问题范围——也许)> 我删除了一些与着色、字体等相关的不必要代码
class chatLabel(QtWidgets.QLabel):
def __init__(self,text):
super(chatLabel, self).__init__(text)
self.setContentsMargins(6,6,6,6)
sizePolicy = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.setSizePolicy(sizePolicy)
self.color = QtGui.QColor("#333C43")
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing, False)
rect = QtCore.QRectF(0,0,self.width()-1,self.height()-1)
p.setPen(Qt.NoPen)
path = QtGui.QPainterPath()
path.setFillRule(Qt.WindingFill )
path.addRoundedRect(rect, 15.0, 15.0)
path.addRect(self.width()-13, 0, 13, 13)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def resizeEvent(self, e): #Due to a bug in Qt, we need this. ref:https://bugreports.qt.io/browse/QTBUG-37673
#heightForWidth rely on minimumSize to evaulate, so reset it before
self.setMinimumHeight( 0 )
# define minimum height
self.setMinimumHeight( self.heightForWidth( self.width() ) )
if self.width()>256:
self.setWordWrap(True)
self.setMinimumWidth(128)
super(chatLabel, self).resizeEvent(e)
这是上述子分类标签的结果
我怎样才能达到我想要的效果?
N.B:我知道我可以用图片来做到这一点,但这需要根据文本大小缩放图片(9 切片)
我想我找到了解决问题的简单方法。
因为问题是如果我在右角做了一个提示,需要移动文本以使文本包含在圆形矩形(气泡)内。我们可以通过在 sheets 样式中使用 padding 来实现这种移动,这将使文本从角落移动。因此,文本将显示为包含在气泡中。
感谢 user9402680 的回答和他的代码片段,我在其中添加了样式 sheet 行以达到所需的效果。
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class chatLabel(QLabel):
def __init__(self,text):
super(chatLabel, self).__init__(text)
self.setContentsMargins(6,6,6,6)
sizePolicy = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.setSizePolicy(sizePolicy)
self.color = QColor("#333C43")
# 17 px margin from right (to make the text included in the bubble
# 8 px margin from left.
self.setStyleSheet("QLabel{padding: 0px 8px 0px 17px;}")
def paintEvent(self, e):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing, False)
#I changed this width from - 1 to - 16 because I can't see the result good.
rect = QRectF(0,0,self.width()- 16,self.height()-1)
p.setPen(Qt.NoPen)
path = QPainterPath()
path.setFillRule(Qt.WindingFill )
path.addRoundedRect(rect, 15.0, 15.0)
#I deleted this object
# path.addRect(self.width()-13, 0, 13, 13)
linePath = QPainterPath()
linePath.moveTo(rect.right() + rect.width()/6 , rect.top())
linePath.lineTo(rect.right() - rect.width()/2, rect.bottom())
linePath.lineTo(rect.right() , rect.top() - rect.height()/3)
# linePath.lineTo(rect.right() - rect.width()/5, rect.top() - rect.height()/2)
path = path.united(linePath)
#cubic bezier curve, please try this , too.
# cubicPath =QPainterPath()
# cubicPath.moveTo(rect.right() - 20, rect.top())
# cubicPath.cubicTo(rect.right() - 20, rect.top() + rect.height()/2, rect.right() , rect.top() , rect.right() + 15, rect.top())
# path = path.united(cubicPath)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def resizeEvent(self, e): #Due to a bug in Qt, we need this. ref:https://bugreports.qt.io/browse/QTBUG-37673
#heightForWidth rely on minimumSize to evaulate, so reset it before
self.setMinimumHeight( 0 )
# define minimum height
self.setMinimumHeight( self.heightForWidth( self.width() ) )
if self.width()>256:
self.setWordWrap(True)
self.setMinimumWidth(128)
super(chatLabel, self).resizeEvent(e)
def main():
try:
app=QApplication([])
except Exception as e:
print(e)
widget = chatLabel("This is the result!")
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class chatLabel(QLabel):
def __init__(self,text):
super(chatLabel, self).__init__(text)
self.setContentsMargins(6,6,6,6)
sizePolicy = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.setSizePolicy(sizePolicy)
self.color = QColor("#333C43")
def paintEvent(self, e):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing, False)
#I changed this width from - 1 to - 16 because I can't see the result good.
rect = QRectF(16,0,self.width()- 16,self.height()-1)
p.setPen(Qt.NoPen)
path = QPainterPath()
path.setFillRule(Qt.WindingFill )
path.addRoundedRect(rect, 15.0, 15.0)
#I deleted this object
# path.addRect(self.width()-13, 0, 13, 13)
linePath = QPainterPath() linePath.moveTo(rect.left() - rect.width()/6 ,rect.top())
linePath.lineTo(rect.left() + rect.width()/2, rect.bottom())
linePath.lineTo(rect.left() , rect.top() - rect.height()/3)
# linePath.lineTo(rect.right() - rect.width()/5, rect.top() - rect.height()/2)
path = path.united(linePath)
#cubic bezier curve, please try this , too.
# cubicPath =QPainterPath()
# cubicPath.moveTo(rect.right() - 20, rect.top())
# cubicPath.cubicTo(rect.right() - 20, rect.top() + rect.height()/2, rect.right() , rect.top() , rect.right() + 15, rect.top())
# path = path.united(cubicPath)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def resizeEvent(self, e): #Due to a bug in Qt, we need this. ref:https://bugreports.qt.io/browse/QTBUG-37673
#heightForWidth rely on minimumSize to evaulate, so reset it before
self.setMinimumHeight( 0 )
# define minimum height
self.setMinimumHeight( self.heightForWidth( self.width() ) )
if self.width()>256:
self.setWordWrap(True)
self.setMinimumWidth(128)
super(chatLabel, self).resizeEvent(e)
def main():
try:
app=QApplication([])
except Exception as e:
print(e)
widget = chatLabel("This is the result!")
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
我正在尝试使 Qlabel 看起来像 Messenger 中的现代聊天气泡(带有三角形尖端的圆形矩形),如下图所示:
我设法让 qlabel 有一个锋利的边缘,但不知道如何制作尖端。问题是在拐角处插入一个三角形路径,qlabel round rect 和文本应该向相反的方向移动,但是这样做会导致文本超出标签区域
这是一个子类标签,带有覆盖的绘画事件和调整大小事件(调整大小用于文字换行,这超出了我的问题范围——也许)> 我删除了一些与着色、字体等相关的不必要代码
class chatLabel(QtWidgets.QLabel):
def __init__(self,text):
super(chatLabel, self).__init__(text)
self.setContentsMargins(6,6,6,6)
sizePolicy = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.setSizePolicy(sizePolicy)
self.color = QtGui.QColor("#333C43")
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing, False)
rect = QtCore.QRectF(0,0,self.width()-1,self.height()-1)
p.setPen(Qt.NoPen)
path = QtGui.QPainterPath()
path.setFillRule(Qt.WindingFill )
path.addRoundedRect(rect, 15.0, 15.0)
path.addRect(self.width()-13, 0, 13, 13)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def resizeEvent(self, e): #Due to a bug in Qt, we need this. ref:https://bugreports.qt.io/browse/QTBUG-37673
#heightForWidth rely on minimumSize to evaulate, so reset it before
self.setMinimumHeight( 0 )
# define minimum height
self.setMinimumHeight( self.heightForWidth( self.width() ) )
if self.width()>256:
self.setWordWrap(True)
self.setMinimumWidth(128)
super(chatLabel, self).resizeEvent(e)
这是上述子分类标签的结果
我怎样才能达到我想要的效果? N.B:我知道我可以用图片来做到这一点,但这需要根据文本大小缩放图片(9 切片)
我想我找到了解决问题的简单方法。
因为问题是如果我在右角做了一个提示,需要移动文本以使文本包含在圆形矩形(气泡)内。我们可以通过在 sheets 样式中使用 padding 来实现这种移动,这将使文本从角落移动。因此,文本将显示为包含在气泡中。
感谢 user9402680 的回答和他的代码片段,我在其中添加了样式 sheet 行以达到所需的效果。
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class chatLabel(QLabel):
def __init__(self,text):
super(chatLabel, self).__init__(text)
self.setContentsMargins(6,6,6,6)
sizePolicy = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.setSizePolicy(sizePolicy)
self.color = QColor("#333C43")
# 17 px margin from right (to make the text included in the bubble
# 8 px margin from left.
self.setStyleSheet("QLabel{padding: 0px 8px 0px 17px;}")
def paintEvent(self, e):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing, False)
#I changed this width from - 1 to - 16 because I can't see the result good.
rect = QRectF(0,0,self.width()- 16,self.height()-1)
p.setPen(Qt.NoPen)
path = QPainterPath()
path.setFillRule(Qt.WindingFill )
path.addRoundedRect(rect, 15.0, 15.0)
#I deleted this object
# path.addRect(self.width()-13, 0, 13, 13)
linePath = QPainterPath()
linePath.moveTo(rect.right() + rect.width()/6 , rect.top())
linePath.lineTo(rect.right() - rect.width()/2, rect.bottom())
linePath.lineTo(rect.right() , rect.top() - rect.height()/3)
# linePath.lineTo(rect.right() - rect.width()/5, rect.top() - rect.height()/2)
path = path.united(linePath)
#cubic bezier curve, please try this , too.
# cubicPath =QPainterPath()
# cubicPath.moveTo(rect.right() - 20, rect.top())
# cubicPath.cubicTo(rect.right() - 20, rect.top() + rect.height()/2, rect.right() , rect.top() , rect.right() + 15, rect.top())
# path = path.united(cubicPath)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def resizeEvent(self, e): #Due to a bug in Qt, we need this. ref:https://bugreports.qt.io/browse/QTBUG-37673
#heightForWidth rely on minimumSize to evaulate, so reset it before
self.setMinimumHeight( 0 )
# define minimum height
self.setMinimumHeight( self.heightForWidth( self.width() ) )
if self.width()>256:
self.setWordWrap(True)
self.setMinimumWidth(128)
super(chatLabel, self).resizeEvent(e)
def main():
try:
app=QApplication([])
except Exception as e:
print(e)
widget = chatLabel("This is the result!")
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class chatLabel(QLabel):
def __init__(self,text):
super(chatLabel, self).__init__(text)
self.setContentsMargins(6,6,6,6)
sizePolicy = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.setSizePolicy(sizePolicy)
self.color = QColor("#333C43")
def paintEvent(self, e):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing, False)
#I changed this width from - 1 to - 16 because I can't see the result good.
rect = QRectF(16,0,self.width()- 16,self.height()-1)
p.setPen(Qt.NoPen)
path = QPainterPath()
path.setFillRule(Qt.WindingFill )
path.addRoundedRect(rect, 15.0, 15.0)
#I deleted this object
# path.addRect(self.width()-13, 0, 13, 13)
linePath = QPainterPath() linePath.moveTo(rect.left() - rect.width()/6 ,rect.top())
linePath.lineTo(rect.left() + rect.width()/2, rect.bottom())
linePath.lineTo(rect.left() , rect.top() - rect.height()/3)
# linePath.lineTo(rect.right() - rect.width()/5, rect.top() - rect.height()/2)
path = path.united(linePath)
#cubic bezier curve, please try this , too.
# cubicPath =QPainterPath()
# cubicPath.moveTo(rect.right() - 20, rect.top())
# cubicPath.cubicTo(rect.right() - 20, rect.top() + rect.height()/2, rect.right() , rect.top() , rect.right() + 15, rect.top())
# path = path.united(cubicPath)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def resizeEvent(self, e): #Due to a bug in Qt, we need this. ref:https://bugreports.qt.io/browse/QTBUG-37673
#heightForWidth rely on minimumSize to evaulate, so reset it before
self.setMinimumHeight( 0 )
# define minimum height
self.setMinimumHeight( self.heightForWidth( self.width() ) )
if self.width()>256:
self.setWordWrap(True)
self.setMinimumWidth(128)
super(chatLabel, self).resizeEvent(e)
def main():
try:
app=QApplication([])
except Exception as e:
print(e)
widget = chatLabel("This is the result!")
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()