Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Choosing a window to show at app launch



  • First off, forgive me for the potentially basic question. I'm still a bit new to Qt in general, and I suspect I'm over-complicating this issue. I'm working on a desktop program in Qt 5.7. There's MainWindow, which is the general UI, but a new QMainWindow has been added named ControllerWindow. The idea is that if the --controlmode argument is passed at runtime, the app will load and display ControllerWindow instead of MainWindow.

    The catch is that MainWindow has a method that needs to be called -- MainWindow::InitValues() -- which ControllerWindow does not have.

    What's the most "correct" way to handle this? I suppose the easy way is to do something like this, but I don't really like it, and feel like I'm an idiot blanking out on some obvious/simple/better approach:

    if (controlModeEnalbed) {
       ControllerWindow cw;
       cw.show();
       return a.exec();
    } else {
       MainWindow mw;
       mw.InitValues();
       mw.show();
       return a.exec();
    }
    

    It works, but I rather prefer the idea of a single point of return instead of that ugliness. Any recommendations? Thank you for your time!


  • Lifetime Qt Champion

    Hi and welcome to the forums

    What about something like

      ControllerWindow cw;
      MainWindow mw;
      mw.InitValues(); // must call always anyway
      QWidget* ToShow = &mw; // assume mainwin
      if (controlModeEnalbed)  ToShow = &cw;
      ToShow->show();
      return a.exec();
    

  • Qt Champions 2019

    @fny82

    // I assume here that both windows are derived from QMainWindow
    QMainWindow *window = nullptr;
    if (controlModeEnalbed) {
       window = new ControllerWindow();
    } else {
       window = new MainWindow();
       (dynamic_cast<MainWindow*>(window))->InitValues();
    }
    auto result = a.exec();
    delete window;
    return result;
    


  • @fny82
    Assuming your skeleton to be what you desire, you are saying that MainWindow::InitValues() only needs to be called when you are going to show MainWindow, not when you are going to show ControllerWindow, right?

    When controlModeEnabled you only need ControllerWindow to be created, and when not controlModeEnabled you only need MainWindow to be created, right?

    And I assume both your MainWindow & your ControllerWindow are derived (separately) from QMainWindow, right?

    To be fair, your existing code does implement this logic, so it's not too bad! Thought I probably wouldn't do it that way.

    @mrjj's principle works, but always calls MainWindow::InitValues(), and also always constructs & leaves in existence both instances when you only need one, right?

    There are many ways you could approach this slightly differently from yours or @mrjj's.

    If I take his approach I might suggest:

      QMainWindow *qmw;
      if (controlModeEnabled) {
          MainWindow *mw = new MainWindow;
          mw ->InitValues();
          qmw = mw;
      } else {
          ControllerWindow *cw = new ControllerWindow;
          qmw = cw;
      }
      qmw->show();
      # To avoid leakage from the `new` prior to exit you will need to `delete qmw` or similar
       # I don't do that code 'coz I'm using Python, but you should
       # I leave it as an exercise for you to do correctly!
      return a.exec();
    

    The point here is to new (on the heap) whichever (only) one you want, rather than messing about with local variables on the stack and their scope.

    Another approach would be to derive your own MyBaseMainWindow from QMainWindow, and then derive your two classes from MyBaseMainWindow. Your base MyBaseMainWindow could then have a virtual InitValues() which does nothing, and your MainWindow only need override that with its own code. Then you can always use your_derived_main_window->InitValues() at compile-time. If your two main window classes share other common code this can be nice.

    One other possibility without a base class to avoid the need for calling mw.InitValues(); which you don't like might be to execute it from within MainWindow::showEvent() when first showing. Depends how early you need the initializations.

    In both these alternatives you'd still want the news rather than local variables.


  • Lifetime Qt Champion

    Hi,

    Since you need to call InitValues in all cases, why put it in your widget ? It looks like it could be a helper function in your main.cpp that you call as part of your application startup.



  • Brilliant suggestions. I knew I was over-complicating this. Thank you so much for the feedback and suggestions in this thread. It was immensely helpful and allowed me to solve this problem. I used a version of JonB and jsulm's suggestions, where I used a pointer to get around the scope issue and cast it to run the init method in the event the control mode argument was not in use. This did the trick beautifully. :)

    Simplified example of what I ended up using:

    QMainWindow* startupWindow = nullptr;
    
    if (controlModeEnabled) {
       startupWindow = new ControllerWindow();
       startupWindow->show();
    } else {
       startupWindow = new MainWindow();
       (dynamic_cast<MainWindow*>(startupWindow))->InitValues();
       startupWindow->show();
    }
    
    auto execResult = a.exec();
    delete startupWindow;
    
    return execResult;
    


  • @fny82 said in Choosing a window to show at app launch:

    startupWindow = new MainWindow();
    (dynamic_cast<MainWindow*>(startupWindow))->InitValues();

    I find this ugly/unwieldy! That's why I wrote:

      MainWindow *mw = new MainWindow;
      mw ->InitValues();
      qmw = mw;
    

    Create an explicit MainWindow* variable first so as to use its method without casting, then assign it to your shared QMainWindow* variable.


Log in to reply