[SOLVED] How to destroy parent QWidget window and create&show new QWidget as parent?



  • Hi,
    I'm a little confused about how to destroy my parent window and create a new one:
    @class window_settings : public QWidget
    {
    Q_OBJECT

    public:
    int columns;
    my_QTextEdit* edit_person[10];
    QPushButton* button_create;

    window_settings(int columns, QWidget* parent = 0);
    

    public slots:
    void createMainWindow();
    };

    class window_main : public QWidget
    {
    Q_OBJECT

    public:
    QTableWidget* table;
    QStringList table_headers_h;

    window_main(int rows, int columns, QStringList &table_headers_h, QWidget* parent = 0);
    

    };

    int main(int argc, char** args)
    {
    FreeConsole();
    QApplication app(argc, args);

    window_settings window_one(10, NULL);
    window_one.show();
    
    app.exec();
    

    return 0;
    }

    window_settings::window_settings(int columns, QWidget* parent) : QWidget(parent)
    {
    this->columns = columns;
    for(int i = 0; i < columns; i++)
    {
    this->edit_person[i] = new my_QTextEdit(my_QTextEdit::TEXT, this);
    }
    button_create = new QPushButton("Create MainWindow", this);
    connect(button_create, SIGNAL(clicked()), this, SLOT(createMainWindow()));
    }

    void window_settings::createMainWindow()
    {
    QStringList table_headers_h;
    for(int i = 0; i < this->columns; i++)
    {
    table_headers_h << this->edit_person[i]->toPlainText();
    }
    this->destroy(true, true);
    window_main window_two(100, this->columns+1, table_headers_h, NULL);
    window_two.show();
    }@
    When I start program, the first window (settings) appears and everything works great. BUT when I click the button "Create MainWindow", the window_main appears 0.5 seconds and closes again with error "program doesn't work correctly anymore.." but program is still running...

    When I create an instance of window_main at first, everything works great, so there is no error in constructor of window_main...

    How do I destroy the first window and create + show the second one correctly?


  • Lifetime Qt Champion

    Hi,

    @
    window_main window_two(100, this->columns+1, table_headers_h, NULL);
    window_two.show();
    } <- window_two has ceased to exist here@

    You would need to allocate it on the heap.

    If I may, your design is not the most clean. You should rather separate both. Show your configuration dialog first, then get the information you need from it and construct your main window after.



  • Hi,
    thank you for your reply!

    Oh well, that sounds logical! I'll test it soon. What I'm asking myself then is: where jumps program after calling window_two.show() ? I mean, after showing first window in int main() it follows the code @app.exec()@ where event loop starts and after destroying first window, program should jump back to int main(), right or wrong? Why don't I have to start a new event loop after showing the second window? Where will program jump after showing it (it must be another scope, otherwise I wouldn't need to create it dynamically)...

    Question 2:
    [quote]You should rather separate both. Show your configuration dialog first, then get the information you need from it and construct your main window after.[/quote]
    Maybe I didn't understand you right, but isn't that exactly what I'm doing?
    At first, I create first dialog (window_settings) in int main(), then I get the user input via button_create --> createMainWindow() and then --> create main_window...
    Maybe I misunderstood you..


  • Lifetime Qt Champion

    As long as you don't exit the event loop it continues to run.

    In some way yes, but to keep things clean: it's not your configuration dialog job to create a new MainWindow.

    My proposition:
    @
    ConfigurationDialog dialog;
    dialog.exec();
    int columns = dialog.columns();
    int int rows = dialog.rows();
    MainWindow mw(columns, rows);
    mw.show();
    return app.exec();
    @



  • Hi again,
    [quote]As long as you don’t exit the event loop it continues to run[/quote]
    So do you have a guess why my program won't exit after closing the second window in my example?

    [quote]In some way yes, but to keep things clean: it’s not your configuration dialog job to create a new MainWindow.[/quote]
    Ok, that member of QDialogs "exec()" is exaclty the thing I need! Isn't there a way to solve this with to QWidget windows? Is there no way to "create" QWidget window_one, then wait till it's closed and then create QWidget window_two? What do you say to a try like this:
    @QWidget* window_settings = new QWidget();
    window_settings.show();
    eventloop* loop = new QEventLoop;
    connect(window_settings, SIGNAL(destroyed()), loop, SLOT(quit()));
    QWidget* window_main = new QWidget();@
    I will take a look at your advice with QDialog, but I'd also like to know whether this way is a possibility or not AND I really want to know why my example in the previous post won't exit after closing the second window... this is still confusing me much!


  • Lifetime Qt Champion

    Because you might have destroyed the visual part of your widget but it never closed properly. Your call to destroy should not be there, it's not a function that you usually play with.

    The behavior you are describing reflects exactly the one from QDialog (which is a QWidget)
    Sure you can do it with QWidget, but you are going to rewrite QDialog so why not use it ?

    One thing you should do first is take a look at Qt's tutorials, examples and demos. You'll have a better starting point to write your software



  • Ah you've been faster^^
    Well, I read that destroy() is usually managed by QWidgets destructor..
    I'll take a look at the tutorials again, maybe I'll see things more clear after that. What I need is an overview about how to manage a program with multiple windows.
    Should I call it all from the main function and wait until execution call returns (like a star configuration):
    @int main()
    {
    QDialog window_settings;
    window_settings.exec();
    QWidget window_main;
    window_main.show();
    app.exec();
    return 0;
    }@
    Or is it better to do it like in a loop, creating next window from the member functions of first window (like i tried in my first post)...

    Does it depend on what I'm trying to do or is there a general way?


  • Lifetime Qt Champion

    There's no simple answer, that will depend on what your software should do.

    Don't return 0 like that, return what app.exec() returns. Your application may exit giving an error.



  • Ok, I've updated my code, maybe you can take a final look at it and tell me if there's something fundamentally wrong with it or not (I hope not, because it works fine!):
    @ int main(int argc, char** args)
    {
    FreeConsole();
    QApplication app(argc, args);

    QStringList table_headers_h;
    table_headers_h << "Amount";
    for(int i = 1; i < 11; i++)
    {
      table_headers_h << "Person "+QString::number(i);
    }
    
    window_main* window_m = new window_main(100, 11, table_headers_h, NULL);
    window_m->show();
    window_m->settings();
    

    return app.exec();
    }

    void window_main::settings()
    {
    window_settings* window_s = new window_settings(10, this);
    window_s->exec();
    }

    window_settings::window_settings(int columns, QWidget* parent) : QDialog(parent)
    {
    this->parent = parent; // save the main_windows pointer so I can update it later from this QDialog instance

    for(int i = 0; i < columns; i++)
    {
      this->edit_person[i] = new my_QTextEdit(my_QTextEdit::TEXT, this);
    }
    button_create = new QPushButton("Create", this);
    button_create->setGeometry(QRect(5, (5+columns*30), 150, 30));
    connect(button_create, SIGNAL(clicked()), this, SLOT(createMainWindow()));
    

    }

    void window_settings::createMainWindow()
    {
    QStringList table_headers_h;
    QString header_item;
    table_headers_h << "Amount";
    for(int i = 0; i < this->columns; i++)
    {
    header_item = (this->edit_person[i]->toPlainText() != "") ? this->edit_person[i]->toPlainText() : "Person "+QString::number(i+1);
    table_headers_h << header_item;
    }
    ((window_main*)this->parent)->table_headers_h = table_headers_h; // downcasting QWidget pointer to window_main...
    ((window_main*)this->parent)->table->setHorizontalHeaderLabels(table_headers_h);
    this->close();
    }@

    I have moved the creation of QDialog to a function, so I can open that setting_window by button click on main window later again...

    Three questions left:

    1. is that code ok or still fundamentally wrong?
    2. I always use "FreeConsole();" to avoid opening command box... problem is, it still opens for 0.5s and that isn't pretty... how can I avoid this flicker?
    3. Is it better to downcast via qobject_cast or can I do it like I did with simply using ((window_main*)parent) pointer?

    Thank you for paying attention one last time to that topic :)


  • Lifetime Qt Champion

    It might be working but it's not clean at all.

    First thing, what does FreeConsole do there ?

    Widgets already have a parent method to retrieve it, no need for a member variable.

    Don't use c-style cast with QObject derived class, use qobject_cast.

    BUT, that design is wrong: keep the responsibilities separated.

    Your setting widget should just handle the settings, it's not its job to modify the content of your main window. The main window itself should handle that.

    Take the case where you change some stuff in your main window, something as little as just one variable name, now you have to look at three different places to change that. I know that with modern IDE it's not a real problem but the fact is that your code is going to be a nightmare to maintain.

    Please take the time to look at Qt's examples, they show nicely how to do what you want.



  • [quote]First thing, what does FreeConsole do there ?[/quote]As I already mentioned, I need it to close the command box that opens every time I start my program. I read in google that this is an easy way to hide the command box, but the problem is the flicker at the beginning, because it still opens for 0.5s and I see it appearing (what sucks and I need a solution to avoid it).
    Any solutions for that?
    [quote]Widgets already have a parent method to retrieve it, no need for a member variable.[/quote]Oh yes, that's right :) So many great functions provided by Qt classes!! I will spend lots of days finding new good features that make my life easier :)
    [quote]Don’t use c-style cast with QObject derived class, use qobject_cast.[/quote]Ok...
    [quote]Your setting widget should just handle the settings, it’s not its job to modify the content of your main window. The main window itself should handle that.Take the case where you change some stuff in your main window, something as little as just one variable name, now you have to look at three different places to change that. I know that with modern IDE it’s not a real problem but the fact is that your code is going to be a nightmare to maintain.[/quote]Oh yes, good aspect! I'm sure that at the latest when my program grows I'd have to spend lots of time thinking about opportunities to make code more dynamic like a layout, where I only have to change things in one sheet...
    Thank you for that advice, that will help me much while planing future applications!!
    What I additionally did is including the QDialog into main_window class as a member with a function to get input:
    @class window_settings : public QDialog
    {
    Q_OBJECT

    public:
    int columns;
    my_QTextEdit* edit_person[10];
    QPushButton* button_create;

    window_settings(int columns, QWidget* parent = 0);
    QStringList getInputTextEdit();
    

    };

    class window_main : public QWidget
    {
    Q_OBJECT

    public:
    window_settings* win_settings;
    QTableWidget* table;
    QStringList table_headers_v, table_headers_h;
    QListWidget* list;
    QPushButton *button_settings, *button_calculate;

    window_main(int rows, int columns, QStringList &table_headers_h, QWidget* parent = 0);
    void updateAll();
    

    public slots:
    void settings();
    void calculate();
    };

    int main(int argc, char** args)
    {
    FreeConsole();
    QApplication app(argc, args);

    QStringList table_headers_h;
    table_headers_h << "Amount";
    for(int i = 1; i < 11; i++)
    {
      table_headers_h << "Person "+QString::number(i);
    }
    
    window_main* win_main = new window_main(100, 11, table_headers_h, NULL);
    win_main->win_settings = new window_settings(10, win_main);
    win_main->show();
    win_main->settings();
    

    return app.exec();
    }

    window_main::window_main(int rows, int columns, QStringList &table_headers_h, QWidget* parent) : QWidget(parent)
    {
    //....
    button_calculate = new QPushButton("Calculate", this);
    connect(button_calculate, SIGNAL(clicked()), this, SLOT(calculate()));

    button_settings = new QPushButton("Settings", this);
    connect(button_settings, SIGNAL(clicked()), this, SLOT(settings()));
    

    }

    void window_main::settings()
    {
    this->win_settings->exec();
    }@
    How do you think about this? QDialog opens now each time user clicks window_main button "Settings".
    In window_main function "updateAll()" (not shown in code), it calls QDialog function getInputTextEdit() and retrieves the stored inputs to update itsself...
    Good or bad way?


  • Lifetime Qt Champion

    Do you have QT += console somewhere in you pro file ? If so, just remove it.

    @
    QStringList table_headers_h;
    table_headers_h << "Amount";
    for(int i = 1; i < 11; i++)
    {
    table_headers_h << "Person "+QString::number(i);
    }
    @

    Should rather be in your widget constructor.
    If you don't want to, then you should pass your QStringList as a const reference.

    You still have a memory leak, you never delete win_main.

    Are you still modifying window_main from your settings dialog ?



  • [quote]Do you have QT += console somewhere in you pro file ?[/quote]I'm directly compiling. I did a little program that directly calls g+++ with the needed includes, so what do I have to change to avoid displaying the command line box?

    [quote]Should rather be in your widget constructor.[/quote]Thats right, it is there, I just moved it to main() for posting it, because I did not want to show the whole constructor to shorten the code & comment.

    [quote]You still have a memory leak, you never delete win_main.[/quote]Oh yes, I'll fix that, but where should I do that? The only way would be to connect window closing signal to a function that deletes win_main, right? But closing the window is not the only way to close an application, so how can I avoid the memory leak if someone closes the application via taskbar / taskmanager or something else? Is there also a genereal QApplication closing signal or something else?

    [quote]Are you still modifying window_main from your settings dialog ?[/quote]No, I have two functions now:

    • function A (win_main member) calls function B (win_settings member) and retrieves the userinput from win_settings. So win_main updates itsself...

  • Lifetime Qt Champion

    Why don't you use qmake ?

    You have several possibilities but since your program is pretty simple:

    @
    window_main win_main(100, 11, table_headers_h);
    @

    is enough



  • I thought Qt is known for good care about automatically deleting dynamically created QWidgets? I strongly use the QWidget* parent method to initialize each new QWidget as a child of win_main...
    So the only thing compiler has to do is caring about deleting win_main...
    Why doesn't this happen after closing the event-loop?
    Where would I have to manually call it's destructor then? I don't know...


  • Lifetime Qt Champion

    Yes it does but it can't do everything:

    @
    window_main* window_m = new window_main(100, 11, table_headers_h); << window_m is allocated on the heap, with no parent, so it's outside of Qt's scope.
    window_m->show();

    return app.exec();
    @

    Either use:

    @
    window_main window_m(100, 11, table_headers_h);
    window_m.show();
    return app.exec();
    @

    or

    window_main* window_m = new window_main(100, 11, table_headers_h, NULL);
    window_m->show();

    int ret = app.exec();
    delete mainwindw_main;
    return ret;
    @



  • Ah there we go. Then it is a little bit the way I already tried to go.
    First:
    [quote]window_main* window_m = new window_main(100, 11, table_headers_h, NULL); [/quote]That is exaclty what I'm doing, take a look at my constructor:
    @window_main::window_main(int rows, int columns, QStringList &header, QWidget* parent = 0) : QWidget(parent)
    {
    ...
    }@
    I don't pass the NULL pointer directly by calling constructor but I declare constructor with it as default value... should be the same, isn't it?

    [quote]int ret = app.exec(); delete mainwindw_main; return ret;[/quote]That is exactly what I already thought of... So I still have to delete the parent QWidget manually? Then that will be the only way, and for that reason I should really take care of all my widgets to be childs of at least one parent, so I'm always safe when I just delete one QWidget.. is that the right way?


  • Lifetime Qt Champion

    Yes it is, but that was just a simplified copy paste of your code to show the possibilities.

    Not the parent but parentless widgets (which generally ends up being the "parent"), unless you set the WA_DeleteOnClose flag on that widget.



  • Ok, I think I got it now..
    I'll do it like you recommended and delete the "parent" (parentless aka NULL pointer) after app.exec() returned and finally, I'll return the returned value from app.exec()...

    Thank you so much for your help and endurance!


  • Lifetime Qt Champion

    Well, in this case, there's no real reason to use a pointer for your widget. Just allocate it on the stack and let it get destroyed when going out of scope.



  • Ok, you're right. I'll change this, thank's :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.