How to know child Window is destroyed form parent Window - c++

I have a parent-child window in my Qt application. Parent class is a QDialog named A and child class is QMainWindow named B. Now I want that whenever B is closed through the 'X' button a signal is to be emitted which can be caught by a slot in class A through which I want certain functionality to be implemented. Is there a predefined signal in Qt I can use?
I want something like this:
B *b=new B;
//some code
connect(b,SIGNAL(destroyed()),this,&A::doSomething);
B also has a QWidget which I can use to detect the destroyed signal. How do I implement this? Do I need to emit a custom signal from ~B() ?
Edit: I don't want to destroy the object b as this would require a reallocation when I want to recreate the window B from A and I want to keep the parameters of b.

Your solution would only work if you set a Qt::WA_DeleteOnClose attribute to your B widget:
b->setAttribute(Qt::WA_DeleteOnClose);
Another option would be to reimplement close event and emit a custom signal there.

Connect your object like this:
widget = new QWidget();
//widget->show(); //optional using
connect(widget, &QWidget::destroyed, this, &MainWindow::widgetDestroy);
widget->setAttribute(Qt::WA_DeleteOnClose);
.cpp :
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButtonNew_clicked()
{
widget = new QWidget();
widget->show();
connect(widget, &QWidget::destroyed, this, &MainWindow::widgetDestroy);
widget->setAttribute(Qt::WA_DeleteOnClose);
}
void MainWindow::on_pushButtonDel_clicked()
{
delete widget;
}
void MainWindow::widgetDestroy()
{
qDebug()<< "deleted."; //after destroy widget this function calling.
}
.h :
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>
#include <QDebug>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void widgetDestroy();
void on_pushButtonNew_clicked();
void on_pushButtonDel_clicked();
private:
Ui::MainWindow *ui;
QWidget *widget;
};
#endif // MAINWINDOW_H
.ui :

Related

Can't bind signal to slot in my Qt application

I'm new to Qt and I have a very simple demo app. It just include a QLineEdit widget and I want to invoke a function test() when I press ctrl+p in the QLineEdit.
Below are the related files.
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QShortcut>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QShortcut *s = new QShortcut(QKeySequence("Ctrl+P"), ui->lineEdit);
connect(s, SIGNAL(activated()), ui->lineEdit, SLOT(test()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void test(){
qDebug() << "test() triggered!" << endl;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void test();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
When I compile the application, I saw below messages in the debug panel and the application didn't respond to ctrl+p.
QObject::connect: No such slot QLineEdit::test() in ..\ShortcutIssueDemo\mainwindow.cpp:13
QObject::connect: (receiver name: 'lineEdit')
What's the problem with it?
You have 2 misconceptions:
The connection indicates the link between the object that emits the signal, the signal, the object to which the slot belongs and the slot. In your case it is obvious that the object to which the slot "slot" belongs is this.
If the old syntax (SIGNAL & SLOT) is to be used then "test" must be declared as slot.
So for the above there are 2 possible solutions:
Change to :
connect(s, SIGNAL(activated()), this, SLOT(test()));
public slots:
void test();
Or use new syntax:
connect(s, &QShortcut::activated, this, &MainWindow::test);
Between both solutions, the second one is better since it will indicate errors in compile-time instead of silent errors in run-time.
By default, the context of the shortcut is Qt::WindowShortcut, that is, it will fire when the key combination is pressed and the window has focus, if only when QLineEdit has focus then you have to change the context to Qt::WidgetShortcut:
s->setContext(Qt::WidgetShortcut);
You have received the error message saying there is no such slot...
Note that u haven't marked test() as slot, hence in <mainwindow.h>, replace
void test();
by
public slots: void test();
And the slot test() belongs to the mainwindow not to s, hence use this instead of s

How to display the input of QLineEdit in a dialog on the label of mainwindow?

I'm new to QtCreator and created a button on the mainwindow that opens a dialog. In the dialog i added QLineEdit and a button. When i click on this button, i want the input text to be displayed on a Qlabel in the mainwindow.
I found another question which is basicly the same, but for me it has to work the other way around.
So In the header of MainWindow.h i have added to public:
void setLabelText(QString str);
I created the getter/setter function and added it to MainWindow.cpp:
void MainWindow::setLabelText(QString str)
{
ui->label->setText(str);//it is label dialog
}
And in the windowdialog.cpp i have added under button click() slot:
void WindowDialog::on_pushButton_clicked()
{
QString str = ui->lineEdit->text();
MainWindow MainWindow;
MainWindow.setLabelText(str);
}
It compiles without errors. Yet when i click on the button in the dialog, it won't print the results in the mainwindow... I have been trying to find another examples about how to do this, but couldn't really find one yet. So what am i missing? Or how do i make this work?
You can use signals and slots for this. https://doc.qt.io/qt-5/signalsandslots.html
you would need to connect the signal to a slot by using this command
connect(dlg,&DialogWindow::transmit,this,&MainWindow::update);
anytime you want to call the update function you would just need to emit the signal.
emit transmit(ui->lineEdit->text());
I created a dialog window class in this example.
Dialog.h
#ifndef DIALOGWINDOW_H
#define DIALOGWINDOW_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE
class DialogWindow : public QDialog
{
Q_OBJECT
public:
DialogWindow(QWidget *parent = nullptr);
~DialogWindow();
signals:
void transmit(QString txt);
private slots:
void on_buttonBox_accepted();
private:
Ui::Dialog *ui;
};
#endif // DIALOGWINDOW_H
Dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
DialogWindow::DialogWindow(QWidget *parent)
: QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
DialogWindow::~DialogWindow()
{
}
void DialogWindow::on_buttonBox_accepted()
{
emit transmit(ui->lineEdit->text());
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "dialog.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void update(QString txt);
private:
Ui::MainWindow *ui;
DialogWindow *dlg;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
dlg = new DialogWindow(NULL);
connect(dlg,&DialogWindow::transmit,this,&MainWindow::update);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dlg->show();
}
void MainWindow::update(QString txt)
{
ui->label->setText(txt);
}
In this line MainWindow MainWindow;, you create a new MainWindow, but you want to set the text on your existing MainWindow.
Instead you might want to pass a reference or pointer to the MainWindow to the dialog, once you create it.
If you pass your MainWindow as the parent of the QDialog, you could do it this way:
void WindowDialog::on_pushButton_clicked()
{
QString str = ui->lineEdit->text();
static_cast<MainWindow*>(parent())->setLabelText(str);
}
A safer version using dynamic_cast:
void WindowDialog::on_pushButton_clicked()
{
QString str = ui->lineEdit->text();
MainWindow* window = dynamic_cast<MainWindow*>(parent());
if (!window) {
throw std::runtime_error{"Parent was not a MainWindow"};
}
window->setLabelText(str);
}

Widget initialized later will not show in MainWindow

I am learning Qt using Qt 5.13 on MacOS.
First I define MyWidget inherited from QWidget. MyWidget has a QPushButton, but this button will be created in a slot function called 'fresh', not in constructor.
I add MyWidget in MainWindow (inherited from QMainWindow), and defined another button_2 to emit signal to callMyWidget's 'fresh' function to create button.
If I did not hide MyWidget in MainWindow first, MyWidget's button will not show. If I hide MyWidget first, everything seems OK.
I hope to know the reason. Thanks
I tried to repaint or update MyWidget in 'fresh' function, but did not help.
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include<QPushButton>
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
public slots:
void fresh();
private:
QPushButton* myButton;
};
#endif // WIDGET_H
mywidget.cpp
#include "mywidget.h"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
}
MyWidget::~MyWidget()
{
}
void MyWidget::fresh()
{
myButton = new QPushButton(this);
myButton->setStyleSheet("QPushButton { background-color: green;}");
show();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"mywidget.h"
#include<QHBoxLayout>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
signals:
public slots:
private:
MyWidget* myWidget;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
QWidget* qwidget = new QWidget;
myWidget = new MyWidget(this);
QPushButton* button = new QPushButton("Show",this);
QHBoxLayout* mainLayout = new QHBoxLayout;
mainLayout->addWidget(myWidget);
mainLayout->addWidget(button);
qwidget->setLayout(mainLayout);
setCentralWidget(qwidget);
//myWidget->hide();
connect(button,&QPushButton::clicked,myWidget,&MyWidget::fresh);
}
main.cpp
#include "mywidget.h"
#include"mainwindow.h"
#include<QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
If I add myWidget->hide(); in mainwindow.cpp, it seems right.
If I remove it, the green button will not show, even if I repaint or update or show in fresh function.
'mywidget' is taking the whole space, if you want to know if it is taking all the space or not :
mywidget->setStyleSheet("* {border: 4px solid orange;}")
Since you are using a layout, you might want to determine the minimum size of a QPushButton. the QPushButton has a default horizontal size policy : "Minimum", by default. Maybe if you set the minimum width using this function : "setMinimumWidth(int width)", might fix your problem.
Also, don't forget to call this :
myButton->show();
Every object that inherits from QObject should be shown with this func ".show".
Here is all the flags for QSizePolicy will help you understand what is going on in layouts (layouts work a lot with QSizePlicy flags) : https://doc.qt.io/qt-5/qsizepolicy.html#Policy-enum .
Unless you don't want the layout, you have to specify the position and the size in this way :
mywidget->setGeometry(QPoint(x, y), QSize(width, height));
and the same thing for your buttons.

Trouble implementing connect() with signals and slots

While doing some threading tutorials I got carried away and decided to make a gui which will show me the effect of multiple threads writing to one variable and using mutex.
The app uses mainwindow.ui menu to create a new instance of the threaddialog class every time which then runs it's own thread, displaying it's count loop on labels. Before I get onto doing the loop and having a mutex 'toggle', I am trying to connect the count update between the mainwindow and threaddialog so mainwindow can show the global count updating.
I can't get the connect() right, I am trying to pass it a pointer to the new threaddialog I just made before it, as that will be signalling the count, and the signal itself. Then for slot I use the this pointer to send the address of MainWindow, as that is where the slot is located, and the slot name itself.
As it stands, the connect() line gives me this error for both signal and slot parameters.
C:\Users\btank\Documents\Qt Projects\QThreadClasses\mainwindow.cpp:46: error: C3867: 'ThreadDialog::gCountUpdate': non-standard syntax; use '&' to create a pointer to member
I have read the whole page on Qt signals and slots official docs to try and understand what I'm doing wrong but no luck and need help. I don't believe I'm doing anything wrong regarding sending those pointers to connect().
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGridLayout>
#include <threaddialog.h>
#include <QLinkedList>
#include <QDebug>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
QGridLayout *layout = new QGridLayout();
void NewThread();
~MainWindow();
private slots:
void on_actionNew_Thread_triggered();
void on_actionDelete_Thread_triggered();
public slots:
void setGCount(int gCount);
private:
QLinkedList<ThreadDialog *> list;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <threaddialog.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//Clear memory list before using
list.clear();
qDebug() << list.count();
// Set layout in widget
QWidget *window = new QWidget();
window->setLayout(layout);
// Add widget to main window central widget
setCentralWidget(window);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionNew_Thread_triggered()
{
NewThread();
}
void MainWindow::NewThread()
{
ThreadDialog *newThread = new ThreadDialog(list.count());
qDebug() << list.count();
connect(newThread, newThread->gCountUpdate, this, this->setGCount);
newThread->run();
list << newThread;
layout->addWidget(newThread);
}
void MainWindow::on_actionDelete_Thread_triggered()
{
layout->removeWidget(list.last());
delete list.last();
list.removeLast();
qDebug() << list.count();
}
void MainWindow::setGCount(int gcount)
{
ui->lblGCount->setText(QString::number(gcount));
}
threaddialog.h
#ifndef THREADDIALOG_H
#define THREADDIALOG_H
#include <QDialog>
#include <QThread>
namespace Ui {
class ThreadDialog;
}
class ThreadDialog : public QDialog
{
Q_OBJECT
public:
explicit ThreadDialog(int count, QWidget *parent = 0);
void run();
~ThreadDialog();
signals:
void gCountUpdate(int uCount);
private:
Ui::ThreadDialog *ui;
};
#endif // THREADDIALOG_H
threaddialog.cpp
#include "threaddialog.h"
#include "ui_threaddialog.h"
#include "mainwindow.h"
ThreadDialog::ThreadDialog(int count, QWidget *parent) :
QDialog(parent),
ui(new Ui::ThreadDialog)
{
ui->setupUi(this);
// Setup UI
ui->lblTCount->setText(QString("Thread %1").arg(count));
}
ThreadDialog::~ThreadDialog()
{
delete ui;
}
void ThreadDialog::run()
{
for(int i = 0; i < 100; i++)
{
qDebug() << (QString("Thread loop %1").arg(i));
ui->lblTNum->setText(QString("Thread %1").arg(i));
emit this->gCountUpdate(i);
QThread::sleep(100);
}
}
This is how it should look like:
connect(newThread, &ThreadDialog::gCountUpdate
this, &MainWindow::setGCount);
You need to get a pointer to the method, not call the method.

Qt MainWindow crashing

Edit: I removed the destructor from the slot. But now I have memory leaking problems. Each new window that I open occupies some memory,and when I close it,the memory stays occupied
When I execute the program,and open new windows, they are opened normally. When I close any of them, the whole application crashes (not only that specific window),and I get the crash error.
What am I doing wrong?
mainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QHBoxLayout;
class QTextEdit;
class QWidget;
class QDialog;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void closeWindow();
void newWindow();
private:
Ui::MainWindow *ui;
MainWindow *tempMainWindow;
QHBoxLayout * mainLyt;
QTextEdit *txtEdit;
QWidget *mainWidget;
};
#endif // MAINWINDOW_H
mainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QWidget>
#include <QHBoxLayout>
#include <QTextEdit>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mainWidget=new QWidget();
mainLyt=new QHBoxLayout();
txtEdit=new QTextEdit();
mainLyt->addWidget(txtEdit);
mainWidget->setLayout(mainLyt);
setCentralWidget(mainWidget);
connect(ui->actionExit,SIGNAL(triggered()),this,SLOT(closeWindow()));
connect(ui->actionNew,SIGNAL(triggered()),this,SLOT(newWindow()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeWindow()
{
this->close();
delete txtEdit;
delete mainLyt;
delete mainWidget;
this->~MainWindow();
}
void MainWindow::newWindow()
{
tempMainWindow=new MainWindow(this);
tempMainWindow->show();
}
If you pass to QWidget(), QHBoxLayout() and QTextEdit() also this (which is the parent), at the delection of the MainWindow Qt will delete for you the ui and all the additional widgets yur defined in the construstor. In this way you can avoid to call closeWindow() method.
delete ui is also not necessary.
ui->setupUi(this);
mainWidget = new QWidget(this);
mainLyt = new QHBoxLayout(this);
txtEdit = new QTextEdit(this);
I'm trying to make basic text editor,and when New is triggered, it should open a new window for new text document. Is there some better way to do this?
Yes. It's called a factory, and it can be a static method as it doesn't operate on any object. You can call it from a slot, of course.
I imagine you'll need to pass a file name to the newly created window - that could be an argument to the factory method and the factory slot. If the "new" window is empty, then this is not an issue.
Other issues:
There is no reason to keep the mainWidget member: it is always available as centralWidget().
There's also no reason to have the members other than ui as pointers. It is actually a premature pessimization - it will waste a bit more heap memory.
You don't need a layout for the central widget if it has no child widgets. The QTextEdit instance itself can be the central widget.
The ui instance should be retained using a smart pointer. This makes the destructor completely compiler-generated (it has an empty body).
You don't need anything fancy in the closeWindow slot. Simply delete the instance!
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static MainWindow * createWindow();
void setFileName(const QString & fileName);
public slots:
void closeWindow();
void newWindow();
private:
QScopedPointer<Ui::MainWindow> const ui;
QTextEdit m_txtEdit;
};
void MainWindow::newWindow() {
createWindow()->show();
}
void MainWindow::closeWindow() {
deleteLater();
}
MainWindow * MainWindow::createWindow(QWidget * parent) {
return new MainWindow(parent);
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setCentralWidget(&m_txtEdit);
connect(ui->actionExit, SIGNAL(triggered()), SLOT(closeWindow()));
connect(ui->actionNew, SIGNAL(triggered()), SLOT(newWindow()));
}
MainWindow::~MainWindow()
{}
Let us see your code:
this->close();
delete txtEdit;
delete mainLyt;
delete mainWidget;
this->~MainWindow();
You are trying to destroy them and for the next open you allocate them almost the same way.
What you achieve here is basically performance penalty. I would suggest to hide, modify, and so on the unwanted items instead.