Neues Fenster verschwindet

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

Hoi,

Hier erst mal der code :D
(PySide, alles neueste Versionen mit Python 2.7.4)

Code: Alles auswählen

import sys
from PySide import QtGui, QtCore


class FirstClass(QtGui.QMainWindow):

    def __init__(self):
        super(FirstClass, self).__init__()
        self.startingUI()

    def startingUI(self):

        self.setWindowTitle('Hauptfenster')
        self.resize(800, 400)
        self.statusBar()

        #Menueinstellungen an sich

        menue = self.menuBar()

        #Actions des Menues:
        #datei menue
        menuleiste_datei = menue.addMenu('File')

        datei_exit = QtGui.QAction('Exit', self)
        datei_exit.setStatusTip('Close the programm')
        menuleiste_datei.addAction(datei_exit)
        datei_exit.triggered.connect(self.close)

        #Einstellungen menue
        menuleiste_configurations = menue.addMenu('Configurations')
        configurations_settings = QtGui.QAction('Settings', self)
        configurations_settings.setStatusTip('Configurations(Settings)')
        menuleiste_configurations.addAction(configurations_settings)
        configurations_settings.triggered.connect(self.newwindow)

        self.show()


    def newwindow(self):
        wid = QtGui.QWidget()
        wid.resize(250, 150)
        wid.setWindowTitle('NewWindow')
        wid.show()


def main():

    prog = QtGui.QApplication(sys.argv)
    start = FirstClass()
    sys.exit(prog.exec_())

if __name__== '__main__':
    main()

Wenn ich bei "Configurations" auf "Settings" klicke sollte sich ein neues Fenster dauerhaft öffnen. Allerdings öffnet es sich nur für eine Nanosekunde oder so, und verschwindet dann wieder ins nichts :K
Wenn ich sowas wie time.sleep(5) reinschreibe dann bleibt das Fenster für 5 Sekunden offen, bringt aber nix. Hat es was mit multithreading zu tun, oder was muss ich machen damit das Fenster bleibt?? Oder wo ist der Fehler??

Gruß
BlackJack

@JonnyDamnnox: Wenn die Funktion beendet wird, dann gibt es den Namen `wid` nicht mehr, damit gibt es keine Möglichkeit mehr das Widget-Objekt irgendwie zu erreichen, und deshalb zerstört Python das Widget und gibt den Speicher dafür frei.

Eine Sache die Du anscheinend Grundsätzlich falsch machst ist das Du keinen Objektbaum aufbaust. Wenn ein Qt-Objekt beim erstellen ein Elternobjekt entgegennimmt, dann sollte man das auch tun! Denn sonst weiss Qt und dessen Speicherverwaltung nicht welche Objekte zusammengehören und voneinander abhängen.
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

BlackJack hat geschrieben:@JonnyDamnnox: Wenn die Funktion beendet wird, dann gibt es den Namen `wid` nicht mehr, damit gibt es keine Möglichkeit mehr das Widget-Objekt irgendwie zu erreichen, und deshalb zerstört Python das Widget und gibt den Speicher dafür frei.

Eine Sache die Du anscheinend Grundsätzlich falsch machst ist das Du keinen Objektbaum aufbaust. Wenn ein Qt-Objekt beim erstellen ein Elternobjekt entgegennimmt, dann sollte man das auch tun! Denn sonst weiss Qt und dessen Speicherverwaltung nicht welche Objekte zusammengehören und voneinander abhängen.

Hallo BlackJack,
Das heißt meine Klasse sollte auch von QtGui.QWidget erben? Würde das den Fehler beheben?
edit/ nein geht nicht :D oder meinst du ich soll die Fenster/Widgets ect. alle in die selbe Methode stecken?
Gruß
BlackJack

@JonnyDamnnox: Ich meine Du sollst Dir die Parameter anschauen und wenn da einer `parent` heisst, bitte auch etwas übergeben was in der *Objekthierarchie* — *nicht* in der *Klassenhierarchie* — ein Elternobjekt sein soll.
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

Also ich hab jetzt noch nicht genau verstanden was du meinst. Allerdings ist mir beim Analysieren vom code mit den tipps von dir aufgefallen das man sich ja auf die Methoden beziehen muss und deswegen ein self davor schreiben muss. Wie ich es ja oben auch gemacht hab :roll:
Ich glaub das hab ich einfach gedankenlos hingeschmiert -.-

Code: Alles auswählen

.......
def newwindow(self):
        self.wid = QtGui.QWidget()
        self.wid.resize(250, 150)
        self.setWindowTitle('NewWindow')
        self.wid.show()
.......
So funktioniert es :)
Aber ich denk aber noch über das von dir gesagt nach, ich glaub da könnte ich was wichtiges lernen :)

Gruß
BlackJack

@JonnyDamnnox: Man muss da nicht überall ein magisches `self` davor schreiben. Im Gegenteil würde ich *das* jetzt als gedankenlos bezeichnen. Es löst das Problem nur bedingt, denn es wird zwar eine Referenz auf das Widget behalten, aber eben nur auf eines. Wenn die Methode ein weiteres Mal aufgerufen wird, bekommt man wieder genau das selbe Problem. Und die `setWindowTitle()`-Methode rufst Du jetzt auf dem falschen Objekt auf. Und man sollte ausserhalb der `__init__()`-Methode keine neuen Attribute auf dem Objekt erstellen. Nach dem diese Methode abgelaufen ist, sollte das Objekt in einem vollständigen, konsistenten Zustand sein.

Schau Dir bitte mal die Parameter von `QWidget` an. Da kann und *muss* man ein `parent` übergeben, wenn man das mit der Speicherverwaltung ordentlich machen will.
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

Ok gut, ich werde mir das später noch mal näher anschauen, aber erst mal noch eine Frage.
Wie kann ich denn "Sachen" dem main-window hinzufügen? Ich will ja so etwas wie einen rudimentären webbrowser basteln und ich hätte gerne ein line-edit direkt unter dem "menu-bar" Ich kann zwar lineedit aufrufen, aber das erscheint dann seperat, mit eigenem Titel usw. . Ich hatte etwas versucht wie das hier:

Code: Alles auswählen

...
        self.lineedit = QtGui.QLineEdit()
        self.layout = QtGui.QHBoxLayout()
        self.layout.addWidget(self.lineedit)
        self.setLayout(self.layout)
        self.show()
...
Aber da passiert überhaupt nix. In den meisten Tutorials steht auch immer nur wie man Fenster mit buttons herstellt, nicht wie man innerhalb eines Fenster etwas macht. Könntest du mir da noch kurz helfen? :)

Gruß
AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Hi,

hast du auch mal die Doc zum QMainWindow gelesen, die Aufgabe des MainWindow, bzw. dessen Vorteil besteht darin eben Menüs, Statusleisten, Toolbars usw. anzuzeigen. Den eigentlichen Inhalt macht dann das Central-Widget, welches ein Widget deiner Wahl sein kann. Du kannst das also so machen:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import sys
from PySide import QtCore, QtGui, QtWebKit

class MainWindow(QtGui.QMainWindow):

    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.resize(300, 150)
        self.setWindowTitle("Beispiel")

        mainWidget = QtGui.QWidget()
        self.setCentralWidget(mainWidget)

        line = QtGui.QLineEdit()
        edit = QtGui.QTextEdit()
        button = QtGui.QPushButton("Click")

        layout = QtGui.QVBoxLayout()
        layout.addWidget(line)
        layout.addWidget(edit)
        layout.addWidget(button)

        mainWidget.setLayout(layout)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    view = MainWindow()
    view.show()
    sys.exit(app.exec_())
Edit:
Hier sieht man eben wie die "eigentlichen" Inhalte seperat in einem einfachen QWidget, mit einem Layout, geordnet werden. Menüs und Toolbars würden dann aber wieder zum QMainWindow gehören. Wenn man mag kann man das natürlich auch in eine Methode packen. Also z.B. eine create_content_widget() oder sowas...

Übrigens kommt es bei den anderen Fenstern darauf an was du mit denen machen willst, vielmals sind es ja Dialoge, dann empfiehlt sich ein QDialog und die Verwendung von exec_(), wobei die Fenster dann modal sind, zumindest nach Standard, je nach Plattform kann das aber anders ausfallen. Alle anderen Fenster die du ja dann eh permanent brauchst (wenn es keine Dialoge sind) kann man dann direkt in der QApplication Loop referenzieren und mit show() zeigen lassen.

AlphaX2
BlackJack

@AlphaX2: Du machst den gleichen Fehler hast auch *nirgends* ein Elternobjekt angegeben. Das ist 'ne ziemlich zentrale Sache für die Speicherverwaltung unter Qt.
AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Okay Interessant, ich hab das bisher immer so gehandhabt. :roll: Wie würde es denn richtig aussehen/gemacht? Würde mich hier über Aufklärung freuen. :)

Danke!
BlackJack

@AlphaX2: Das habe ich doch schon geschrieben: Wenn ein Objekt beim erstellen ein Argument `parent` entgegennimmt, dann sollte man das auch tun und das „übergeordnete” QObject dort übergeben welches dieses Objekt benötigt.

Das ist ziemlich grundlegend und wird deshalb auch in der Qt-Dokumentation bei QObject und dem in dieser Beschreibung verlinkten Object Trees & Ownership erklärt.
AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Okay, würde ja dann heißen, dass in meinem Beispiel der Code so geändert werden müsste:

Code: Alles auswählen

        mainWidget = QtGui.QWidget(parent=self)
Wenn das eigentlich wichtig ist, warum is dass dann in den PySide examples immer ausgelassen - an denen hab ich mich eben oft orientiert. Dort wird im __init__ noch angegeben das parent per default None ist, bei den Aufrufen aber auch nichts übergeben? :K Im Fall von Dialogen hab ich es aber meist schon so gemacht, weil die sonst offen bleiben, auch wenn das Hauptfenster geschloßen wird.
BlackJack

@AlphaX2: Den Argumentnamen muss man nicht unbedingt angeben, weil es ja das erste Argument ist. Es wird anscheinend gerne weg gelassen weil Python ja auch eine Speicherverwaltung hat, und man auf Python-Seite dafür sorgen kann, dass ein Qt-Objekt nicht zerstört wird, in dem man eine Referenz darauf behält. Wenn man das *nicht* macht, dann muss man Qt allerdings mittels der Elternbeziehung zwischen Qt-Objekten mitteilen, dass man ein Objekt noch benötigt auch wenn Python es sonst zerstören würde.
AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Okay gut, glaube dann werde ich das demnächst mit einbauen.

Sehr schön, wieder was gelernt! :)
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

Oh man, wie gern würd ich das alles so gut verstehen wie BlackJack :cry:

@AlphaX2: Ja stimmt, die Idee mit dem QWidget gefällt mir auch besser, ich werde das später noch umfummeln im code dann. Aber erst mal noch eine Frage, und zwar wie bekomme ich den den input vom user?? In den tutorials wird das mit einem dialog gemacht. Ich will das aber mit dem lineedit machen. Also das ich oben in die Leiste eine URL eingebe, dann return drücke und dann die URL im Fenster unten drunter geladen wird(so webbrowser mäßig). Funktioniert das überhaupt mit self.lineEdit.text() ? Also so funktioniert der code schon mal nicht. Ich will allerdings auch kein dialog Fenster benutzen, weil die komplett anders aussehen als ich mir das vorstelle :D und so baukastenmäßig ist auch doof. Wenn ichs hinbekomme will ich später noch die Leisten ect. selbst designen.
Es erscheint ein runtime error, Failed to connect signal returnPressed()

Code: Alles auswählen

import sys
from PySide import QtGui, QtCore, QtWebKit


class FirstClass(QtGui.QMainWindow, QtGui.QWidget):

    def __init__(self):
        super(FirstClass, self).__init__()
        self.startingUI()

    def startingUI(self):

        self.setWindowTitle('Hauptfenster')
        self.resize(800, 600)
        self.statusBar()

        #Menueinstellungen an sich

        menue = self.menuBar()
        #Actions des Menues:
        #datei menue
        menuleiste_datei = menue.addMenu('File')

        datei_exit = QtGui.QAction('Exit', self)
        datei_exit.setStatusTip('Close the programm')
        menuleiste_datei.addAction(datei_exit)
        datei_exit.triggered.connect(self.close)

        #Einstellungen menue
        menuleiste_configurations = menue.addMenu('Configurations')
        configurations_settings = QtGui.QAction('Settings', self)
        configurations_settings.setStatusTip('Configurations(Settings)')
        menuleiste_configurations.addAction(configurations_settings)
        configurations_settings.triggered.connect(self.newwindow)

        self.webview = QtWebKit.QWebView(self)
        self.webview.move(1, 50)
        self.webview.resize(800, 600)


        self.lineEdit = QtGui.QLineEdit(self)
        userinput = self.lineEdit.text()
        self.lineEdit.move(1, 20)
        self.lineEdit.resize(600, 20)
        self.lineEdit.returnPressed.connect(self.webview.load(QtCore.QUrl(userinput))

        self.lineEdit.show()

        self.webview.show()

        self.show()

    def newwindow(self):
        self.wid = QtGui.QWidget()
        self.wid.resize(250, 150)
        self.setWindowTitle('NewWindow')
        self.wid.show()

def main():

    app = QtGui.QApplication(sys.argv)
    start = FirstClass()
    sys.exit(app.exec_())

if __name__== '__main__':
    main()

AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Hallo,

na die Frage wird doch durch mein Beispiel erklärt!

Du nutzt mit deiner FirstClass ja ein QMainWindow, dass hat aber quasi "nur" die Aufgabe Leisten, Menüs usw. darzustellen. Aber das MainWindow braucht ein CentralWidget in dem dann die eigentlichen Inhalte eingebaut werden. Darum hab ich ja auch das setCentralWidget(). Im Beispiel is das einfach ein QWidget und in dem QWidget sind dann wieder alle anderen Elemente drinnen, also eine LineEdit, usw. Außerdem empfiehlt es sich eher Layouts zu verwenden, wie das ja in meinem Beispiel auch passiert, statt alles statisch mit move zu setzen. :)
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

Hey AlphaX2, ja schon aber meine Frage war ja wie ich den text vom user mit dem lineEdit rein bekomme, am besten in eine variable so das ich diese das bei QUrl eingeben kann. Also das man die url oben eingibt und dann die Seite geladen wird. Ich hab gelesen das der string auch kein herkömmlicher string ist, sondern ein "QString", ich hab schon 1-2 Sachen dazuh gefunden, leider hat mir nix weiter geholfen :K

Gruß
AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Du musst mal überlegen was du bisher eigentlich machst, denn im moment nimmst du im Konstruktor schon den Inhalt des LineEdit auf. Heißt also die Variable wird nie mehr aktualisiert. :) Aber eigentlich sollte ja erst mit dem drücken von Return die Adresse aufgenommen werden. Also so in der Richtung:

Code: Alles auswählen

        # Noch im Konstruktor
        self.lineEdit.returnPressed.connect(self.load_page)

...

    def load_page(self):
        adress = self.lineEdit.text()
        self.webview.load(QtCore.QUrl(adress))
AlphaX2
JonnyDamnnox
User
Beiträge: 68
Registriert: Sonntag 10. März 2013, 21:14

Hm ok danke, so ähnlich hatte ich das auch vor, im konstruktor klappts natürlich. Aber würdest du Vorschlagen das ich startingUi + lineEditect komplett in __init__ verlege? Also startingUi weg und nur noch __init__ (vorerst) ?

Gruß
AlphaX2
User
Beiträge: 53
Registriert: Dienstag 28. Juni 2011, 10:42

Naja kann man machen, muss man nicht. Gehen sollte beides, an und für sich werden gerne die Menüs und die Actions in eine Methode verlagert. Die absoluten Hauptelemente könnte man auch direkt im __init__ lassen.

AlphaX2
Antworten