Search code examples
c++qtqt5qstackedwidget

How to show the correct GUI on a QStackedWidget depending on the choice on a QListWidget


How to properly show the right user interface on the QStacked Widget depending on the choice on QListWidget?

Below I have a QDialog that contains:

1 QListWidget

1 QStackedWidget

stack

Depending on the selected choice of the list, for example, Vessel Position System, or Output the interface should look like below, but the problem is that unfortunately the QStacked Widget is currently not changing the QWidgets as I select QListWidgets, actually is not showing anything. A Minimal Verifiable Example can be found here. Just clone it and it will work:

output

Below the code:

optionsdialog.h

#include <QDialog>
#include "vesselpossystemwidget.h"
#include "outputdialog.h"
#include "sonarsystem.h"
namespace Ui {
class OptionsDialog;
}

class OptionsDialog : public QDialog
{
    Q_OBJECT
public:
    explicit OptionsDialog(QWidget *parent = nullptr);
    ~OptionsDialog();

private:
    Ui::OptionsDialog *ui;
    VesselPosSystemWidget *mVesPos;
    OutputDialog *mOutput;
    SonarSystem *mSonar;
};

#endif // OPTIONSDIALOG_H

optionsdialog.h

#include "optionsdialog.h"
#include "ui_optionsdialog.h"

OptionsDialog::OptionsDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::OptionsDialog)
{
    ui->setupUi(this);
    switch(ui->stackedWidget->currentIndex()){

      case 0:
        // Go to positioning system
        mVesPos = new VesselPosSystemWidget();
        mVesPos->show();
        break;
      case 1:
        // Go to sonar set up...
        mSonar = new SonarSystem();
        mSonar->show();
        break;
      case 2:
        // Go to output
        mOutput = new OutputDialog();
        mOutput->show();
        break;
      default:
        break;
    }
}

OptionsDialog::~OptionsDialog()
{
    delete ui;
}

vesselposwidget.h

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class VesselPosSystemWidget; }
QT_END_NAMESPACE

class VesselPosSystemWidget : public QWidget
{
    Q_OBJECT

public:
    VesselPosSystemWidget(QWidget *parent = nullptr);
    ~VesselPosSystemWidget();

private:
    Ui::VesselPosSystemWidget *ui;
};
#endif // VESSELPOSSYSTEMWIDGET_H

vesselposwidget.cpp

#include "vesselpossystemwidget.h"
#include "ui_vesselpossystemwidget.h"

VesselPosSystemWidget::VesselPosSystemWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::VesselPosSystemWidget)
{
    ui->setupUi(this);
}

VesselPosSystemWidget::~VesselPosSystemWidget()
{
    delete ui;
}

output.h

#include <QDialog>

namespace Ui {
class OutputDialog;
}

class OutputDialog : public QDialog
{
    Q_OBJECT

public:
    explicit OutputDialog(QWidget *parent = nullptr);
    ~OutputDialog();

private:
    Ui::OutputDialog *ui;
};

#endif // OUTPUTDIALOG_H

output.cpp

#include "outputdialog.h"
#include "ui_outputdialog.h"

OutputDialog::OutputDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::OutputDialog)
{
    ui->setupUi(this);
}

OutputDialog::~OutputDialog()
{
    delete ui;
}

What I have done so far:

I am exploring the possibility to use a QStacked Widget as I am not very familiar with it and wanted to practice with a basic example. I was investigating this source and also this one

Also from the same source there is more documentation that I read. However I was not fully able to understand clearly the use of this particular widget.

In addition to that I tried a switch - case loop as I shown above on the code but I Am not sure why it didn't work

Can anyone please provide some guidance on how to properly show the right user interface on the QStacked Widget depending on the choice on QListWidget? Any direction for solving this issue is appreciated.


Solution

  • A QStackedWidget is a container widget for multiple child widgets which shows only one of them at a time.

    Hence, all child widgets should be added at once.

    The currentIndex can be used to control which of them should be visible currently.

    A minimal reproducible example to demonstrate this – testQStackedWidgetMin.cc:

    // Qt header:
    #include <QtWidgets>
    
    // main application
    int main(int argc, char **argv)
    {
      qDebug() << "Qt Version:" << QT_VERSION_STR;
      QApplication app(argc, argv);
      // setup GUI
      QWidget qWinMain;
      QHBoxLayout qHBox;
      QListWidget qList;
      qList.addItem(QString::fromUtf8("Config. 1"));
      qList.addItem(QString::fromUtf8("Config. 2"));
      qList.addItem(QString::fromUtf8("Output"));
      qHBox.addWidget(&qList);
      QStackedWidget qStack;
      QLabel qConfig1(QString::fromUtf8("<b>Config Page 1</b>"));
      qStack.addWidget(&qConfig1);
      QLabel qConfig2(QString::fromUtf8("<b>Config Page 2</b>"));
      qStack.addWidget(&qConfig2);
      QLabel qOutput(QString::fromUtf8("<b>Output Page</b>"));
      qStack.addWidget(&qOutput);
      qHBox.addWidget(&qStack, 1);
      qWinMain.setLayout(&qHBox);
      qWinMain.show();
      // install signal handlers
      QObject::connect(&qList, &QListWidget::currentRowChanged,
        &qStack, &QStackedWidget::setCurrentIndex);
      // runtime loop
      return app.exec();
    }
    

    Output:

    Snapshot of testQStackedWidgetMin

    Note:

    One of the essential statements of this sample is the signal-slot connection:

      QObject::connect(&qList, &QListWidget::currentRowChanged,
        &qStack, &QStackedWidget::setCurrentIndex);
    

    The QListWidget::currentRowChanged signal of the QListWidget qList is connected to the QStackedWidget::setCurrentIndex() function of the QStackedWidget qStack. This is responsible for updating of qStack for any (interactive) row change in qList.


    A not so minimal sample according to the intention of OP – testQStackedWidget.cc:

    // Qt header:
    #include <QtWidgets>
    
    class Config1: public QWidget {
      private:
        // GUI
        QVBoxLayout _qVBox;
        QLabel _qLbl;
    
      public:
        // constructor.
        Config1();
        // destructor.
        virtual ~Config1() = default;
        // disabled:
        Config1(const Config1&) = delete;
        Config1& operator=(const Config1&) = delete;
    };
    
    
    Config1::Config1(): QWidget()
    {
      // setup GUI
      _qLbl.setText(QString::fromUtf8("<b>Config Page 1</b>"));
      _qVBox.addWidget(&_qLbl);
      setLayout(&_qVBox);
    }
    
    class Config2: public QWidget {
      private:
        // GUI
        QVBoxLayout _qVBox;
        QLabel _qLbl;
    
      public:
        // constructor.
        Config2();
        // destructor.
        virtual ~Config2() = default;
        // disabled:
        Config2(const Config2&) = delete;
        Config2& operator=(const Config2&) = delete;
    };
    
    Config2::Config2(): QWidget()
    {
      _qLbl.setText(QString::fromUtf8("<b>Config Page 2</b>"));
      _qVBox.addWidget(&_qLbl);
      setLayout(&_qVBox);
    }
    
    class Output: public QWidget {
      private:
        // GUI
        QVBoxLayout _qVBox;
        QLabel _qLbl;
    
      public:
        // constructor.
        Output();
        // destructor.
        virtual ~Output() = default;
        // disabled:
        Output(const Output&) = delete;
        Output& operator=(const Output&) = delete;
    };
    
    Output::Output(): QWidget()
    {
      _qLbl.setText(QString::fromUtf8("<b>Output Page</b>"));
      _qVBox.addWidget(&_qLbl);
      setLayout(&_qVBox);
    }
    
    class OptionDialog: public QDialog {
      private:
        // GUI
        QHBoxLayout _qHBox;
        QListWidget _qList;
        QStackedWidget _qStack;
        Config1 _qConfig1;
        Config2 _qConfig2;
        Output _qOutput;
    
      public:
        // constructor.
        OptionDialog();
        // destructor.
        virtual ~OptionDialog() = default;
        // disabled:
        OptionDialog(const OptionDialog&) = delete;
        OptionDialog& operator=(const OptionDialog&) = delete;
    };
    
    OptionDialog::OptionDialog(): QDialog()
    {
      // setup GUI
      _qList.addItem(QString::fromUtf8("Config. 1"));
      _qList.addItem(QString::fromUtf8("Config. 2"));
      _qList.addItem(QString::fromUtf8("Output"));
      _qHBox.addWidget(&_qList);
      _qStack.addWidget(&_qConfig1);
      _qStack.addWidget(&_qConfig2);
      _qStack.addWidget(&_qOutput);
      _qHBox.addWidget(&_qStack, 1);
      setLayout(&_qHBox);
      // install signal handlers
      QObject::connect(&_qList, &QListWidget::currentRowChanged,
        &_qStack, &QStackedWidget::setCurrentIndex);
    }
    
    // main application
    int main(int argc, char **argv)
    {
      qDebug() << "Qt Version:" << QT_VERSION_STR;
      QApplication app(argc, argv);
      // setup GUI
      OptionDialog qDlgOpt;
      qDlgOpt.show();
      // runtime loop
      return app.exec();
    }
    

    Output:

    Snapshot of testQStackedWidget


    The CMakeLists.txt, I used for creation of the VisualStudio solution:

    project(QStackedWidget)
    
    cmake_minimum_required(VERSION 3.10.0)
    
    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
    #set(CMAKE_CXX_STANDARD 17)
    #set(CMAKE_CXX_STANDARD_REQUIRED ON)
    set(CMAKE_CXX_EXTENSIONS OFF)
    
    find_package(Qt5Widgets CONFIG REQUIRED)
    
    include_directories("${CMAKE_SOURCE_DIR}")
    
    add_executable(testQStackedWidgetMin
      testQStackedWidgetMin.cc)
    
    target_link_libraries(testQStackedWidgetMin
      Qt5::Widgets)
    
    add_executable(testQStackedWidget
      testQStackedWidget.cc)
    
    target_link_libraries(testQStackedWidget
      Qt5::Widgets)