Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

updating radio buttons programmatically



  • I use a QDataWidgetMapper object in my dialog to access the fields.

        m_mapper->addMapping(ui->ipAddress, TAG_IPV4ADDRESS);
    

    This maps a line edit to a column in my model. But I don't think I can simply do the same thing for a button group.

    EDIT: by the way, I don't have to use radio buttons for this. They seemed like a good choice, but if there's a better method, I'm happy to entertain ideas.



  • @mzimmers said in updating radio buttons programmatically:

    I use a QDataWidgetMapper object in my dialog

    having stated this from the very beginning should have saved time and effort, and allow us to narrow down the issue... :-)

    Given that said, maybe this post may help



  • @Pablo-J.-Rogina said in updating radio buttons programmatically:

    @mzimmers said in updating radio buttons programmatically:

    I use a QDataWidgetMapper object in my dialog

    having stated this from the very beginning should have saved time and effort, and allow us to narrow down the issue... :-)

    Sorry -- I thought I had done so here:
    https://forum.qt.io/topic/93723/updating-radio-buttons-programmatically/4

    In any event, it may be time to consider a different approach.

    SGaist: looking at your response, I have a couple questions:

    1. you mention customizing my address type widget - are you referring to the buttongroup?
    2. What do you mean by "the setter"?

  • Lifetime Qt Champion

    I mean make a custom widget e.g.:

    class IpAddressTypeWidget : public Widget
    {
        Q_OBJECT
        Q_PROPERTY(QString addressType GET addressType SET setAddressType NOTIFY addressTypeChanged USER true)
    
    public:
        explicit IpAddressTypeWidget(QWidget *parent=nullptr);
    
        QString addressType() const;
    
    signals:
        void addressTypeChanged(const QString& type);
    
    public slots:
        void setAddressType(const QString& type);
    };
    

    In setAddressType, you update the radio buttons based on the string given. Note that you can also use an enum and it's the role of the parser to use the correct enum when changing the address type. It would be cleaner but for a first round, it might be easier to use a QString.



  • OK, I think I understand what you're suggesting, but some of the mechanics still aren't clear to me.

    Does this new widget replace any of my existing widgets, or does it just exist (invisibly) within my dialog?

    Do I need to pass it my ui pointer at construction to give it access to the rest of the widgets?


  • Lifetime Qt Champion

    It's an independent widget where you put your QGroupBox which contains the two QRadioButton.



  • I'm still wading through this, but I did discover a necessary change in the Q_PROPERTY macro:

        Q_PROPERTY(QString ipSource READ ipSource WRITE SetIpSource NOTIFY ipSourceChanged USER true)
    

    It wouldn't compile with the GET/SET terms.



  • SGaist: in your example above, where does the signal come from that connects to the slot setAddressType()?


  • Lifetime Qt Champion

    IIRC, you are using a QDataWidgetMapper for that editor ? Correct, then with that definition, when you call addMapping it will use that property to get and set the data from that widget in the same manner as it does for QLineEdit or QSpinBox.



  • OK, I think I get this in principle. From an implementation standpoint, how/when should the edit dialog retrieve this information from the custom widget -- another signal/slot?

    Thanks...


  • Lifetime Qt Champion

    I'm not sure I'm following you here. Isn't that widget already using QDataWidgetMapper ?



  • Yes:

    in editdialog.cpp

    IpSourceWidget m_isw; // IpSourceWidget is my custom widget
    m_mapper->addMapping(&m_isw, TAG_IP_CONFIG_SRC);
    

    But don't I still have to use this to update the group box? Like this:

        if (qs.toStdString() == IP_SOURCE_TXT[IP_SOURCE_DHCP])
        {
            ui->groupBoxIpAssignment->setChecked(IP_SOURCE_DHCP);
        }
        else
        {
            ui->groupBoxIpAssignment->setChecked(IP_SOURCE_STATIC);
        }
    

    EDIT: OK, that wasn't right. I changed it to this:

     QButtonGroup *m_qbg;    // button group for IP source selection.
     ...
     if (qs.toStdString() == IP_SOURCE_TXT[IP_SOURCE_DHCP])
    {
        m_qbg->button(IP_SOURCE_DHCP)->setChecked(true);
    }
    else
    {
        m_qbg->button(IP_SOURCE_STATIC)->setChecked(true);
    }
    

    This at least gets the correct value displaying in the edit dialog. Still not sure I'm really doing this right, though.


  • Lifetime Qt Champion

    Don't forget to emit the corresponding notification signal if the address type has changed.



  • Not sure I understand. The code above is in a slot in my edit dialog. Elsewhere I have a connect:

    IpSourceWidget *m_isw;
    ...
    QObject::connect(m_isw, &IpSourceWidget::ipSourceChanged, this, &EditDialog::updateIpSource);
    

    Is this the signal you're referring to?


  • Lifetime Qt Champion

    I was thinking about where you are emitting ipSourceChanged.



  • Well, maybe that's part of the problem...I wasn't aware I needed to use that signal.

    I'm still feeling my way through this. My IpSource widget doesn't contain any display items; it just handles the model updating. It communicates with the edit dialog through some shared variables:

    IpSourceWidget::IpSourceWidget(QWidget *parent, DeviceModel *d, QModelIndex *qmi) :
        QWidget(parent), m_d(d), m_qmi(qmi)
    {
    }
    
    IpSourceWidget::~IpSourceWidget()
    {
    }
    
    QString IpSourceWidget::ipSource() const
    {
        return QString::fromStdString(IP_SOURCE_TXT[m_ics]);
    }
    
    void IpSourceWidget::setIpSource(const QString &source)
    {
        m_d->getModel()->setData(*m_qmi, source);
    }
    

    I'm getting unexpected results, though. For example, after I press my "commit" button, the argument passed into setIpSource is set to "false." No idea where that's coming from.


  • Lifetime Qt Champion

    Why does IpSourceWidget know about DeviceModel and the corresponding QModelIndex ?

    The idea behind that widget is that you use it the same way as the QLineEdit you have in your editor widget.

    For example in a widget like QLineEdit you have something like:

    void MyWidget::setMyStringProperty(const QString &newValue)
    {
        if (newValue == currentValue) {
            return;
        }
    
        currentValue = newValue;
        // Do stuff if needed
        emit myStringPropertyValueChanged(newValue);
    }
    


  • None of my other widgets require me to do anything other than calls to addMapping(). That's partly why this is so new to me.

    In your example, is currentValue a member variable (or a persistent local), or am I obtaining it from the model? I originally thought the latter, but now I'm thinking I got it wrong.


  • Lifetime Qt Champion

    Most of the time, it's a member variable.

    In the case of your widget, it could be something like:

    void IpSourceWidget::setIpSource(const QString& ipSource)
    {
        if ((ipSource == IP_SOURCE_DHCP && ui->dhcpButton->isChecked()) ||
             ipSource == IP_SOURCE_STATIC && ui->staticButton->isChecked())) {
            return;
        }
    
       if (ipSource == IP_SOURCE_DHCP) {
            ui->dhcpButton->setChecked(true);
       } else {
            ui->staticButton->setChecked(true);
       }
       emit ipSourceChanged(ipSource);
    }
    


  • That all makes sense, but who consumes the signal? You've already updated the ui, so the dialog doesn't need it (that I can see).

    Thanks...

    EDIT: oh, and continuing with this design, I guess the ipSource widget would merely return the member variable?


  • Lifetime Qt Champion

    In this case, QDataWidget mapper will use it. Because you also have to implement the handling of that signal when you click on either of the radio button.

    As for the getter:

    QString IpSourceWidget::ipSource() const
    {
        QString sourceType = "Unkown";
        if (ui->dhcpButton->isChecked() {
            sourceType = IP_SOURCE_DHCP ;
        } else {
            sourceType = IP_SOURCE_STATIC;
        }
        return sourceType;
    }
    


  • OK, this is starting to come together. Now...as I mentioned above, currently none of the display widgets (radio button or group box) reside in my custom widget. Is this a problem, or is there some way for me to access the widgets in the dialog ui (perhaps passing the ui variable in the c'tor)?


  • Lifetime Qt Champion

    The group box and buttons should be moved out of your original editor widget and into IpSourceWidget.



  • That's how I tried it originally, but then they insisted in showing up in separate windows. Can you point me to the right document to read to get them into the edit widget?

    Thanks...


  • Lifetime Qt Champion

    I could have wrote it clearer. Remove them from your original editor and either use designer to create the IpSourceWidget UI or do it by code.



  • OK, this is (yet another) first for me...I assume/hope that it's possible to superimpose my IpSourceWidget onto my EditDialog widget? (If I'm using the write terms)


  • Lifetime Qt Champion

    I think you might be overcomplicating how things are working. IpSourceWidget is just a widget that's slightly more complex than a QLineEdit. In your editor widget, put an empty widget where IpSourceWidget should be and then use the promote feature to use it.

    Or, in the constructor of your editor widget, you can prepend an instance of IpSourceWidget to the layout you are using for your editor widget.



  • OK, I created a new QT designer form class. I'm getting compile errors that suggest my class is incomplete.

    Here's my header file; what might I be missing?

    #include <QGroupBox>
    
    namespace Ui 
    {
        class IpSource;
    }
    
    class IpSource : public QGroupBox
    {
        Q_OBJECT
        Q_PROPERTY(QString ipSource READ ipSource WRITE setIpSource NOTIFY ipSourceChanged USER true)
    
    public:
        explicit IpSource(QWidget *parent = nullptr);
        ~IpSource();
        QString ipSource() const;
    
    signals:
        void ipSourceChanged(const QString &source);
    
    public slots:
        void setIpSource(const QString &source);
    
    private:
        Ui::IpSource *ui;
    };
    

    And the start of my c'tor:

    IpSource::IpSource(QWidget *parent) :
        QGroupBox(parent),
        ui(new Ui::IpSource)
    {
    ...
    

    I get this:
    C:\Users\MZimmers\CD desktop apps\Qt projects\wb_utility\ipsource.cpp:6: error: invalid use of incomplete type 'class Ui::IpSource'
    ui(new Ui::IpSource)
    ^ // caret pointing to IpSource



  • @mzimmers Check that you have

    include "ui_ipsource.h";
    

    in ipsource.cpp file



  • @medyakovvit thanks for looking. Yes, I do have that. Here's the start of my source file:

    #include "ipsource.h"
    #include "ui_ipsource.h"
    
    IpSource::IpSource(QWidget *parent) :
        QGroupBox(parent),
        ui(new Ui::IpSource)
    {
        ui->setupUi(this);
    }
    


  • @mzimmers Check that "ui_ipsource.h" contains what you expect.



  • Ah...that revealed the problem. In my fumbling around, I'd renamed the class in Designer. I just changed it back, and it seems to be good now. Thanks...now back to my struggles with those radio buttons.



  • I've added a Widget to my edit dialog, and promoted it to class IpSource. It displays correctly, but I can't access it programmatically, at least not with the "ui->" pointer I'm accustomed to using.

    Did I miss a step? I've never used promotions before.
    0_1534868870450_ipsource.PNG

    EDIT: this question is irrelevant to the thread, so I'm going to ask it in a separate thread.



  • OK, I believe I now have this beaten into submission. Here's my understanding of the necessary ingredients:

    1. upon determining the need for an internal (programmatic) change to the status of the radio buttons, the model needs to be set or updated appropriately:
        // the IP configuration source.
        QString qs = QString::fromStdString(msg->getValue(msgTag[TAG_IP_CONFIG_SRC]));
        QVariant qv = m_model->data(m_model->index(row, TAG_IP_CONFIG_SRC));
        if (qs != qv)
        {
            m_model->setData(m_model->index(row, TAG_IP_CONFIG_SRC), qs);
            emit ipSourceChanged(qs);
        }
    
    1. a custom designer class, based on QGroupBox, is necessary:
    class IpSource : public QGroupBox
    {
        Q_OBJECT
        Q_PROPERTY(QString ipSource READ ipSource WRITE setIpSource NOTIFY ipSourceChanged USER true)
    
    public:
        explicit IpSource(QWidget *parent = nullptr);
        ~IpSource();
        QString ipSource() const;
    
    signals:
        void ipSourceChanged(const QString &source);
    
    public slots:
        void setIpSource(const QString &source);
    
    private:
        Ui::IpSource *ui;
    
    1. from your parent dialog (if that's the right term), create an empty widget, and promote it to your custom widget.
    2. the syntax for retrieving the value of the radio button group was a little tricky at first, but it goes like this:
            string s = ui->ipSource->ipSource().toStdString();
    

    (you can ignore the StdString part if you're just using QStrings; I have to share my constants header file with a firmware app.

    If anyone feels that the above needs correction/improvement, please let me know.

    Thanks to everyone who helped on this.


Log in to reply