Qt: Unterschiedliches Verhalten beim Schließen von Fenstern



  • Hallo,

    ich bin derzeit dabei, mich ein wenig mit Qt auseinanderzusetzen und versuche gerade, über ein Fenster beliebig viele Fenster des gleichen Typs zu öffnen und diese auch wieder zu schließen zu können.

    Dies ist einer meiner bisherigen Ansätze dazu:

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
    	QApplication a(argc, argv);
    	MainWindow w;
    	w.show();
    
    	return a.exec();
    }
    

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QGridLayout>
    #include <QPushButton>
    #include <QMenuBar>
    #include <QMenu>
    #include <QCloseEvent>
    #include <QVector>
    
    class MainWindow : public QMainWindow
    {
    	Q_OBJECT
    
    	static QVector<MainWindow*> windows;
    	int index;
    
    	QWidget* mainwidget;
    	QGridLayout* grid;
    
    	QMenuBar* menubar;
    
    	void createMenuBar();
    	void createLayouts();
    
    	void closeEvent(QCloseEvent *event);
    
    public:
    	explicit MainWindow(QWidget *parent = 0);
    	~MainWindow();
    
    private slots:
    	void createNewWindow();
    	void deleteThisWindow();
    	void deleteLastOpenedWindow();
    	void deleteAllWindows();
    	void message();
    };
    
    #endif // MAINWINDOW_H
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include <QApplication>
    #include <QMessageBox>
    
    QVector<MainWindow*> MainWindow::windows;
    
    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
    {
    	qDebug("ctor");
    
    	mainwidget = new QWidget;
    	index = -1;
    
    	createMenuBar();
    	createLayouts();
    
    	mainwidget->setLayout(grid);
    
    	this->setCentralWidget(mainwidget);
    }
    
    MainWindow::~MainWindow()
    {
    	qDebug("dtor");
    }
    
    void MainWindow::closeEvent(QCloseEvent* event)
    {
    	QMessageBox::StandardButton x = QMessageBox::question( this, "Close?", tr("Are you sure?\n"), QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes, QMessageBox::Yes);
    	if (x != QMessageBox::Yes)
    	{
    		event->ignore();
    	}
    	else
    	{
    		event->accept();
    		deleteThisWindow();
    	}
    
    }
    
    void MainWindow::createLayouts()
    {
    	grid = new QGridLayout;
    
    	QIcon icon("Icon.png");
    	QString pushtext = "PUSH";
    
    	QPushButton* push = new QPushButton;
    	push->setText(pushtext);
    	push->setIcon(icon);
    	QObject::connect(push, SIGNAL(clicked(bool)), this, SLOT(createNewWindow()));
    
    	QPushButton* close = new QPushButton;
    	close->setText("close");
    	QObject::connect(close, SIGNAL(clicked(bool)), this, SLOT(deleteThisWindow()));
    
    	grid->addWidget(push, 1, 0);
    	grid->addWidget(close, 2, 0);
    }
    
    void MainWindow::createMenuBar()
    {
        menubar = new QMenuBar;
    
        QMenu* menu = new QMenu;
        menu = menuBar()->addMenu("&Menu");
    
        QMenu* more = new QMenu;//(tr("&More"));
        more = menu->addMenu("More");
    
        QAction* act0 = new QAction;
        act0 = menu->addAction("M&aximize");
        QObject::connect(act0, SIGNAL(triggered()), this, SLOT(showMaximized()));
        QAction* act1 = new QAction;
        act1 = menu->addAction("M&inimize");
        QObject::connect(act1, SIGNAL(triggered()), this, SLOT(showMinimized()));
        QAction* act2 = new QAction;
        act2 = menu->addAction("&Close");
        QObject::connect(act2, SIGNAL(triggered()), this, SLOT(deleteThisWindow()));
        QAction* act3 = new QAction;
        act3 = menu->addAction("E&xit");
        QObject::connect(act3, SIGNAL(triggered()), this, SLOT(deleteAllWindows()));
        QObject::connect(act3, SIGNAL(triggered()), qApp, SLOT(quit()));
    
        QAction* act01 = new QAction;
        act01 = more->addAction("New &Window");
        QObject::connect(act01, SIGNAL(triggered(bool)), this, SLOT(createNewWindow()));
        QAction* act02 = new QAction;
        act02 = more->addAction("Clo&se Window");
        QObject::connect(act02, SIGNAL(triggered(bool)), this, SLOT(deleteThisWindow()));
        QAction* act03 = new QAction;
        act03 = more->addAction("&Close All Windows");
        QObject::connect(act03, SIGNAL(triggered(bool)), this, SLOT(deleteAllWindows()));
        QAction* act04 = new QAction;
        act04 = more->addAction("Close &Last Opened Window");
        QObject::connect(act04, SIGNAL(triggered(bool)), this, SLOT(deleteLastOpenedWindow()));
    
    }
    
    void MainWindow::createNewWindow()
    {
        windows.push_back(new MainWindow);
        windows.back()->index = windows.size() - 1;
        windows.back()->show();
        qDebug("new");
    }
    
    void MainWindow::deleteThisWindow()
    {
    	if(index >= 0 && index < windows.size())
    	{
    	    QString qs = "delete this: ";
    	    qs += QString::number(index);
    	    qs += " | ";
    	    qs += QString::number(windows.size() - 1);
    	    qDebug(qs.toStdString().c_str());
    
    	    int cur = index;
    	    delete MainWindow::windows[cur];
    	    qDebug("deleted");
    	    MainWindow::windows.remove(cur);
    	    qDebug("remove");
    	}
    	else
    	{
    		this->close();
    		qDebug("close");
    	}
    }
    
    void MainWindow::deleteAllWindows()
    {
    	for(auto a : MainWindow::windows)
    	{
    		qDebug("auto delete");
    		delete a;
    	}
    	MainWindow::windows.clear();
    	qDebug("clear complete");
    }
    
    void MainWindow::deleteLastOpenedWindow()
    {
    	if(windows.size() > 0)
    	{
    		delete windows.back();
    		windows.pop_back();
    		qDebug("delete last");
    	}
    	else
    		QMessageBox::information(this, "Info", "No Window Left!");
    }
    
    void MainWindow::message()
    {
    	QMessageBox::information(this, "Message", "Standard-Message.\n\n!!!");
    }
    

    Das in main.cpp erstellte Fenster öffnet sich und es lassen sich darüber sowohl über die Buttons als auch über das Menü neue Fenster öffnen.
    Aber sobald ich über das Menü der neu geöffneten Fenster versuche, ein Fenster zu schließen, stürzt das Programm mit einem Segmentation-Fault ab. Die Buttons aller Fenster funktioneren jedoch.

    Sollten auch sonst noch Fehler in dem Code vorhanden sein (z.B. fehlende deletes), bin ich über jeden Tipp froh.

    Vielen Dank!



  • Statt das Fenster sofort zu löschen -> schließen, dann deleteLater() aufrufen.
    Oder Qt::WA_DeleteOnClose-Attribut setzen und einfach nur schließen.
    Deine Kinder haben übrigens keine Eltern.
    Und es sollte wohl immer nur ein einziges QMainWindow existierten.



  • Ich wollte die eben immer sofort aufräumen, auch, damit der QVector nicht zu groß wird und der Speicherverbrauch nicht allzu groß wird. Qt::WA_DeleteOnClose hat eben auch den Nachteil, dass der Zeiger im QVector bleibt. Wie funktioniert deleteLater() denn genau?
    Und wenn man das so macht, wie schließt man dann zuverlässig alle Fenster? Weil die Zeiger bleiben ja in dem QVector und die werden ein weiteres Mal geschlossen, obwohl sie schon gar nicht mehr offen sind.

    Welche Kinder meinst du denn genau? Die Fenster sollen ja unabhängig voneinander existieren können und wenn man das Ausgangsfenster schließt, sollen die anderen Fenster offen bleiben. Das funktioniert auch alles ganz gut.
    Wie soll ich das denn dann machen? QWidgets für die Zusatzfenster nehmen? Dann müsste ich eben, damit die Fenster gleich sind, den gesamten Code aus MainWindow in eine neue Klasse Widget schreiben. Hat es einen Grund, weshalb man nur ein QMainWindow haben soll?

    Und was mich eben vor allem auch interessiert (das ganze Programm hat ja eigentlich keinen praktischen Nutzen): warum funktioniert exakt der gleiche SLOT über den Button, über das Menü aber nicht.



  • Qt::WA_DeleteOnClose hat eben auch den Nachteil, dass der Zeiger im QVector bleibt.

    Genau das ist Dein Problem. Der Vector sollte "Normal", also wenn du Qt Mechanismen verwendest, nicht existieren.
    Qt Objecte verwalten sich über die Parent-Child Beziehung.

    Wie 46583 schon sagt, QMainwindow ist für multiple Instanzen nicht so wirklich geeignet. Es ist eher die Basis vom MDI Fenster Layouts. Also 1 Mainwindow, viele Childs

    Wenn du schon viele Fenster Ohne Parents bauen willst, nimm QWidget.
    Leite QApplication ab, und lass die alle Fenster Verwalten.

    void createNewWindow();
    void deleteThisWindow();
    void deleteLastOpenedWindow();
    void deleteAllWindows();

    diese Slots gehoeren nicht an ein "Window" weil sie beziehen sich ja nicht auf diese Instanz. Sondern an den Verwalter, oder sollten wie die Liste auch statisch sein. (Statische Slots gibts nicht!).
    warum sollte ne Instanz wissen, welches das letzte erzeugt fenster war ?

    Wie funktioniert deleteLater() denn genau?

    statt das delete und den Destruktor direkt aufzurufen, wird dem fenster ne Nachricht geschickt / in die Queue gestellt. Das führt dazu, das alle Msg erst abgearbeitet werden, und dann das fenster geschlossen, inklusive DTor aufgerufen.



  • Vielen Dank, ich werde es dann noch einmal mit QWidgets und QApplication versuchen, wobei ich mich bei QApplication noch einmal informieren müsste, wie genau man eine solche Verwaltung schreiben kann.

    Die Slots habe ich eben in die Klasse gepackt, weil die schließlich irgendwohin müssen. Und so, wie ich den Rest umgesetzt habe, war das eigentlich der einzige Weg. Aber jetzt weiß ich ja, wie es besser ginge.

    deleteLater() klingt dann ja eigentlich ganz interessant.
    Danke an euch beide.


Anmelden zum Antworten