Abfangen einzelner Zeichen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

nachfolgend habe ich für euch ein kleines Beispiel geschrieben. Was möchte ich tun? Nun, ich möchte grundlegend jedes Zeichen einzeln abfangen, die der Benutzer eingibt (in diesem Fall in die QLineEdit). Dieses Problem versuche ich derzeit mit dem eventFilter() zu lösen, indem ich diese Funktion reimplementierte. Allerdings hat mein Beispiel ein kleinen Schönheitsfehler. Wenn ich nun in der QLineEdit Zeichen (Buchstaben, Zahlen etc.) eintippe, dann wird nur das aktuell getippte Zeichen ausgegeben, die anderen bereits getippten Zeichen verschwinden. Wie kann ich das Problem lösen?

An dieser Stelle eine kleine Anmerkung: Ich weiß, das QLineEdit das textChanged()-Signal bereit hält. Allerdings wüsste ich nicht, wie ich an jedes einzelnes Zeichen käme, ohne gleich den gesamten Inhalt der QLineEdit() mittels der text()-Methode zubekommen und diese dann durch die For-Schleife zu jagen.

Code: Alles auswählen

#!/usr/bin/env python
import sys

from PyQt4.QtCore import QEvent
from PyQt4.QtGui import QMainWindow, QWidget, QApplication, QVBoxLayout, QLineEdit


class Window(QMainWindow):

    def __init__(self):
        
        QMainWindow.__init__(self)
        
        centralwidget = QWidget(self)
        self.input_line_edit = QLineEdit()
                
        layout = QVBoxLayout(centralwidget)        
        layout.addWidget(self.input_line_edit)
        
        self.setCentralWidget(centralwidget)

        # Install the event filter to QlineEdit object
        self.input_line_edit.installEventFilter(self)

    def eventFilter(self, received_object, event):
        if(event.type() == QEvent.KeyPress):

            self.input_line_edit.setText(unicode((event.text())))
            
            print u"You Pressed: {}".format(event.text())

            # I returned True right here, because I want the event
            # would be filtered and reach the obj.
            return True

        else:
            return super(QMainWindow, self).eventFilter(received_object, event)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    app.exec_()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du setzt den Text auf »event.text()« und wunderst Dich, dass genau dieses Zeichen im Textfeld erscheint? Dein Code hat zu viele Leerzeilen und Klammern an ungewöhnlichen Stellen. Das macht ihn schwer lesbar.

Code: Alles auswählen

    def eventFilter(self, received_object, event):
        if event.type() == QEvent.KeyPress:
            print u"You Pressed: {}".format(event.text())
        return super(QMainWindow, self).eventFilter(received_object, event)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Besten dank. Manchmal bin ich echt ein Dussel. Du meintest, dass mein Quelltext zu viele Leerzeilen habe. Ich habe absichtlich alles weiter auseinander "gezogen", damit die Einzelschritte besser sichtbar werden. Zumindest hilft mir das immer. Und zu viele Klammern? Da waren doch nur die Klammern bei der If-Abfrage "zu viel" :)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Was mich ein wenig beschäftigt und wobei ich mir nicht sicher bin, ist die Frage, warum die reimplementierte eventFilter()-Methode die super()-Methode, auf die die eventFilter()-Methode angewendet wurde, zurückgibt?
BlackJack

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

@BlackJack: Ich bin noch mehr verwirrt. Stecht nicht direkt hinter der return-Anweisund die super()-Methode?
BlackJack

@Sophus: Da steht der *Aufruf* der super()-Funktion und dann deren Rückgabewert. Da wird also nicht die Methode zurückgegeben sondern das Ergebnis des Aufrufs der Methode.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@"BlackJack: Stimmt, erst wird die super()-Funktion aufgerufen, und anschließend den, der dabei raus kam, zurückgegeben.

Aber ich habe noch zwei Frage:

Warum muss ich in der eventFilter()-Methode den eigenen "Konstrukteur" aufrufen und den Wert zurückgeben? Ich habe ja mal versucht zu verstehen, indem ich mir die Rückgabewerte mittels der Print-Anweisung anzeigen ließ. Es wird ja solange False zurückgegeben, bis man eine Taste drückt. Aber wozu ist das nötig? Irgendwie fehlt mir zum Verständnis ein kleiner Schritt.

Dann die zweite Frage.

Ich habe eben versucht, anstatt der super()-Funktion die Methode der Eltern-Klasse direkt aufzurufen.

Also aus:

Code: Alles auswählen

return super(QMainWindow, self).eventFilter(received_object, event)
wird:

Code: Alles auswählen

return QMainWindow.__init__(self).eventFilter(received_object, event)
Aber dann bekomme ich folgende Fehlermeldung:
Traceback (most recent call last):
File "C:\Users\Sophus\Desktop\py_scripts\check_press.py", line 31, in eventFilter
return QMainWindow.__init__(self).eventFilter(received_object, event)
AttributeError: 'NoneType' object has no attribute 'eventFilter'
BlackJack

@Sophus: Du musst die Methode auf der Basisklasse nicht aufrufen. Kannst das ja mal weg lassen, dann siehst Du ja was (nicht) passiert. Was aber auch irgendwie offensichtlich sein sollte was nicht passiert wenn man die nicht aufruft.

Du hast *nicht* versucht statt `super()` die Methode der Elternklasse direkt aufzurufen. *Das* hätte nämlich geklappt. Du hast stattdessen unsinnigerweise versucht die `__init__()` noch mal aufzurufen, was a) keinen Sinn macht und b) grundsätzlich `None` liefert, und darauf kann man dann natürlich nichts aufrufen was eigentlich zu `QWindow` bzw. `QObject` gehört. Das hätte so aussehen müssen:

Code: Alles auswählen

return QMainWindow.eventFilter(self, received_object, event)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich habe mal das Beispiel erweitert. Nachfolgend werde ich euch das Beispiel vorstellen.

Mein Problem ist, dass - sobald ich den eventFilter() installiert habe - die Cursortasten, Löschen- und Entfern-Tasten nicht wirklich greifen. In diesem Beispiel möchte ich, dass der Benutzer nicht so oft mit der Leertaste rumspielt. Ich möchte also seine Leertaste überwachen.

Problem-Hergang. Sobald das Beispiel-Programm gestartet wird,kann man mittels der Schaltflächen den eventFilter() installieren, und wahlweise auch deinstallieren. Wenn der eventFilter() nun installiert ist, und ich markiere den getippten Text im QLineEdit()-Objekt und drücke anschließend die Lösch- und/oder Entfern-Taste, dann passiert gar nichts. Der Inhalt des QLineEdit()-Objektes wird nicht gelöscht. Muss ich jetzt hingehen und sämtliche Tasten nun händisch verwalten?

Code: Alles auswählen

#!/usr/bin/env python
import sys

from PyQt4.QtCore import QEvent, Qt
from PyQt4.QtGui import QMainWindow, QWidget, QApplication, QVBoxLayout, QLineEdit, QPushButton

class Window(QMainWindow):

    def __init__(self, parent=None):
        
        QMainWindow.__init__(self, parent)

        self.count_space_pressed = 0
        self.current_pos = None

        self.init_ui()
        self.init_signal_slot_push_button()

    def init_ui(self):
        centralwidget = QWidget(self)
        self.input_line_edit = QLineEdit(self)

        
        self.close_push = QPushButton(self)
        self.close_push.setEnabled(False)
        self.close_push.setText("Close")

        self.push_install = QPushButton(self)
        self.push_install.setText("Install eventFilter")
        
        self.push_deinstall = QPushButton(self)
        self.push_deinstall.setText("Deinstall eventFilter")
                
        layout = QVBoxLayout(centralwidget)        
        layout.addWidget(self.input_line_edit)
        layout.addWidget(self.push_install)
        layout.addWidget(self.push_deinstall)
        layout.addWidget(self.close_push)
        
        self.setCentralWidget(centralwidget)
        return

    def install_filter_event(self, widget_object):
        widget_object.installEventFilter(self)
        return

    def deinstall_filter_event(self, widget_object):
        widget_object.removeEventFilter(self)
        return

    def init_signal_slot_push_button(self):

        self.close_push.clicked.connect(self.close)
        self.push_install.clicked.connect(lambda: self.install_filter_event(self.input_line_edit))
        self.push_deinstall.clicked.connect(lambda: self.deinstall_filter_event(self.input_line_edit))
        return

    def strip_string(self, content, site=None):
        if site == "right":
            return content.rstrip()
        elif site == "right_left":
            return content.strip()
        elif site == "left":
            return content.lstrip()

    def eventFilter(self, received_object, event):

        content_line_edit = unicode(received_object.text())
       
        if event.type() == QEvent.KeyPress:

            if event.key() == Qt.Key_Space:
                '''
                    Yes, the user did press the Space-Key. We
                    count how often he pressed the space key.
                '''
                self.count_space_pressed = self.count_space_pressed + 1

                if int(self.count_space_pressed) > 1:
                    '''
                        The user did press the space key more than 1 time.
                    '''
                    
                    self.close_push.setEnabled(False)

                    '''
                        Now we know the user did press the
                        space key more than 1 time. We take a look,
                        if variablenamed (sel.current_pos) is None.
                        That means, no current position is saved.
                    '''
                    if self.current_pos is None:
                        '''
                            Well no current position is saved,
                            that why we save the new position anf
                            then we set the position of the cursor.
                        '''

                        self.current_pos = received_object.cursorPosition()

                        received_object.setCursorPosition(int(self.current_pos))
                        
                        received_object.clear()
                        received_object.setText(self.strip_string(content_line_edit, site="right"))

                    else:
                        '''
                            Well the user press the space key again, for
                            example 3, 4, 5, 6 times we want to keep the
                            old position of the cursor until he press
                            no space key.
                        '''                        
                        received_object.setCursorPosition(int(self.current_pos))

                        '''
                            We have to remove all spaces in a string
                            on the right side and set the content on QLineEdit-widget.
                        '''
                        received_object.clear()
                        received_object.setText(self.strip_string(content_line_edit, site="right"))

                else: pass                

            else:
                '''
                    No the user didn't press the space key.
                    So we set all setting on default.
                '''
                self.close_push.setEnabled(True)
                self.current_pos = None
                self.count_space_pressed = 0

                received_object.clear()
                received_object.setText(self.strip_string(content_line_edit, site="left"))

        # Call Base Class Method to Continue Normal Event Processing
        return QMainWindow.eventFilter(self, received_object, event)

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

Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du änderst den Inhalt Deines Editfeldes und wunderst Dich, dass Kopieren und Löschen dabei nicht funktioniert? Hör einfach auf, den Inhalt zu ändern, wenn Du das überhaupt nicht brauchst. Nebenbei solltest Du Dir ein paar Unsitten abgewöhnen: Leerzeilen, wo sie nicht zur Lesbarkeit beitragen, keine »return« oder »else: pass« die nichts tun. Mehrzeilige Strings sind kein Ersatz für Kommentare. Diese werden mit # gemacht.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: An welchen Stellen soll ich aufhören die Inhalte des QLineEdit()-Objektes zu verändern? Wie du siehst, muss ich an einige Stellen die Inhalte verändern, wenn ich die Leerzeichen per lstrip()/rstrip()/strip() löschen will. Du meinst sicherlich die Zeilen 104, 120 und 134 - korrekt?
Antworten