Qsplitter in qpsplitter

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Hallo, ich hab da mal ne wahrscheinlich banale Frage:

ich möchte dynamisch Splitter in einander schachteln, also Splitter in Splitter USW.
Jedoch sollen die Splitter wieder dynamisch "unsplitted" werden, also durch den user z.b.

Statisch ist mir das klar (addwidet qsplitter).

Es gibt jedoch kein delwidget oder was ähnliches (jedenfalls habe ich nix gefunden)

Wie kann man das denn in pyqt lösen ?

Danke im Vorraussetzung für jeden Tip.
ichisich
User
Beiträge: 134
Registriert: Freitag 1. Januar 2010, 11:52

So ganz versteh ich nicht was Du am End willst aber evt. hilft es ja das Widget einfach zu verstecken ?

Code: Alles auswählen

widget.hide()
#oder die equivalente Funktion (Slot):
setVisible(false)
#weiter wären da noch 
widget.close()
# was da geht lies bitte in der Doku
Wenn ein Widget irgendwo aus einem anderen Grund zerstört wird sollte sich Qt um das entfernen aus dem Splitter kümmern.

Vielleicht hilfts !?

Falls nicht erklär mal wie der User die Widgets und Splitter dynamisch verwalten soll

Gruß
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Danke für den Tipp,

hide funktioniert (falls das widget noch benötigt wird) und
destroy ebenfalls (wenn nicht).

Ich hab mir das viel zu kompliziert gemacht, einfach den betroffene splitter
Destroyen, pyqt löscht alle Child Widgets und dann ggf. einen neuen Splitter
Mit addwidget hinzufügen.

Zu deiner Frage, wozu das Ganze:

Im linken Frame sollen unterschiedliche Objekte aufgelistet werden und
je nach Objekt-Typ werden im rechten Frame andere Inhalte ggf. In mehreren
Frames (Splitter) angezeigt.

Der Benutzer kann also links unterschiedliche Objekte anklicken und
erhält rechts je nach Objekt-Typ eine andere Bildschirmaufteilung.
Somit muss die Aufteilung für das zuvor angeklickte Objekt vorher gelöscht werden.


Nochmals danke für deine Antwort.
lunar

"QWidget.destroy()" ist nicht dafür gedacht, von außen aufgerufen zu werden. Diese Methode löscht das Steuerelemente und seine Kinder sofort, was schief gehen kann, wenn das Steuerelement noch benutzt wird (e.g. durch das Fenstersystem). Verwende "QWidget.deleteLater()", um Steuerelemente permanent zu löschen.
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Lunar, danke für deine Anmerkung.
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Also ich dachte ja ich wäre mit dem Problem durch, aber das klappt alles trotzdem nicht.

Ich habe hier mal einen Beispielcode zusammengestellt:

Code: Alles auswählen

import sip
import sys
from   PyQt4.QtCore import *
from   PyQt4.QtGui  import *

class Standard_Window(QMainWindow):
    
    def __init__ (self, parent):
        super(Standard_Window, self).__init__(parent)
        
        self.setMinimumSize(400,300) 
        self.resize(800,600)
        self.setWindowTitle("Windowtitel")
        self.statusBar().showMessage("Statuszeile")
        self.setAutoFillBackground(True)
        return
    
    def Standard_Frame(self,frame):
        frame.setFrameStyle(QFrame.StyledPanel)
        frame.setBackgroundRole(QPalette.Light)
        frame.setAutoFillBackground(True)        
        return frame
            
if __name__ == '__main__':
        app = QApplication(sys.argv)

        window   = Standard_Window(None)
        splitter = QSplitter(Qt.Horizontal)

        frame1   = QFrame()
        frame1   = window.Standard_Frame(frame1)
        
        frame2   = QFrame()
        frame2   = window.Standard_Frame(frame2)
        
        splitter.addWidget(frame1)
        splitter.addWidget(frame2)
        splitter.setStretchFactor(0, 1)
        splitter.setStretchFactor(1, 1)

        window.setCentralWidget(splitter)

        test = False

        if test == True:

            splitter2 = QSplitter(Qt.Vertical)

            frame21   = QFrame()
            frame21   = window.Standard_Frame(frame21)
        
            frame22   = QFrame()
            frame22   = window.Standard_Frame(frame22)
        
            splitter2.addWidget(frame21)
            splitter2.addWidget(frame22)
            splitter2.setStretchFactor(0, 1)
            splitter2.setStretchFactor(1, 1)

            # "alten" Frame aus Splitter löschen
            frame2.hide()
            frame2.deleteLater()
            print("Anzahl nach delete/destroy: ",splitter.count())
            sip.delete(frame2)
            print("Anzahl nach sip.delete: ",splitter.count())
            
            # 2.Splitter in 1.Splitter einsetzen
            splitter.addWidget(splitter2)
            print("Anzahl nach add(Splitter2): ",splitter.count())
            
            splitter.setStretchFactor(0, 1)
            splitter.setStretchFactor(1, 1)

        window.show()
        app.exec_()
Wenn wie im Code test = False ist, dann erscheint ein Window mit einem Splitter in der Mitte und links und recht je ein Frame
und jeweils eine halbe Windowseite breit. So weit so gut.

Wenn ihr test = True setzt, wird der rechte Frame gelöscht und ein 2-ter Splitter mit zwei Frames in den ersten Splitter eingesetzt.

Jedoch nimmt nun der erste Frame aus dem ersten Splitter fast das gesamte Window ein.
Er soll jedoch nur die Hälfte einnehmen und die rechte Hälfte durch den 2. Splitter aufteilen lassen.

Ich dachte erst es liegt am löschen des 2. Frames (jedoch scheint es daran nicht zu liegen)
ich habe es auf alle erdenklichen Arten (deleteLater bis zu sip.delete) versucht jedoch bleibt das Resultat das Gleiche.

Auch das setzen den Stretchfactors nach einsetzen des 2.Splitter wirkt nicht.

Was kann ich noch machen, ich bin mit meinem Latein am Ende ...

Danke für jede Anregung.
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

ich glaube du suchst einen Fehler, den es gar nicht gibt. Solange du keinen Inhalt in den Frames hast, spielt es doch keine Rolle wie das Layout aussieht. Wenn du zum Beispiel statt ein Frame ein QLabel einsetzt wird der rechte Teil grösser.

Code: Alles auswählen

splitter2.addWidget(QLabel(" ".join([str(i) for i in range(40)])))
splitter2.addWidget(frame22)
Die ganzen stretch Funktionen kannst du auch weglassen.

Deinen Konstruktor (oder wie der in Python auch immer heisst) von Standard_Window schreibt man üblicherweise so:

Code: Alles auswählen

def __init__ (self, parent = None):
        super(Standard_Window, self).__init__(parent)
also das None als default Wert. Dann kannst du auf das None beim erstellen des Standard_Window verzichten.
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Hallo DaMutz, danke für deine Antwort.

Ich möchte das Label nicht direkt in den Splitter setzen, sondern z.B. in den Frame22.
Habe ich gemacht, sieht auch so aus wie ich mit das vorstelle.

Aber jetzt muss ich den Splitter von Hand in die Mitte setzen um das Label sehen zu können.
Genau das soll automatisch geschehen und nicht der Benutzer machen müssen.
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

die Standard_Frame Methode sollte man wohl eher nicht in diese Klasse integrieren. Die Funktion hat mit der Klasse gar nichts zu tun. Ich habe die QFrame Klasse erweitert, ist meiner Meinung nach um einiges besser.
Ich weiss nicht ob meine Lösung, so ist wie du es erwartest. Vielleicht weiss jemand anderes eine elegantere Lösung:

Code: Alles auswählen

#~ coding: utf-8

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class StandardWindow(QMainWindow):
    def __init__ (self, parent = None):
        super(StandardWindow, self).__init__(parent)
        
        self.setMinimumSize(400,300) 
        self.resize(800,600)
        self.setWindowTitle("Windowtitel")
        self.statusBar().showMessage("Statuszeile")
        self.setAutoFillBackground(True)
    
class StandardFrame(QFrame):
    def __init__(self, parent = None):
        super(StandardFrame, self).__init__(parent)
        self.setFrameStyle(QFrame.StyledPanel)
        self.setBackgroundRole(QPalette.Light)
        self.setAutoFillBackground(True)

def main():
    app = QApplication(sys.argv)

    window = StandardWindow()
    splitter = QSplitter(Qt.Horizontal)
    splitter.setChildrenCollapsible(False)

    frame1 = StandardFrame()
    frame2 = StandardFrame()
    
    splitter.addWidget(frame1)
    splitter.addWidget(frame2)

    window.setCentralWidget(splitter)

    test = True

    if test == True:
        layout = QVBoxLayout()
        for label_text in ['hallo', 'python-forum']:
            label = QLabel(label_text)
            layout.addWidget(label)
            
        splitter2 = QSplitter(Qt.Vertical)

        frame21 = StandardFrame()
        frame22 = StandardFrame()
        frame22.setLayout(layout)
    
        splitter2.addWidget(frame21)
        splitter2.addWidget(frame22)

        # "alten" Frame aus Splitter löschen
        frame2.deleteLater()
        
        # 2.Splitter in 1.Splitter einsetzen
        splitter.addWidget(splitter2)
    window.show()
    app.exec_()
        
if __name__ == '__main__':
    main()
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Hallo DaMutz,

klar die Standard_Frame Methode als Erweiterung der QFrame Klasse ist natürlich genau richtig so.

Die Verwendung von QVBoxLayout im frame22 bringt den richtigen Effekt, den Frameinhalt anzuzeigen.

Wenn ich jetzt noch frame1 auf 1/4 der Windowbreite fixieren kann (unabhängig von der rechten Hälfte)
dann bin ich super zu frieden.
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

Code: Alles auswählen

splitter.moveSplitter(50, 1)
Benutzeravatar
py_fan_berlin
User
Beiträge: 13
Registriert: Dienstag 7. September 2010, 21:00

Klappt super, if test = True (nach addWidget(splitter2).

Geht aber nicht, if test = False ???
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

weil du dort in den Frames noch keinen Inhalt hast. Ich würde mich jetzt nicht mehr länger daran aufhalten und erstmals den Inhalt programmieren.
Antworten