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. [SOLVED] How to do a custom range for a Spinbox
Forum Updated to NodeBB v4.3 + New Features

[SOLVED] How to do a custom range for a Spinbox

Scheduled Pinned Locked Moved General and Desktop
20 Posts 2 Posters 6.1k 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.
  • A Offline
    A Offline
    astodolski
    wrote on last edited by
    #1

    Is it possible to create a custom range for a spinbox? Additionally, that range could be changed at run time. The range would have a initial value (design time) of -20 to 25. Those values are for a spinner that determines zoom ratio for a loaded image. The reason for the custom range is that I cannot have zero in the range of values - either the initial range or any calculated range thereafter. If re-implementing the class, how then would you keep the spinner from selecting zero in the range from minimum to maximum? It would have to, for instance go from -1 to 1 or vice-versa.

    If created a custom spinner class and in designer promoted the spinner to use it, using the validate method doesn't help in "skipping" zero when clicking up/down within the range unless I'm missing something.

    Thanks in advance

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

      Hi astodolski,

      You can simply reimplement the setValue slot, to have to obtain the correct behavior.

      You could try something like this (not tested)
      @
      void MySpinBox::setValue(int newValue)
      {
      if (newValue == 0)
      newValue = (value() == 1) ? -1 : 1;

      QSpinBox::setValue(newValue);
      }
      @

      You would better reuse the existing range/value than creating your own, to leverage existing logic. And this may be one way of achieving this.

      Here we simply transform the target value if it was zero, but use the base class implementation to handle range/value/etc.

      Regards

      Adeneo Embedded - www.adeneo-embedded.com

      1 Reply Last reply
      0
      • A Offline
        A Offline
        astodolski
        wrote on last edited by
        #3

        [quote author="Adrien Leravat" date="1377088782"]Hi astodolski,

        You can simply reimplement the setValue slot, to have to obtain the correct behavior.

        You could try something like this (not tested)
        @
        void MySpinBox::setValue(int newValue)
        {
        if (newValue == 0)
        newValue = (value() == 1) ? -1 : 1;

        QSpinBox::setValue(newValue);
        }
        @

        You would better reuse the existing range/value than creating your own, to leverage existing logic. And this may be one way of achieving this.

        Here we simply transform the target value if it was zero, but use the base class implementation to handle range/value/etc.

        Regards[/quote]

        Thanks for the post. Trying to see why the slot never gets a hit:

        Header
        @
        #ifndef SCALESPINBOX_H
        #define SCALESPINBOX_H

        #include <QSpinBox>
        #include <QKeyEvent>
        #include <QRegExpValidator>

        class QRegExpValidator;

        class ScaleSpinBox : public QSpinBox
        {
        Q_OBJECT
        public:
        explicit ScaleSpinBox(QWidget *parent = 0);

        signals:

        public slots:
        virtual void setValue(int val);

        protected:
        virtual QValidator::State validate(QString &input, int &pos) const;
        int valueFromText(const QString &text) const;
        QString textFromValue(int value) const;
        virtual void fixup(QString &str) const;

        private:
        QRegExpValidator *validator;

        };

        #endif // SCALESPINBOX_H

        @

        Class code

        @
        #include "scalespinbox.h"

        ScaleSpinBox::ScaleSpinBox(QWidget *parent) : QSpinBox(parent)
        {
        validator = new QRegExpValidator(QRegExp("[-1-3]"), this);
        }

        void ScaleSpinBox::setValue(int val)
        {
        if(val == 0)
        val = (value() == 1)? -1 : 1;

        QSpinBox::setValue(val);
        

        }

        QValidator::State ScaleSpinBox::validate(QString &input, int &pos) const
        {
        return validator->validate(input, pos);
        }

        int ScaleSpinBox::valueFromText(const QString &text) const
        {
        bool ok;
        return text.toInt(&ok, 10);
        }

        QString ScaleSpinBox::textFromValue(int value) const
        {
        return QString::number(value, 10);
        }

        void ScaleSpinBox::fixup(QString &str) const
        {
        bool ok;
        int value = str.toInt(&ok, 10);

        if(ok)
        {
            if (value == 0)
            {
                value = -1;
                str = QString::number(value);
            }
        }
        
        else
        {
            QSpinBox::fixup(str);
        }
        

        }

        @

        Perhaps because I use the spinner as-is without calling setValue().

        1 Reply Last reply
        0
        • A Offline
          A Offline
          Adrien Leravat
          wrote on last edited by
          #4

          Well I would think it is used by all input methods to modifly the value of the spinbox, and so that you have nothing to do but reimplementing it. But I'm not sure it is declared virtual in QSpinBox. May be an idea to check if yes or not.

          Actually your code should work too, without the setValue. I was not aware of the "fixup" method role. I'm not sure you need to reimplement textFromValue and valueFromText though. You seem to have kept the initial displaying of values.

          Are the validate and fixup correctly called ?

          Adeneo Embedded - www.adeneo-embedded.com

          1 Reply Last reply
          0
          • A Offline
            A Offline
            astodolski
            wrote on last edited by
            #5

            Validate and fixup get called "as needed" I need to come up with the proper way to look back at the prior value and perhaps place that in the fixup method. I though that re-implementing setValue was the solution.

            1 Reply Last reply
            0
            • A Offline
              A Offline
              Adrien Leravat
              wrote on last edited by
              #6

              Ok, I thought it didn't worked.

              If "setValue" isn't called, then a little trick is to make ScaleSpinBox some active proxy of the QSpinBox:

              • connect the valueChanged signal to some slot in ScaleSpinBox
              • In that slot, test the new value against the previous, locally stored in your ScaleSpinBox class, and if necessary, call setValue with your corrected value
              • update previous value
              • emit a new signal (like scaleValueChanged(int)), with the corrected value

              This is not a solution as good as reimplementing the setValue method, as the component will have the "0" value before corrected value is set, but by binding on the scaleValueChanged signal, you will always receive a appropriate value.

              Adeneo Embedded - www.adeneo-embedded.com

              1 Reply Last reply
              0
              • A Offline
                A Offline
                astodolski
                wrote on last edited by
                #7

                I'm not sure I understand. Would you mind editing the code I posted as an illustration of your idea?

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Adrien Leravat
                  wrote on last edited by
                  #8

                  Sure,

                  @
                  #ifndef SCALESPINBOX_H
                  #define SCALESPINBOX_H

                  #include <QSpinBox>

                  class ScaleSpinBox : public QSpinBox
                  {
                  Q_OBJECT
                  public:
                  explicit ScaleSpinBox(QWidget *parent = 0);

                  signals:
                  void scaleValueChanged(int);

                  private slots:
                  void onValueChanged(int);

                  private:
                  int m_nPreviousValue;
                  };

                  #endif // SCALESPINBOX_H
                  @

                  @
                  #include "scalespinbox.h"

                  ScaleSpinBox::ScaleSpinBox(QWidget *parent) : QSpinBox(parent)
                  {
                  connect(this, SIGNAL(valueChanged(int)), SLOT(onValueChanged(int)));
                  }

                  void ScaleSpinBox::onValueChanged(int val)
                  {
                  if (val != m_nPreviousValue)
                  {
                  if(val == 0)
                  {
                  val = (m_nPreviousValue == 1) ? -1 : 1;
                  setValue(val);
                  }

                      m_nPreviousValue = val;
                      emit scaleValueChanged(val);
                  }
                  

                  }
                  @

                  Adeneo Embedded - www.adeneo-embedded.com

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    astodolski
                    wrote on last edited by
                    #9

                    Thanks for that. How does the previous value (m_nPreviousValue) get assigned?

                    1 Reply Last reply
                    0
                    • A Offline
                      A Offline
                      Adrien Leravat
                      wrote on last edited by
                      #10

                      Line 14 ;). But it must be initialized to something like -INF in the cosntructor

                      Adeneo Embedded - www.adeneo-embedded.com

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        astodolski
                        wrote on last edited by
                        #11

                        [quote author="Adrien Leravat" date="1377179330"]it must be initialized to something like -INF in the cosntructor[/quote]

                        I saw the need to do that after my question. However I get a nasty crash.

                        "HEAP CORRUPTION DETECTED"

                        This only occured in Qt Creator. VS2010 didn't dsiplay the error - Hmmm

                        1 Reply Last reply
                        0
                        • A Offline
                          A Offline
                          Adrien Leravat
                          wrote on last edited by
                          #12

                          When defining it to -INF ? Well any other mecanism to go through it the first time will do else, like a flag

                          Adeneo Embedded - www.adeneo-embedded.com

                          1 Reply Last reply
                          0
                          • A Offline
                            A Offline
                            astodolski
                            wrote on last edited by
                            #13

                            [quote author="Adrien Leravat" date="1377183155"]When defining it to -INF ? Well any other mecanism to go through it the first time will do else, like a flag[/quote]

                            No actually I set it to INT_MIN in the xtor. When I took out the code to do the validate and the actual validator object, there were no issues.

                            It seems though that by doing an additional signal that my calling method defines a signal/slot mechanism for the slot to be called when the value is changed. I think what is happening is that the slot in the caller gets called twice.

                            For example:

                            The caller has in its xtor:

                            @
                            connect(ui->sbScaleImage, SIGNAL(valueChanged(int)), this, SLOT(zoomImage(int)));
                            @

                            The slot zoomImage gets called when the up/down arrows are clicked in the UI. The slot gets called now 2x

                            1 Reply Last reply
                            0
                            • A Offline
                              A Offline
                              Adrien Leravat
                              wrote on last edited by
                              #14

                              Yes you have to connect to the new signal (if it wasn't a typo). The original one will be emitted twice because the internal value changes twice. But the signal scaleValueChanged (note that this is a different signal) should be emitted only once if I made no mistake.

                              Edit: In my code, the m_nPreviousValue variable should be updated BEFORE calling setValue, as the emit will be synchronous. My bad, wrote it too quickly.

                              @
                              if (val != m_nPreviousValue)
                              {
                              if(val == 0)
                              {
                              val = (m_nPreviousValue == 1) ? -1 : 1;
                              m_nPreviousValue = val;
                              setValue(val);
                              }
                              else
                              {
                              m_nPreviousValue = val;
                              }

                                  emit scaleValueChanged(val);
                              }
                              

                              @

                              Adeneo Embedded - www.adeneo-embedded.com

                              1 Reply Last reply
                              0
                              • A Offline
                                A Offline
                                astodolski
                                wrote on last edited by
                                #15

                                It appears that the slot zoomImage(int ctlValue) gets called twice when passing through zero be it up or down. One call of the slot every other time. :( To be clear, this is the slot which is part of the UI. I don't want to confuse things. I DID make the corrections to the code you posted.

                                EDIT: Changing to the new signal scaleValueChanged(int) doesn't prevent the spinner from selecting zero.

                                1 Reply Last reply
                                0
                                • A Offline
                                  A Offline
                                  Adrien Leravat
                                  wrote on last edited by
                                  #16

                                  Any progress with your issue ?

                                  A step by step debugging in the slot would clearly highly any issue in the logic. Maybe I missed something in your explanation.

                                  Adeneo Embedded - www.adeneo-embedded.com

                                  1 Reply Last reply
                                  0
                                  • A Offline
                                    A Offline
                                    astodolski
                                    wrote on last edited by
                                    #17

                                    Hi,

                                    Well the slot in the UI works. That is, the slot that gets connected to the spinbox valueChanged signal in the UI class. However, the issue is that it gets called 2x depending on the signal that is used in the ScaleSpinBox class. If I
                                    use this constructor
                                    @
                                    ScaleSpinBox::ScaleSpinBox(QWidget *parent) : QSpinBox(parent)
                                    {
                                    connect(this, SIGNAL(scaleValueChanged(int)), SLOT(onValueChanged(int)));
                                    m_nPreviousValue = INT_MIN;

                                    }
                                    @

                                    Then the slot associated with the UI gets called ONCE. However the control is NOT prevented from going from 1 to zero. In fact
                                    @ScaleSpinBox::onValueChanged(int val)@ never gets called.

                                    If I were to change to this constructor:

                                    @
                                    ScaleSpinBox::ScaleSpinBox(QWidget *parent) : QSpinBox(parent)
                                    {
                                    connect(this, SIGNAL(valueChanged(int)), SLOT(onValueChanged(int)));
                                    m_nPreviousValue = INT_MIN;
                                    }
                                    @

                                    the slot in the UI gets called 2x.

                                    1 Reply Last reply
                                    0
                                    • A Offline
                                      A Offline
                                      Adrien Leravat
                                      wrote on last edited by
                                      #18

                                      Ok so the problem to me is that your UI is connected to the "valueChanged" signal. Actually it should be connected only to "scaleValueChanged". The "valueChanged" is used only by your ScaneSpinBox class, but should not be used elsewhere.

                                      It bowls down to:

                                      • Connecting the "valueChanged" signal to the "onValueChanged" slot of the ScaleSpinBox class
                                      • Connecting your UI to the "scaleValueChanged" signal, the one emitted by the ScaleSpinBox class, and only this one

                                      Adeneo Embedded - www.adeneo-embedded.com

                                      1 Reply Last reply
                                      0
                                      • A Offline
                                        A Offline
                                        astodolski
                                        wrote on last edited by
                                        #19

                                        That solved it, thanks much

                                        1 Reply Last reply
                                        0
                                        • A Offline
                                          A Offline
                                          Adrien Leravat
                                          wrote on last edited by
                                          #20

                                          Glad to here ! :)

                                          Don't forget the "solved" tag in addition the title ;)

                                          Adeneo Embedded - www.adeneo-embedded.com

                                          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