Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QCheckBox and QDataWidgetMapper
Forum Updated to NodeBB v4.3 + New Features

QCheckBox and QDataWidgetMapper

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 2 Posters 836 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    pip010
    wrote on last edited by pip010
    #1

    I am trying to drive a group of QCheckBox using model driven approach using QDataWidgetMapper. However I encounter several issues, for starters the data method never get called:

    QVariant CheckboxModel::data(const QModelIndex& index, int role) const

    therefore I am unable to set the enabled/disabled state of the checkbox

    Furthermore, in the method:

    bool CheckboxModel::setData(const QModelIndex& index, const QVariant& value, int role)

    when I do check for the state of the checkbox like :

    if (role == Qt::EditRole || role == Qt::CheckStateRole)
    {
    	if (value.isValid() && !value.isNull())
    	{
    	
    		Qt::CheckState cb_qt_state = static_cast<Qt::CheckState>(value.toUInt());
    			
    		
    		if (Qt::CheckState::Checked == cb_qt_state)
    		{
    			m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::On);
    		}
    
    		if (Qt::CheckState::Unchecked == cb_qt_state)
    		{
    			m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::Off);
    		}
    
    		if (Qt::CheckState::PartiallyChecked == cb_qt_state)
    		{
    			m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::On);
    		}
    	}
    }
    

    it is always in state Qt::CheckState::PartiallyChecked when it should be fully checked, if looking at the GUI.

    Any suggestions on how to achieve the desired behavior using QDataWidgetMapper or some example of stand alone checkboxes not part of table/list views?

    eyllanescE 1 Reply Last reply
    0
    • P pip010

      I am trying to drive a group of QCheckBox using model driven approach using QDataWidgetMapper. However I encounter several issues, for starters the data method never get called:

      QVariant CheckboxModel::data(const QModelIndex& index, int role) const

      therefore I am unable to set the enabled/disabled state of the checkbox

      Furthermore, in the method:

      bool CheckboxModel::setData(const QModelIndex& index, const QVariant& value, int role)

      when I do check for the state of the checkbox like :

      if (role == Qt::EditRole || role == Qt::CheckStateRole)
      {
      	if (value.isValid() && !value.isNull())
      	{
      	
      		Qt::CheckState cb_qt_state = static_cast<Qt::CheckState>(value.toUInt());
      			
      		
      		if (Qt::CheckState::Checked == cb_qt_state)
      		{
      			m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::On);
      		}
      
      		if (Qt::CheckState::Unchecked == cb_qt_state)
      		{
      			m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::Off);
      		}
      
      		if (Qt::CheckState::PartiallyChecked == cb_qt_state)
      		{
      			m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::On);
      		}
      	}
      }
      

      it is always in state Qt::CheckState::PartiallyChecked when it should be fully checked, if looking at the GUI.

      Any suggestions on how to achieve the desired behavior using QDataWidgetMapper or some example of stand alone checkboxes not part of table/list views?

      eyllanescE Offline
      eyllanescE Offline
      eyllanesc
      wrote on last edited by
      #2

      @pip010 By default the QDataWidgetMapper uses the widget's userProperty, and in the case of the QCheckBox it is the "checked" property that returns a boolean causing conflict with Qt::CheckState. The solution is to create a QProperty based on Qt::CheckState.

      #include <QApplication>
      #include <QCheckBox>
      #include <QDataWidgetMapper>
      #include <QGridLayout>
      #include <QStandardItemModel>
      #include <QTableView>
      
      
      class CheckBox: public QCheckBox{
          Q_OBJECT
          Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged)
      public:
          CheckBox(QWidget *parent=nullptr): QCheckBox(parent){
              connect(this, &QCheckBox::stateChanged, this, &CheckBox::checkStateChanged);
          }
      Q_SIGNALS:
          void checkStateChanged();
      };
      
      class StandardItemModel: public QStandardItemModel{
      public:
          using QStandardItemModel::QStandardItemModel;
          QVariant data(const QModelIndex &index, int role) const{
              return QStandardItemModel::data(index,
                                              role == Qt::EditRole ? Qt::CheckStateRole: role);
          }
          bool setData(const QModelIndex &index, const QVariant &value, int role){
              return QStandardItemModel::setData(index,
                                                 value,
                                                 role == Qt::EditRole ? Qt::CheckStateRole: role);
          }
      };
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
      
          StandardItemModel model(0, 5);
      
          for(int row =0; row < 6; row++){
              for(int column = 0; column < model.columnCount(); column++){
                  QStandardItem *item = new QStandardItem;
                  item->setCheckable(true);
                  item->setCheckState(((row + column) % 2 == 0) ? Qt::Checked: Qt::Unchecked);
                  item->setUserTristate(true);
                  model.setItem(row, column, item);
              }
          }
          QTableView *view = new QTableView;
          view->setSelectionBehavior(QAbstractItemView::SelectRows);
          view->setSelectionMode(QAbstractItemView::SingleSelection);
          view->setModel(&model);
      
          QDataWidgetMapper mapper;
          mapper.setModel(&model);
      
          QObject::connect(view->selectionModel(),
                           &QItemSelectionModel::currentChanged,
                           &mapper,[&mapper](const QModelIndex & current, const QModelIndex &){
              mapper.setCurrentIndex(current.row());
          });
      
          QWidget widget;
          QGridLayout *lay = new QGridLayout(&widget);
          lay->addWidget(view, 0, 0, 1, model.columnCount());
          for(int i=0; i < model.columnCount(); i++){
              CheckBox *checkbox = new CheckBox;
              checkbox->setTristate(true);
              lay->addWidget(checkbox, 1, i);
              mapper.addMapping(checkbox, i, "checkState");
              QObject::connect(checkbox, &CheckBox::stateChanged, &mapper, &QDataWidgetMapper::submit);
          }
          widget.show();
      
          return a.exec();
      }
      
      #include "main.moc"
      

      If you want me to help you develop some work then you can write to my email: e.yllanescucho@gmal.com.

      1 Reply Last reply
      3
      • P Offline
        P Offline
        pip010
        wrote on last edited by
        #3

        Thanks @eyllanesc , I think understand what you mean by 'QDataWidgetMapper uses the widget's userProperty' but I am super surprised that the default QCheckBox is not working out of the box. Creating a custom checkbox will drive working through the designer (GUI) impossible and I really appreciate it.

        However, it is still unclear how I am supposed to drive the enabled/disabled state for each checkbox. Btw, I am not using any view (QTableView) and using QAbstractTableModel for the model, treating each column data for the state of a checkbox.

        class CheckboxModel : public QAbstractTableModel
        {
        	Q_OBJECT
        	nn::ICheckBoxStates* m_cbStates;
        public:
        	using Columns = nn::ISurfacesCollectionGUI::Columns;
        public:
        	CheckboxModel(QObject* parent = 0);
        	void Bind(nn::ICheckBoxStates&);
        	Qt::ItemFlags flags(const QModelIndex& index) const override;
        	//QModelIndex index(int, int, const QModelIndex&) const override;
        	//QModelIndex parent(const QModelIndex&) const override;
        	int rowCount(const QModelIndex&) const override;
        	int columnCount(const QModelIndex&) const override;
        	QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
        	//QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
        	bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
        
        };
        

        but again, my problem is that neither the data method get called when the GUI shows nor the flags method which should determine the enabled/disabled state of the checkbox. So is there a way to drive this through the model and not resort to code hacks?

        eyllanescE 1 Reply Last reply
        0
        • P Offline
          P Offline
          pip010
          wrote on last edited by
          #4

          here is the cpp file:

          CheckboxModel::CheckboxModel( QObject* parent)
          	:
          	QAbstractTableModel(parent),
          	m_cbStates(nullptr)
          {
          }
          
          void CheckboxModel::Bind(nn::ICheckBoxStates& p_cbStates)
          {
          	beginInsertRows(QModelIndex(), 0, 1);
          	m_cbStates = &p_cbStates;
          	endInsertRows();
          	//emit dataChanged(createIndex(1,0), createIndex(1, static_cast<int>(nn::CheckboxID::COUNT)));
          
          	m_cbStates->Connect< CheckBoxStatesEvents::StateChanged >([this](CheckboxID p_id, CheckboxState p_val)
          	{
          		//auto idx1 = createIndex(0, 0);
          		auto idx = createIndex(0, static_cast<int>(p_id));
          		emit dataChanged(idx, idx);
          	});
          	
          }
          
          Qt::ItemFlags CheckboxModel::flags(const QModelIndex& index) const
          {
          	if (!index.isValid())
          		return Qt::ItemIsEnabled;
          
          	return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
          }
          
          //QModelIndex CheckboxModel::index(int r, int c, const QModelIndex&) const
          //{
          //	return createIndex(r, c);
          //}
          //
          //QModelIndex CheckboxModel::parent(const QModelIndex& p) const
          //{
          //	return p;
          //}
          
          int CheckboxModel::rowCount(const QModelIndex&) const
          {
          	return 1;
          }
          
          int CheckboxModel::columnCount(const QModelIndex&) const
          {
          	return static_cast<int>(nn::CheckboxID::COUNT);
          }
          
          QVariant CheckboxModel::data(const QModelIndex& index, int role) const
          {
          	if (index.column() < static_cast<size_t>(CheckboxID::COUNT))
          	{
          		if (role == Qt::DisplayRole || role == Qt::EditRole)
          		{
          			CheckboxState val = m_cbStates->GetState(static_cast<CheckboxID>(index.row()));
          			
          			return val == CheckboxState::On ? QVariant::fromValue(1) : QVariant::fromValue(0);
          		}
          	}
          
          	return QVariant();
          }
          
          bool CheckboxModel::setData(const QModelIndex& index, const QVariant& value, int role)
          {
          	if (role == Qt::EditRole || role == Qt::CheckStateRole)
          	{
          		if (value.isValid() && !value.isNull())
          		{
          			auto vvv = value.toInt();
          			
          			Qt::CheckState cb_qt_state = static_cast<Qt::CheckState>(value.toUInt());
          				
          			
          			if (Qt::CheckState::Checked == cb_qt_state)
          			{
          				m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::On);
          			}
          
          			if (Qt::CheckState::Unchecked == cb_qt_state)
          			{
          				m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::Off);
          			}
          
          			if (Qt::CheckState::PartiallyChecked == cb_qt_state)
          			{
          				m_cbStates->SetState(static_cast<CheckboxID>(index.column()), CheckboxState::On);
          			}
          		}
          	}
          
          	return true;
          }
          
          1 Reply Last reply
          0
          • P pip010

            Thanks @eyllanesc , I think understand what you mean by 'QDataWidgetMapper uses the widget's userProperty' but I am super surprised that the default QCheckBox is not working out of the box. Creating a custom checkbox will drive working through the designer (GUI) impossible and I really appreciate it.

            However, it is still unclear how I am supposed to drive the enabled/disabled state for each checkbox. Btw, I am not using any view (QTableView) and using QAbstractTableModel for the model, treating each column data for the state of a checkbox.

            class CheckboxModel : public QAbstractTableModel
            {
            	Q_OBJECT
            	nn::ICheckBoxStates* m_cbStates;
            public:
            	using Columns = nn::ISurfacesCollectionGUI::Columns;
            public:
            	CheckboxModel(QObject* parent = 0);
            	void Bind(nn::ICheckBoxStates&);
            	Qt::ItemFlags flags(const QModelIndex& index) const override;
            	//QModelIndex index(int, int, const QModelIndex&) const override;
            	//QModelIndex parent(const QModelIndex&) const override;
            	int rowCount(const QModelIndex&) const override;
            	int columnCount(const QModelIndex&) const override;
            	QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
            	//QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
            	bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
            
            };
            

            but again, my problem is that neither the data method get called when the GUI shows nor the flags method which should determine the enabled/disabled state of the checkbox. So is there a way to drive this through the model and not resort to code hacks?

            eyllanescE Offline
            eyllanescE Offline
            eyllanesc
            wrote on last edited by
            #5

            @pip010 1) in my demo I use the QTableView only to visualize the change, 2) The QCheckbox by default handles the property checked (true, false) but not checkState. so if you want to use my custom QCheckbox then create a new file (.h and .cpp) and copy my code there, and then promote it: https://doc.qt.io/qt-5/designer-using-custom-widgets.html

            If you want me to help you develop some work then you can write to my email: e.yllanescucho@gmal.com.

            1 Reply Last reply
            1
            • P Offline
              P Offline
              pip010
              wrote on last edited by
              #6

              @eyllanesc thanks for the tip, will look into it. How about the more general question about driving the checkboxes from the model alone? Why is my data and flags methods never called?

              eyllanescE 1 Reply Last reply
              0
              • P pip010

                @eyllanesc thanks for the tip, will look into it. How about the more general question about driving the checkboxes from the model alone? Why is my data and flags methods never called?

                eyllanescE Offline
                eyllanescE Offline
                eyllanesc
                wrote on last edited by
                #7

                @pip010 I don't know, if you upload a demo (keep it as small as possible) in a GitHub repo then I can test it.

                If you want me to help you develop some work then you can write to my email: e.yllanescucho@gmal.com.

                1 Reply Last reply
                0
                • P Offline
                  P Offline
                  pip010
                  wrote on last edited by
                  #8

                  OK, as I found out you CANNOT control the state of checkbox via model. Quite unfortunate. On the previous point about how to handle the 3state of QCheckBox (why a 3state to begin with) the solution seems a complete overkill, again every unfortunate that QDataWidgetMapper does not support it by default. Unlike QCombobox, similar issue, where one can opt for banding different property:
                  dataWidgetMapper->addMapping(combo, columnInTheModel, "currentIndex");
                  such constructs seems not possible with QCheckbox.

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved