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

Launcher for multiple apps



  • Hello,

    I have recently started learning how to use Qt , and for my project I need to make an application launcher window, which allows the user to start one (only one) from a number of Qt apps. A visual example of what is my final aim can be found here, the NRF Connect application. When you start it, a window listing different application pops up, and you select one of them.

    I attempted to create a QMainWindow with a few buttons, and tried to launch different windows upon clicking them. However, these windows disappear immediately. My aim is to close the main window, so the user cannot start another instance of the same app, or another app from the list.

    Here is my code. Can you please direct me to a suitable solution.

    #include "launcher.h"
    #include "ui_launcher.h"
    #include "appone.h"
    #include "apptwo.h"
    
    Launcher::Launcher(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::Launcher)
    {
        ui->setupUi(this);
    }
    
    Launcher::~Launcher()
    {
        delete ui;
    }
    
    void Launcher::on_app2Btn_clicked()
    {
        AppTwo app;
        app.show();
        //this->hide();
    }
    
    void Launcher::on_app1Btn_clicked()
    {
        AppOne app;
        app.show();
        //this->hide();
    }
    

    Thanks


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Your app variables are on the stack and therefore lives as long as the method they are declared in.

    From your linked example, it looks rather like you have a QStackedWidget showing the appropriate things to do. That stacked widget seems to be controlled by an equivalent of a QListWidget.



  • You probably know by now that Qt is an event driven "tool".
    // stadard event application
    QApplication app(argc, argv);

    'I would recommend to

    1. Build plain standard "main" implementing the QApplication "loop" first.
    2. Refrain from using "app" name for your "child" applications

    This may not satisfy your task (straight from the box) , but I have been relatively successful using Qt "MDI" example as top menu driven "application ". For what its worth - using "MDI" example gives relatively standard "look and feel" of window ( as a concept, not OS) applications.



  • @SGaist I was typing my reply and did not see yours first.
    From my latest encounter with lack of understanding of "how to implement" any widget in code or using QDesigner - I think this "stacked widget " being compared to "tab widget" (as documented ) could be a prime example how Qt code - QStackedWidget and QtDesigner "tab widget" ARE TWO different and "not talking to each other " ways to code.

    End of off subject comment.


  • Lifetime Qt Champion

    @AnneRanch what tab widget vs stacked widget ?
    In any case, a QTabWidget is built on top of QStackedWidget and QTabBar.



  • Thank you for the input. I am now creating application instances as pointer variables upon each invocation. Here's my code, which is somewhat working, but I get the feel that it's lacking flexibility for adding new applications without having to extend it substantially.

    I'd like to hear your suggestions on how to make the application skeleton easy to extend with more apps ("tools") in the future. One thing that comes to mind is make the Launcher class have a vector of apps (or tools), which can be added dynamically. The Selector class can the send a signal containing a numeric value corresponding to the vector index of the app that needs to be launched.

    /******************************************************************************************/
    class Selector : public QMainWindow
    {
        Q_OBJECT
    public:
        Selector(QWidget *parent = nullptr);
        ~Selector();
    private slots:
        void on_pushButton_clicked();
        void on_pushButton_2_clicked();
    private:
        Ui::Selector *ui;
    signals:
        void runToolA();
        void runToolB();
    };
    
    Selector::Selector(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::Selector)
    {
        ui->setupUi(this);
    }
    
    Selector::~Selector()
    {
        delete ui;
    }
    
    void Selector::on_pushButton_clicked()
    {
        emit runToolA();
    }
    
    void Selector::on_pushButton_2_clicked()
    {
        emit runToolB();
    }
    /******************************************************************************************/
    class Launcher : public QObject
    {
        Q_OBJECT
    public:
        explicit Launcher(QObject *parent = nullptr);
    signals:
        void toolLaunched();
        void toolClosed();
    public slots:
        void launchToolA();
        void launchToolB();
    private:
        ToolA *toolA = nullptr;
        ToolB *toolB = nullptr;
    };
    
    void Launcher::launchToolA()
    {
        toolA = new ToolA();
        connect(toolA, &ToolA::windowClosed, this, &Launcher::toolClosed);
        toolA->show();
        emit toolLaunched();
    }
    
    void Launcher::launchToolB()
    {
        toolB = new ToolB();
        connect(toolB, &ToolB::windowClosed, this, &Launcher::toolClosed);
        toolB->show();
        emit toolLaunched();
    }
    /******************************************************************************************/
    class ToolA;
    }
    class ToolA : public QMainWindow
    {
        Q_OBJECT
    public:
        explicit ToolA(QWidget *parent = nullptr);
        ~ToolA();
    signals:
        void windowClosed();
    private:
        Ui::ToolA *ui;
    
        void closeEvent(QCloseEvent *event);
    };
    
    ToolA::ToolA(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::ToolA)
    {
        ui->setupUi(this);
        this->setAttribute(Qt::WA_DeleteOnClose, true);     // Widget will be deleted automatically when closed
    }
    
    ToolA::~ToolA()
    {
        delete ui;
    }
    /******************************************************************************************/
    void ToolA::closeEvent(QCloseEvent *event) {
        emit windowClosed();
        event->accept();
    }
    
    class ToolB : public QMainWindow
    {
        Q_OBJECT
    public:
        explicit ToolB(QWidget *parent = nullptr);
        ~ToolB();
    signals:
        void windowClosed();
    private:
        Ui::ToolB *ui;
    
        void closeEvent(QCloseEvent *event);
    };
    
    ToolB::ToolB(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::ToolB)
    {
        ui->setupUi(this);
        this->setAttribute(Qt::WA_DeleteOnClose, true);
    }
    
    ToolB::~ToolB()
    {
        delete ui;
    }
    
    void ToolB::closeEvent(QCloseEvent *event) {
        emit windowClosed();
        event->accept();
    }
    /******************************************************************************************/
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        Selector selector;          // Window allows user to select Tool A or Tool B by pressing a button
        Launcher launcher;          // The Launcher object creates an instance of the selected Tool application
    
        QObject::connect(&selector, &Selector::runToolA, &launcher, &Launcher::launchToolA);
        QObject::connect(&selector, &Selector::runToolB, &launcher, &Launcher::launchToolB);
    
        QObject::connect(&launcher, &Launcher::toolLaunched, &selector, &Selector::hide);
        QObject::connect(&launcher, &Launcher::toolClosed, &selector, &Selector::show);
    
        selector.show();
    
        return a.exec();
    }
    /******************************************************************************************/
    

  • Lifetime Qt Champion

    You indeed should refactor that so that you are independent of the number of tools. Make a base class that all tools will use as a base so you do not duplicate code uselessly.



  • That's the way I'd like to go. Unfortunately I can't use a Tool interface, as the only commonality between tools is that they emit a windowClosed() event; that means the interface will need to inherit QObject, and if a tool inherits QMainWindow, as it most certainly will, that will lead to multiple inheritance. But I can't be certain that a tool will inherit QMainWindow, so I'd like to avoid creating an abstract base class inheriting QMainWindow.

    With this in mind, I think the windowClosed() event should be renamed to something more generic, such as toolClosed().

    Anyway, I think if a tool has a QMainWindow, or another widget, instead of inheriting from it, it will solve the problem with the inheritance. I'll try that and come back if I still can't figure it out.

    Thanks for the suggestion!


Log in to reply