QRadioButton group and QDataWidgetMapper



  • 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.



  • Looks really useful for the right situation... thanks for taking the trouble to post it :-)



  • Code translated in Pyside :

    @
    from PySide.QtCore import *
    from PySide.QtGui import *

    class optionGroup(QWidget):

    def init(self, parent):
    QWidget.init (self, parent)
    self.ListeBoutons = []
    self.currentSelection_ = -1

    def litSelection (self):
    return self.currentSelection_

    def setCurrentSelection (self, selection):
    self.currentSelection_ = -1
    for elem in self.ListeBoutons:
    if elem[1] == selection:
    elem[0].setChecked(True)
    self.currentSelection_ = selection

    def setSelectionId (self, Bouton, ident):
    self.ListeBoutons.append([Bouton, ident])
    Bouton.toggled[bool].connect(self.buttonToggled)

    def buttonToggled (self, checked):
    if checked:
    Bouton = self.sender()
    for elem in self.ListeBoutons:
    if elem[0] == Bouton:
    self.currentSelection_ = elem[1]
    self.selectionChanged.emit(self.currentSelection_)

    currentSelection = Property(int, litSelection, setCurrentSelection, user = True)
    selectionChanged = Signal(int)
    @



  • Hi jmelbye

    can u please elaborate how to provide Id for each and every radiobutton and then store in mdoel



  • Hi jmelbye

    can u please elaborate how to provide Id for each and every radiobutton and then store in mdoel



  • and also i get this error while exec
    error: 'QMap<Key, T>::iterator::operator bool() const [with Key = int, T = QRadioButton*]' is private



  • and also i get this error while exec
    error: 'QMap<Key, T>::iterator::operator bool() const [with Key = int, T = QRadioButton*]' is private


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.