我想知道如何根据它的最大宽度/高度来最好地截断QLabel中的文本。 传入的文本可以是任意长度,但为了保持整洁的布局,我想截断长字符串以填充最大空间(小部件的最大宽度/高度)。
E.g:
'A very long string where there should only be a short one, but I can't control input to the widget as it's a user given value'
会变成:
'A very long string where there should only be a short one, but ...'
基于当前字体所需的空间。
我怎样才能做到最好?
这是我所追求的一个简单示例,尽管这是基于字数,而不是可用空间:
import sys
from PySide.QtGui import *
from PySide.QtCore import *
def truncateText(text):
maxWords = 10
words = text.split(' ')
return ' '.join(words[:maxWords]) + ' ...'
app = QApplication(sys.argv)
mainWindow = QWidget()
layout = QHBoxLayout()
mainWindow.setLayout(layout)
text = 'this is a very long string, '*10
label = QLabel(truncateText(text))
label.setWordWrap(True)
label.setFixedWidth(200)
layout.addWidget(label)
mainWindow.show()
sys.exit(app.exec_())
答案 0 :(得分:9)
更简单 - 使用QFontMetrics.elidedText方法并重载paintEvent,这是一个例子:
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QApplication,\
QLabel,\
QFontMetrics,\
QPainter
class MyLabel(QLabel):
def paintEvent( self, event ):
painter = QPainter(self)
metrics = QFontMetrics(self.font())
elided = metrics.elidedText(self.text(), Qt.ElideRight, self.width())
painter.drawText(self.rect(), self.alignment(), elided)
if ( __name__ == '__main__' ):
app = None
if ( not QApplication.instance() ):
app = QApplication([])
label = MyLabel()
label.setText('This is a really, long and poorly formatted runon sentence used to illustrate a point')
label.setWindowFlags(Qt.Dialog)
label.show()
if ( app ):
app.exec_()
答案 1 :(得分:1)
我发现@Eric Hulser 的回答虽然很棒,但在将标签放入另一个小部件时不起作用。
我通过将 Eric 的响应和 Qt Elided Label Example 混在一起来想出这个。如此处所述,它允许传入不同的省略模式并垂直保留文本(当然是水平省略!)。
实施 according to the docs 的布局阶段我不清楚,所以我不能很好地谈论它。基本上,它会检查标签文本是否超出标签的宽度;如果是,它会忽略文本。
a "valid" line 的含义也不清楚。删除这些检查会导致应用程序崩溃。我的猜测是当它不超出小部件时该行是有效的。
如果你想使用 PySide,
无论如何,享受吧!
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
class EliderLabel(QtWidgets.QLabel):
elision_changed = QtCore.pyqtSignal(bool)
def __init__(self, text='', mode=QtCore.Qt.ElideRight, **kwargs):
super().__init__(**kwargs)
self._mode = mode
self.elided = False
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.setText(text)
def setText(self, text):
self._contents = text
# Changing the content require a repaint of the widget (or so
# says the overview)
self.update()
def text(self):
return self._contents
def minimumSizeHint(self):
metrics = QtGui.QFontMetrics(self.font())
return QtCore.QSize(0, metrics.height())
def paintEvent(self, event):
super().paintEvent(event)
did_elide = False
painter = QtGui.QPainter(self)
font_metrics = painter.fontMetrics()
# fontMetrics.width() is deprecated; use horizontalAdvance
text_width = font_metrics.horizontalAdvance(self.text())
# Layout phase, per the docs
text_layout = QtGui.QTextLayout(self._contents, painter.font())
text_layout.beginLayout()
while True:
line = text_layout.createLine()
if not line.isValid():
break
line.setLineWidth(self.width())
if text_width >= self.width():
elided_line = font_metrics.elidedText(self._contents, self._mode, self.width())
painter.drawText(QtCore.QPoint(0, font_metrics.ascent()), elided_line)
did_elide = line.isValid()
break
else:
line.draw(painter, QtCore.QPoint(0, 0))
text_layout.endLayout()
self.elision_changed.emit(did_elide)
if did_elide != self.elided:
self.elided = did_elide
self.elision_changed.emit(did_elide)
class MyDialog(QtWidgets.QWidget):
def __init__(self):
super().__init__()
text = 'This is a really, long and poorly formatted runon sentence used to illustrate a point'
label = EliderLabel(text, parent=self)
label.elision_changed.connect(self.on_elide)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
def on_elide(self, val):
print('Elided: ', val, flush=True)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
dia = MyDialog()
dia.show()
sys.exit(app.exec_())
答案 2 :(得分:0)
您可以通过QFontMetrics
确定宽度来实现此目的,请参阅this answer。
您可能想要使用或创建一些能够快速找到切割位置的算法,除非在简单的for循环中完成它就足够了。