6

QLabelの最大幅/高さに基づいて、テキストを最適に切り捨てる方法を考えています。着信テキストは任意の長さにすることができますが、整然としたレイアウトを維持するために、最大量のスペース(ウィジェットの最大幅/高さ)を満たすように長い文字列を切り捨てたいと思います。

例えば:

 '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_())
4

4 に答える 4

11

さらに簡単-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_()
于 2012-08-01T17:43:12.697 に答える
1

@Eric Hulserの答えは素晴らしいものの、ラベルが別のウィジェットに配置されたときに機能しないことがわかりました。

これは、Ericの応答とQt ElidedLabelExampleを一緒にハッキングすることで思いついたものです。ここに書かれているように、さまざまなエリジオンモードを渡すことができ、テキストを垂直方向に保持します(もちろん、水平方向に省略されます!)。

ドキュメントに従って実装されたレイアウトフェーズは私には明確ではないので、私はそれについてうまく話すことができません。基本的に、ラベルテキストがラベルの幅を超えていないことを確認します。含まれている場合は、テキストが削除されます。

また、「有効な」行が何を意味するのかも明確ではありません。これらのチェックを削除すると、アプリがクラッシュします。私の推測では、この線はウィジェットを超えていない場合に有効です。

PySideを使用する場合は、

  • PyQt5-> PySide2
  • pyqtSignal->信号

とにかく、お楽しみください!

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_())

于 2021-05-20T23:01:48.110 に答える
0

これは、で幅を決定することで実現できます。この回答QFontMetricsを参照してください。

単純なforループで十分でない限り、すばやくカットする場所を見つけるアルゴリズムを使用または作成することをお勧めします。

于 2012-07-12T07:06:42.000 に答える
0

提供された領域の中央にQLabelを表示したい場合のより簡単なソリューション

label.setAlignment(Qt.AlignmentFlag.AlignCenter)
label.minimumSizeHint = lambda self=label: QSize(0, QLabel.minimumSizeHint(self).height() )
于 2022-02-02T06:16:40.753 に答える