Texte in den Widgets kürzen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

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 :wink: ) 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:

Code: Alles auswählen

if __name__ == '__main__':
    app = Example()
    app.run()
Die Variable nennst du app, aber Example ist ein QWidget, keine QApplication. Run() ist da etwas verwegen...
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@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?
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

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.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

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()
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

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.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Madmartigan: Ich glaube, wir verstehen uns falsch. Der Benutzer soll den Text nicht bearbeiten können. Nur werden bestimmte Informationen in der QLineEdit erst zur Laufzeit angezeigt. Im bisherigen Beispiel wurde bereits der Text im Quelltext eingegeben. Aber zum Beispiel bei Pfadanzeige oder dergleichen werden diese Informationen erst nachträglich angezeigt. Stelle wir uns also vor, in der Datenbank ist eine Pfad-Angabe hinterlegt, und dieser soll nun über QLineEdit angezeigt werden. Das QLineEdit hat den Modus ReadOnly. Der Nutzer soll nicht in der Lage sein den Pfad direkt zu ändern, sondern wird gezwungen über eine Art QFileDialog zum entsprechen Datei-Objekt zu navigieren. Also keine Direkte Änderung. Dennoch besteht das Problem ja darin, wenn der Text nachträglich geladen wird, dass dies zwar gekürzt werden kann, aber nicht mehr seinen ursprünglichen Zustand erreicht.
BlackJack

@Sophus: Und warum ist das ein Problem? Wenn das LineEdit sowieso nur zur Anzeige dient, dann schreibst Du den (gekürzten) Text da ja nur rein, aber liest ihn nie aus. Denn das würde ja bedeuten das Du die GUI an der Stelle nicht zur Interaktion mit dem Nutzer verwenden würdest, sondern zum Speichern von Daten. Speicher den Wert halt woanders. Ungekürzt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Um das Problem zu veranschaulichen: Ich habe den Beispiel-Quelltext um eine eine Funktion erweitert, in welcher eine Textdatei geöffnet, und der Inhalt dieser Textdatei direkt auf QLineEdit ausgegeben wird. Verkleinere ich nun das Fenster, verkürzt sich der Text, aber vergrößere ich den Text, habe ich ein Problem. So in der Art meinte ich es.

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
        

def open_file(file_path):
    try:
    
        with open(file_path, 'r') as custom_file:

            data = custom_file.read()
            return data
    except (OSError, IOError) as e:
        '''
        If there isn't a file than create a new file with content.
        '''
        with open(file_path, 'w') as custom_file:

            custom_file.write("C:\Example\File\Very\Long\Path\To\Your\File\\")
   
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.push_button = QPushButton("Click to load file")
        self.push_button.clicked.connect(lambda:self.manage_file("Text_File.txt"))
        
        self.initUI()

    def manage_file(self, file_path):
        result = open_file(file_path)
        self.mText = QtCore.QString(result)
        self.line_edit_text.setText(result)
    
    def initUI(self):
    
        v_layout = QVBoxLayout()
        v_layout.addStretch(1)       
        v_layout.addWidget(self.line_edit_text)
        v_layout.addWidget(self.push_button)
        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
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Example()
    window.show()
    sys.exit(app.exec_())
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: ich frage mich immer mal wieder, wann Du Dir denn die Grundlagen von Python aneignest? Für was sind Deiner Meinung Exceptions da? In open_file wird eine Datei gelesen oder geschrieben. Eine Funktion sollte nur eine Sache machen. Ganz schlimm ist, dass bei einer nicht-vorhandenen Datei die Funktion gar nichts zurückgibt. Wenn eine Datei nicht gelesen werden kann, ist die Wahrscheinlichkeit auch nicht gering, dass sie nicht geschrieben werden kann. Wie Du oben schreibst, willst Du in Deinem Editfeld einen Dateinamen anzeigen, der aus einem Auswahldialog kommt. Wie kommst Du da auf die Idee, bei einem Beispiel, statt einem konstanten String den Inhalt einer Datei zu nehmen?
Was glaubst Du bewirkt ein "or None" in einem if? Nackte Excepts solltest Du nicht nehmen, dieses try-except scheint mir sowieso ziemlich unnötig.
Für ein Beispiel solltest Du auch Deine Imports aufräumen. Und vor allem sollte das Problem klar werden, das ich hier nicht sehe.
Am besten machst Du Dir für Dein LineEdit eine eigene Klasse:

Code: Alles auswählen

import sys
from PyQt4.QtCore import Qt
from PyQt4.QtGui import (QApplication, QLineEdit, QFontMetrics, QVBoxLayout,
                        QWidget, QPushButton)


class ShorteningLabel(QLineEdit):
    def __init__(self, *args, **kw):
        QLineEdit.__init__(self, *args, **kw)
        self.setReadOnly(True)
        self.fm = QFontMetrics(self.font())
        self.text = ""

    def setText(self, text):
        self.text = text
        self.resizeEvent()

    def resizeEvent(self, event=None):
        QLineEdit.setText(self, self.fm.elidedText(self.text, Qt.ElideMiddle, self.width() - 10))


class Example(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumWidth(100)
        self.line_edit_text = ShorteningLabel()
        self.push_button = QPushButton("Click to select file")
        self.push_button.clicked.connect(self.manage_file)
        v_layout = QVBoxLayout()
        v_layout.addStretch(1)
        v_layout.addWidget(self.line_edit_text)
        v_layout.addWidget(self.push_button)
        self.setLayout(v_layout)

    def manage_file(self):
        self.line_edit_text.setText("C:\\Example\\File\\Very\\Long\\Path\\To\\Your\\File\\")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Example()
    window.show()
    sys.exit(app.exec_())
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Ich denke das Problem ist allen klar, man konnte es ja bereits mit dem ersten Post des Codes testen.

Wenn du keine Nutzer-Interaktion anbietest, dann ist der Fall doch einfach. Du änderst den Code ja nur programmatisch, kennst also den Moment der Änderung. Dabei speicherst du den übergebenen Text in einer separaten Variable und setzt im resizeEvent() den Anzeigetext entsprechend. Bei dem Breiten-Test nimmst du natürlich nicht den angezeigten Text, sondern den Originaltext aus der gespeicherten Variable.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Deine Anmerkung ist vollkommen unbegründet. Es dient nur zu Testzwecken, und da habe ich absichtlich auf Akkuratesse verzichtet. Mir ist klar, dass die Funktion nicht die entsprechenden Namen hat, und das die Funktion mehr macht als es soll. Mir ging es nur darum, dass ich das Problem auf dem schnellsten Weg aufzeigen kann, wo ich stecke. Und ob ich nun einen Rückgabewert von einem QFileDialog nehme oder mir eine Datei auslese. Das Problem ist einfach, dass zur Laufzeit die QLineEdit gefüllt wird - ganz gleich auf welche Art und Weise.

Ich melde mich später wieder, da ich noch unterwegs bin.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo,

ich glaube, ich bin der Sache etwas näher gekommen. Zunächst habe ich meine Imports aufgeräumt, und anschließend habe ich (Sirius3 zu Liebe) die Lese- und Schreibe-Funktion aufgeteilt. Es gibt also eine Funktion, die nur lesend auf eine Textdatei zugreift und eine, die nur schreibend zugreift. Ich habe die Try-Except-Angelegenheit - in Bezug auf den Zugriff der Textdatei - in den View-Bereich verlegt, also in die manage_file()-Funktion. Ich habe erst einmal auf den Hinweis, eine modifizierte QLineEdit-Klasse anzulegen, beiseite gelegt. In meinem Quelltext wird bisher alles gemacht, wie ich es mir vorgestellt habe.

Nun bin ich gespannt, ob ich hierbei etwas übersehen habe und freue mich auf Kritik bzw Anmerkungen.

Code: Alles auswählen

import sys
from PyQt4.QtCore import Qt
from PyQt4 import QtCore
from PyQt4.QtGui import QApplication,\
                        QLineEdit,\
                        QFontMetrics,\
                        QVBoxLayout,\
                        QWidget,\
                        QPushButton,\
                        QFont
        

def write_text_file(file_path):

    with open(file_path, "w") as custom_file:   

        custom_file.write("C:\Example\File\Very\Long\Path\To\Your\File\\")


def read_file(file_path):
    
    with open(file_path, 'r') as custom_file:

        return custom_file.read()
        
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.setReadOnly(True)
                         
        self.mText = QtCore.QString()

        self.push_button = QPushButton("Click to load file")
        self.push_button.clicked.connect(lambda:self.manage_file("Text_File.txt"))
        
        self.initUI()

    def manage_file(self, file_path):
        
        try:
            result = read_file(file_path)
            self.mText = QtCore.QString(result)
            self.line_edit_text.setText(result)
            self.resizeEvent()

        except (OSError, IOError) as e:
            '''
            If there isn't a file than create a new file with content.
            '''
            write_text_file(file_path)
            result = read_file(file_path)
            self.mText = QtCore.QString(result)
            self.line_edit_text.setText(result)
            self.resizeEvent()
    
    def initUI(self):
    
        v_layout = QVBoxLayout()
        v_layout.addStretch(1)       
        v_layout.addWidget(self.line_edit_text)
        v_layout.addWidget(self.push_button)
        self.setLayout(v_layout)

    def resizeEvent(self, event=None) :
        self.line_edit_text.setText(self.fm.elidedText(self.mText, Qt.ElideMiddle, self.line_edit_text.width() -10))

        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Example()
    window.show()
    sys.exit(app.exec_())

Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich möchte dieses Thema wiederbeleben. Ein neues Thema mit dem gleichen Inhalt zu eröffnen, erschien mir unangemessen. Im nachfolgenden Quelltext seht ihr ein ausführbares Programm. Es geht um das Verkürzen eines Textes im QLineEdit(). Im Gegensatz zu meiner früheren Vorstellung geht es hierbei darum, dass der Benutzer innerhalb der QLineEdit() interagieren kann. Das Programm funktioniert soweit tadellos.

Was funktioniert schon mal?
Wenn der Benutzer das Programm startet und im QLineEdit() einen sehr laaaaaaaaangen Text eintippt, dann wird der Text noch nicht verkürzt angezeigt. Sobald aber das QLineEdit() den Fokus verliert, wird der Text verkürzt angezeigt. Um den Fokus zu verlieren habe ich absichtlich ein QPushButton() eingebaut. Wenn der Benutzer das Fenster größer zieht, wird der originale Text angezeigt, soweit das QLineEdit() es zulässt.

Was funktioniert NICHT?
Angenommen, der Benutzer möchte später noch einmal den laaaaangen Text ändern und klickt in dieses Widget. Meine Idee ist, dass im Widget dann der originale Text angezeigt wird - eben ungekürzt. Dies habe ich auch in der setText()-Methode von der ElidingLineEdit()-Klasse auch versucht - daher auch das Argument 'allow_truncate_text '. Aber irgendwie will mir das nicht gelingen.

Code: Alles auswählen

import sys
from PyQt4.QtCore import Qt, QEvent, QString
from PyQt4.QtGui import (QApplication, QLineEdit, QFontMetrics, QVBoxLayout,
                        QWidget, QPushButton)

class ElidingLineEdit(QLineEdit):
    def __init__(self, text = QString(), parent = None):
        '''
            NOTICE:
            =======
            Class initialiser for Eliding text lineedit
        '''
        QLineEdit.__init__(self, parent)
        self.saved_text = text

        self.font_metrics = QFontMetrics(self.font())

        self.textEdited[QString].connect(self.saveText)
        self.editingFinished.connect(self.shortenText)

    def setText(self, text, allow_truncate_text = True):
        '''
            NOTICE:
            =======
            Override the setText()-method of QLineEdit()-class to display
            the shortened text.

            PARAMETERS:
            ===========
            :text                   -   The given text that holds the current widget's text.
                                    
            :allow_truncate_text    -   Defines whether the given text should be shortened.
                                        By default the given text should be shortend.

            :return                 -   Nothing is returned. The statement 'return'
                                        terminates a function. That makes sure that the
                                        function is definitely finished.
        '''
        #   For example the QLineEdit loses the focus (FocusOut), the text has to be shortened.
        if allow_truncate_text:
            QLineEdit.setText(self, self.font_metrics.elidedText(self.saved_text, Qt.ElideRight, self.width() -10))

        else:
        #   Otherwise the QLineEdit gets the focus, the text should NOT be shortened.
        #   For example: The user is typing a text or later he clicks this QLineEdit()
        #   again to change the text possibly
            print "Original text", unicode(self.saved_text)
        return

    def resizeEvent(self, rEvent):
        '''
            NOTICE:
            =======
            Override the resizeevent()-method of QLineEdit()-class to shorten the text.

            PARAMETERS:
            ===========
            :rEvent     -   The given QResizeEvent class contains event
                            parameters for resize events - for example accept()-method.

            :return     -   Nothing is returned. The statement 'return'
                            terminates a function. That makes sure that the
                            function is definitely finished.
        '''
        QLineEdit.setText(self, self.font_metrics.elidedText(self.saved_text, Qt.ElideRight, rEvent.size().width() -10))

        rEvent.accept()

        return

    def saveText(self, new_text):
        '''
            NOTICE:
            =======
            Implement the saveText()-method to save the text as it is changing.

            PARAMETERS:
            ===========
            :new_text   -   The given text have to save.

            :return     -   Nothing is returned. The statement 'return'
                            terminates a function. That makes sure that the
                            function is definitely finished.
        '''
        self.saved_text = new_text

        return

    def shortenText(self ):
        '''
            NOTICE:
            =======
            Implement the saveText()-method to save the text as it is changing.

            PARAMETERS:
            ===========
            :new_text   -   The given text have to save.

            :return     -   Nothing is returned. The statement 'return'
                            terminates a function. That makes sure that the
                            function is definitely finished.
        '''
        QLineEdit.setText( self, self.font_metrics.elidedText( self.saved_text, Qt.ElideRight, self.width() ) )

        return

class Example(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumWidth(80)

        self.init_ui()
        
        #   This attribute is used to say
        #   whether a text should be displayed original or not.
        #   By default, the text shouldn't be displayed original.
        self._show_original_text = False

        self._original_text = ""
        self._copied_original_text = None
        
    def init_ui(self):
        #   Create an instance of QLineEdit()
        self.line_edit_text = ElidingLineEdit()
        self.line_edit_text.setPlaceholderText("Hell world I am here where you are")
        #   We must implement the eventFilter method
        #   and enable this property to the widgets that are needed with
        self.line_edit_text.installEventFilter(self)

        #   Create an instance of QPushButton()
        self.push_button = QPushButton("Just click me, nothing will happen.")
        self.push_button.installEventFilter(self)

        #   Add the both widgets to the layout, that is created.
        v_layout = QVBoxLayout()
        v_layout.addStretch(1)
        v_layout.addWidget(self.line_edit_text)
        v_layout.addWidget(self.push_button)
        self.setLayout(v_layout)

        #   Set a slot, connect() the textChanged-slot to the handle_text_change()-method.
        self.line_edit_text.textChanged.connect(lambda text: self.handle_text_change(text, allow_cut_text = False))
        
    def handle_text_change(self,
                           text = None,
                           allow_cut_text = True):
        self._original_text = text
        self.line_edit_text.setText(text, allow_truncate_text = allow_cut_text)

    def eventFilter(self, obj, event):
        #   The eventFilter method has as information
        #   the object and type of event. So we have to
        #   listen to the focus events.
        if event.type() == QEvent.FocusIn:
            if obj == self.line_edit_text:
                self._show_original_text = False
                self.line_edit_text.setText(self.line_edit_text.saved_text, allow_truncate_text = False)
        if event.type() == QEvent.FocusOut:
            if obj == self.line_edit_text:
                self._show_original_text = True
                self.line_edit_text.setText(self._original_text, allow_truncate_text = True)
        return super(QWidget, self).eventFilter(obj, event)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Example()
    window.show()
    sys.exit(app.exec_())
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es empfiehlt sich generell auf Überladung von Methoden zu verzichten, außer die sind extra dazu gedacht. Ich würde setText also unangepasst lassen, und stattdessen einfach nur in den entsprechenden Signalen den Text abgreifen und setzen.

Alternativ geht vielleicht sowas wie ein stacked Widget, bei dem du das kurze Widget über das normale malst, solange das nicht den Fokus hat. Das mag aber wiederum öde sein beim Versuch, das Fokus-bekommen dann wieder nach unten zu delegieren.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Ist es nicht schon standard, dass bestimmte Widgets überschrieben werden, eben weil man sich eine andere Funktionalität wünscht? Auf den originalen Text kann zugreifen, allerdings, wenn ich diesen dann der setText()-Methode übergebe, ändert sich gar nichts.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das Problem bei Überladung ist: du weißt nicht, was die Methode alles tut. Entscheidest aber “selbstherrlich” das du das original nicht aufrufen musst. Es kann aber sehr gut sein, das da etwa passiert, das zwingend notwendig für das funktionieren des Widgets ist.

Aus diesem Grund ist man von Überladung ehr weggekommen. Und ja, das ist kein Gesetz, und es gibt klar Fälle wo sie genutzt werden muss oder soll. Das hier ist keiner.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Vermutlich hast du Recht. Gerade weil ich mich noch als Anfänger zähle, sollte ich die Finger von den Re-Implementierungen lassen. Ich dachte nur, es wäre in Ordnung, da Siriu3 es auch so verfahren ist, dass setText()-Methoden der QLineEdit()-Klasse überschrieben wurde.

Mein nachfolgender Lösungsansatz wurde auf deine Kritik nochmals kräftig überarbeitet und es scheint wie gewünscht zu funktionieren. Der Benutzer schreibt seinen langen Text in die QLineEdit(). Während des Schreibens wird der Text nicht gekürzt. Verliert die QLineEdit() den Fokus, dann wird der Text gekürzt. Wenn der Benutzer das Fenster vergrößert, wird, soweit wie möglich, der originale Text angezeigt. Macht der Benutzer das Fenster sehr klein, so dass der Text wieder gekürzt wird und klickt dann in die QLineEdit(), so dass diese den Fokus erhält, dann wird der Text ungekürzt angezeigt. Ich hoffe, ich freue mich nicht zu früh 8)

Code: Alles auswählen

import sys
from PyQt4.QtCore import Qt, QEvent, QString
from PyQt4.QtGui import (QApplication, QLineEdit, QFontMetrics, QVBoxLayout,
                        QWidget, QPushButton)

class Example(QWidget):
    def __init__(self, elide = Qt.ElideMiddle,  parent = None):
        QWidget.__init__(self, parent)
        
        self.setMinimumWidth(80)

        self.font_metrics = QFontMetrics(self.font())

        self.saved_text = ""

        self._elide = elide

        self.init_ui()
        
    def init_ui(self):
        #   Create an instance of QLineEdit()
        self.line_edit_text = QLineEdit()
        self.line_edit_text.setPlaceholderText("Hell world I am here where you are")
        #   We must implement the eventFilter method
        #   and enable this property to the widgets that are needed with
        self.line_edit_text.installEventFilter(self)

        #   Create an instance of QPushButton()
        self.push_button = QPushButton("Just click me, nothing will happen.")
        self.push_button.installEventFilter(self)

        #   Add the both widgets to the layout, that is created.
        v_layout = QVBoxLayout()
        v_layout.addStretch(1)
        v_layout.addWidget(self.line_edit_text)
        v_layout.addWidget(self.push_button)
        self.setLayout(v_layout)

        #   Set a slot, connect() the textChanged-slot to the handle_text_change()-method.
        self.line_edit_text.textEdited[QString].connect(self.save_text)

    def eventFilter(self, obj, event):
        #   The eventFilter method has as information
        #   the object and type of event. So we have to
        #   listen to the focus events.
        if event.type() == QEvent.FocusIn:
            if obj == self.line_edit_text:
                self.line_edit_text.setText(self.saved_text)
                print "You clicked in the LineEdit"
        if event.type() == QEvent.FocusOut:
            if obj == self.line_edit_text:
                print "You lost focus"
                self.line_edit_text.setText(self.font_metrics.elidedText(self.saved_text, self._elide, self.width() -10))
        return super(QWidget, self).eventFilter(obj, event)

    def save_text(self, new_text):
        '''
            NOTICE:
            =======
            Implement the saveText()-method to save the text as it is changing.

            PARAMETERS:
            ===========
            :new_text   -   The given text have to save.

            :return     -   Nothing is returned. The statement 'return'
                            terminates a function. That makes sure that the
                            function is definitely finished.
        '''
        self.saved_text = new_text

        return

    def resizeEvent(self, rEvent):
        '''
            NOTICE:
            =======
            Override the resizeevent()-method of QLineEdit()-class to shorten the text.

            PARAMETERS:
            ===========
            :rEvent     -   The given QResizeEvent class contains event
                            parameters for resize events - for example accept()-method.

            :return     -   Nothing is returned. The statement 'return'
                            terminates a function. That makes sure that the
                            function is definitely finished.
        '''
        self.line_edit_text.setText(self.font_metrics.elidedText(self.saved_text, self._elide, rEvent.size().width() -10))

        rEvent.accept()

        return


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Example()
    window.show()
    sys.exit(app.exec_())
Nur habe ich ein kleines kosmetisches Problem. Wenn der Text in der Mitte gekürzt wird, dann wird der Text auf der linken Seiten nicht korrekt angezeigt.
Bild
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das Problem verstehe ich nicht. Links oder rechts wird doch angeschnitten. Wenn du das nicht willst, musst du weiter kürzen?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Es wird doch in der Mitte gekürzt, richtig? Demzufolge dürfe links und rechts nichts passieren? Ich dachte zuerst daran links +10 zu schreiben:

Code: Alles auswählen

self.line_edit_text.setText(self.font_metrics.elidedText(+10, self.saved_text, self._elide, self.width() -10))
Aber das geht nicht:
Traceback (most recent call last):
File "C:\Users\Sophus\Desktop\kuerzung.py", line 192, in eventFilter
self.line_edit_text.setText(+10, self.font_metrics.elidedText(self.saved_text, self._elide, self.width() -10))
TypeError: QLineEdit.setText(QString): argument 1 has unexpected type 'int'
Antworten