Seite 1 von 2
Texte in den Widgets kürzen
Verfasst: Montag 28. März 2016, 21:26
von Sophus
Hallo Leute,
im diesem lauffähigen Quelltext bzw. Programm geht es darum, dass bestimmte Texte (hier in diesem Beispiel) im QLabel und QLineEdit gekürzt werden soll, sobald das Fenster zu sehr verkleinert wird. Dieses Phänomen begegnet uns häufig dort, wenn der Pfad zu lang ist, wird dieser entweder in der Mitte oder am Ende gekürzt, damit der Inhalt weiterhin im Blick bleibt und nicht "verschwindet".
Was habe ich getan? Ich habe sowohl für QLabel als auch für QLineEdit jeweils eine Subclass geschrieben, um die Klassen der jeweiligen Widgets zu überschreiben. Das Kürzen der Texte klappt auch soweit. Allerdings habe ich beim QLineEdit ein kleines Problem. Sobald ich diese Anwendung starte, sieht mein QLineEdit aus wie ein Label und nicht eben wie ein QLineEdit. Was genau habe ich hier übersehen?
Code: Alles auswählen
import sys
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QApplication,\
QLineEdit,\
QLabel,\
QFontMetrics,\
QHBoxLayout,\
QVBoxLayout,\
QWidget,\
QIcon,\
QPushButton,\
QToolTip,\
QBrush,\
QColor,\
QFont,\
QPainter
qt_app = QApplication(sys.argv)
class Example(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setMinimumWidth(100)
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout()
v_layout.addStretch(1)
lbl = ExtendedTruncateTextLabel(self)
lbl.setText("This is a really, long and poorly formatted runon sentence used to illustrate a point.")
lbl_1 = ExtendedTruncateTextLabel(self)
lbl_1.setText("Here you also see a long text, but isn't important.")
l_text = ExtendedTruncateTextLineEdit()
l_text.setText("In the widget namend QLineEdit is also a very long text.")
v_layout.addWidget(lbl)
v_layout.addWidget(lbl_1)
v_layout.addWidget(l_text)
self.setLayout(v_layout)
def run(self):
self.show()
qt_app.exec_()
class ExtendedTruncateTextLineEdit(QLineEdit):
def __init(self, parent):
QLineEdit.__init__(self, parent)
def paintEvent(self, event):
painter = QPainter(self)
metrics = QFontMetrics(self.font())
elided = metrics.elidedText(self.text(), Qt.ElideMiddle, self.width())
painter.begin(self)
painter.drawText(self.rect(), self.alignment(), elided)
painter.end()
class ExtendedTruncateTextLabel(QLabel):
def __init(self, parent):
QLabel.__init__(self, parent)
def paintEvent(self, event):
painter = QPainter(self)
metrics = QFontMetrics(self.font())
elided = metrics.elidedText(self.text(), Qt.ElideMiddle, self.width())
painter.begin(self)
painter.drawText(self.rect(), self.alignment(), elided)
painter.end()
if __name__ == '__main__':
app = Example()
app.run()
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 07:48
von Madmartigan
Das liegt daran, dass paintEvent(self, event) die Standard-Darstellung überschreibt. Du musst natürlich den Aufruf des Events an QLineEdit "weiterleiten".
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 14:05
von Sophus
@Madmartigan: Ich verstehe das nicht ganz. Den Event-Aufruf an QLineEdit weiterleiten? Ich bin gerade etwas verwirrt.
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 16:05
von Madmartigan
Schau dir einfach mal
paintEvent() von
ExtendedTruncateTextLineEdit an.
Was wird da gezeichnet? ... Nur der "gekürzte" Text.
Wenn du das Widget ebenfalls gezeichnet haben möchtest, musst du vorher
paintEvent(...) der Parent-Klasse aufrufen. In deinem Falle fehlt also der Aufruf von
paintEvent(self, event) der Basisklasse
QLineEdit.
Existiert eigentlich ein spezieller Grund, warum du
paintEvent überschreibst, anstatt einfach das Text Attribut des
QLineEdit zu setzen?
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 17:19
von Sophus
@Madmartigan : Nur damit ich dich etwas verstehe. Dieses
paintEvent() ist ein EventHandler, richtig? In diesem Falle wird es in der
ExtendedTruncateTextLineEdit()-Klasse reimplementiert. Das heißt, beim Aufruf dieser Klasse wird das Event sofort mit ausgelöst. Daher bin ich verwirrt, wo ich es vorher aufrufen soll.
Hier mein Beispiel, ob ich dich richtig verstanden habe:
Code: Alles auswählen
class ExtendedTruncateTextLineEdit(QLineEdit):
def __init(self, parent):
QLineEdit.__init__(self, parent)
def paintEvent(self, event):
""" Handle the paint event for the basic class of QLineEdit.
This paint handler draws the text and the widget.
"""
super(ExtendedTruncateTextLineEdit, self).paintEvent(event)
painter = QPainter(self)
metrics = QFontMetrics(self.font())
elided = metrics.elidedText(self.text(), Qt.ElideMiddle, self.width())
painter.drawText(self.rect(), self.alignment(), elided)
Allerdings habe ich hier ein Problem. Ich sehe den Text total verschwommen.
Zu deiner Frage: Ich hatte nicht daran gedacht. Wie sähe das denn aus?
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 17:55
von BlackJack
@Sophus: Was heisst ”verschwommen”? Schau mal genau hin, Du lässt rufst den ursprünglichen Code zum Zeichnen auf und danach zeichnest Du da noch deinen gekürzten Text drüber. Da der ursprüngliche Code zum Zeichnen alles zeichnet, also Rahmen, Hintergrund, *und Text*, macht das so nicht viel Sinn.
Interessant wäre auch wie sich Dein abgeleitet LineEdit eigentlich verhält wenn der Benutzer den gekürzt angezeigten Text bearbeiten will? Funktioniert das überhaupt sinnvoll?
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 18:07
von Sophus
@Black: Ich wurde darauf hingewiesen, dass ich das drawEvent-Ereignis an die Basisklasse der QLineEdit "weiterleiten" soll. Daher dachte ich, ich verbinde beim Aufruf der drawEvent-Funktion auf diesem Weg
super(ExtendedTruncateTextLineEdit, self).paintEvent(event) an die Basis Klasse. Vermutlich und mit Sicherheit habe ich hier komplett falsch gedacht.
Aber selbst, wenn ich das wie folgt mache, erziele ich nicht den gewünschten Effekt: QLineEdit wird nicht gezeichnet, sondern nur der Text.
Code: Alles auswählen
class ExtendedTruncateTextLineEdit(QLineEdit):
def __init(self, parent):
QLineEdit.__init__(self, parent).paintEvent(event)
def paintEvent(self, event):
painter = QPainter(self)
metrics = QFontMetrics(self.font())
elided = metrics.elidedText(self.text(), Qt.ElideMiddle, self.width())
painter.drawText(self.rect(), self.alignment(), elided)
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 18:32
von BlackJack
@Sophus: Natürlich wird bei dem Quelltext nur der Text gezeichnet, denn der dort gezeigte Code zeichnet ja nur den Text.
Die Methode der Basisklasse aufzurufen funktioniert nicht, denn die zeichnet alles, inklusive dem ungekürzten Text.
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 18:36
von Sophus
@BlackJack: Und wie wäre der Lösungsansatz? Irgendwie sehe ich den Wald vor lauter Bäumen nicht.
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 18:41
von BlackJack
@Sophus: Mein Lösungsansatz wäre es erst einmal zu klären wie sich das denn überhaupt sinnvoll verhalten soll wenn der Benutzer versucht den Text zu bearbeiten. Denn dafür ist dieses Widget schliesslich da.
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 18:50
von Sophus
In meinem Fall wird das Widget QLineEdit nur lesend freigegeben. Dort soll der Pfad zu einer bestimmten Datei ausgegeben werden. Rechts neben dem LineEdit-Widget ist dann ein pushButton. Wenn man also den Pfad ändern will, betätigt man einfach den pushButton und das entsprechende Dialog-Fenster öffnet sich. Es ist dem Anwender also nicht möglich direkt über das LineEdit-Widget den Pfad zu ändern. Aber du bringst dennoch eine Interessante Überlegung mit. Denn es soll ja nicht schaden, über den eigenen Tellerrand hinauszusehen.
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 19:04
von BlackJack
@Sophus: Na wenn das eh nur zur Anzeige dienen soll, dann kannst Du doch einfach wie schon vorgeschlagen den Text selber kürzen und den normalen Code zur Anzeige verwenden.
Re: Texte in den Widgets kürzen
Verfasst: Dienstag 29. März 2016, 19:07
von Sophus
In meinem Fall sieht aber die QLineEdit nicht aus wie ein Widget als solches, sondern wie ein Label, und genau da stecke ich buchstäblich fest.
Re: Texte in den Widgets kürzen
Verfasst: Mittwoch 30. März 2016, 14:42
von Madmartigan
Ein Widget vom Typ
QLineEdit macht aber auch nur wirklich Sinn, wenn der Nutzer da eine Eingabe machen kann. Wenn es lediglich der Anzeige dient, dann reicht doch ein
QLabel völlig aus. Dafür müsstest du ja nicht einmal eine eigene Klasse aberben.
Verschwommen siehst du den Text in deinem Beispiel, da du ihn zwei mal zeichnest. Das erste Mal zeichnet das
QLineEdit seinen Content für dich und dann zeichnest du mit dem
QPainter nochmals drüber.
Du müsstest also effektiv das innere Rechteck des Eingabefeldes mit einer undurchlässigen Farbe (weiß) füllen, bevor du den Text zeichnest. Das hört sich zwar komisch, ist aber zudem auch noch völlig unnötig. Meine Frage hinsichtlich des Grundes für das Überschreiben von
paintEvent() war ja absichtlich gestellt.
Wenn du unbedingt den Look eines
QLineEdit haben willst, dann musst du das Widget eben auch selbst zeichnen. Eine Hälfte davon und die andere Hälfte von was anderem ... das geht eben nicht.

Re: Texte in den Widgets kürzen
Verfasst: Mittwoch 30. März 2016, 15:20
von Sophus
@Madmartigan : Ich bin noch einmal auf deinem Text-Attribut-Hinweis zurückgekommen. Was habe ich gemacht? Ich habe im Resize-Ereignis angesetzt, und mir gedacht: Sobald das Fenster in seiner Größe verändert wird, werden auch die darin befindlichen Widgets verändert. Und der Text wird im
setText()-Attribut gekürzt, bzw. wieder hergestellt.
Allerdings habe ich hierbei ein kleines kosmetisches Problem. Sobald das Fenster verkleinert und der Text im LineEdit verkürzt wird, verschwindet ein kleiner Bruchteil des Textes in der linken Ecke. Links im LineEdit verschwinden die ersten 2-3 Buchstaben.
Code: Alles auswählen
import sys
from PyQt4.QtCore import Qt
from PyQt4 import QtCore
from PyQt4.QtGui import QApplication,\
QStyleOptionFrameV2,\
QLineEdit,\
QLabel,\
QFontMetrics,\
QHBoxLayout,\
QVBoxLayout,\
QWidget,\
QIcon,\
QPushButton,\
QToolTip,\
QBrush,\
QColor,\
QFont,\
QPalette,\
QStyle,\
QPainter
qt_app = QApplication(sys.argv)
class Example(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setMinimumWidth(100)
self.fm = QFontMetrics(self.font())
self.mText = QtCore.QString("This is a really, long and poorly formatted runon sentence used to illustrate a point")
self.line_edit_text = QLineEdit()
self.initUI()
def initUI(self):
v_layout = QVBoxLayout()
v_layout.addStretch(1)
v_layout.addWidget(self.line_edit_text )
self.setLayout(v_layout)
def resizeEvent( self, event ) :
self.line_edit_text .setText(self.fm.elidedText( self.mText, Qt.ElideMiddle, event.size().width() ) )
def run(self):
self.show()
qt_app.exec_()
if __name__ == '__main__':
app = Example()
app.run()
Re: Texte in den Widgets kürzen
Verfasst: Mittwoch 30. März 2016, 15:55
von Madmartigan
Das Problem ist hier:
Code: Alles auswählen
self.line_edit_text .setText(self.fm.elidedText( self.mText, Qt.ElideMiddle, event.size().width() ) )
event.size().width() ist nicht die korrekte Breite gegen die du prüfen willst. Was du brauchst ist doch eher die Breite des
QLineEdits.
Code: Alles auswählen
self.line_edit_text .setText(self.fm.elidedText( self.mText, Qt.ElideMiddle, self.line_edit_text.width() ) )
Du kannst da aus kosmetischen Gründen (für pixel-Perfektionismus

) noch ein Offset hinzugeben.
Code: Alles auswählen
self.line_edit_text .setText(self.fm.elidedText( self.mText, Qt.ElideMiddle, self.line_edit_text.width() - 10 ) )
Dann wird der Text rechtzeitig gekürzt und es "verschwindet" kein Text mehr.
Nur am Rande:
Die Variable nennst du
app, aber
Example ist ein
QWidget, keine
QApplication.
Run() ist da etwas verwegen...
Re: Texte in den Widgets kürzen
Verfasst: Mittwoch 30. März 2016, 16:08
von Sophus
@Madmartigan Besten Dank für die Hilfestellung. Eine Frage: Inwiefern ist run() etwas verwegen? Gut, es sieht nicht standardisiert aus, wenn man in der Example()-Klasse eine run()-Funktion schreibt, um daraus die eigentliche QApplication()-Klasse aufzurufen. Aber falsch ist diese Art doch nicht oder? Oder erkaufe ich mir dabei Nachteile?
Und dann habe ich zu deinem Offset noch eine Frage. Was besagt die Zahl -10? Heißt das, sobald -10 Pixel erreicht wird, wird der Text gekürzt?
Re: Texte in den Widgets kürzen
Verfasst: Mittwoch 30. März 2016, 18:57
von Madmartigan
Sophus hat geschrieben:Aber falsch ist diese Art doch nicht oder? Oder erkaufe ich mir dabei Nachteile?
Falsch ... nicht zwingend, aber nicht zu empfehlen. (1)
qt_app ist global deklariert. (2) Warum muss deine
Example-Klasse Kenntnis der
QApplication-Instanz besitzen?
Sophus hat geschrieben:Und dann habe ich zu deinem Offset noch eine Frage. Was besagt die Zahl -10? Heißt das, sobald -10 Pixel erreicht wird, wird der Text gekürzt?
Nein, die -10 ist nur ein Beispiel. Ich würde dafür auch keine
magic-number nehmen, sondern das Offset an entsprechender Stelle deklarieren. Wenn das nur an der Stelle im Code verwendet wird, ist die -10 aber auch ok. Man fragt sich eben nur nach ein paar Wochen/Monaten mal schnell nach deren Herkunft. Daher ist es besser dafür eine gescheite Variable zu vergeben, deren Name aussagekräftig ist.
In dem Beispiel wird also geprüft, ob die Textbreite > (Eingabefeldbreite - 10px) ist. Dadurch wird der Text bereits gekürzt, bevor er die maximale Breite des
QLineEdit erreicht. Probier es einfach aus, du siehst den Effekt sofort.
Re: Texte in den Widgets kürzen
Verfasst: Mittwoch 30. März 2016, 19:01
von Sophus
Verdammte Axt! Es war einfach zu schön um wahr zu sein. Ich habe meinen vorherigen Quelltext dahingehend modifiziert, dass ich einen Text nach dem Start des Programmes in das vorgesehene QLineEdit-Widget eintippe, und dann die Fenstergröße verändere. Was habe ich getan? Durch das
textChanged()-Attribut der QLineEdit wird die Eingabe mittels der
own_textChanged()-Funktion in die
self.mText-Variable gespeichert. Anschließend verändere ich die Fenstergröße, der Text wird auch gekürzt, aber beim vergrößern der Fenstergröße wird der Text nicht mehr vollständig angezeigt - der Text behält seine gekürzte Version bei.
Meine naive Erklärung wäre: Durch die
elidedText()-Methode von der
QFontMetrics()-Klasse überschreibt beim Kürzen des Textes die
self.mText-Variable. Und beim Vergrößern des QLineEdit-Widgets ist kein "originaler" Text vorhanden, um vom gekürzten Text zum ursprünglichen Text zu gelangen. In Anbetracht meiner naiven Erklärung weiß ich leider nicht, wie ich dieses Problem umgehen kann. Eine zweite Variable anlegen, in der der originale unangetastete Text hinterlegen wurde, erscheint mir sinnlose.
Bevor die Frage auftaucht, weshalb ich einen Text zur Laufzeit kürzen will. Mir geht es darum, dass bestimmte Informationen erst zur Laufzeit in die QLineEdit geladen werden, zum Beispiel aus einer Datenbank oder einer Textdatei. Das heißt, der nachträglich geladene Text muss kürzbar und wieder herstellbar sein. Um das zu simulieren wollte ich auf Datenbankanbindungen verzichten, und auf einfacher Weise eine Eingabe in die QLineEdit vornehmen.
Code: Alles auswählen
import sys
from PyQt4.QtCore import Qt
from PyQt4 import QtCore
from PyQt4.QtGui import QApplication,\
QStyleOptionFrameV2,\
QLineEdit,\
QLabel,\
QFontMetrics,\
QHBoxLayout,\
QVBoxLayout,\
QWidget,\
QIcon,\
QPushButton,\
QToolTip,\
QBrush,\
QColor,\
QFont,\
QPalette,\
QStyle,\
QPainter
qt_app = QApplication(sys.argv)
class Example(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setMinimumWidth(100)
self.fm = QFontMetrics(self.font())
self.line_edit_text = QLineEdit()
self.line_edit_text.textChanged.connect(self.own_textChanged)
self.mText = QtCore.QString()
self.initUI()
def own_textChanged(self, prompt_text):
self.mText = prompt_text
def initUI(self):
v_layout = QVBoxLayout()
v_layout.addStretch(1)
v_layout.addWidget(self.line_edit_text)
self.setLayout(v_layout)
def resizeEvent(self, event) :
if self.mText == "" or None:
pass
else:
try:
self.line_edit_text.setText(self.fm.elidedText(self.mText, Qt.ElideMiddle, self.line_edit_text.width() -10))
except: pass
def run(self):
self.show()
qt_app.exec_()
if __name__ == '__main__':
app = Example()
app.run()
Re: Texte in den Widgets kürzen
Verfasst: Donnerstag 31. März 2016, 08:32
von Madmartigan
Es war eigentlich abzusehen, dass das passiert, oder?

Blackjack hatte die Frage ja bereits gestellt - soll der Nutzer in der Lage sein den Text zu editieren? Wenn ja, was hast du dir dafür überlegt?
Eine zusätzliche Variable zur Speicherung des Originaltextes wäre nicht unüblich, allerdings bist du die Probleme damit nicht automatisch los. Du müsstest in dem Falle immer noch herausfinden, ob der Nutzer den Text gerade ändert, damit du den Originaltext in der Eingabe zur Verfügung stellen kannst.
*Pseudo*
- Setze Text programmatisch
- Prüfe Textbreite > Eingabefeldbreite -> JA? Speichere Originaltext in Variable, kürze angezeigten Text
Bis hierhin kein Problem, oder?
- will Nutzer Text ändern -> JA? ersetze gekürzten Text mit Originaltext
Die Stelle ist bereits nicht mehr trivial. Du müsstest eventuell auf die Fokus-Events hören, denn der Text muss ja ersetzt werden, BEVOR der Nutzer Änderungen vornehmen darf.
-------
Eine mögliche Idee wäre z.B. ein QLineEdit mit zwei Modi.
(1)
Read-Only: der Originaltext wird gespeichert und gekürzt angezeigt.
(2)
Edit-Modus: Der Nutzer "aktiviert" per Click/DoppelClick den Edit-Modus, der gekürzte Text wird durch den Originaltext ersetzt.
Im Modus 2 kann also auf dem Originaltext gearbeitet werden. Wichtig ist nun, beim Verlust des Fokus den Text wieder in der Variable zu speichern und ggf. in der Anzeige zu kürzen. Damit befindet sich das Eingabefeld quasi wieder im Zustand 1.
Ich würde ggf. das Konzept überdenken, für den simplen Anwendungsfall scheint mir der Aufwand einfach nicht gerechtfertigt. Generell würde ich auch überlegen, ob der Nutzer diese Texte wirklich ändern können muss. Wenn nicht, dann verwende ein
QLabel zur Anzeige, oder setze alle
QLineEdit einfach auf
Read-Only.