I wanted to be able to represent an exclusive set of options as several radio buttons. I also wanted to be able to store an integer Id corresponding to which option was selected in my database, and ideally, use QDataWidgetMapper to do it. Yes, a combo box could serve here, but for this particular UI, I would prefer option buttons (in part because there are other widgets that become enabled/disabled as a result of the selected option, and they are laid out in a way that helps make the relationship apparent.)
I came across QButtonGroup and thought that might solve my problem, but QButtonGroup is not a QWidget, which is needed by QDataWidgetMapper. I also came acroos QGroupBox which is a QWidget, but it doesn’t have a property to get at the currently selected option (and why should it – a group box could have all sorts of different sets of child items.)
I thought this would be a fairly common problem, so I wanted to present my solution so that:
Those of you who have tackled this problem before can chime in on the solution
Those of you who are looking for a solution have a starting place
I wanted to use a widget as a starting place, and then add to the interface:
A method for assigning an integer Id to each option button
A property for getting/setting the current Id / selected option button
The currently selected propery needs to be a property in order to work with QDataWidgetMapper. The best widget to use as a starting place seemed to be…well…QWidget!
Here is my OptionGroup class:
optiongroup.h
#ifndef OPTIONGROUP_H
#define OPTIONGROUP_H
#include <QWidget>
#include <QMap>
class QRadioButton;
class OptionGroup : public QWidget
{
Q_OBJECT
Q_PROPERTY(int currentSelection READ currentSelection WRITE setCurrentSelection USER true)
public:
explicit OptionGroup(QWidget *parent = 0);
int currentSelection() const;
void setCurrentSelection(int selection);
void setSelectionId(QRadioButton *button, int id);
signals:
void selectionChanged(int selection);
public slots:
void buttonToggled(bool checked);
private:
int currentSelection_;
QMap<int, QRadioButton*> buttonMap_;
QMap<QRadioButton*, int> revButtonMap_;
};
#endif // OPTIONGROUP_H
optiongroup.cpp:
#include <QRadioButton>
#include "optiongroup.h"
OptionGroup::OptionGroup(QWidget *parent) :
QWidget(parent), currentSelection_(-1)
{
}
int OptionGroup::currentSelection() const
{ return currentSelection_; }
void OptionGroup::setCurrentSelection(int selection)
{
// If the specified selection id is not in our button map,
// then it is invalid, set selection to -1. Otherwise,
// update the selection to user specified value
auto iter = buttonMap_.find(selection);
if (iter == buttonMap_.end() || selection < 0) {
currentSelection_ = -1;
for (iter = buttonMap_.begin(); iter != buttonMap_.end(); ++iter)
iter.value()->setChecked(false);
} else {
iter.value()->setChecked(true);
currentSelection_ = selection;
}
}
void OptionGroup::setSelectionId(QRadioButton* button, int id)
{
// Make sure we got a valid Id (non-negative)
// Also then listen for signals from this button
if (id >= 0) {
buttonMap_[id] = button;
revButtonMap_[button] = id;
connect(button, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));
}
}
void OptionGroup::buttonToggled(bool checked)
{
if (checked == true) {
QRadioButton* btn = qobject_cast<QRadioButton*>(sender());
Q_ASSERT(btn);
currentSelection_ = revButtonMap_[btn];
emit selectionChanged(currentSelection_);
}
}
Note that you’ll need to call setSelectionId() to assign an Id to each QRadioButton. -1 is used to represent an invalid or unset Id, so be sure to pass something that is greater than or equal to 0.
If you had:
ui->optBtnA which corresponds with Id 0
ui->optBtnB which corresponds with Id 1
ui->optBtnC which corresponds with Id 2
And they were all children of an OptionGroup ui->optionGroup, you would do this:
ui->optionGroup.setSelectionId(ui->optBtnA, 0);
ui->optionGroup.setSelectionId(ui->optBtnB, 1);
ui->optionGroup.setSelectionId(ui->optBtnC, 2);
...
// QDataWidgetMapper mapper;
// int optionField; This is which field in your data model you are storing the option value in
mapper.addMapping(ui->optionGroup, optionField);
This seems to be working well enough. Having the two maps for looking up Ids/QRadioButton* is redundant. Since the list of options is always likely to be pretty small, so I might use a QList of QPair’s and just sequentially search them.
Hit me with your critiques, and hope this helps somebody else out.
↧