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. QDial and wrapping
QtWS25 Last Chance

QDial and wrapping

Scheduled Pinned Locked Moved Solved General and Desktop
13 Posts 3 Posters 1.2k 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.
  • A Offline
    A Offline
    admkrk
    wrote on last edited by
    #1

    Hi,

    I am trying to simulate a rotary encoder using a QDial. For the most part it works fine except when the value wraps around.

    void MainWindow::on_dialTime_valueChanged(int value)
    {
        if(setHrs)
        {
            if(previousHour < value)
                ++curHrs;
            else
                --curHrs;
            curHrs = calculateHours(curHrs);
            ui->timeHours->display(curHrs);
            previousHour = value;
        }
    

    calculateHours() just wraps the time when it goes above 12, or below 1. When the value of the QDial wraps, the displayed value jumps back, or forward, by one. For example; 4 3 2 1 2 1 12 11. I could probably write a function to do something similar when the value wraps, but that seems sloppy.

    I have set single step to equal page step, and that moves the dial as I would like, close enough to what a rotary encoder would do. The problem is a n encoder does not reset when it wraps around. It just adds or subtracts one no matter where it is turned to.

    To be clear, this is used to set the time on a clock, so it also has to set minutes and seconds. There are also four other timers that need to be set as well. I can live with the staggered output as this is only meant to simulate the hardware for an MCU while I develop the code for that, but it would be nice if I could get it to work as expected.

    Thanks in advance for any suggestions that will help.

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Might be a sill question but isn't the wrapping property what you want to implement ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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

        Hi,

        Thanks for your reply. Yes, but as I mentioned the value resets when it wraps. For example in the range I have set after 11 it jumps to 1, or the other way when decreasing. That is what was causing the jump. I tried adding a function to just determine the direction.

        int MainWindow::dialDirection(int value)
        {
            // should only happen at startup
            if(previousValue == -1)
            {
                previousValue = value;
                return 3;
            }
        
            if(previousValue < value)
            {
                if(value == 1 && previousValue == 11)
                    // Dial rolled over, still increasing
                    return 1;
            }
        
            if(previousValue > value)
            {
                if(value == 11 && previousValue == 1)
                    // Dial rolled over, still decreasing
                    return -1;
            }
        
            previousValue = value;
        
            return 0;
        }
        

        That seems to be doing the trick with a little change in the other function.

        void MainWindow::on_dialTime_valueChanged(int value)
        {
            direction = dialDirection(value);
        
            if(setHrs)
            {
                if(direction == 1/*previousHour < value*/)
                    ++curHrs;
                else
                    --curHrs;
        
                curHrs = calculateHours(curHrs);
                ui->timeHours->display(curHrs);
            }
        

        I have not really tested it to see if it works other than for the hours, but it will eliminate a few variables if it does. Using it directly with curHrs does not seem to work:

        curHrs += direction;
        

        I am not really surprised, that would have been too easy. Need to look and see what is happening, but if I can eliminate the if/else also I will be happy.

        Pablo J. RoginaP 1 Reply Last reply
        0
        • A admkrk

          Hi,

          Thanks for your reply. Yes, but as I mentioned the value resets when it wraps. For example in the range I have set after 11 it jumps to 1, or the other way when decreasing. That is what was causing the jump. I tried adding a function to just determine the direction.

          int MainWindow::dialDirection(int value)
          {
              // should only happen at startup
              if(previousValue == -1)
              {
                  previousValue = value;
                  return 3;
              }
          
              if(previousValue < value)
              {
                  if(value == 1 && previousValue == 11)
                      // Dial rolled over, still increasing
                      return 1;
              }
          
              if(previousValue > value)
              {
                  if(value == 11 && previousValue == 1)
                      // Dial rolled over, still decreasing
                      return -1;
              }
          
              previousValue = value;
          
              return 0;
          }
          

          That seems to be doing the trick with a little change in the other function.

          void MainWindow::on_dialTime_valueChanged(int value)
          {
              direction = dialDirection(value);
          
              if(setHrs)
              {
                  if(direction == 1/*previousHour < value*/)
                      ++curHrs;
                  else
                      --curHrs;
          
                  curHrs = calculateHours(curHrs);
                  ui->timeHours->display(curHrs);
              }
          

          I have not really tested it to see if it works other than for the hours, but it will eliminate a few variables if it does. Using it directly with curHrs does not seem to work:

          curHrs += direction;
          

          I am not really surprised, that would have been too easy. Need to look and see what is happening, but if I can eliminate the if/else also I will be happy.

          Pablo J. RoginaP Offline
          Pablo J. RoginaP Offline
          Pablo J. Rogina
          wrote on last edited by
          #4

          @admkrk this is my approach, trying to split the rotary encoder value changed signal from the use of such event, I mean, from using the encoder increasing or decreasing.
          I created a QDialog with a QDial, radio buttons for selecting hours or minutes to set and a QLCDNumber (simplified to just one LCD, you'll figure out)

          dialog.h

          class Dialog : public QDialog
          {
              Q_OBJECT
          
          public:
              explicit Dialog(QWidget *parent = nullptr);
              ~Dialog();
          
              enum EncoderDirection {
                Increase = 1,
                Decrease = -1
              };
              Q_ENUM(EncoderDirection)
          
          signals:
              void encoderValueChanged(EncoderDirection direction);
          
          private slots:
              void on_dial_valueChanged(int value);
              void updateLCD(EncoderDirection direction);
          
          private:
              Ui::Dialog *ui;
              int currentValue;
          };
          
          

          dialog.cpp

          Dialog::Dialog(QWidget *parent) :
              QDialog(parent),
              ui(new Ui::Dialog)
          {
              ui->setupUi(this);
              currentValue = ui->dial->value();
              ui->dial->setFocus();
              connect(this, &Dialog::encoderValueChanged, this, &Dialog::updateLCD);
          }
          
          Dialog::~Dialog()
          {
              delete ui;
          }
          
          void Dialog::on_dial_valueChanged(int value)
          {
              EncoderDirection encoderDirection;
              if (value > currentValue) {
                  if (currentValue == ui->dial->minimum()) {
                      encoderDirection = EncoderDirection::Decrease;
                  } else {
                      encoderDirection = EncoderDirection::Increase;
                  }
              } else {
                  if (currentValue == ui->dial->maximum()) {
                      encoderDirection = EncoderDirection::Increase;
                  } else {
                      encoderDirection = EncoderDirection::Decrease;
                  }
              }
              currentValue = value;
              emit encoderValueChanged(encoderDirection);
          }
          
          void Dialog::updateLCD(EncoderDirection direction) {
              if (ui->radioHour->isChecked()) {
                  int hour = ui->lcdNumber->intValue() + direction;
                  if (hour > 12) {
                      hour = 1;
                  } else if(hour < 1) {
                      hour = 12;
                  }
                  ui->lcdNumber->display(hour);
              }
          // ... if radio button is set for minutes, update that display etc...
          }
          

          Upvote the answer(s) that helped you solve the issue
          Use "Topic Tools" button to mark your post as Solved
          Add screenshots via postimage.org
          Don't ask support requests via chat/PM. Please use the forum so others can benefit from the solution in the future

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

            Hi Pablo, that is a nice clean example and shows me a couple ways I can clean my code up.

            Obviously my previous solution did not work. One problem I was having is that the values are different when rolling over one direction compared to the other. It still needs cleaned up, but I got it working with this.

            int MainWindow::dialDirection(int value)
            {
                // should only happen at startup
                if(previousValue == -1)
                {
                    previousValue = value;
                    return 3;
                }
            
                if(previousValue > value)
                {
                    if(value == 1 && previousValue == 11)
                    {
                        // Dial rolled over, so increasing
                        previousValue = value;
                        return 1;
                    }
                    else
                    {
                        // Decreasing
                        previousValue = value;
                        return -1;
                    }
                }
            
                if(previousValue < value)
                {
                    if(value == 12 && previousValue == 2)
                    {
                        // Dial rolled over, so decreasing
                        previousValue = value;
                        return -1;
                    }
                    else
                    {
                        // Increasing
                        previousValue = value;
                        return 1;
                    }
                }
            
                // Something bad happened
                return 0;
            }
            

            And setting the time.

            void MainWindow::on_dialTime_valueChanged(int value)
            {
                direction = dialDirection(value);
            
                if(setHrs)
                {
                    curHrs += direction;
                    curHrs = calculateHours(curHrs);
                    ui->timeHours->display(curHrs);
                }
            

            Thank you very much.

            I am marking this solved, but I noticed I am getting this in the debug output.

            QMetaObject::connectSlotsByName: No matching signal for on_dial_clicked()
            QObject::connect: No such signal Dial::valueChanged() in ..\MistingGui\mainwindow.cpp:36
            QObject::connect:  (sender name:   'dialTime')
            QObject::connect:  (receiver name: 'MainWindow')
            

            It is working, so it cannot be an error. Could it just be that I am using the old style of connect? Or is something I did wrong subclassing QDial?

            Pablo J. RoginaP 1 Reply Last reply
            0
            • A admkrk

              Hi Pablo, that is a nice clean example and shows me a couple ways I can clean my code up.

              Obviously my previous solution did not work. One problem I was having is that the values are different when rolling over one direction compared to the other. It still needs cleaned up, but I got it working with this.

              int MainWindow::dialDirection(int value)
              {
                  // should only happen at startup
                  if(previousValue == -1)
                  {
                      previousValue = value;
                      return 3;
                  }
              
                  if(previousValue > value)
                  {
                      if(value == 1 && previousValue == 11)
                      {
                          // Dial rolled over, so increasing
                          previousValue = value;
                          return 1;
                      }
                      else
                      {
                          // Decreasing
                          previousValue = value;
                          return -1;
                      }
                  }
              
                  if(previousValue < value)
                  {
                      if(value == 12 && previousValue == 2)
                      {
                          // Dial rolled over, so decreasing
                          previousValue = value;
                          return -1;
                      }
                      else
                      {
                          // Increasing
                          previousValue = value;
                          return 1;
                      }
                  }
              
                  // Something bad happened
                  return 0;
              }
              

              And setting the time.

              void MainWindow::on_dialTime_valueChanged(int value)
              {
                  direction = dialDirection(value);
              
                  if(setHrs)
                  {
                      curHrs += direction;
                      curHrs = calculateHours(curHrs);
                      ui->timeHours->display(curHrs);
                  }
              

              Thank you very much.

              I am marking this solved, but I noticed I am getting this in the debug output.

              QMetaObject::connectSlotsByName: No matching signal for on_dial_clicked()
              QObject::connect: No such signal Dial::valueChanged() in ..\MistingGui\mainwindow.cpp:36
              QObject::connect:  (sender name:   'dialTime')
              QObject::connect:  (receiver name: 'MainWindow')
              

              It is working, so it cannot be an error. Could it just be that I am using the old style of connect? Or is something I did wrong subclassing QDial?

              Pablo J. RoginaP Offline
              Pablo J. RoginaP Offline
              Pablo J. Rogina
              wrote on last edited by
              #6

              @admkrk said in QDial and wrapping:

              Or is something I did wrong subclassing QDial?
              you should share the code of your subclass. It's hard to say without being able to see it.

              It is working, so it cannot be an error.

              Definitely it is not good :-) Such message should not appear at all

              Upvote the answer(s) that helped you solve the issue
              Use "Topic Tools" button to mark your post as Solved
              Add screenshots via postimage.org
              Don't ask support requests via chat/PM. Please use the forum so others can benefit from the solution in the future

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

                Hi Pablo,

                I really did not do much with the subclass, just made it so it would ignore clicks. Clicking it sets which part of the time to change, like the radio button in your example.
                In mainWindow.cpp

                connect(ui->dialTime, SIGNAL(valueChanged()), this, SLOT(on_dialTime_valueChanged()));
                

                The beggining of on_dialTime_valueChanged() is in my previous post.

                #ifndef DIAL_H
                #define DIAL_H
                
                #include <QObject>
                #include <qdial.h>
                
                class Dial : public QDial
                {
                    Q_OBJECT
                public:
                    Dial(QWidget *parent = nullptr);
                
                signals:
                    void clicked();
                
                protected:
                    void mousePressEvent(QMouseEvent *me) override;
                    void mouseReleaseEvent(QMouseEvent *me) override;
                    void mouseMoveEvent(QMouseEvent *me) override;
                };
                
                #endif // DIAL_H
                
                #include "dial.h"
                
                #include <QMouseEvent>
                
                Dial::Dial(QWidget *parent)
                {
                    Q_UNUSED(parent)
                }
                
                void Dial::mousePressEvent(QMouseEvent *me)
                {
                    me->ignore();
                    emit clicked();
                }
                
                void Dial::mouseReleaseEvent(QMouseEvent *me)
                {
                    me->ignore();
                }
                
                void Dial::mouseMoveEvent(QMouseEvent *me)
                {
                    me->ignore();
                }
                
                Pablo J. RoginaP 1 Reply Last reply
                0
                • A admkrk

                  Hi Pablo,

                  I really did not do much with the subclass, just made it so it would ignore clicks. Clicking it sets which part of the time to change, like the radio button in your example.
                  In mainWindow.cpp

                  connect(ui->dialTime, SIGNAL(valueChanged()), this, SLOT(on_dialTime_valueChanged()));
                  

                  The beggining of on_dialTime_valueChanged() is in my previous post.

                  #ifndef DIAL_H
                  #define DIAL_H
                  
                  #include <QObject>
                  #include <qdial.h>
                  
                  class Dial : public QDial
                  {
                      Q_OBJECT
                  public:
                      Dial(QWidget *parent = nullptr);
                  
                  signals:
                      void clicked();
                  
                  protected:
                      void mousePressEvent(QMouseEvent *me) override;
                      void mouseReleaseEvent(QMouseEvent *me) override;
                      void mouseMoveEvent(QMouseEvent *me) override;
                  };
                  
                  #endif // DIAL_H
                  
                  #include "dial.h"
                  
                  #include <QMouseEvent>
                  
                  Dial::Dial(QWidget *parent)
                  {
                      Q_UNUSED(parent)
                  }
                  
                  void Dial::mousePressEvent(QMouseEvent *me)
                  {
                      me->ignore();
                      emit clicked();
                  }
                  
                  void Dial::mouseReleaseEvent(QMouseEvent *me)
                  {
                      me->ignore();
                  }
                  
                  void Dial::mouseMoveEvent(QMouseEvent *me)
                  {
                      me->ignore();
                  }
                  
                  Pablo J. RoginaP Offline
                  Pablo J. RoginaP Offline
                  Pablo J. Rogina
                  wrote on last edited by
                  #8

                  @admkrk said in QDial and wrapping:

                  connect(ui->dialTime, SIGNAL(valueChanged()), this, SLOT(on_dialTime_valueChanged()));

                  Two things here:

                  1. Please use the new syntax for signals & slots, that way you receive compile-error messages regarding something wrong with connecting signals to slots
                  2. Since you're naming your slot as "on_<qt-widget-name>_<signal-name> I guess Qt is using the auto-connection feature to already connect such signal (i.e. valueChanged) to this slot, so you ended up with double connection.

                  Upvote the answer(s) that helped you solve the issue
                  Use "Topic Tools" button to mark your post as Solved
                  Add screenshots via postimage.org
                  Don't ask support requests via chat/PM. Please use the forum so others can benefit from the solution in the future

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

                    I think I got the new syntax figured out, those messages went away. However, I now get a new one

                    QMetaObject::connectSlotsByName: No matching signal for on_dial_clicked()
                    

                    I thought it might be the syntax issue again, but changing that one did not make a difference this time. I now have

                    connect(ui->dialTime, &Dial::clicked, this, &MainWindow::on_dial_clicked);
                    connect(ui->dialTime, &QDial::valueChanged, this, &MainWindow::on_dialTime_valueChanged);
                    

                    Changing on_dial_clicked() to on_dialTime_clicked() got rid of the message, but now it does not work.

                    void MainWindow::on_dialTime_clicked()
                    {
                        ++timeSet;
                        if(timeSet > 3)
                            timeSet = 0;
                    
                        switch(timeSet)
                        {
                        //...
                        }
                    }
                    

                    Since timeSet is cycling between 0 and 2, judging from the results, I assume I am getting the double connection you mentioned.

                    As one last test, I changed the name to dialClicked(). That got rid of the message and it still functioned properly. I am unsure what to make of all that.

                    Pablo J. RoginaP 1 Reply Last reply
                    0
                    • A admkrk

                      I think I got the new syntax figured out, those messages went away. However, I now get a new one

                      QMetaObject::connectSlotsByName: No matching signal for on_dial_clicked()
                      

                      I thought it might be the syntax issue again, but changing that one did not make a difference this time. I now have

                      connect(ui->dialTime, &Dial::clicked, this, &MainWindow::on_dial_clicked);
                      connect(ui->dialTime, &QDial::valueChanged, this, &MainWindow::on_dialTime_valueChanged);
                      

                      Changing on_dial_clicked() to on_dialTime_clicked() got rid of the message, but now it does not work.

                      void MainWindow::on_dialTime_clicked()
                      {
                          ++timeSet;
                          if(timeSet > 3)
                              timeSet = 0;
                      
                          switch(timeSet)
                          {
                          //...
                          }
                      }
                      

                      Since timeSet is cycling between 0 and 2, judging from the results, I assume I am getting the double connection you mentioned.

                      As one last test, I changed the name to dialClicked(). That got rid of the message and it still functioned properly. I am unsure what to make of all that.

                      Pablo J. RoginaP Offline
                      Pablo J. RoginaP Offline
                      Pablo J. Rogina
                      wrote on last edited by
                      #10

                      @admkrk said in QDial and wrapping:

                      connect(ui->dialTime, &QDial::valueChanged, this, &MainWindow::on_dialTime_valueChanged);

                      if you keep naming your slots as on_<widget-name>_<signal-name> please don't also use that explicit connection

                      Upvote the answer(s) that helped you solve the issue
                      Use "Topic Tools" button to mark your post as Solved
                      Add screenshots via postimage.org
                      Don't ask support requests via chat/PM. Please use the forum so others can benefit from the solution in the future

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

                        Hi Pablo,

                        I am still a little confused. It is sort of a habit for me to name my slots on_<random-name>_<signal-name>, where random-name is something meaningful to me. In this case I have four other dials that need the same functionality, so using widget-name would not necessarily be the best solution to begin with.

                        @Pablo-J-Rogina said in QDial and wrapping:

                        if you keep naming your slots as on_<widget-name>_<signal-name> please don't also use that explicit connection

                        That sounds like I do not even need to have the connect() statement in the first place if I use that naming convention. I have mostly been writing code for 8-bit micro-controllers lately, so maybe I am being extra thick headed about this.

                        Pablo J. RoginaP 1 Reply Last reply
                        0
                        • A admkrk

                          Hi Pablo,

                          I am still a little confused. It is sort of a habit for me to name my slots on_<random-name>_<signal-name>, where random-name is something meaningful to me. In this case I have four other dials that need the same functionality, so using widget-name would not necessarily be the best solution to begin with.

                          @Pablo-J-Rogina said in QDial and wrapping:

                          if you keep naming your slots as on_<widget-name>_<signal-name> please don't also use that explicit connection

                          That sounds like I do not even need to have the connect() statement in the first place if I use that naming convention. I have mostly been writing code for 8-bit micro-controllers lately, so maybe I am being extra thick headed about this.

                          Pablo J. RoginaP Offline
                          Pablo J. RoginaP Offline
                          Pablo J. Rogina
                          wrote on last edited by
                          #12

                          @admkrk said in QDial and wrapping:

                          That sounds like I do not even need to have the connect() statement in the first place if I use that naming convention

                          That's right. I guess there's no way to avoid the "autoconnection" feature when the uic tool generates C++ code from the .ui file, see this bug (open since 2012-11-07...).

                          So if you feel comfortable naming slots like on_<widget-name>_<signal-name> keep in mind to avoid doing the explicit connect as well.

                          As further reference, this is the method in QMetaObject class responsible for th autoconnection feature.

                          Upvote the answer(s) that helped you solve the issue
                          Use "Topic Tools" button to mark your post as Solved
                          Add screenshots via postimage.org
                          Don't ask support requests via chat/PM. Please use the forum so others can benefit from the solution in the future

                          1 Reply Last reply
                          2
                          • A Offline
                            A Offline
                            admkrk
                            wrote on last edited by
                            #13

                            Thank you Pablo, that cleared up my confusion. I can verify that my other connect() statement was causing a double connection also since if works fine after removing it. I will definitely keep this in mind for the future.

                            Thanks again, Kirk

                            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