我试图使Qlabel看起来像信使中的现代聊天气泡(带有三角形尖端的圆形矩形),如下图所示:
我设法使qlabel具有一个尖锐的边缘,但无法获得如何制作尖端的方法。问题是在拐角处插入三角形路径,qlabel圆角矩形,文本应沿相反的方向移动,但是这会使文本脱离标签区域
这是带有重写的绘画事件和调整大小事件的子类标签(调整大小用于自动换行,这超出了我的问题范围-也许)>我删除了一些与颜色,字体等有关的不必要代码。
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切片)
答案 0 :(得分:1)
我理解你的话如下:
说明
您不能同时显示三角形的尖端和文本,因为圆角矩形的大小(我称其为“气泡”)几乎等于标签的大小。因此,我尝试对其进行更改。
要更改此设置,我根据每个文本计算了气泡的大小。 我做了这个功能:
calc_textswidth(self, text, minimumwidth)
这将计算每个字符的文本宽度。 并返回长度。 如果长度大于气泡宽度(标签宽度-线路径长度) ,我插入了“ \ n”进行包装。
如果设置setWrap(True)
,它将变得混乱。因为这意味着如果文本到达标签的末尾,文本将自动换行。因此,我删除了该方法。
text = self.text()
text = text.replace("\n", "")
为了重新计算每次调整大小的文本的位置,将所有文本连接为一个字符串很重要,然后我们计算字符串的整个长度, 我们会在长度超过气泡宽度时对文本进行分割。
我们一遍又一遍地做。
作为说明,我将标签的大小和气泡的大小进行了划分。 气泡的宽度根据文本的长度而减小。
PS
计算非常冗长。也许,如果您使用列表理解等,它将变得紧凑...
我希望这种计算不是该应用程序的瓶颈...
如果仍然存在,请随时问我。
更新
正如您所说,我的代码是该行的陷阱。 我将线的边缘移到中心点。
我认为这是最上端的最佳地方。
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)
#I changed this color because I can't see the text good.
self.color = QColor("#333C43")
self.tip_length = 15
self.coodinate_point = 10
self.setText(text)
self.initial_minimumwidth = 128
self.setMinimumWidth(self.initial_minimumwidth)
self.initial_maximumwidth = 128
self.setMaximumWidth(self.initial_maximumwidth)
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()- self.tip_length,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() + 15 , rect.top())
center = rect.center()
linePath.lineTo(center.x() , rect.bottom())
linePath.lineTo(rect.right() , rect.top() - rect.height())
path = path.united(linePath)
p.fillPath(path, self.color)
super(chatLabel, self).paintEvent(e)
def checktextsinside(self, text):
font = self.font()
fontmetrics = QFontMetricsF(font)
fontwidth = fontmetrics.width(text)
return fontwidth
def checkeachcharinside(self, text, minimumwidth):
font = self.font()
fontmetrics = QFontMetricsF(font)
t_sum = 0
t_join = ""
chat_data = []
for num, t in enumerate(text):
cw = fontmetrics.widthChar(t)
t_sum += cw
t_join += t
if t_sum > minimumwidth - self.tip_length :
chat_data.append(t_join+"\n")
t_sum = 0
t_join = ""
#append the final extra t_join
chat_data.append(t_join)
return t_sum, chat_data
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
if self.width()>256:
self.setMinimumWidth(128)
text = self.text()
text = text.replace("\n", "")
n_width, chat_data = self.checkeachcharinside(text, self.initial_minimumwidth - (self.tip_length + self.coodinate_point))
joint_text = ""
for joint in chat_data:
joint_text += joint
self.setText(joint_text)
self.setMinimumSize(QSize(n_width + self.tip_length, self.heightForWidth( self.width())))
super(chatLabel, self).resizeEvent(e)
def main():
try:
app=QApplication([])
except Exception as e:
print(e)
widget = chatLabel("This is the result typing!Please Don't wrap!Yaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
答案 1 :(得分:0)
我认为我找到了解决问题的简单方法。
由于问题是如果我在右上角做一个笔尖,则需要对文本进行移位才能使文本包含在圆角矩形(气泡)内。我们可以通过在样式表中使用填充来进行此更改,这将使文本从角落移开。因此,文本将显示为包含在气泡中。
感谢user9402680的回答和他的代码段,我在其中添加了样式表行以实现所需的效果。
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()