I'm connecting a signal/slot but the slot is called multiple times (1, 2, 3...) every time I trigger the option, here are my classes:
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
Dialog *dialog;
signals:
void s1(QString s);
private slots:
void on_actionTooo_triggered();
};
#endif // MAINWINDOW_H
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionTooo_triggered()
{
dialog = new Dialog(this);
QString s = "hello";
qDebug() << "CONNECT: " << connect(this, SIGNAL(s1(QString)),
dialog, SLOT(s1(QString)), Qt::UniqueConnection);
emit s1(s);
dialog->show();
}
dialog.h:
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
public slots:
void s1(QString str);
private:
Ui::Dialog *ui;
};
dialog.cpp:
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::s1(QString str)
{
qDebug() << str << endl;
}
Every time the option is triggered in the main window, I connect the signal (just once), to the new dialog, but when I run, for example two times, it prints two "hello". If I put a disconnect(this, 0, 0, 0); before I connect the signals it works.
It seems odd to me that it maintains the connect even when the object is destroyed. It binds the connect to the same object created before. Is this the expected behavior?
You are creating a new Dialog everytime you call on_actionTooo_triggered(). This Dialog isn't deleted at the end of your function. Therefore with the next call the signal MainWindow::s1(QString) is emitted to the two different Dialogs which results in multiple ouptuts in qDebug.
void MainWindow::on_actionTooo_triggered()
{
dialog = new Dialog(this);
QString s = "hello";
qDebug() << "CONNECT: " << connect(this, SIGNAL(s1(QString)),
dialog, SLOT(s1(QString)), Qt::UniqueConnection);
emit s1(s);
dialog->show();
}
This code creates a new dialog each time it's triggered and sets up a connection between the newly created dialog and the MainWindow. While the connection is specified as "Unique", a new dialog is created each time, so the connection is not unique as the dialog instance is different.
Your code does not show that the dialog is being deleted and even if you close its window, the instance still remains in memory, so multiple objects are receiving the same signal.
Your Dialog is never destroyed in this code. It's not even leaked as you pass this as a parent in the Dialog's constructor, which makes the MainWindow responsible for this Dialog memory.
Thus you permanently create additional Dialog which slot will be triggered, and thus explaining why you see an incrementally increasing repetition of the output.
Related
I'm trying to connect two frames with a custom signal but I'm not really getting it.
This code is just an example of what im trying to do in my program, my objective is to transfer data between frames.
Files:
(sender)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void send();
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
private slots:
void on_pushButton_clicked();
};
#endif // MAINWINDOW_H
On "mainwindow.cpp" I've got the void on_pushButton_clicked() that emits the signal and shows the new frame:
private slot void:
void MainWindow::on_pushButton_clicked()
{
emit send();
Dialog sw;
sw.setModal(true);
sw.exec();
}
(receiver):
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog();
private slots:
void receive();
private:
Ui::Dialog *ui;
int a;
};
#endif // DIALOG_H
and the .cpp:
#include "dialog.h"
#include "ui_dialog.h"
#include "mainwindow.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
a=0;
MainWindow w;
connect(&w, SIGNAL(send()), this, SLOT(receive()));
qDebug() << a;
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::receive(){
qDebug() << "ola";
a++;
}
Conclusion:
So basicly the function Dialog doesn't print the qDebug(), and 'a' is still 0, so I conclude that the connection isn't set/executed.
Thanks all,
Best regards,
Dylan Lopes.
edit: Wrote a conclusion on the end of the post.
Consider the code in your Dialog constructor...
MainWindow w;
connect(&w, SIGNAL(send()), this, SLOT(receive()));
This creates a locally scoped MainWindow on the stack and connects its send() signal to the Dialog's receive() slot. But the MainWindow -- and, hence, the connection -- will be destroyed as soon as the Dialog constructor has completed.
In addition, looking at MainWindow::on_pushButton_clicked...
void MainWindow::on_pushButton_clicked()
{
emit send();
Dialog sw;
sw.setModal(true);
sw.exec();
}
You emit the send() signal before constructing the Dialog.
I don't really know enough about what you're trying to achieve to provide a definitive answer, but in the interests of getting some kind of signal/slot interaction you might want to do the following: change the Dialog constructor to...
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
ui->setupUi(this);
a=0;
qDebug() << a;
}
And change MainWindow::on_pushButton_clicked to...
void MainWindow::on_pushButton_clicked()
{
Dialog sw;
connect(this, &MainWindow::send, &sw, &Dialog::receive);
emit send();
sw.setModal(true);
sw.exec();
}
That should at least result in Dialog::receive being invoked and you can work from there.
Connection between a signal and a slot doesn't mean that the signal function will be triggered.
You still need to emit your signal so that a gets updated.
Creating an empty slot isn't working either, as slots are the receiving point of a signal. In this case, on_pushButton_clicked() gets triggered whent he push button is clicked. This doesn't trigger send unless you call EMIT(send()) (IIRC, you emit a signal with EMIT, is that still the case?).
I have list with pointers QPushButton:
QList<QPushButton*> listButtons;
In this code I am adding dynamically buttons
listButtons.push_back(new QPushButton("Cancel", this));
connect(listButtons.last(), SIGNAL (clicked(listButtons.size)), this, SLOT(handleButton(int))); //this doesn't work
How can I also save index of every button, so I can keep track, what button user clicked, because every button has to cancel specific task.
I use C++98, so I can not use Lambda function
You have to use sender() function in your slot.
like this:
void MainWindow::buttonClicked()
{
QObject *senderObj = sender(); // This will give Sender object
QString senderObjName = senderObj->objectName();
qDebug()<< "Button: " << senderObjName;
}
See a complete example that i made for you:
.cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i =0; i<10; i++)
{
listButtons.push_back(createNewButton());
connect(listButtons[i], &QPushButton::clicked, this, &MainWindow::buttonClicked);
QString text = listButtons[i]->text();
ui->widget->layout()->addWidget(listButtons[i]);
}
}
MainWindow::~MainWindow()
{
delete ui;
}
QPushButton* MainWindow::createNewButton()
{
QPushButton* button = new QPushButton("ButtonText");
button->setObjectName("ButtonName");
return button;
}
void MainWindow::buttonClicked()
{
QObject *senderObj = sender(); // This will give Sender object
QString senderObjName = senderObj->objectName();
qDebug()<< "Button: " << senderObjName;
}
.h file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QList<QPushButton*> listButtons;
QPushButton *createNewButton();
void buttonClicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
More info:
sender() function returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns 0. The pointer is valid only during the execution of the slot that calls this function from this object's thread context.
Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.
An easy way to do that would be to capture the index (or whatever you want to pass to the slot) in a lambda connected to the signal that then pass it on to the slot.
The general idea being like so:
auto button = new QPushButton("Cancel", this);
auto index = /* code to get what you want to pass */ ;
connect(button, &QPushButton::clicked,
this, [index, this](bool){ this->handleButton(index); });
You can refer to this page https://doc.qt.io/archives/qq/qq16-dynamicqobject.html.
Or looks up the implementation of QWebChannel. It maps Qt signal to the v8 environment, based on the above technology.
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.
What I simply want to do is connect a signal inside a thread to a slot in the main thread to handle UI changes.
This is basically the current state of my thread, nothing fancy but it's just for testing purposes atm:
// synchronizer.h
class Synchronizer : public QObject
{
Q_OBJECT
public:
Synchronizer();
signals:
void newConnection(std::wstring id);
private:
QTimer timer;
private slots:
void synchronize();
}
// synchronizer.cpp
Synchronizer::Synchronizer()
{
connect(&timer, SIGNAL(timeout()), this, SLOT(synchronize()));
timer.start();
}
void Synchronizer::synchronize()
{
emit newConnection(L"test");
}
And here's how my MainWindow looks:
// mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
Synchronizer synchronizer;
private slots:
void addConnection(std::wstring id);
}
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&synchronizer, SIGNAL(newConnection(std::wstring)),
this, SLOT(addConnection(std::wstring)));
QThread *thread = new QThread;
// The problems starts here?
synchronizer.moveToThread(thread);
thread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::addConnection(std::wstring id)
{
// Add a new connection to QListWidget
ui->connectionList(QString::fromStdWString(id));
}
If I remove there lines:
synchronizer.moveToThread(thread);
thread->start();
everything seems to work as expected, that is a new item is added every second to a QListWidget but as soon as I move the synchronizer object to thread it simply stops working. I'd presume it has something to do with the connect context but I'm not really sure how something like this should be achieved as I'm quite new to Qt.
It seems that the in this case was simply the fact that I am using std::wstring as an argument in the signal without registering the type first and after adding the following line qRegisterMetaType<std::wstring>("std::wstring"); to the code, everything worked as expected.
If I would have read the output console more carefully I would have solved the problem without too much hassle as it was clearly stated that:
QObject::connect: Cannot queue arguments of type 'std::wstring'
So simply speaking, read the compiler output and don't be stupid like me :)
I'm trying to make a simple program consisting of a button and a label. When the button is pressed, it should change the label text to whatever is in a QString variable inside the program. Here's my code so far:
This is my widget.h file:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
Ui::WidgetClass *ui;
QString test;
private slots:
void myclicked();
};
And here's the implementation of the Widget class:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::WidgetClass)
{
ui->setupUi(this);
test = "hello world";
connect(ui->pushButton, SIGNAL(clicked()), ui->label, SLOT(myclicked()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::myclicked(){
ui->label->setText(test);
}
It runs but when the button is clicked, nothing happens. What am I doing wrong?
Edit: after i got it working, the text in the label was larger than the label itself, so the text got clipped. I fixed it by adding ui->label->adjustSize() to the definition of myclicked().
You are connecting the signal to the wrong object. myclicked() is not a slot of QLabel, it is a slot of your Widget class. The connection string should be:
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(myclicked()));
Take a look at the console output of your program. There should be an error message saying something like:
Error connecting clicked() to
myclicked(): No such slot defined in QLabel