setFocus Problem (nach löschen eines anderen Widgets)

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
pumpkinhead
User
Beiträge: 5
Registriert: Donnerstag 4. September 2014, 09:16

Hallo,
in dem Beispiel wird ein QLineEdit durch einen QPushButton aktiviert. Das QLineEdit Widget sollte automatisch fokusiert sein. Das funktioniert auch, aber nur wenn der Button nicht gelöscht wird. Wird der QPushButton gelöscht, dann hat .setFocus() keine Wirkung. Warum bekommt das QLineEdit keinen Fokus, wenn ich vorher den Button lösche (in der Art und Weise wie das geschieht)?

Es funktionier auch, wenn ich den Button erst nach dem .setFocus() Befehl lösche - leider ist das für das tatsächliche Programm keine Option.
Ich bin für alle Idee/Tipps dankbar!
Torsten

Code: Alles auswählen

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
     
    from PyQt4.QtGui import *
     
    class Form(QWidget):
     
        def drawtextBox(self):
            self.answerLine = QLineEdit()
            self.textBox = QHBoxLayout()
            self.textBox.addWidget(self.answerLine)
            self.mainLayout.addLayout(self.textBox)
     
        def drawcontinueBox(self):
            self.submitButton = QPushButton("Continue")
            self.continueBox = QHBoxLayout()
            self.continueBox.addWidget(self.submitButton)
            self.mainLayout.addLayout(self.continueBox)
            self.submitButton.clicked.connect(self.submitContinue)
     
        ## Routine to Remove a Component ("which" = which BoxLayout)
        def removeItem(self, which):
            for cnt in reversed(range(which.count())):
                widget = which.takeAt(cnt).widget()
                if widget is not None:
                    widget.deleteLater()
     
        def __init__(self, parent=None):
            super(Form, self).__init__(parent)
     
            self.mainLayout = QVBoxLayout(self)
     
            self.drawcontinueBox()
     
            self.setLayout(self.mainLayout)
            self.setWindowTitle("Test")
     
        def submitContinue(self):
            self.removeItem(self.continueBox)      ## Here is the problem
     
            self.drawtextBox()
            self.answerLine.setFocus()
     
    if __name__ == '__main__':
        import sys
        app = QApplication(sys.argv)
        screen = Form()
        screen.show()
        sys.exit(app.exec_())
BlackJack

@pumpkinhead: Ich vermute mal durch das Löschen wird der Fokus verändert.

Ich finde das sowieso ziemlich komisch gelöst, darum würde ich als Alternative mal `QStackedWidget` vorschlagen, statt ausserhalb der `__init__()` neue Attribute zu definieren einfach am Anfang alles erstellen und dann einfach umschalten zwischen dem Button und der Textzeileneingabe.
pumpkinhead
User
Beiträge: 5
Registriert: Donnerstag 4. September 2014, 09:16

Vielen Dank für den Hinweis.

"Am Anfang alles erstellen" hat irgendwie nicht hingehauen. Das GUI Layout wechselt sehr dynamisch:
1: Button (nach click)
2: wird alles gelöscht und es erscheint ein Label und ein QLineEdit (nach Return)
3: wird alles gelöscht und es kommt eine Reihe von Buttons (die ein Skala bilden) (nach Klick)
4: kommt wieder ein Label und ein QLineEdit
5: usw (auch ist es nicht fix in welcher Reihenfolge etwas kommt - wird durch eine externe Datei vorgegeben)

Aber QStackedWidget klingt nach einer Möglichkeit,
Danke!
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

In der Doku liest man zu QLayout::removeItem ( QLayoutItem * item )
Removes the layout item item from the layout. It is the caller's responsibility to delete the item.

Der Button inklusive Layout sind also nicht länger Bestandteil des Parent-Layouts, aber die Objekte existieren nach wie vor.
Dynamische Erstellung von GUI-Objekten hat bisweilen interessante Hürden. Wenn das nachträgliche Erstellen dieser Objekte nicht zwingend notwendig ist, dann halte dich an das, was BlackJack dir geraten hat. Erstelle alle Widgets im Konstruktor und benutze hide()/show() nach Bedarf.

EDIT: Okay, habe deinen letzten Eintrag übersehen. Für das von dir beschriebene Vorhaben bietet sich das QStackWidget dann wirklich an.
pumpkinhead
User
Beiträge: 5
Registriert: Donnerstag 4. September 2014, 09:16

Hallo,
ist QStackedWidget/ QStackedLayout sehr weit verbreitet?
Ich kann online keinen einfach verständlichen Beispiel-Code finden.
Kann mir jemand einen Tipp/ Link geben?

Andere Frage zum Thema. Warum ist meine Vorgehensweise ungewöhnlich und in was für Probleme würde ich damit kommen?
Danke
BlackJack

@pumpkinhead: Wie sieht bei einem so einfachen Widget unverständlicher Beispielcode aus und wieso muss man über die Dokumentation hinausgehend nach so etwas suchen!? So ein `QStackedWidget` hat doch nur je zwei Properties, Signals, und Slots, und 8 Methoden wobei nichts davon in irgendeiner Weise kompliziert ist.
pumpkinhead
User
Beiträge: 5
Registriert: Donnerstag 4. September 2014, 09:16

Lieber BlackJack,
der Beispielcode ist ein Ausschnitt aus einem komplexeren Programm, welches ich mit meinen Kenntnissen (d.h. Qt-Anfänger) bisher nur so bewerkstelligen konnte. Das Ursprungsprogramm hat das Problem mit dem .setFocus. Den Code habe ich soweit reduziert bis sich das Problem herauskristalisiert hat - das Problem aber zur Veranschaulichung noch da ist. Ich versuche natürlich mit dem Code nicht einen Button und eine Textfeld zu erzeugen.

Leider sind bei mir Programmierkentnisse nicht angeboren und ich muss unweigerlich eine Phase durchlaufen in der mein Code alles andere als gut ist. Die Einfachheit des QStackedWidgets mag ein Teil des Problems sein. Anhand der Beispiele im Netz - in denen einfach ein QStackedWidget aufgerufen wird und dann nichts anderes als ein setCurrentIndex... auftaucht - ist die Funktionsweise schwer nachvollziehbar.
Vielen Dank für den Hinweis mit dem QStackedWidget und nichts für ungut.
Torsten
BlackJack

@pumpkinhead: Deinen ersten Absatz habe ich nicht verstanden. Also den Inhalt schon, aber nicht warum Du den schreibst.

Und nochmal die Frage warum das Beispiel und die *ausführliche Beschreibung* in Worten in der Dokumentation nicht ausreicht!? Dieses Widget ist keine Raketenwissenschaft. Das ist supereinfach, sagen wir mal im Gegensatz zu den MVC-Geschichten wo mehrere, deutlich kompliziertere Klassen auf nicht-triviale Weise miteinander interagieren müssen.

Was konkret verstehst Du daran nicht? Das verstehe ich nicht. Wenn ich nämlich jetzt ein Beispiel schreiben würde, dann würde das auf etwas hinaus laufen wie das was in der Dokumentation steht. `QStackedWigdet` erstellen, und dann eine Methode um die einzelnen Widgets hinzuzufügen und dann noch eine andere um jeweils auszuwählen welches davon aktuell angezeigt wird. Das Beispiel verwendet dazu eine `QComboBox`, bei Dir ist das über den Code verteilt bei den jeweiligen Aktionen die Du beschrieben hast. Statt bei dem Knopfdruck also den Knopf zu zerstören und Texteingabe und neuen Knopf zu erstellen, musst Du an der Stelle einfach nur die passende ”Seite” in dem `QStackedWidget` auswählen wo diese beiden drauf sind. Mehr gibt es da nicht zu zeigen als in dem Beispiel in der Dokumentation. Das ist einfach so einfach.

Edit:

Code: Alles auswählen

import sys

from PyQt4 import QtGui


class Window(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.pages = QtGui.QStackedWidget(self)

        page = QtGui.QWidget(self.pages)
        button = QtGui.QPushButton('Continue')
        layout = QtGui.QVBoxLayout()
        layout.addWidget(button)
        page.setLayout(layout)
        self.pages.addWidget(page)
        button.clicked.connect(self.do_continue)

        page = QtGui.QWidget(self.pages)
        self.answer_line = QtGui.QLineEdit()
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.answer_line)
        page.setLayout(layout)
        self.pages.addWidget(page)
        self.answer_line.returnPressed.connect(self.process_answer)

        self.answer_display = QtGui.QLabel()

        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.pages)
        layout.addWidget(self.answer_display)
        self.setLayout(layout)

    def do_continue(self):
        self.answer_line.clear()
        self.pages.setCurrentIndex(1)

    def process_answer(self):
        self.answer_display.setText(self.answer_line.text())
        self.pages.setCurrentIndex(0)


def main():
    application = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(application.exec_())


if __name__ == '__main__':
    main()
pumpkinhead
User
Beiträge: 5
Registriert: Donnerstag 4. September 2014, 09:16

Danke BlackJack,
für das Beispiel. Ich habe wie gesagt etwas komplizierteres erwartet - was irgendwie dazu geführt hat, dass ich es nicht gecheckt habe.

Ich bin aber noch nicht sicher, ob das für mein Programm tatsächlich funktioniert. Das Problem ist, dass ich nicht nur 2 oder 3 "pages" brauche. Das Programm muss so flexibel sein, dass es auch 100 pages sein können, jede mit einem etwas anderen Layout und anderen Widgets. Wieviele pages es sind weiß ich nicht vorher und auch das genaue Layout weiß ich nicht vorher - beides wird durch eine benutzererstellte Textdatei bestimmt. Ich teste gerade, ob das funktioniert.

Deshalb nochmal zurück zu meiner ursprünglichen Frage. Gibt es eine Möglichkeit herauszufinden warum das Löschen des "Continue"-Buttons dazu führt, dass das danach erstellte QLineEdit keinen Fokus bekommt?
Danke
BlackJack

@pumpkinhead: Nun, weil das `QLineEdit` nicht nach dem Löschen erstellt wird, sondern davor. Die „Continue”-Schaltfläche wird erst gelöscht wenn Deine Rückruffunktion/-methode komplett durchlaufen ist und die Kontrolle wieder an die Qt-Hauptschleife zurückgegeben hat.
Antworten