PDA

View Full Version : Joining together a QComboBox, QStringList, and enum



Phlucious
24th May 2012, 23:16
I've done multiple searches (Google and otherwise) to see if there's an elegant Qt way to join together a QString and an enum for a QComboBox without having to manage two separate objects (a const QStringList and an enum), often defined in two separate places, and without hardcoding the link. Having not found anything, I thought I'd post something I came across today.

This centers on QComboBox's little-discussed ability to store "UserData" fields in its associated data model. To associate the QString with an enum simply use the third field of QComboBox::insertItem.

For example:


enum Fruit
{
APPLE,
ORANGE,
TANGERINE,
STARFRUIT
}


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
/* Set up the GUI */
ui->setupUi(this);

/* generate the default options with associated values */
myComboBox->insertItem(myComboBox->count(), "Star Fruit", STARFRUIT);
myComboBox->insertItem(myComboBox->count(), "Granny Smith", APPLE);
myComboBox->insertItem(myComboBox->count(), "Navel Orange", ORANGE);
myComboBox->insertItem(myComboBox->count(), "Fancy Orange", TANGERINE);
//etc

/* add another few */
int nextCode = 84;
myComboBox->insertItem(myComboBox->count(), "user-entered text", nextCode++);
myComboBox->insertItem(myComboBox->count(), "more text", nextCode++);
myComboBox->insertItem(0, "at the beginning", nextCode++);
}

/* data retrieval */
int MainWindow::getCode(QString label)
{
return myComboBox->findText(label)->toInt();
}
QString MainWindow::getLabel(int code = APPLE)
{
return myComboBox->itemText(myComboBox->findData(code)->toInt());
}


Note that a big advantage here is that order suddenly doesn't matter any more since the "code" has been divorced from the QComboBox::currentIndex, allowing you to sort the QComboBox by code or by label, to insert at arbitrary locations (perhaps with user-entered data), and to still switch-case using the enum without error-prone hard-coding of fixed values. Plus, you only have to enter the strings once, in a single location, significantly reducing mistakes whenever you need to change the label of a particular item.

Re-use is a piece of cake, too. If you use the same list frequently, you can easily sub-class QComboBox and insert the list into the constructor:

class FruitComboBox : public QComboBox
{
Q_OBJECT
public:
explicit ClassComboBox(QWidget *parent = 0);
QStringList fruitList() const;
int currentFruit() const;

enum Fruits
{
APPLE,
PEAR,
ORANGE
};
signals:
void currentFruitChanged(QString);
void currentFruitChanged(int);
public slots:
void insertFruit(const QString& newlabel);
void setCurrentFruit(const QString& fruit);
void setCurrentFruit(int fruitcode);
protected:
int m_nextFruitCode;
}

FruitComboBox::FruitComboBox(QWidget *parent) :
QComboBox(parent), m_nextFruitCode(-1)
{
/* generate the default list */
insertItem(count(), "I love apples!", APPLE);
insertItem(count(), "I love pears!", PEAR);
insertItem(count(), "ZOMG oranges!", ORANGE);
}

void FruitComboBox::insertFruit(const QString& newlabel)
{
insertItem(0, newlabel, m_nextFruitCode--);
}


Personally, I like to decrement the "nextCode" field so that it's easy to tell the difference between the enum values (>= 0) and the user-entered fields (<0).

Hope that helps the next person trying to do something like this. Comments?


PS—I've accomplished similar functionality using a QMap<QString, int> of label-enum pairs and then using QMap::keys() to populate the QComboBox, but the proposed method is more flexible.

Spitfire
25th May 2012, 17:47
I was using that approach for quite some time, but it never occured to me that it's may not be that common knowledge.
Thanks for taking the effort to post it :)