Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Qt Academy Launch in California!

    Solved Custom IP Editor Plugin crashes in Application

    General and Desktop
    ip editor customplugin gui development
    2
    7
    2172
    Loading More Posts
    • 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.
    • Sh1gs
      Sh1gs last edited by

      Hello,

      Due to the nature of my job, I can't post a whole lot of code in the topics I create, however, for this case, I was given permission to type out my code since no one else in my department has a lot of experience with this.

      Anyhoo, I took a lot of inspiration from this post. The biggest difference being that I am adding touch screen functionality with a popup number pad. Please bare with me as I'm still learning the intricacies that QT offers for custom plugins and such. If you see a better way to do things, please let me know.

      Now, the problem I am having is, I create a custom IP Editor plugin (I will provide the code shortly). I add the plugin to my application by including the .pri. I also put the .dll in the qt/plugins/designer folder. When I run my application and I go to use my plugin, it works fine for a time, and then it crashes. I've pulled my hair out trying to figure out why and I just can't seem to get it.

      My plugin contains the following files:

      numpad.h

      #include <QWidgets>
      
      namespace Ui{
      class Numpad;
      }
      
      class NumPad : public QWidget
      {
           Q_OBJECT
      
      public:
           explicit NumPad(QWidget *parent = 0);
           QPushButton *seven, *eight, *nine, *four, *five, *six, *one, *two, *three, *zero, *dot, *del, *done;
           ~NumPad();
      
      signals:
           void sendData(const QString &text);
      
      private slots:
           void btnClicked();
      
      private:
           Ui::NumPad *ui;
           void createNumPad();
      };
      

      I also have a numpad.ui file. It contains a QFrame named numPad. In the .cpp file, the buttons are being added to that QFrame. Admittedly, I could've added the buttons manually and only had the one function that sends the text, I may amend this later.

      numpad.cpp

      #include "numpad.h"
      #include "ui_numpad.h"
      
      NumPad::NumPad(QWidget *parent) : QWidget(parent), ui(new Ui::NumPad)
      {
           ui->setupUI(this);
           createNumPad();
      }
      
      void NumPad::createNumPad()
      {
           seven = new QPushButton(tr("7"), ui->numPad);
           seven->setFocusPolicy(Qt::NoFocus);
           seven->setStyleSheet("font:bold; font-size:12pt;");
           seven->setGeometry(0,0,61,36);
      ...
           del = new QPushButton(tr("DEL"), ui->numPad);
           del->setFocusPolicy(Qt::NoFocus);
           del->setStyleSheet("font:bold; font-size:12pt;");
           del->setGeometry(0,144,183,36);
           del->setAutoRepeat(true);
      
           connect(seven, &QPushButton::clicked, this, &NumPad::btnClicked);
      ...
      }
      
      void NumPad::btnClicked()
      {
           QPushButton *btn = qobject_cast<QPushButton*>(sender());
           emit this->sendData(btn->text());
      }
      
      NumPad::~NumPad
      {
           delete ui;
      }
      

      In my ipeditor.ui I have 4 LineEdits, and 3 Labels so it looks similar to ___.___.___.___ I have a frame around all of those so it looks like a nice cohesive unit, rather than 7 different widgets. Then my IpEditor files are as follows:

      ipeditor.h

      #include <QWidgets>
      #include "numpad.h"
      
      namespace Ui{
      class IPEditor;
      }
      
      class IPEditor : public QWidget
      {
           Q_OBJECT
      
      public:
           explicit IPEditor(QWidget *parent = 0);
           ~IPEditor();
      
      public slots:
           void receiveData(const QString &text);
           void txtChanged(QLineEdit *input);
      
      signals:
           void sigTxtChanged(QLineEdit *input);
      
      protected:
           bool eventFilter(QObject *obj, QEvent *e);
      
      private:
           Ui::IPEditor *ui;
           class Numpad *num;
           QLineEdit *curEdit;
           QList<QLineEdit*> leList;
           void movNextLE(int i);
           void movPrevLE(int i);
           void popupNumPad();
           void setupIP();
      };
      

      ipeditor.cpp

      #include "ipeditor.h"
      #include "ui_ipeditor.h"
      
      IPEditor::IPEditor(QWidget *parent) : QWidget(parent), ui(new Ui::IPEditor)
      {
           ui->setupUI(this);
           this->setFixedSize(183,32);
           leList << ui->firstOct << ui->secondOct << ui->thirdOct << ui->fourthOct; //putting my ui LineEdits in a list
           setupIP();
      }
      
      void IPEditor::setupIP()
      {
      //Upon initialization, the IP Editor is empty, and only allows the user to click on the first LineEdit
           for(int i = 0; i != leList.size(); i++)
             if(leList[i]->text.isEmpty(){
               leList[i]->setFocusPolicy(Qt::NoFocus);
               leList[i]->setReadOnly(true);
               leList[i]->setMaxLength(3);
               leList[i]->setValidator(new QIntValidator(0,255, this));
             }
             leList[0]->setFocusPolicy(Qt::ClickFocus);
             leList[0]->setReadOnly(false);
             leList[i]->installEventFilter(this);
           }
           connect(this, SIGNAL(sigTxtChangedd(QLineEdit*)), this, SLOT(txtChanged(QLineEdit*)), Qt::QueuedConnection);
      }
      
      bool IPEditor::eventFilter(QObject *obj, QEvent *e)
      {
           if(e->type() == QEvent::FocusIn){
             curEdit = qobject_cast<QLineEdit*>(obj);
      //this assumes the user has closed the popupnumpad and intends to change one of the LineEdits
             if(!curEdit->text.isEmpty(){ 
               if(num->isVisible())
                 num->close();
             }
             popupNumPad();
           }
           else if(e->type() == QEvent::KeyPress){
             QKeyEvent *leEvent = static_cast<QKeyEvent*>(e);
             for(int i=0; i != leList.size(); i++){
               QLineEdit *input = leList[i];
               if(input == obj){
                 switch(leEvent->key()){
                 case Qt::Key_0:
                   if(input->text().isEmpty() || input->text() == "0")
                     movNextLE(i);
                   emit txtChanged(input);
                   break;
                 case Qt::Key_Backspace:
                   if(input->text().isEmpty() || input->cursorPosition() == 0)
                     movPrevLE(i);
                   break;
                 case Qt::Key_Period:
                   movNextLE(i);
                   break;
                 case Qt::Key_Tab:
                   movNextLE(i);
                   return true;
                   break;
                 case Qt::Key_Return:
                   num->close();
                   input->clearFocus();
                   input->setFocusPolicy(Qt::ClickFocus);
                   return true;
                   break;
                 case Qt::Key_Enter:
                   num->close();
                   input->clearFocus();
                   input->setFocusPolicy(Qt::ClickFocus);
                   break;
                 case Qt::Key_Left:
                   if(input->cursorPosition == 0)
                     movPrevLE(i);
                   break;
                 case Qt::Key_Right:
                   if(input->text().isEmpty() || input->text().size() == input->cursorPosition()))
                     movNextLE(i);
                 default:
                   emit txtChanged(input);
                   break;
                 }
               }
             }
           }
           return QOBject::eventFilter(obj, e);
      }
      
      void IPEditor::popupNumPad()
      {
      //calculates geometry of parent application and will popup the numberpad above or below the IP Editor
           num = new NumPad(parentWidget());
           QRect guiArea = parentWidget()->geometry();
           QPoint point(this->geometry().x(), this->geometry().y() + ui->ipFrame->height());
           QRect frame = num->geometry();
      
           if(point.y() + num->height() > guiArea.bottom()){
             frame.setX(point.x());
             frame.setY(point.y() - ui->ipFrame->height() - num->height());
             num->setGeometry(frame.x(), frame.y(), num->width(), num->height());
             connect(num, SIGNAL(sendData(QString)), this, SLOT(receiveData(QString)));
             num->show();
           }
           else{
             frame.setX(point.x());
             frame.setY(point.y());
             num->setGeometry(frame.x(), frame.y(), num->width(), num->height());
             connect(num, SIGNAL(sendData(QString)), this, SLOT(receiveData(QString)));
             num->show();
          }
          num->setAttribute(Qt::WA_DeleteOnClose);
      }
      
      void IPEditor::receiveData(const QString &text)
      {
           if(text == "DEL"){
             QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier, text, true);
             QApplication::sendEvent(this, event);
             QApplication::postEvent(curEdit, event);
             QKeyEvent *rE = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier, text, true);
             QApplication::sendEvent(this, rE);
             QApplication::postEvent(curEdit, rE;
           }
           else if(text == "DONE"){
             QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, text);
             QApplication::sendEvent(this, event);
             QApplication::postEvent(curEdit, event);
           }
           else if(text == "."){
             QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Period, Qt::NoModifier, text);
             QApplication::sendEvent(this, event);
             QApplication::postEvent(curEdit, event);
           }
           else if(text == "0"){
             QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, text);
             QApplication::sendEvent(this, event);
             QApplication::postEvent(curEdit, event);
           }
           else{
             QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, 0, Qt::NoModifier, text);
             QApplication::sendEvent(this, event);
             QApplication::postEvent(curEdit, event);
           }
      }
      
      void IPEditor::txtChanged(QLineEdit *input)
      {
           for(int i = 0; i != leList.size(); i++){
             if(input == leList[i]{
               if((input->text().size() == 2 && input->text().size() == input->cursorPosition()) || input->text() == "0"){
                 if(i+1 != leList.size())
                   movNextLE(i);
               }
             }
           }
      }
      
      void IPEditor::movNextLE(int i)
      {
           if(i+1 != leList.size()){
             num->close();
             leList[i+1]->setFocus();
             leList[i+1]->setReadOnly(false);
             leList[i+1]->setFocusPolicy(Qt::ClickFocus);
             leList[i+1]->setCursorPosition(0);
             leList[i+1]->selectAll();
           }
      }
      
      void IPEditor::movPrevLE(int i)
      {
           if(i!=0){ //this was added in the event that the user wanted to clear the IP Editor
             if(leList[i]->text().isEmpty()){
               leList[i]->setReadOnly(true);
               leList[i]->setFocusPolicy(Qt::NoFocus);
             }
           num->close();
           leList[i-1]->setFocus();
           leList[i-1]->setCursorPosition(leList[i-1]->text().size());
           }
      }
      
      IPEditor::~IPEditor()
      {
           delete ui;
      }
      

      I know it's really clunky, and can be improved. What I'm having trouble is when I use this plugin in an application, say I enter an IP Address 123.12.15.5 and I close the popup number pad, if I click on any of the LineEdits to change the mask, it crashes. But it doesn't always crash after clicking a LineEdit immediately. Sometimes it's the 4th or 5th LineEdit that I click that it'll crash. I don't understand. I THINK it has something to do with the Focus of each LineEdit creating an instance of the popup numberpad and then closing/deleting it when another LineEdit gains focus. So, I'm not sure if I should instead do something like
      num->hide(); and then deleteLater

      Any insight would be appreciated. I sincerely apologize if this is jumbled, but I will do my best to clarify any questions you have.

      1 Reply Last reply Reply Quote 0
      • SGaist
        SGaist Lifetime Qt Champion last edited by

        Hi,

        Can you post the stack trace of your crash ?

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

        Sh1gs 1 Reply Last reply Reply Quote 1
        • Sh1gs
          Sh1gs @SGaist last edited by

          Hello @SGaist

          Please forgive my noob of a question, how do I get the stack trace? I'm using Windows.

          1 Reply Last reply Reply Quote 0
          • SGaist
            SGaist Lifetime Qt Champion last edited by

            When running Qt Creator and your app crashes it shows a window at the bottom with the call stack. You need to use a debug build of your app.

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

            Sh1gs 1 Reply Last reply Reply Quote 0
            • Sh1gs
              Sh1gs @SGaist last edited by

              @SGaist

              It seems that I get a segmentation fault when it gets to my eventFilter with the line

              if(num->isVisible())
              

              My intention here is to check if a numpad already exists, for example, moving between LineEdits that already have text in them, so that another numpad isn't created. If I take this line out completely, my numpad stays open and won't close even when "Done" or "Enter" is pressed.

              1 Reply Last reply Reply Quote 0
              • SGaist
                SGaist Lifetime Qt Champion last edited by

                Didn't you used to delete your numpad object ? If so you are likely trying to access a dangling pointer.

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

                Sh1gs 1 Reply Last reply Reply Quote 1
                • Sh1gs
                  Sh1gs @SGaist last edited by

                  @SGaist

                  Ok, after fiddling around with debugging and testing things, I believe I have solved it. So my thought process was the user inputs an IP Address 123.123.123.123 and before closing the popup numberpad, clicks on the different LineEdits to change the IP Mask 123.15.16.5. That is why I was checking if(num->isVisible()) because then it would close/delete/popup each time a LineEdit is clicked. I was having problems/segmentation faults because after the user clicks "Done" (or presses Enter/Return on the keyboard), the popup is deleted so checking isVisible()) resulted in errors.

                  To fix this, I created a boolean value numVis that is true if the numberpad is visible, and false otherwise. It is this value that I am now checking and everything seems to work just fine. I hope this makes sense.

                  I will go ahead and mark this as solved.

                  1 Reply Last reply Reply Quote 0
                  • First post
                    Last post