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
) : 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());
}
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());
}
To copy to clipboard, switch view to plain text mode
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:
{
Q_OBJECT
public:
explicit ClassComboBox
(QWidget *parent
= 0);
int currentFruit() const;
enum Fruits
{
APPLE,
PEAR,
ORANGE
};
signals:
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
) :{
/* 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--);
}
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--);
}
To copy to clipboard, switch view to plain text mode
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.
Bookmarks