Qt action won't connect to slot - c++

Writing a school project in c++ and qt. It is supposed to be a block editor (like draw.io). I generate blocks as a buttons and setting them to a grid. Each button is supposed to have own menu to be able to get edited, deleted, etc. (image example:
We are encountering a problem, that our action won't connect to a slot. Function akceAkce is supposed only to print 1 onto output (via qInfo). But when I click on the menu button, it does nothing. Any suggestions appreciated. Thanks!
void BlockItem::createButton() {
this->button = new QPushButton("+");
this->buttonMenu = new QMenu(this->button);
this->connectBlocks = new QAction(tr("Connect"), this->buttonMenu);
connect(this->connectBlocks, &QAction::triggered, this, &BlockItem::akceAkce);
this->buttonMenu->addAction(this->connectBlocks);
this->button->setMenu(this->buttonMenu);
}
void BlockItem::akceAkce() {
qInfo("1");
}

I would suggest a possible explanation: you put the BlockItem instance on the stack, and it's going out of scope somewhere in your code, and destroyed consequently, while the push button, menu and action all survive it, since they're instantiated with new, thus they live on the heap.
Something like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
BlockItem blockitem;
blockitem.createButton();
ui->gridLayout->addWidget(blockitem.getButton());
}
In the sample code above, blockitem goes out of scope at the end of the method, so it is destroyed and the connection loses its receiver side (i.e. breaks).
The same would happen if you instantiate a BlockItem object on the heap, using new, but accidentally delete it somewhere. Again, the signal sender (the QAction object) survives the receiver and the connection is broken.

Related

How to release memory?

I have Created a simple QT application for my university assignment. What i have done is pop up a new QManinWindow from a Above QMainWindow. When i click a button from the main ui it will pop up a new QMainWindow object (Note Pad)
Note pad is also a QMainWindow object
My Problem is when I'm creating the object it takes some memory from the ram but when I'm closing it (pop up window) memory is not releasing. When each time I'm pressing a button memory is allocated but application does not relese the memory when im closing it. Please check the main screen of the app.
i just want to know how to release that memory. I have tried so many things but nothing worked well.
I have set the closeEvent public on NotePad class and I listen the close event from main object when its get triggered i have deleted the poped up object. But it cause ad unexpected stop.
void MainWindow::on_notePadBtn_clicked()
{
NotePad *notePad = new NotePad(this);
notePad->raise();
notePad->activateWindow();
notePad->show();
}
NotePad::NotePad(QWidget *parent) :QMainWindow(parent),ui(new Ui::NotePad) {
ui->setupUi(this);
this->setWindowTitle("Note Pad");
}
You don't really need to override closeEvent, Qt has Qt::WA_DeleteOnClose attribute, that does exactly what you want, you can use it like this:
//...
NotePad *notePad = new NotePad(this);
notePad->setAttribute(Qt::WA_DeleteOnClose);
notePad->raise();
notePad->activateWindow();
notePad->show();
//...
I'm not familiar with Qt.
But to my understanding if you use the operator new
you must use delete (in a scope where you have access to the pointer created with new).
Object *foo = new Object();
// Do stuff with foo...
delete foo;
// DO NOT use foo from now on.
Hope that helps a bit, maybe. Like I said I'm not familiar with Qt
so if you have doubts about how some features are implemented you should look at the their docs.
(cf: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf §3.7.4p63)

Qt connect in constructor not connecting?

A google search gives these as the top three results:
Qt: Connect inside constructor - Will slot be invoked before object is initialized?
Qt can I connect signals/slots to self in constructor?
QT Connect Signal Slot and initialize in constructor
According to those, it seems like it ought to "just work" like anything else. But this code doesn't:
EditorList::EditorList(..., QWidget* parent) :
QWidget(parent)
{
...
Processing* processing = Processing::getInstance();
connect(this, SIGNAL(reorderDelete(DataSequence*,ListType,QList<int>)), processing, SLOT(reorderDelete(DataSequence*,ListType,QList<int>)));
...
buttonDelete = new QPushButton(this);
connect(buttonDelete, SIGNAL(clicked(bool)), this, SLOT(buttonDeleteClick()));
...
}
...
void EditorList::buttonDeleteClick()
{
...
QList<int> locations;
...
emit reorderDelete(mySequence, myListType, locations); //breakpoint 1 here
}
//-----------------------------------------------------------------
void Processing::reorderDelete(DataSequence* sequence, ListType listType, QList<int> locations)
{
if(sequence) //breakpoint 2 here
{
sequence->reorderDelete(listType, locations);
}
}
The reason for this structure, instead of calling mySequence->reorderDelete directly, is to have it done in Processing's thread instead of the UI's. I hope I haven't stripped out too much detail to show the problem; this is a rather large project.
When I click my delete button, I hit breakpoint 1 (so far, so good), but I don't hit breakpoint 2. My other signals/slots work across threads, but their connects are not in constructors. I want to make this one automatic so that every instance is "just connected" without having to remember to do it. Can I not do that?
Okay, I got it. Leaving up for others to find.
According to this, my ListType enum was blocking the system from making the connection. It only works with system-known datatypes because emitting a SIGNAL actually stores a copy for the SLOT(s) to read later. I knew that, but I thought it was more like a stack frame that could take anything. Apparently not.
It also works to put a call to qRegisterMetaType<ListType>("ListType"); somewhere before the connect. (I put it in my main window's constructor.) This makes the datatype known so that the connection can work anyway.
I'm hitting both breakpoints now.
Make sure you have used Q_OBJECT macros in your class

Qt::WA_DeleteOnClose

I am learning Qt and trying some examples in the book "Foundations of Qt Development".
In the book, there is a section teaching Single Document Interface with an example creating a simple app like a notepad.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle(QString("%1[*] - %2").arg("unnamed").arg("SDI"));
connect(ui->docWidget->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool)));
createActions();
createMenu();
createToolbars();
statusBar()->showMessage("Done");
}
The book said that "Setting the windows attribute to Qt::WA_DeleteOnClose so that Qt takes care of deleting the window from memory as soon as it is closed.
How it works?
Because if i use setAttribute(Qt::WA_DeleteOnClose);, when I end the program, there is a Debug Assertion Failed warning:_BLOCK_TYPEIS_VALID(pHead->nBlockUse).
There is no problem if the setAttribute is removed.
Qt takes care of the deletion by itself if you set all the parenting right (if you create a new QObject/QWidget set the parent in the constructor). If the parent will be destructed, then the children will be too. In your main file, you can create the mainwindow on the stack, such that it will be destructed at the end of scope.
To call addToolbar you don't need this->, since it is a method of the class anyway.
The toolbar ptr should be a member to access it easily later on. But initialize it with nullptr (or NULL if you don't have c++11) in the initialization list of the constructor to know whether it is initialized or not.
The addToolBar call should work. A workaround would be to create a QToolBar on your own and add the pointer to the MainWindow using another addToolBar overload.

QDialog flash back after create

I have a QListWidget on my QMainWindow, and I connect the itemDoubleClicked(QListWidgetItem*) signals to a slot like the following code:
connect(listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(popUpMyDialog(QListWidgetItem*)));
My popUpMyDialog(QListWidgetItem*) function is like:
QMyDialog *myDialog = new QMyDialog(this);
myDialog->show();
QMyDialog is a class which I inherent from QDialog, and no operation except ui->setupUi(this); was done.
But when I try to double click on the item of QListWidget, the myDialog flashed and disappear very soon.
So I had tried to write some code to judge if the myDialog is deleted like that:
QMyDialog *myDialog = new QMyDialog(this);
connect(myDialog, SIGNAL(destroyed(QObject*)), this, SLOT(handleQMyDialogClose(QObject*)));
myDialog->show();
and the slots function handleQMyDialogClose(QObject*) just do:
qDebug() << "myDialog is closed";
When I double click on the item of item of QListWidget, the console print myDialog is closed, which means the myDialog object is deleted, but I don't delete the pointer, so I feel confused.
I Googled it and tried to setModal attributes to myDialog , but it take no effect.
I tried to copy the same code to my Mac, the strange thing is that it runs perfect.
I tried to add a for loop in my popUpMyDialog(QListWidgetItem*) like that:
QMyDialog *myDialog = new QMyDialog(this);
connect(myDialog, SIGNAL(destroyed(QObject*)), this, SLOT(handleQMyDialogClose(QObject*)));
myDialog->show();
for(int i = 0; i < 100; ++i) {qDebug() << i;}
to block the thread, and find that the myDialog window work prefect, but if I comment the for loop code, it flash back again.
So, I want to know what error happened to my code, and how I can try to handle it.
My coding environment is:
Windows 10 and Mac OS X 10.10.4 Yosemite, the version of Qt is Qt5.5.0, and on my Windows , the Qt runs with mingw.
So, you have the following method:
void Foo::popUpMyDialog(QListWidgetItem*) {
QMyDialog *myDialog = new QMyDialog(this);
myDialog->show();
}
The only reason why the dialog would get prematurely destroyed is if the instance of Foo, that the dialog is a child of, got destructed.
To troubleshoot the issue, first try to create a parentless dialog:
void Foo::popUpMyDialog(QListWidgetItem*) {
QMyDialog *myDialog = new QMyDialog;
myDialog->show();
}
If that dialog remains visible, then you know that you gave it a wrong, short-lived parent. The solution would be to find another parent. To avoid leaking the dialogs, you can give the dialog a Qt::WA_DeleteOnClose attribute.
QDialog has an exec() function that "blocks" the execution (as you do with your loop), to use instead of show() so your dialog can stay visible. Isn't it what you are looking for?

QLineEdit editingFinished signal twice when changing focus?

I've found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler. In my case I am a bit stuck as I am getting the editFinished signal twice even when my slot handler is doing nothing.
For a test, I have an array of QLineEdit which use a signalMapper to connect the editingFinished() signals to a single slot. The signalMapper passes the array index so I can see where the signal came from.
eg:
testenter::testenter(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testenter)
{
// setup the UI according to the .h file
ui->setupUi(this);
signalMapper = new QSignalMapper(this);
// init the labels and edit boxes
for (int i = 0; i < 10; i++)
{
pm_label[i] = new QLabel(ui->scrollArea);
QString text = QString("Number %1").arg(i);
pm_label[i]->setText(text);
pm_label[i]->setGeometry(10,20+i*30, 50, 20);
pm_label[i]->show();
pm_editBox[i] = new QLineEdit(ui->scrollArea);
pm_editBox[i]->setGeometry(80,20+i*30, 50, 20);
pm_editBox[i]->show();
signalMapper->setMapping(pm_editBox[i], int(i));
connect(pm_editBox[i], SIGNAL(editingFinished()), signalMapper, SLOT(map()));
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(data_entry(int)));
}
void testenter::data_entry(int entry)
{
//dummy
}
When run in the debugger, if I enter data into one box then either hit return or select another box with the mouse (ie change focus) , then it calls data_entry twice, the first time with index of the box that is losing focus and the 2nd time with the box which gets the focus.
So my question: Am I missing something? Is this expected behaviour or a bug?
If a bug, anyone know a way round it as I wanted to use this signal to do custom validation on data when it is entered (by either return, tab or mouse click to change focus).
First off, no this isn't expected behavior, i.e. selecting a QLineEdit should not cause it's editingFinished signal to be emitted.
There are a couple of possible things that may cause this problem:
You've accidentally connected a signal twice to a slot
The slot map() is causing the newly selected box to lose focus
In the same vain, if you're debugging and using a break point to detect when the slots are getting called you may be causing the QLineEdit to lose focus when the active application changes from your QWidget to your debugger, again causing the signal to be sent again.
If you're having problems because of a doubly connected slot, which doesn't seem to be the case because you're specifically getting a signal from two different QLineEdits, you can make sure that this isn't happening by specifying the connection type, the connect method actually has an additional optional argument at the end which allows you to change the type from a DefaultConnection to a UniqueConnection.
That being said, data validation is something that Qt has an established mechanism for, and I suggest that you use it if possible, look into extending the QValidator abstract base class Ref Doc. You then tell each of your QLineEdit's to use the same validator.
I have run into the same issue. It really does emit the signal twice, which is a known bug: https://bugreports.qt.io/browse/QTBUG-40 which however has not been addressed for a very long time.
Finally I found that the best solution in my case is to change the signal from editingFinished to returnPressed. As a side effect this behaves much more predictably from the user perspective. See also here: http://www.qtforum.org/article/33631/qlineedit-the-signal-editingfinished-is-emitted-twice.html?s=35f85b5f8ea45c828c73b2619f5750ba9c686190#post109943
The OP "found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler." Well, that is my situation also, and here is where I ended up. So, at the risk of going off topic...
In my situation, when my slot receives the editingFinished signal sent from the QLineEdit, I launch a modal QMessageBox to ask the user something. The appearance of that message box is what triggers the QLineEdit to send the second, undesirable editingFinished signal.
A post in the bug report (https://bugreports.qt.io/browse/QTBUG-40) mentioned by #V.K. offers a workaround which helped me. The following is my implementation of the workaround. I let Qt magic mojo automatically connect the QLineEdit signal to my MainWindow slot.
void MainWindow::on_textbox_editingFinished( void )
{
QLineEdit * pTextbox = qobject_cast<QLineEdit *>( QObject::sender() );
if ( !pTextbox->isModified() )
{
// Ignore undesirable signals.
return;
}
pTextbox->setModified( false );
// Do something with the text.
doSomething( pTextbox->text() );
}
void MainWindow::doSomething( QString const & text )
{
QMessageBox box( this );
box.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
box.setText( "Are you sure you want to change that text value?" );
if ( box.exec() == QMessageBox::Yes )
{
// Store the text.
m_text = text;
}
}