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. Subclassed QLineEdit styleSheet update triggers stack overflow
QtWS25 Last Chance

Subclassed QLineEdit styleSheet update triggers stack overflow

Scheduled Pinned Locked Moved Solved General and Desktop
qlineeditstack-overflow
6 Posts 4 Posters 565 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.
  • B Offline
    B Offline
    BartM
    wrote on last edited by
    #1

    I created a QSuperLine class that inherits from QLineEdit. The idea is for the line to nicely change it's background - flash - when the text is changed either by a user or from within the QApplication. I do have a separate thread (named updater) that emits a freshUpdate signal with 100 msec intervals.

    In principle this works as expected on 2 - 3 lines. However, when I have to update a large number of lines in my app (ca. 50), I can see that they 'flash' one by one instead of behaving independently, and - when they do - at some point I am getting a stack overflow exception.

    Could someone more experienced with Qt tell me what could be improved in the below code to avoid this problem?

    Below is my qsuperline.h content, QLineEdits have been promoted to qSuperLines in the Qt Designer. I use 64-bit MSVC.

    #ifndef QSUPERLINE_H
    #define QSUPERLINE_H
    #pragma once
    
    #include <QLineEdit>
    #include <QObject>
    #include "updater.h"
    #include <QThread>
    
    extern void delay(int millisecondsWait);
    
    class QSuperLine : public QLineEdit
    {
        Q_OBJECT
    public:
        QThread * T1 = new QThread();
        updater * Up1 = new updater();
        bool isBusy;
        QString InitialContent;
    
    QSuperLine (QWidget *parent = nullptr) : QLineEdit(parent) {
        Up1->moveToThread(T1);
        InitialContent = "";
        isBusy = false;
        QObject::connect( T1, &QThread::started, Up1, &updater::workNow);
        QObject::connect( Up1, &updater::freshUpdate, this, &QSuperLine::resetLine);
        T1->start();
    
    }
    
    QSuperLine::~QSuperLine() {
    
    }
    
    public slots:
        int QSuperLine::resetLine(int a) {
            if (InitialContent != this->text() && isBusy == false) {
                isBusy = true;
                InitialContent = this->text();
                this->setStyleSheet("background-color: rgb(20, 80, 180);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("background-color: rgb(20, 80, 160);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("background-color: rgb(20, 80, 140);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("background-color: rgb(20, 80, 120);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("background-color: rgb(20, 80, 100);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("background-color: rgb(20, 80, 80);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("background-color: rgb(20, 80, 40);border-style: solid;border-width: 0px;border-color: red;border-radius: 9px;color: cyan;");
                delay(50);
                this->setStyleSheet("");
                this->isBusy = false;
    
            }
            return 0;
        }
    
    };
    
    #endif // QSUPERLINE_H
    

    The delay function is defined elsewhere as follows:

    void delay(int millisecondsWait)
    {
        QEventLoop loop;
        QTimer t;
        t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
        t.start(millisecondsWait);
        loop.exec();
    }
    

    According to the QtDesigner debugger, stack overflow occurs at line 14 of the following asm code:

    Qt5Guid!__chkstk [d:\agent\_work\63\s\src\vctools\crt\vcstartup\src\misc\amd64\chkstk.asm @ 67]:
    0x7ff802413cc0                   sub     rsp,10h
    0x7ff802413cc4  <+    4>         mov     qword ptr [rsp],r10
    0x7ff802413cc8  <+    8>         mov     qword ptr [rsp+8],r11
    0x7ff802413ccd  <+   13>         xor     r11,r11
    0x7ff802413cd0  <+   16>         lea     r10,[rsp+18h]
    0x7ff802413cd5  <+   21>         sub     r10,rax
    0x7ff802413cd8  <+   24>         cmovb   r10,r11
    0x7ff802413cdc  <+   28>         mov   r11,qword ptr gs:[10h]
    0x7ff802413ce5  <+   37>         cmp     r10,r11
    0x7ff802413ce8  <+   40>         bnd jae Qt5Guid!__chkstk+0x42 (00007ff8`02413d02)
    0x7ff802413ceb  <+   43>         and     r10w,0F000h
    0x7ff802413cf1  <+   49>         lea     r11,[r11-1000h]
    0x7ff802413cf8  <+   56>         mov     byte ptr [r11],0   # <- stack overflow here.
    

    Any suggestion would be wellcomed.

    JonBJ KroMignonK 2 Replies Last reply
    0
    • B BartM

      I created a QSuperLine class that inherits from QLineEdit. The idea is for the line to nicely change it's background - flash - when the text is changed either by a user or from within the QApplication. I do have a separate thread (named updater) that emits a freshUpdate signal with 100 msec intervals.

      In principle this works as expected on 2 - 3 lines. However, when I have to update a large number of lines in my app (ca. 50), I can see that they 'flash' one by one instead of behaving independently, and - when they do - at some point I am getting a stack overflow exception.

      Could someone more experienced with Qt tell me what could be improved in the below code to avoid this problem?

      Below is my qsuperline.h content, QLineEdits have been promoted to qSuperLines in the Qt Designer. I use 64-bit MSVC.

      #ifndef QSUPERLINE_H
      #define QSUPERLINE_H
      #pragma once
      
      #include <QLineEdit>
      #include <QObject>
      #include "updater.h"
      #include <QThread>
      
      extern void delay(int millisecondsWait);
      
      class QSuperLine : public QLineEdit
      {
          Q_OBJECT
      public:
          QThread * T1 = new QThread();
          updater * Up1 = new updater();
          bool isBusy;
          QString InitialContent;
      
      QSuperLine (QWidget *parent = nullptr) : QLineEdit(parent) {
          Up1->moveToThread(T1);
          InitialContent = "";
          isBusy = false;
          QObject::connect( T1, &QThread::started, Up1, &updater::workNow);
          QObject::connect( Up1, &updater::freshUpdate, this, &QSuperLine::resetLine);
          T1->start();
      
      }
      
      QSuperLine::~QSuperLine() {
      
      }
      
      public slots:
          int QSuperLine::resetLine(int a) {
              if (InitialContent != this->text() && isBusy == false) {
                  isBusy = true;
                  InitialContent = this->text();
                  this->setStyleSheet("background-color: rgb(20, 80, 180);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("background-color: rgb(20, 80, 160);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("background-color: rgb(20, 80, 140);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("background-color: rgb(20, 80, 120);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("background-color: rgb(20, 80, 100);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("background-color: rgb(20, 80, 80);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("background-color: rgb(20, 80, 40);border-style: solid;border-width: 0px;border-color: red;border-radius: 9px;color: cyan;");
                  delay(50);
                  this->setStyleSheet("");
                  this->isBusy = false;
      
              }
              return 0;
          }
      
      };
      
      #endif // QSUPERLINE_H
      

      The delay function is defined elsewhere as follows:

      void delay(int millisecondsWait)
      {
          QEventLoop loop;
          QTimer t;
          t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
          t.start(millisecondsWait);
          loop.exec();
      }
      

      According to the QtDesigner debugger, stack overflow occurs at line 14 of the following asm code:

      Qt5Guid!__chkstk [d:\agent\_work\63\s\src\vctools\crt\vcstartup\src\misc\amd64\chkstk.asm @ 67]:
      0x7ff802413cc0                   sub     rsp,10h
      0x7ff802413cc4  <+    4>         mov     qword ptr [rsp],r10
      0x7ff802413cc8  <+    8>         mov     qword ptr [rsp+8],r11
      0x7ff802413ccd  <+   13>         xor     r11,r11
      0x7ff802413cd0  <+   16>         lea     r10,[rsp+18h]
      0x7ff802413cd5  <+   21>         sub     r10,rax
      0x7ff802413cd8  <+   24>         cmovb   r10,r11
      0x7ff802413cdc  <+   28>         mov   r11,qword ptr gs:[10h]
      0x7ff802413ce5  <+   37>         cmp     r10,r11
      0x7ff802413ce8  <+   40>         bnd jae Qt5Guid!__chkstk+0x42 (00007ff8`02413d02)
      0x7ff802413ceb  <+   43>         and     r10w,0F000h
      0x7ff802413cf1  <+   49>         lea     r11,[r11-1000h]
      0x7ff802413cf8  <+   56>         mov     byte ptr [r11],0   # <- stack overflow here.
      

      Any suggestion would be wellcomed.

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @BartM
      For the overflow exception, you should look at the debugger's stack trace window to see what the trace back to your code is, not just the location of the overflow you show.

      I happen to have done something very similar to what you describe, but I do it very differently from your approach. You might like to consider whether it would suit you situation better.

      I don't use any threads. Nor waiting event loops. (And in your case you are invoking the main event loop many times simultaneously, that may be problematic.) Nor any sub-classes. And all my flashes occur synchronised. And there are no problems.

      I have many widgets (mine are actually QSpinBoxes). The user may change one, and as a result many of the others' values are recalculated and so change. I want the user to see all the changed ones flash for a few seconds. Here is the outline of how I do it:

      • I have one module-level QTimer.

      • I have one module-level QList for widgets which are currently flashing.

      • The elements in this list are lightweight structs holding a pointer to one widget together with a "counter" for flashing: this is added to the list and loaded with the desired number of flashes when the widget's value is changed, and decrements toward 0 on each QTimer tick. When a widget's counter reaches 0 the flashing is stopped and the widget is removed from the flash list. On each tick the color is changed to implement the flash.

      • Each time the timer ticks my slot iterates the flash list, decrementing each element's counter, doing the color change, and checking for reaching 0 to remove from the list.

      • If the user changes another spin box while a previous flashing is still on-going, new elements can be added to the list, or if it finds that it needs to flash an existing widget already on the list in can reset the counter to starting number to restart that item flashing.

      Since there is only one timer all flashes are synchronised.

      It works well for me, and seems a simpler solution. It can be applied to any widget without requiring subclassing. I suspect you have a problem with the multiple delays and threads. Threads are the root of all evil!

      1 Reply Last reply
      3
      • mrjjM Offline
        mrjjM Offline
        mrjj
        Lifetime Qt Champion
        wrote on last edited by
        #3

        @BartM said in Subclassed QLineEdit styleSheet update triggers stack overflow:

        freshUpdate signal with 100 msec intervals.

        If you raise this value to say 2 secs. does it still crash ?

        1 Reply Last reply
        0
        • B BartM

          I created a QSuperLine class that inherits from QLineEdit. The idea is for the line to nicely change it's background - flash - when the text is changed either by a user or from within the QApplication. I do have a separate thread (named updater) that emits a freshUpdate signal with 100 msec intervals.

          In principle this works as expected on 2 - 3 lines. However, when I have to update a large number of lines in my app (ca. 50), I can see that they 'flash' one by one instead of behaving independently, and - when they do - at some point I am getting a stack overflow exception.

          Could someone more experienced with Qt tell me what could be improved in the below code to avoid this problem?

          Below is my qsuperline.h content, QLineEdits have been promoted to qSuperLines in the Qt Designer. I use 64-bit MSVC.

          #ifndef QSUPERLINE_H
          #define QSUPERLINE_H
          #pragma once
          
          #include <QLineEdit>
          #include <QObject>
          #include "updater.h"
          #include <QThread>
          
          extern void delay(int millisecondsWait);
          
          class QSuperLine : public QLineEdit
          {
              Q_OBJECT
          public:
              QThread * T1 = new QThread();
              updater * Up1 = new updater();
              bool isBusy;
              QString InitialContent;
          
          QSuperLine (QWidget *parent = nullptr) : QLineEdit(parent) {
              Up1->moveToThread(T1);
              InitialContent = "";
              isBusy = false;
              QObject::connect( T1, &QThread::started, Up1, &updater::workNow);
              QObject::connect( Up1, &updater::freshUpdate, this, &QSuperLine::resetLine);
              T1->start();
          
          }
          
          QSuperLine::~QSuperLine() {
          
          }
          
          public slots:
              int QSuperLine::resetLine(int a) {
                  if (InitialContent != this->text() && isBusy == false) {
                      isBusy = true;
                      InitialContent = this->text();
                      this->setStyleSheet("background-color: rgb(20, 80, 180);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("background-color: rgb(20, 80, 160);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("background-color: rgb(20, 80, 140);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("background-color: rgb(20, 80, 120);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("background-color: rgb(20, 80, 100);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("background-color: rgb(20, 80, 80);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("background-color: rgb(20, 80, 40);border-style: solid;border-width: 0px;border-color: red;border-radius: 9px;color: cyan;");
                      delay(50);
                      this->setStyleSheet("");
                      this->isBusy = false;
          
                  }
                  return 0;
              }
          
          };
          
          #endif // QSUPERLINE_H
          

          The delay function is defined elsewhere as follows:

          void delay(int millisecondsWait)
          {
              QEventLoop loop;
              QTimer t;
              t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
              t.start(millisecondsWait);
              loop.exec();
          }
          

          According to the QtDesigner debugger, stack overflow occurs at line 14 of the following asm code:

          Qt5Guid!__chkstk [d:\agent\_work\63\s\src\vctools\crt\vcstartup\src\misc\amd64\chkstk.asm @ 67]:
          0x7ff802413cc0                   sub     rsp,10h
          0x7ff802413cc4  <+    4>         mov     qword ptr [rsp],r10
          0x7ff802413cc8  <+    8>         mov     qword ptr [rsp+8],r11
          0x7ff802413ccd  <+   13>         xor     r11,r11
          0x7ff802413cd0  <+   16>         lea     r10,[rsp+18h]
          0x7ff802413cd5  <+   21>         sub     r10,rax
          0x7ff802413cd8  <+   24>         cmovb   r10,r11
          0x7ff802413cdc  <+   28>         mov   r11,qword ptr gs:[10h]
          0x7ff802413ce5  <+   37>         cmp     r10,r11
          0x7ff802413ce8  <+   40>         bnd jae Qt5Guid!__chkstk+0x42 (00007ff8`02413d02)
          0x7ff802413ceb  <+   43>         and     r10w,0F000h
          0x7ff802413cf1  <+   49>         lea     r11,[r11-1000h]
          0x7ff802413cf8  <+   56>         mov     byte ptr [r11],0   # <- stack overflow here.
          

          Any suggestion would be wellcomed.

          KroMignonK Offline
          KroMignonK Offline
          KroMignon
          wrote on last edited by
          #4

          @BartM said in Subclassed QLineEdit styleSheet update triggers stack overflow:

          Could someone more experienced with Qt tell me what could be improved in the below code to avoid this problem?

          I have some questions about this code:

          • why do you create a new thread for each instance? Is this really necessary?
          • is QSuperLine::resetLine() slot only called from &updater::freshUpdate signal?
          • why not use a QTimer() and a counter to handle the stylesheet changes? Something like:
          class QSuperLine : public QLineEdit
          {
              Q_OBJECT
          public:
              QThread * T1 = new QThread();
              updater * Up1 = new updater();
              QString InitialContent;
          
          QSuperLine (QWidget *parent = nullptr) : QLineEdit(parent) {
              Up1->moveToThread(T1);
              InitialContent = "";
              isBusy = false;
              QObject::connect( T1, &QThread::started, Up1, &updater::workNow);
              QObject::connect( Up1, &updater::freshUpdate, this, &QSuperLine::resetLine);
              T1->start();
          
              connect(&mStyleSheetTmr, &QTimer::timeout, this, &QSuperLine::doBlink);
              mStyleSheetTmr.setSingleShot(true);
              mStyleSheetTmr.SetInterval(50);
              mAnimeState  = 0;
          }
          
          inline bool IsBusy() const { return mAnimeState > 0; }
          
          private slots:
              void doBlink()
              {
                  QString newStyle;
                  switch(mAnimeState)
                  {
                  case 0:
                      newStyle = "background-color: rgb(20, 80, 180);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  case 1:
                      newStyle = "background-color: rgb(20, 80, 160);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  case 2:
                      newStyle = "background-color: rgb(20, 80, 140);border-style: solid;border-width: 2px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  case 3:
                      newStyle = "background-color: rgb(20, 80, 120);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  case 4:
                      newStyle = "background-color: rgb(20, 80, 100);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  case 5:
                      newStyle = "background-color: rgb(20, 80, 80);border-style: solid;border-width: 1px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  case 6:
                      newStyle = "background-color: rgb(20, 80, 40);border-style: solid;border-width: 0px;border-color: red;border-radius: 9px;color: cyan;";
                      break;
                  }
                  this->setStyleSheet(newStyle);
                  if(newStyle.isEmpty())
                  {
                      mAnimeState = 0;
                  }
                  else
                  {
                      ++mAnimeState;
                      mStyleSheetTmr.start();
                  }
              }
          
          public slots:
              void resetLine(int a) {
                  if (InitialContent != this->text() && mAnimeState == 0) {
                      InitialContent = this->text();
                      doBlink();
                  }
              }
          
          private:
             QTimer mStyleSheetTmr;
             int mAnimeState;
          };
          

          It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

          1 Reply Last reply
          2
          • B Offline
            B Offline
            BartM
            wrote on last edited by BartM
            #5

            Thanks a lot for all the suggestions. Indeed, as suggested by @JonB and @KroMignon - the main issue was the number of threads failing to run concurently. Changing the delay to 2 seconds did not solve the problem, it just delayed the inevitable.

            So what I did was as follows:

            1. Check which lines change by comparing their ->text() value with a localy stored QString,
            2. Pointers to the lines that changed were then appended to a QList<QLineEdit*>
            3. This new list of pointers was then forwarded to another thread running in the background that iterates through the list, and emits a signal to setStyleSheet that is then received in the main thread.
            4. After the completion of 'flashing' the list is cleared and make availabe for the next cycle.

            It works like a charm. Thanks a lot for the example with QSpinBoxes, it looks like the same approach can be indeed applied to other widgets and does not cause any stack problems. I don't know why I haven't thought about it... Though I'm a Qt noob :-)

            Again, thank you!

            Edit: If someone reads this in the future - the only catch with that method was the necessity to declare
            Q_DECLARE_METATYPE(QList<QLineEdit*>) in the header file. Withouth that the side thread was unable to process the QList<QLineEdit*>.

            JonBJ 1 Reply Last reply
            1
            • B BartM

              Thanks a lot for all the suggestions. Indeed, as suggested by @JonB and @KroMignon - the main issue was the number of threads failing to run concurently. Changing the delay to 2 seconds did not solve the problem, it just delayed the inevitable.

              So what I did was as follows:

              1. Check which lines change by comparing their ->text() value with a localy stored QString,
              2. Pointers to the lines that changed were then appended to a QList<QLineEdit*>
              3. This new list of pointers was then forwarded to another thread running in the background that iterates through the list, and emits a signal to setStyleSheet that is then received in the main thread.
              4. After the completion of 'flashing' the list is cleared and make availabe for the next cycle.

              It works like a charm. Thanks a lot for the example with QSpinBoxes, it looks like the same approach can be indeed applied to other widgets and does not cause any stack problems. I don't know why I haven't thought about it... Though I'm a Qt noob :-)

              Again, thank you!

              Edit: If someone reads this in the future - the only catch with that method was the necessity to declare
              Q_DECLARE_METATYPE(QList<QLineEdit*>) in the header file. Withouth that the side thread was unable to process the QList<QLineEdit*>.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by
              #6

              @BartM
              I am glad you changed approach.

              This new list of pointers was then forwarded to another thread running in the background that iterates through the list, and emits a signal to setStyleSheet that is then received in the main thread.

              I still don't know why you need any thread at all, seems more complicated than it needs to be. But if you're happy that's fine :)

              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