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. many troubles with a simple Delegate
Forum Updated to NodeBB v4.3 + New Features

many troubles with a simple Delegate

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 3 Posters 1.7k Views 1 Watching
  • 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.
  • W Offline
    W Offline
    wasawi
    wrote on last edited by
    #1

    Hi all,

    I have been working on a simple MVC example to fit my needs but I'm facing many troubles at many levels and I just started with a very simple case. I have been reading the documentation and many posts here but it seems that I still miss a lot of understanding. I'm still a newbie so please bear with me.

    My model is a string and integer pair where the integer may have different ranges. The delegate is applied to the integer column only. I want a QSlider and a QSpinBox to change the ints.
    To go straight to the point, let me post my simple code and list all the troubles I'm facing.

    QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
        const QStyleOptionViewItem & option,
        const QModelIndex & index ) const
    {
    
    	QWidget* editor = new QWidget(parent);
    
    	QSlider *slider = new QSlider(editor);
    	slider->setOrientation(Qt::Orientation::Horizontal);
    	slider->setMinimumHeight(20);
    	slider->setMinimumWidth(100);
    	// Todo: 
    	// find a way to get properties from QModelIndex
    	// so that we can set dynamic ranges...		
    	slider->setMinimum(0);
    	slider->setMaximum(100);
    
    
    	QSpinBox *spinbox = new QSpinBox(editor);
    	spinbox->setMinimumWidth(20);
    	spinbox->setFrame(false);
    	spinbox->setMinimum(0);
    	spinbox->setMaximum(100);
    	spinbox->selectAll(); //<- not working!
    
    	connect(slider, SIGNAL(valueChanged(int)), spinbox, SLOT(setValue(int)));
    	connect(spinbox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
    	connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onSpinboxValueChanged(int)));
    
    	QHBoxLayout *layout = new QHBoxLayout;
    	layout->addWidget(slider);
    	layout->addWidget(spinbox);
    	editor->setLayout(layout);
    
    	return editor;
    }
    
    void SpinBoxDelegate::setEditorData(QWidget *editor,
                                        const QModelIndex &index) const
    {
        int value = index.model()->data(index, Qt::EditRole).toInt();
    
    	for (auto widget : editor->findChildren<QSlider*>()) {
    		widget->setValue(value);
    	}
    	for (auto widget : editor->findChildren<QSpinBox*>()) {
    		widget->setValue(value);
    	}
    }
    
    void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                       const QModelIndex &index) const
    {
    	int value = index.model()->data(index, Qt::EditRole).toInt();
    	for (auto widget : editor->findChildren<QSlider*>()) {
    		value = widget->value();
    	}
    	for (auto widget : editor->findChildren<QSpinBox*>()) {
    		value = widget->value();
    	}
    	model->setData(index, value, Qt::EditRole);
    }
    

    This is how it looks like:
    0_1526352644192_2018-05-15 11_18_16-Main Qt Window.png

    Problems:
    1- In the image you can immediately see the widget is drawn on top of the table cell... That's not good at all.
    2- The QSpinbox is not selected automatically on creation. Tried several codes, non worked.
    3- I don't know how to change the range(min/max) of the Qslider depending on QModelIndex ranges. If anyone could please point to a doc/example explaining how to do that.
    4- the widget adds some margin. Tried to editor->setContentsMargins(QMargins(0, 0, 0, 0)), no luck.
    5- do not know how to resize table cell to widget size. I don't even know if that code must be on the delegate class. Otherwise where?

    Thank you for any hints.
    Jordi

    1 Reply Last reply
    0
    • A Offline
      A Offline
      ambershark
      wrote on last edited by ambershark
      #2

      I modified the SpinBoxDelegate example from Qt to tackle some of these issues for you. Here is the pic:

      0_1526361395815_Screenshot from 2018-05-14 22-14-25.png

      Here's the fixes --

      When I create the editor widget, I set the auto fill background. This will fix the fact that you can see the cell through your editor in your example.

          auto *widget = new QWidget(parent);
          widget->setAutoFillBackground(true);
      

      To fix the margin and position the widget properly I set the margin to 0 on the layout like so:

          auto *layout = new QHBoxLayout();
          layout->addWidget(slider);
          layout->addWidget(spinbox);
          layout->setMargin(0);
          widget->setLayout(layout);
      

      That solves #1 and #4 for you.

      #5 happens automatically for me and if the table widget window isn't big enough it gives me a horizontal scrollbar which is how it should work. What are you looking for beyond that?

      #3 I don't quite understand what you want here... If you explain it further me or someone else can tell you how to get the info you want from the model.

      #2 I haven't tackled this yet. My initial attempt in setting focus didn't work. I will play with it some more if I have time later on. Maybe someone else has a quick tip here.

      Edit: forgot to attach full code... here is the modified delegate.cpp file from the example:

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the examples of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** Commercial License Usage
      ** Licensees holding valid commercial Qt licenses may use this file in
      ** accordance with the commercial license agreement provided with the
      ** Software or, alternatively, in accordance with the terms contained in
      ** a written agreement between you and The Qt Company. For licensing terms
      ** and conditions see https://www.qt.io/terms-conditions. For further
      ** information use the contact form at https://www.qt.io/contact-us.
      **
      ** BSD License Usage
      ** Alternatively, you may use this file under the terms of the BSD license
      ** as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/
      
      /*
          delegate.cpp
      
          A delegate that allows the user to change integer values from the model
          using a spin box widget.
      */
      
      #include "delegate.h"
      
      #include <QSpinBox>
      #include <QHBoxLayout>
      
      //! [0]
      SpinBoxDelegate::SpinBoxDelegate(QObject *parent)
          : QStyledItemDelegate(parent)
      {
      }
      //! [0]
      
      //! [1]
      QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
          const QStyleOptionViewItem &/* option */,
          const QModelIndex &/* index */) const
      {
          /*QSpinBox *editor = new QSpinBox(parent);
          editor->setFrame(false);
          editor->setMinimum(0);
          editor->setMaximum(100);
      
          return editor;*/
          auto *widget = new QWidget(parent);
          widget->setAutoFillBackground(true);
          
          auto *slider = new QSlider(widget);
          slider->setOrientation(Qt::Orientation::Horizontal);
          slider->setMinimumHeight(20);
          slider->setMinimumWidth(100);
          slider->setMinimum(0);
          slider->setMaximum(100);
          
          auto *spinbox = new QSpinBox(widget);
          spinbox->setMinimumWidth(20);
          spinbox->setFrame(false);
          spinbox->setMinimum(0);
          spinbox->setMaximum(100);
          
          connect(slider, SIGNAL(valueChanged(int)), spinbox, SLOT(setValue(int)));
          connect(spinbox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
          
          auto *layout = new QHBoxLayout();
          layout->addWidget(slider);
          layout->addWidget(spinbox);
          layout->setMargin(0);
          widget->setLayout(layout);
          
          return widget;
      }
      //! [1]
      
      //! [2]
      void SpinBoxDelegate::setEditorData(QWidget *editor,
                                          const QModelIndex &index) const
      {
          int value = index.model()->data(index, Qt::EditRole).toInt();
      
          auto *spinBox = static_cast<QSpinBox *>(editor->layout()->itemAt(1)->widget());
          //QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
          spinBox->setValue(value);
      }
      //! [2]
      
      //! [3]
      void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                         const QModelIndex &index) const
      {
          auto *spinBox = static_cast<QSpinBox *>(editor->layout()->itemAt(1)->widget());
          //QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
          spinBox->interpretText();
          int value = spinBox->value();
      
          model->setData(index, value, Qt::EditRole);
      }
      //! [3]
      
      //! [4]
      void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
          const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
      {
          editor->setGeometry(option.rect);
      }
      //! [4]
      

      My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

      1 Reply Last reply
      4
      • A Offline
        A Offline
        ambershark
        wrote on last edited by
        #3

        Solved #2 with a quick focus event check. Here is the custom widget code and the changes to the delegate:

        MyWidget.h

        #pragma once
        #include <QWidget>
        
        class MyWidget : public QWidget
        {
            Q_OBJECT
        
        public:
            MyWidget(QWidget *parent = nullptr);
        
        protected:
            void focusInEvent(QFocusEvent *event);
        };
        

        MyWidget.cpp:

        #include <QSpinBox>
        #include "MyWidget.h"
        #include <QLayout>
        
        MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
        {
        
        }
        
        void MyWidget::focusInEvent(QFocusEvent *event)
        {
            auto *spinbox = static_cast<QSpinBox *>(layout()->itemAt(1)->widget());
            Q_ASSERT(spinbox);
            spinbox->setFocus();
            spinbox->selectAll();
            QWidget::focusInEvent(event);
        }
        

        And then in the delegate.cpp just change new Widget to new MyWidget like so:

        auto *widget = new MyWidget(parent);
        

        So that just leaves #3 which I don't understand what you're after there..

        My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

        1 Reply Last reply
        3
        • W Offline
          W Offline
          wasawi
          wrote on last edited by wasawi
          #4

          Thank you so much.
          This answer reduces the problems to #2 and #3.
          The #2 is a "nice to have" kind of issue, but it is definitely important in the long run.

          For #3 what I mean is that each row of the table may refer to values of different ranges. For example, 0 to 100, 0 to 360, or -1 to 1, etc... So this is basically an general MVC programming question. How to specialize each slider depending on the properties specific to a certain table index? How to insert those properties in the model and how to read them from the delegate so that i can set them to the corresponding QSlider.

          Thank you so much for your help.
          Jordi

          EDIT:
          Well, #2 is fixed now. Thanks!

          A 1 Reply Last reply
          0
          • W wasawi

            Thank you so much.
            This answer reduces the problems to #2 and #3.
            The #2 is a "nice to have" kind of issue, but it is definitely important in the long run.

            For #3 what I mean is that each row of the table may refer to values of different ranges. For example, 0 to 100, 0 to 360, or -1 to 1, etc... So this is basically an general MVC programming question. How to specialize each slider depending on the properties specific to a certain table index? How to insert those properties in the model and how to read them from the delegate so that i can set them to the corresponding QSlider.

            Thank you so much for your help.
            Jordi

            EDIT:
            Well, #2 is fixed now. Thanks!

            A Offline
            A Offline
            ambershark
            wrote on last edited by
            #5

            @wasawi One way to do this is to call setData() on your model with the information you want to get later, i.e. the range. You can put anything in the data for the row and retrieve it with data() later on.

            So you could save a struct/class/QList or a tuple or something with the range you want. Pull that out when you create the editor and set your range. The createEditor function passed the QModelIndex so you can easily grab the data from that.

            My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

            1 Reply Last reply
            1
            • VRoninV Offline
              VRoninV Offline
              VRonin
              wrote on last edited by VRonin
              #6

              #3 is very easy to solve, in setEditorData you already set the value of the widgets so should be trivial to set the range based on the data coming from index

              You probably also want to reimplement updateEditorGeometry so that you can handle resizing while the editor is open

              "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
              ~Napoleon Bonaparte

              On a crusade to banish setIndexWidget() from the holy land of Qt

              1 Reply Last reply
              2
              • W Offline
                W Offline
                wasawi
                wrote on last edited by
                #7

                @ambershark and @VRonin Thank you for your answers.

                I didn't know quite well how to extend my model to get other data than the index data itself. Not sure if what i did is the best solution. Please let me know. This is what I did.

                added a custom role

                enum MyRoles {
                	maxRange = Qt::UserRole
                };
                

                in model::data() added a case for that role

                if (role == MyRoles::maxRange) {
                	return myData.getMax(index.row());
                }
                

                added a new funtion in myData class to provide maxRange

                QVariant getMax(int row) const {
                	auto max = paramGroup[row].getMax();
                	return QVariant(max);
                }
                

                And finally in my delegate i set the max range in both createEditor() and setEditorData()

                int maxRange = index.model()->data(index, MyRoles::maxRange).toInt();
                slider->setMaximum(maxRange);	
                spinbox->setMaximum(maxRange);
                

                Any improvements are welcome.

                Since everything now already works I will mark it as Solved.
                Thank you for your patience!
                J

                A 1 Reply Last reply
                1
                • W wasawi

                  @ambershark and @VRonin Thank you for your answers.

                  I didn't know quite well how to extend my model to get other data than the index data itself. Not sure if what i did is the best solution. Please let me know. This is what I did.

                  added a custom role

                  enum MyRoles {
                  	maxRange = Qt::UserRole
                  };
                  

                  in model::data() added a case for that role

                  if (role == MyRoles::maxRange) {
                  	return myData.getMax(index.row());
                  }
                  

                  added a new funtion in myData class to provide maxRange

                  QVariant getMax(int row) const {
                  	auto max = paramGroup[row].getMax();
                  	return QVariant(max);
                  }
                  

                  And finally in my delegate i set the max range in both createEditor() and setEditorData()

                  int maxRange = index.model()->data(index, MyRoles::maxRange).toInt();
                  slider->setMaximum(maxRange);	
                  spinbox->setMaximum(maxRange);
                  

                  Any improvements are welcome.

                  Since everything now already works I will mark it as Solved.
                  Thank you for your patience!
                  J

                  A Offline
                  A Offline
                  ambershark
                  wrote on last edited by
                  #8

                  @wasawi More work than was needed but by doing it that way it helps you learn a lot about the nature of data() and UserRole.

                  You could have just done:

                  model->setData(index, QStringList() << "10" << "100");
                  
                  // and then later to pull it out
                  
                  auto range = model->data(index).toStringList();
                  slider->setMinimum(range.at(0).toInt());
                  slider->setMaximum(range.at(1).toInt());
                  

                  Code may not be 100% right since I did it from my head with no auto complete or docs, but it can be that easy for your simple use case. Your way is the correct way if you want more complex user data in your model. It allows you to have multiple UserRole/UserRole+1,+2, etc. It allows data to be easily found based off the enum and returned, etc.

                  Bottom line, great job. :)

                  My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                  1 Reply Last reply
                  1

                  • Login

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