0

I want to be able to create a combo box in QT using C++ that works on a model (QAbstractItemModel or QAbstractListModel - in this case the latter) but which works with actual objects not just QString as is the default. Here is the model I have:

// H file
#pragma once

#include <QAbstractListModel>
#include <QList>
#include "Test.h"

using net::draconia::test::model::Test;

namespace net
{
    namespace draconia
    {
        namespace test
        {
            namespace ui
            {
                namespace model
                {
                    class TestModel : public QAbstractListModel
                    {
                        QList<Test> mLstModel;
                    public:
                        TestModel();
                        virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
                        QList<Test> &getModel();
                        virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
                    };
                }
            }
        }
    }
}

// cpp
#include <QtDebug>
#include "TestModel.h"

using namespace net::draconia::test::ui::model;

TestModel::TestModel()
    :   QAbstractListModel()
{
    mLstModel.append(Test("English", "EN"));
    mLstModel.append(Test("French", "FR"));
}

QVariant TestModel::data(const QModelIndex &index, int role) const
{
    Q_UNUSED(role);

    qDebug() << index;
 
    if(index.isValid())
        {
       QVariant retVal;

        retVal.setValue(const_cast<TestModel &>(*this).getModel()[index.row()]);
 
        return(retVal);
        }
    else
        return(QVariant());
}
 
QList<Test> &TestModel::getModel()
{
    return(mLstModel);
}

int TestModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
 
    qDebug() << "There are " << const_cast<TestModel &>(*this).getModel().count() << " rows";

    return(const_cast<TestModel &>(*this).getModel().count());
 }

I don't know if the problem is there - Here's the code for the Test class upon which this combo box is based. As you'll see it is declared as a metatype:

// H file
#pragma once

#include <QString>
#include <QVariant>

namespace net
{
    namespace draconia
    {
        namespace test
        {
            namespace model
            {
                class Test
                {
                    QString msLanguage, msRegion;
                public:
                    Test();
                    Test(const QString &sLanguage, const QString &sRegion);
                    Test(const Test &refCopy);
                    ~Test();

                    QString &getLanguage() const;
                    QString &getRegion() const;
                    void setLanguage(const QString &sLanguage);
                    void setRegion(const QString &sRegion);

                    Test &operator=(const Test &refCopy);
                    bool operator==(const Test &refOther) const;
                    bool operator!=(const Test &refOther) const;
                    operator QString() const;

                    QString toString() const;
                };
            }
        }
    }
}

Q_DECLARE_METATYPE(net::draconia::test::model::Test);

// CPP file
#include "Test.h"

using namespace net::draconia::test::model;

Test::Test()
{ }

Test::Test(const QString &sLanguage, const QString &sRegion)
    :   msLanguage(sLanguage)
    ,   msRegion(sRegion)
{ }

Test::Test(const Test &refCopy)
    :   Test(refCopy.getLanguage(), refCopy.getRegion())
{ }

Test::~Test()
{ }

QString &Test::getLanguage() const
{
    return(const_cast<Test &>(*this).msLanguage);
}

QString &Test::getRegion() const
{
    return(const_cast<Test &>(*this).msRegion);
}

void Test::setLanguage(const QString &sLanguage)
{
    msLanguage = sLanguage;
}

void Test::setRegion(const QString &sRegion)
{
    msRegion = sRegion;
}

Test &Test::operator=(const Test &refCopy)
{
    setLanguage(refCopy.getLanguage());
    setRegion(refCopy.getRegion());

    return(*this);
}

bool Test::operator==(const Test &refOther) const
{
    return  (   (getLanguage() == refOther.getLanguage())
        &&  (getRegion() == refOther.getRegion()));
}

bool Test::operator!=(const Test &refOther) const
{
    return(!operator==(refOther));
}

Test::operator QString() const
{
    return(toString());
}

QString Test::toString() const
{
    return(getLanguage() + "(" + getRegion() + ")");
}

From what I can see in the debug statements, it is entering rowCount and telling me there are 2 rows and it is entering the data method twice to print the rows but there's nothing being printed. Here's the code for the simple window (I do NOT use QML at all - never have nor ever will - I think it's lazy!):

// H file
#pragma once

#include <QComboBox>
#include <QMainWindow>

namespace net
{
    namespace draconia
    {
        namespace test
        {
            namespace ui
            {
                class TestMainWindow : public QMainWindow
                {
                    Q_OBJECT

                    QComboBox *mCboTest;
                protected:
                    QComboBox *getTestComboBox();
                    void initControls();
                    void initWindow();
                public:
                    TestMainWindow(QWidget *parent);
                    ~TestMainWindow();
                };
            }
        }
    }
}

// CPP file
#include <QHBoxLayout>
#include "TestMainWindow.h"
#include "TestModel.h"

using namespace net::draconia::test::ui;
using net::draconia::test::ui::model::TestModel;

QComboBox *TestMainWindow::getTestComboBox()
{
    if(mCboTest == nullptr)
        {
        mCboTest = new QComboBox(this);

        mCboTest->setModel(new TestModel());
        }

    return(mCboTest);
}

void TestMainWindow::initControls()
{
    QLayout *layout = new QHBoxLayout(this);

    layout->addWidget(getTestComboBox());

    setLayout(layout);
}

void TestMainWindow::initWindow()
{
    setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));

    initControls();
}

TestMainWindow::TestMainWindow(QWidget *parent)
    :   QMainWindow(parent)
    ,   mCboTest(nullptr)
{
    initWindow();
}

TestMainWindow::~TestMainWindow()
{ }

If I must I will include the code to the Application class and my main.cpp file but those are the important ones - The rest are boilerplate code. If you question the namespace - I always include namespaces - it keeps code from infesting the global namespace even in examples.

What am I omitting out of my code for the combo box items to be displayed? I copied much of the code from a larger project which is where my problem really is experienced but it happens here too luckily so I can showcase it. In the larger project, I have observer classes that retrieve the selected object from the combo box and use them 'as' the object class themselves. I got this from Java Swing which combo box model items will automatically be called the ToString() for every item to print - Here it doesn't seem so. I thought maybe it would try to cast to QString but it's not even doing that. I'm stumped.

Seth D. Fulmer
  • 490
  • 1
  • 4
  • 21
  • 1
    add `QMetaType::registerConverter(&Test::toString);` after `QApplication a(argc, argv);` – eyllanesc Jul 31 '21 at 20:09
  • That worked to make the first item display but I can't make the list drop down now to show the 2nd item "French" - But if that worked 100% I'd have said to make it an answer so I could approve it. – Seth D. Fulmer Jul 31 '21 at 20:25
  • 1) It works for me with both rows: https://i.imgur.com/tzNs2sf.png, 2) It is a duplicate so it is not necessary to publish answers – eyllanesc Jul 31 '21 at 20:36
  • I wonder what you did to get it to work - and I'm not sure how to get an imgur link or I'd show you a screenshot for what I'm seeing. I did remove the QMetatype though and I still don't see any drop down list either way - just no first item printed without the QMetaType line I guess I"ll figure it out eventually. Thanks! – Seth D. Fulmer Jul 31 '21 at 21:13
  • My test code is: https://github.com/eyllanesc/stackoverflow/blob/master/questions/68605331 – eyllanesc Jul 31 '21 at 21:32
  • Yeah I don't see it drop down for yours either - Now I am expecting a new computer in a day or so - so once I set that up and try running the stuff, I hope the problem goes away and it's some artifact of a crappy Windows setup. – Seth D. Fulmer Aug 01 '21 at 04:40
  • It turns out when the line of code is applied to the original project it works - on this computer still as well. I think it's some artifact maybe of the layout - but it shows on your system so it's weird. On mine it shows the drop downs for the original project but not for the test project and the original has labels and other controls too on the form. The test project just has a single drop down and when I maximize the window, it still won't show the drop down list for me. I'm not worried about it anymore as I'm continuing with my original project. Thank you!! – Seth D. Fulmer Aug 01 '21 at 06:19

0 Answers0