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 thendeleteLater
Any insight would be appreciated. I sincerely apologize if this is jumbled, but I will do my best to clarify any questions you have.
-
Hi,
Can you post the stack trace of your crash ?
-
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.
-
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.
-
Didn't you used to delete your numpad object ? If so you are likely trying to access a dangling pointer.
-
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 Mask123.15.16.5
. That is why I was checkingif(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 checkingisVisible())
resulted in errors.To fix this, I created a boolean value
numVis
that istrue
if the numberpad is visible, andfalse
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.