Fenster mit QHBoxLayout schließen

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

Hallo jerch,
irgendwie verstehe ich dich besser als bei Blackjack. Ich möchte seine Fähigkeiten bezüglich der Pythonprogrammierung nicht in Frage stellen, aber in Puncto Erklärung bleibe ich bei BlackJacks Erklärung immer auf der Strecke. Daher möchte ich dir für deine Geduld danken :-) Nun gehen wir mal deine Anmerkungen Stück für Stück durch.
jerch hat geschrieben: Beide Funktionen übernehmen Konstruktoraufgaben und sind nicht voneinander abhängig. Man könnte die Blöcke daher auch in __init__ packen. Als Subaufruf innerhalb der jeweils anderen macht keinen Sinn - es verschleiert die Unabhängigkeit und macht die Nachvollziehbarkeit des Kontrollflusses unnötigerweise anstrengender. Eine Funktion/Methode sollte eine Funktionseinheit sein, wenn Du z.B. createToolBar in createMenuBar aufrufst, wäre der Name für letzteres "falsch" - die Methode müsste dann eigentlich createMenuAndToolBar heissen.
Hier muss ich dich mal ganz naiv fragen: Was meinst du mit "pack die Methoden in die __init__? Du meinst bestimmt in def __init__(self, parent=None):? Und wie "packt" man dort eine Methode/Funktion rein?
jerch hat geschrieben: Nun zu Deinem Design/Code:
Ich würde die Menü- und Toolbarsache eher im MainWindow halten und die Qt-Standardklassen dafür verwenden. Dann ist das sehr hübsch über den Designer realisierbar ohne viel Quellcode. Was können Deine Kindklassen denn mehr, das Dir am Qt-Portfolio fehlte?
Ich möchte erst einmal versuchen Benutzeroberflächen von Hand zu schreiben, und QT-Designer vorerst aus dem Weg gehen. Und weshalb ich die MenuBar ausgelagert habe und nicht im MainWindow halte? Nun, die MenuBar nimmt an MenuItems zu, und ich will nicht, dass allein durch die MenuBar der Quelltext im MainWindow unendlich lang wird. Im Modul MDI_Menu.py sind schon rund 230 Zeilen an Code. Und dabei ist die MenuBar nach meinen Vorstellungen noch nicht komplett. Und wenn ich das alles im MainWindow halten würde, wäre es ganz schön unübersichtlich.
jerch hat geschrieben: In Zeile 64 und 65 bindest Du Exemplare von MyForm und dem umgebenden SubWindow an Attribute. Da Du im gezeigten Code sonst nichts weiter damit in anderen Methoden machst, brauchst Du die nicht, da sich Qt darum kümmert.
Diese Zeilen hast du angesprochen. Wer sind Exemplare von MyForm?

Code: Alles auswählen

[...]
            self.w = MyForm(self)
            self.SuWindow = self.centralWidget().addSubWindow(self.w)
[...]
Und mit Attribute meinst du in diesem Fall diese Zeile (setAttribute)? Und diese Zeile kann ich komplett löschen? Ich dachte, nur so kann und soll ich den Flag setzen, damit aufgeräumt und Resourcen freigegeben werden.

Code: Alles auswählen

            self.w.setAttribute(Qt.WA_DeleteOnClose)
jerch hat geschrieben: Ohne Qt musst Du sie objektweit binden, sonst räumt Python das Objekt auf. Dann solltest Du aber einen Containertypen nutzen, um alle Referenzen zu merken. So wie Du es jetzt umgesetzt hast, sind mit einem neuen Subfenster die Referenzen (und ohne Qt auch die Objekte) älterer noch offener Subfenster weg.
Zwei Fragen: Was meinst du mit "ohne Qt"? Ich importiere eingangs Qt (from PyQt4.QtCore import Qt). Containertypen? Kannst du mir mal bitte ein kleines Pseudocode als Beispiel präsentieren, was du damit meinst?
jerch hat geschrieben: Zeile 67 + 70-73:
Arrgh! Bitte überleg Dir mal, was Du da machst. Was ich lese: "Ein Button auf w ruft `closeActive` auf, welches das derzeit aktive Fenster der MdiArea schliesst." Wolltest Du das? Oder eher das eigene Subfenster schliessen? Falls letzteres - da ist zuviel Magie drin. Was passiert beim Klicken eines Buttons wohl mit dem Fokus der Subfenster? Ändert der sich? Vor oder nach Singal/Slot-Verarbeitung? Ist das verlässlich?
Mir ist dieser Button eh schleierhaft - wofür brauchst Du den? Die Subfenster bringen doch alles dafür mit.
Du hast Recht, ich will das eigene SubWindow schließen. Und ich habe die Angelegenheit wie folgt gelöst:

Code: Alles auswählen

[...]
    def createNew(self):
        if  self.existUnterfenster(self.Dict_Language.Dict_TestDialog["Title"]):
            reply = QMessageBox.critical(self, (self.Dict_Language.Dict_Message_WindowExists["MessageTitle"]),
            self.Dict_Language.Dict_Message_WindowExists["MessageText"])
            print "MessageBox wurde ausgegeben."
        else:
            self.w = MyForm(self)
            self.SuWindow = self.centralWidget().addSubWindow(self.w)
            self.w.setAttribute(Qt.WA_DeleteOnClose)
            self.w.BClose.clicked.connect(self.closeSubWindow)
            self.w.show()
            
    def closeSubWindow(self):
        self.SuWindow.close()
    [...]
Wozu ich den Schließ-Button brauche? Ich weiß, in der Titelleiste eines jeden QWidgets und QDialoges kann man auf einen tollen X drücken. Aber ich möchte, dass der Anwender auch über einen Button das Fenster schließen kann. Mir ist schleierhaft, weshalb es dir schleierhaft sei? Es gibt sehr viele Anwendungen, die eben genauso arbeiten. Und außerdem denke ich ja auch schon einen Schritt weiter. Da es eine Datenbank-Anwendung werden soll, so sollen im Anschluss auf dem Klick "Speichern" die Datensätze in die Datenbank und das Fenster geschlossen werden und eine MessageBox ausgegeben werden, wo dann in Etwa steht "Erfolgreich gespeichert". Aber verlegen wir die Geschmacksfrage erstmal nach hinten.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Sophus hat geschrieben:Ich möchte erst einmal versuchen Benutzeroberflächen von Hand zu schreiben, und QT-Designer vorerst aus dem Weg gehen. Und weshalb ich die MenuBar ausgelagert habe und nicht im MainWindow halte? Nun, die MenuBar nimmt an MenuItems zu, und ich will nicht, dass allein durch die MenuBar der Quelltext im MainWindow unendlich lang wird. Im Modul MDI_Menu.py sind schon rund 230 Zeilen an Code. Und dabei ist die MenuBar nach meinen Vorstellungen noch nicht komplett. Und wenn ich das alles im MainWindow halten würde, wäre es ganz schön unübersichtlich.
Das im MainWindow zu halten, hat echte Vorteile - z.B. sind die Aktionsobjekte in beiden Leisten nutzbar. Das geht in Deinem Falle auch, allerdings bringst Du dann doch eine Abhängigkeit rein, wo eigentlich keine sein sollte - Du müsstest die Aktionen in einer Leiste erstellen und diese Leiste der anderen bekannt geben, um sie auch dort nutzen zu können.
Mehr Übersichtlichkeit schafft Deine Variante mMn nicht, da Du zum einem auf class-Ebene wunderbar mit Methoden strukturieren kannst (man kann diese idR auch ausblenden in gängigen Editoren) und auf der anderen Seite eh die Aktionen an Methoden im MainWindow bindest. Damit arbeitest Du wieder mit Implementationsdetails, wo es eigentlich nicht nötig wäre.
Die Sache mit dem zunächst Selbstschreiben lasse ich gelten - allerdings läufst Du hier Gefahr, ein Codemonster zu bauen, was Du in 3 Monaten nicht mehr anschauen magst. Mit dem Designer dagegen würdest Du Flexibilität gewinnen. Und für paradigmatische Fingerübungen sind kürzere Sachen geeigneter. Dabei lernt man auch eher, die Wiederverwendbarkeit als kostbar einzuschätzen und die Programme entsprechend zu strukturieren. 3 Probleme später kommt dann häufig der Aha-Moment "Das hab ich da doch schonmal ähnlich umgesetzt" und man kann sich aus dem eigenen Baukasten bedienen und gleich noch Verbesserungen vornehmen.
Sophus hat geschrieben:Diese Zeilen hast du angesprochen. Wer sind Exemplare von MyForm?
Sophus hat geschrieben:Und mit Attribute meinst du in diesem Fall diese Zeile (setAttribute)? Und diese Zeile kann ich komplett löschen? Ich dachte, nur so kann und soll ich den Flag setzen, damit aufgeräumt und Resourcen freigegeben werden.
Ehm weisst Du, wie man zu einem Exemplar kommt? Bzw. was das meint? Unterschied zu Klasse? Ich will Dir nicht zu nahe treten, aber Du scheinst eigentlich nicht verstanden zu haben, was da genau passiert. Die Begriffe sind z.T. verwaschen und ich habe nicht die Zeit und Lust, dass alles mit Dir durchzukauen. Ein gutes Buch zu OOP könnte helfen.
Sophus hat geschrieben:Zwei Fragen: Was meinst du mit "ohne Qt"? Ich importiere eingangs Qt (from PyQt4.QtCore import Qt). Containertypen?
Qt hält einen eigenen Objektbaum vor. Das "rettet" die Objekte im Zusammenspiel mit Python vorm Abräumen durch den GC. Wenn Du nicht weisst, wovon ich rede, dann solltest Du den Pythonweg gehen und das spezielle Verhalten durch den Qt-Objektbaum ignorieren. Wenn Dir das Python-Standardverhalten nicht klar ist - Python-Tutorial. Zum Thema Containertypen: http://lmgtfy.com/?q=container+types+programming&l=1 und für Python: http://lmgtfy.com/?q=container+types+python&l=1
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

jerch hat geschrieben: Ehm weisst Du, wie man zu einem Exemplar kommt? Bzw. was das meint? Unterschied zu Klasse? Ich will Dir nicht zu nahe treten, aber Du scheinst eigentlich nicht verstanden zu haben, was da genau passiert. Die Begriffe sind z.T. verwaschen und ich habe nicht die Zeit und Lust, dass alles mit Dir durchzukauen. Ein gutes Buch zu OOP könnte helfen.
Also, im Falle von:

Code: Alles auswählen

self.SuWindow = self.centralWidget().addSubWindow(self.w)
Ist SubWindow und w ein Exemplar, richtig? Denn über SubWindow komme ich an meinem QWidget heran, und mit w komme ich an die Klasse heran. So verstehe ich das.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Nein, leider völlig falsch. Deine Erklärung im 2. Satz ist Unsinn. `SubWindow` und `w` sind Attributnamen an `self`.

Um zu verstehen, was hier abläuft, geb ich Dir ein paar Fragen mit, welche hilfreich sein könnten:
- Was macht der Zuweisungsoperator in Python?
- Was macht eigentlich `Klassenname()` in Python?
- Unterschied zu `funktionsname()`?
- Was macht der Punktoperator in Python?
- Was sind Attribute?
- Was sind Methoden?
- Was macht die Methode `centralWidget`?
- Was macht die Methode `addSubWindow`?

Mit Rästelraten kommst Du nicht weiter.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

jerch hat geschrieben:Nein, leider völlig falsch. Deine Erklärung im 2. Satz ist Unsinn. `SubWindow` und `w` sind Attributnamen an `self`.

Um zu verstehen, was hier abläuft, geb ich Dir ein paar Fragen mit, welche hilfreich sein könnten:
- Was macht der Zuweisungsoperator in Python?
- Was macht eigentlich `Klassenname()` in Python?
- Unterschied zu `funktionsname()`?
- Was macht der Punktoperator in Python?
- Was sind Attribute?
- Was sind Methoden?
- Was macht die Methode `centralWidget`?
- Was macht die Methode `addSubWindow`?

Mit Rästelraten kommst Du nicht weiter.
- Was macht der Zuweisungsoperator in Python?
Ein Zuweisungsoperator ist ein Gleichheitszeichen. Eine Zuweisung besteht demzufolge aus einem Namen, gefolgt von einem Gleichheitszeichen und einem Wert. Zum Beispiel: x = 1 Hier ist x ein Name und 1 der Wert. Würde ich in einem Interaktionsmodus nun x eingeben, so würde ich den 1 bekommen. Ich stelle mir Variablen wie ein Schuhkarton vor. Der Name der Variable (hier: x) ist sozusagen die Aufschrift des Schuhkartons. Und der Wert 1 sind dann die Schuhe im Karton. Über den Namen der Variablen kann ich also auf meine Schuhe zugreifen.
- Was macht eigentlich `Klassenname()` in Python?
Ein Klassenname schreibt man mit großen Anfangsbuchstaben.
- Unterschied zu `funktionsname()`?
Damit eine Funktion im Programmteil aufgerufen werden kann, braucht es einen Namen, also Funktionsnamen. Im Gegensatz zu einer Name der Klasse beginnen Funktionsnamen mit kleinen Buchstaben.
- Was macht der Punktoperator in Python?
Nun, ich kenne in VB6 die Punktoperatoren als einen Zugriffoperator. Allerdings weiß ich nicht, ob du das meinst. In Python würde ich den Punktoperator sehr deutlich an einem Beispiel der MenuBar zeigen, den dort wird sehr viel mit Punktoperator gearbeitet. Zum Beispiel: self.Attribut.Menuitem.triggered.connect(self.Funktion/Methode).
- Was sind Attribute?
Zum Beispiel kann ein Attribut ein Attribut einer Instanz der Klasse sein und wird bzw. muss dann deshalb mit Hilfe von "self" mit der Instanz verknüpft werden.
- Was sind Methoden?
Man bedient sich an Methoden um eine Funktionalität zu erbringen. In meinem Fall ist die Methodenaufruf self.createToolBar. Mit diesem Aufruf wurde dann die Funktion def createToolBar(self) aufgerufen.
- Was macht die Methode `centralWidget`?
Hierbei ist es wichtig, dass ein QWidget vom QMainWindow erben muss, da QMainWindow diese Methode besitzt. Diese Methode dient dazu, dass man sie mit entsprechendem Inhalt füllen muss, da man auf einem Hauptfenster, welcher von der QMainWindow abgeleitet wurde, nicht einfach so Elemente hinzufügen kann.So Kann man zum Beispiel ein Objekt QWidget auf QMainWindow platzieren und als centralWidget setzen.
- Was macht die Methode `addSubWindow`?
Diese Methode macht es möglich ein QWidget oder auch ein QDialog zum MdiArea-Widget zum Inhalt zu machen. Vorher wird aber ein QWidget zum Inhalt von der Methode ‚addSubWindow()‘. Nachdem dies geschehen ist, wird das QWidget über diese Methode an die Methode ‚centralWidget()‘ gehängt.



Na komm, sag schon, dass ich alles falsch beantwortet habe oder irgendwas durcheinander gebracht habe. Mittlerweile bin ich es ja gewohnt :-)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Sophus:
Nein, das war z.T. richtig. Aber einige der Fragen hast du wohl falsch verstanden bzw. hatte ich unklar formuliert:
Sophus hat geschrieben:- Was macht der Zuweisungsoperator in Python?
Ein Zuweisungsoperator ist ein Gleichheitszeichen. Eine Zuweisung besteht demzufolge aus einem Namen, gefolgt von einem Gleichheitszeichen und einem Wert. Zum Beispiel: x = 1 Hier ist x ein Name und 1 der Wert. Würde ich in einem Interaktionsmodus nun x eingeben, so würde ich den 1 bekommen. Ich stelle mir Variablen wie ein Schuhkarton vor. Der Name der Variable (hier: x) ist sozusagen die Aufschrift des Schuhkartons. Und der Wert 1 sind dann die Schuhe im Karton. Über den Namen der Variablen kann ich also auf meine Schuhe zugreifen.
Ja das Bild passt ganz gut, wobei die Schuhe nicht im Karton liegen, sondern eher ein Zettel, wo drauf steht, wo man sie findet. Das ist insofern wichtig, dass die Schuhe nicht "im Karton" sondern gesondert existieren, da mit Entfernen des Kartons nicht automatisch die Schuhe weg sind, sondern nur der Zettel. Der Zettel ist nur ein Verweis oder eine Referenz auf die Schuhe, während die Schuhe das Objekt sind. Python zählt die Referenzen auf Objekte mit, wenn es keine mehr gibt, wird das Objekt zur Vernichtung freigegeben und irgendwann geschreddert. Deshalb spricht man in Python auch von Bindung eines Objektes an einen Namen und seltener von Variablenzuweisung (das Objekt wird eben nicht der Variablen zugewiesen, technisch korrekt ist es nur eine Referenz). Das das Objekt irgendwo "lebt" (wo muss uns nicht interessieren, da kümmert sich Python drum), erkennt man indirekt an Löschversuchen:

Code: Alles auswählen

class Foo: pass
foo = Foo()

# unsaubere Kurzform: gib Objekt foo aus
# was eigentlich passiert: hole Objekt, welches an foo gebunden ist und gib es aus
# achte auf die Hexnummer (ist die Objekt-ID)
print foo

# unsaubere Kurzform: erzeuge Liste mit Objekt foo und weise Liste l zu
# was eigentlich passiert: hole Objekt, welches an foo gebunden ist, erzeuge eine neue Referenz,
# übergib Referenz an Listenkonstruktor (erzeugt Listenobjekt mit der Referenz als erstes Element),
# binde erstelltes Listenobjekt an l
l = [foo]

# Lösche Objekt foo? --> NEIN!
# hier versagt die Kurzform, gelöscht wird nur der Name mit der Referenz
del foo

# gib die Liste aus (auch wieder unsauber, aber wer will schon schreiben:
# hole Objekt, welches an l gebunden ist, von dem wir wissen, dass es eine Liste ist,
# und gib dieses aus)
# ==> Objekt "lebt" noch, vgl. Objekt-ID
print l
Sophus hat geschrieben:- Was macht eigentlich `Klassenname()` in Python?
Ein Klassenname schreibt man mit großen Anfangsbuchstaben.
Die Frage war nicht, wie man Klassennamen schreibt, sondert zielte auf die Klammern ab - also was passiert, wenn man eine Klasse "aufruft".
Sophus hat geschrieben:- Unterschied zu `funktionsname()`?
Damit eine Funktion im Programmteil aufgerufen werden kann, braucht es einen Namen, also Funktionsnamen. Im Gegensatz zu einer Name der Klasse beginnen Funktionsnamen mit kleinen Buchstaben.
Was ist der Unterschied eines "Klassenaufrufs" zu einem Funktionsaufruf?
Sophus hat geschrieben:- Was macht der Punktoperator in Python?
Nun, ich kenne in VB6 die Punktoperatoren als einen Zugriffoperator. Allerdings weiß ich nicht, ob du das meinst. In Python würde ich den Punktoperator sehr deutlich an einem Beispiel der MenuBar zeigen, den dort wird sehr viel mit Punktoperator gearbeitet. Zum Beispiel: self.Attribut.Menuitem.triggered.connect(self.Funktion/Methode).
Ja und was macht der nune in Python?
Sophus hat geschrieben:- Was sind Attribute?
Zum Beispiel kann ein Attribut ein Attribut einer Instanz der Klasse sein und wird bzw. muss dann deshalb mit Hilfe von "self" mit der Instanz verknüpft werden.
Du kannst doch nicht Attribut mit Attribut erklären ;)
Sophus hat geschrieben:- Was sind Methoden?
Man bedient sich an Methoden um eine Funktionalität zu erbringen. In meinem Fall ist die Methodenaufruf self.createToolBar. Mit diesem Aufruf wurde dann die Funktion def createToolBar(self) aufgerufen.
Und was ist der Unterschied zu Funktionen? Wie stehts um Methoden und Attribute?
Sophus hat geschrieben:- Was macht die Methode `centralWidget`?
Hierbei ist es wichtig, dass ein QWidget vom QMainWindow erben muss, da QMainWindow diese Methode besitzt. Diese Methode dient dazu, dass man sie mit entsprechendem Inhalt füllen muss, da man auf einem Hauptfenster, welcher von der QMainWindow abgeleitet wurde, nicht einfach so Elemente hinzufügen kann.So Kann man zum Beispiel ein Objekt QWidget auf QMainWindow platzieren und als centralWidget setzen.
Ok, die Frage nochmal expliziter: Was macht die Methode `QWidget * QMainWindow::centralWidget () const`? (Antwort siehe Qt-Doku)
Sophus hat geschrieben:- Was macht die Methode `addSubWindow`?
Diese Methode macht es möglich ein QWidget oder auch ein QDialog zum MdiArea-Widget zum Inhalt zu machen. Vorher wird aber ein QWidget zum Inhalt von der Methode ‚addSubWindow()‘. Nachdem dies geschehen ist, wird das QWidget über diese Methode an die Methode ‚centralWidget()‘ gehängt.
Was macht die Methode `QMdiSubWindow * QMdiArea::addSubWindow ( QWidget * widget, Qt::WindowFlags windowFlags = 0 )`? (Antwort siehe Qt-Doku)

Wenn Du die Fragen beantwortest, kannst Du obige Programmzeile verstehen und weiss auch, wo sich die Exemplare "verstecken". Evtl. unter Klärung der Frage "Was ist in Python alles Objekt?" und den Unterschied von Klasse zu Exemplar ("Instanz").

Edit: Kommentare waren unvollständig. Desweiteren in Deiner Aussage
Sophus hat geschrieben:Diese Methode macht es möglich ein QWidget oder auch ein QDialog zum MdiArea-Widget zum Inhalt zu machen.
macht das "oder auch" keinen Sinn. Die Klasse QMdiArea erlaubt QWidget-Typen. Falls Dir nicht klar ist, warum ein QDialog-Typ auch funktioniert --> OOP, Stichwort Vererbung schafft Klarheit.
Antworten