Custom IP Editor Plugin crashes in Application



  • 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.


  • Lifetime Qt Champion

    Hi,

    Can you post the stack trace of your crash ?



  • Hello @SGaist

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


  • Lifetime Qt Champion

    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.



  • @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.


  • Lifetime Qt Champion

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



  • @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.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.