Qt: How to handle custom events with connect? - c++

Here is a "hello world" button that display a window and button. I would like to do a cout or any other custom functionality when the button is clicked but I'm stuck.
#include <QApplication>
#include <QPushButton>
#include <iostream>
int main(int argc, char **argv){
// create app
QApplication app (argc, argv);
// create window
QWidget window;
window.setWindowTitle("MyWindow");
window.setFixedSize(600, 480);
// create button
QPushButton *button = new QPushButton(&window);
button->setGeometry(10, 10, 100, 35);
button->setText("hello!");
// event handling
// HERE IS THE PROBLEM
QObject::connect(button, SIGNAL(clicked()), ???, SLOT(???));
// show window
window.show();
}
How can I append a custom function to the SLOT? So I can console log stuff and handle the event on my own way? I can connect it to a QMediaPlayer for example to start/stop but I'm still very confused on how to use the signal/slot.

You need do all this things inside QObject subclass, or you can use new syntax of signal and slot and in this case you will be able to do this in main() function, use lambdas and so on. But it can be done only in Qt5.
QObject::connect(button, &QPushButton::clicked, someFunction);
If you want do this with old syntax then you need create some subclass and then create custom slots. The most common example you can find here: http://qt-project.org/doc/qt-4.8/mainwindows-application.html

Related

Can I create a slot without creating a class?

Just a simple prog to illustrate the issue in a larger one. What I am trying to do is to create a function for signal/slot connection without using any classes. I am new to OOP and don't want to have to do with classes at the moment. So, I am creating a function before the main function and hope this will work... but it doesn't. The prog can be compiled and executed, but I get a notification: Object::connect: No such slot QTextEdit::onClicked(). I anticipate the issue can very easily be solved as it is rather a mistake of expression than of understanding, so thanks in advance for your help! :)
#include <QtGui>
#include <QtCore>
void onClicked (QTextEdit text, QString a)
{
text.setText(a);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget mw;
mw.setWindowTitle("Main Window");
mw.resize(400, 400);
mw.show();
QLabel label ("Enter something:", &mw);
label.setAlignment(Qt::AlignHCenter);
label.show();
QLineEdit line (&mw);
line.show();
QString a = line.text();
QTextEdit text (&mw);
text.show();
QPushButton btn ("Convert", &mw);
QObject::connect(
&btn,
SIGNAL(clicked()),
&text,
SLOT(onClicked()));
btn.show();
QVBoxLayout layout_mw;
layout_mw.addWidget(&label);
layout_mw.addWidget(&line);
layout_mw.addWidget(&btn);
layout_mw.addWidget(&text);
mw.setLayout(&layout_mw);
return app.exec();
}
well,it is true that slots are only available in QObjects BUT you can for sure connect signals to lambdas too :)
so, in this case you can uses the helpful lambda and omit the custom function
void onClicked (QTextEdit text, QString a) :
like
QObject::connect(&btn, &QPushButton::clicked,
[](QTextEdit te, QString s){qDebug() << "am in...";});
You can read in the Qt documentation that:
All classes that inherit from QObject or one of its subclasses (e.g.,
QWidget) can contain signals and slots.
So you can not define slots in main.cpp. You have two options. Either define a class derived from QObject and add your slot there or use Qt 5.* and connect your signal to a lambda using the new syntax for connections.

My qt desktop app keeps crashing after I close out of my starting child dialog and go into my parent dialog

So I am quite new to qt, and am trying to open a desktop app with a child dialog, and when I click a button it points to the parent dialog. I managed to do that, but whenever I run it and click 'go' it points me to my parent window and then crashes! I got the code from this link. the first answer was what got it to work, and the second one didn't work at all. my MainWindow (parent) dialog is survey and my child dialog is global. (I know I named them weird. Still learning).
my main.cpp
#include "survey.h"
#include <QApplication>
#include "global.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget survey;
survey.show();
global popup(&survey);
popup.show();
/*
survey w;
w.show();
*/
return a.exec();
}
my global.cpp
#include "global.h"
#include "ui_global.h"
#include "survey.h"
global::global(QWidget *parent) :
QDialog(parent),
ui(new Ui::global)
{
ui->setupUi(this);
}
global::~global()
{
delete ui;
}
void global::on_Go_clicked()
{
//survey *nWin;
auto win = new survey();
win->setAttribute(Qt::WA_DeleteOnClose);
win->show();
deleteLater();
}
What do I need to change so my desktop app doesn't crash when I run it??
You're calling deleteLater on a global instance, which you did not instantiated with new, it's in your main:
global popup(&survey);
When delete is called, your application crashes.
No need to call deleteLater in your slot.

Loading QDialog is blank when showed

I am working on a code that allows the user to control a piezo (PZ193E) in a MainWindow created with Qt Designer form.
However, when I call the function designed to connect the piezo to the computer (from an external library given by the constructor) my UI freeze until the connection is established.
I am trying to display a QDialog with a QLabel in it, telling the user to wait while the connection is processing, but when I do so, the QDialog shows up but without the label. It is only displayed when the connection is established and when the QDialog can close.
Here is how I coded my dialog :
In the .h :
QDialog *_waitQD = new QDialog;
QVBoxLayout *_waitQVBL = new QVBoxLayout;
QLabel *_waitQL = new QLabel("Loading...");
In the .cpp :
_waitQD->setMinimumSize(QSize(95,35));
_waitQVBL->addWidget(_waitQL);
_waitQD->setLayout(_waitQVBL);
And then I call :
_waitQD->show();
if (_piezo.connected()) // bool funtion that return true if the connection is established
_waitQD->close();
This is what it looks like:
The ideal solution is not blocking the main thread while establishing the connection; the workaround is adding QApplication::processEvents(); after the call to show
you need to call app.exec()
#include <QApplication>
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDialog *_waitQD = new QDialog;
QVBoxLayout *_waitQVBL = new QVBoxLayout;
QLabel *_waitQL = new QLabel("Loading...");
_waitQD->setMinimumSize(QSize(95,35));
_waitQVBL->addWidget(_waitQL);
_waitQD->setLayout(_waitQVBL);
_waitQD->show();
return app.exec();
}

can't close app from dialog box (Qt app)

Getting right to it I have a MainWindow and a dialog window which is executed if a condition is met but the problem is I can't get the app to quit if the cancel button from the dialog window is clicked. I've tried putting qApp->quit() in the slot function for the cancel button. I've tried connecting the cancel button slot to the predefined close slot for the MainWindow object via a clickme() signal from the dialog class. (as shown below)
qt application does not quit I read the answer to this question which I think got me close because it made me realize that I can't quit the app before showing the MainWindow but making that change didn't solve the problem. I even tried to explicitly emit the clickme() signal from cancel button slot but that actually caused the OS to throw a signal which threw an error at me saying "the inferior stopped because it received a signal from the operating system signal name: SIGSEGV
signal meaning: segmentation fault"
Here's my code:
Notice warning; // this is the object for the dialog window also all of this code is in main.cpp
warning.setModal(true);
QObject::connect(&warning, SIGNAL(clickme()), &warning, SLOT(on_Cancel_button_clicked()));
QObject::connect(&warning, SIGNAL(clickme()), &w, SLOT(close()));
warning.exec();
Also before that code is
MainWindow w;
w.show();
Also while writing this question I tried this
QObject::connect(&warning, SIGNAL(clickme()), qApp, SLOT(quit()));
But that still didn't work. If you need more info just let me know.
Update: I'm starting to think that the reason I'm having so much trouble with this connect signal/slot function is because it's not designed to connect two windows of two different classes and I should rework my app to do everything from the MainWindow class which is a shame because when I picture a GUI program I picture multiple windows connected to each other regardless of whether or not the object representing each window is from the same class as the others yet I have such a hard time trying do that with the QT framework when it comes to trying to connect objects of different classes.
Update: please forgive me. I assume that the code that I originally thought was the answer would work and took a break from working on the program before actually testing out that code. Going back to it now I discovered that it doesn't work. The code I'm referring to is the following
QMessageBox msg;
msg.setText("Continue?");
msg.addButton(QMessageBox::Yes);
msg.addButton(QMessageBox::No);
QObject::connect(&msg, &QMessageBox::rejected,
&app, &QApplication::quit); // this line doesn't work for me and I don't know why
QObject::connect(&msg, &QMessageBox::accepted, [&dlg]{
(new QLabel("I'm running")).show();
});
QApp->quit(); should work. Remove warning.setModal(true); The dialog becomes modal when you call exec(). SetModal(true) should be used with show() according to Qt docs. So this may be causing your problem.
I think I've found the problem.
Probably, you're calling exec() twice:
To enter the QApplicationevent loop
To execute the dialog.
Use show() instead of exec() for the dialog. You have an example below where you can check the signal/slot works well. In your application, you need the slot to close the window, but:
With the line of code dialog.exec();, the app keeps running. This is your issue.
With the line of code dialog.show();, the app stops.
By the way, I saw your last question update, but it is not correct. In fact, of course you can connect two different classes.
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QApplication>
#include <QMainWindow>
#include <QAbstractButton>
#include <QDebug>
#include "dialog.h"
class Window : public QMainWindow
{
Q_OBJECT
public:
Window()
{
dialog = new Dialog();
dialog->setText("Continue?");
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
auto onClick = [this]() {
auto role = dialog->buttonRole(dialog->clickedButton());
if (role == QMessageBox::NoRole) {
qDebug() << "QMessageBox::NoRole";
QApplication::quit();
}
if (role == QMessageBox::YesRole) {
qDebug() << "QMessageBox::YesRole";
}
};
QObject::connect(dialog, &QMessageBox::buttonClicked, onClick);
dialog->show(); // this must be show(), not exec()
}
virtual ~Window() { delete dialog; }
private:
Dialog *dialog;
public slots:
void windowSlot() { qDebug() << Q_FUNC_INFO;
close();
}
};
#endif // WINDOW_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QMessageBox>
class Dialog : public QMessageBox
{
Q_OBJECT
public:
Dialog() {}
virtual ~Dialog() {}
};
#endif // DIALOG_H
main.cpp
#include <QApplication>
#include <QtGui>
#include "window.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.setWindowTitle("window");
window.show();
return app.exec();
}
Update #1: a very interesting post.

QProcess saving to QTextEdit

What I'm trying to do is launch a program within another program using QProcess and then save the output from the launched program into a QTextEdit of the launcher program. Every time I launch this program I want it to add more text to the QTextEdit. Now I get the program to launch but then after the text is supposed to be written it crashes. Here is the code:
#include <QWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QProcess>
#include <QVBoxLayout>
#include <QApplication>
class Widget : public QWidget
{
Q_OBJECT
QTextEdit* text;
public:
Widget() : text(new QTextEdit) {
QPushButton* addBtn = new QPushButton("Add Module");
text->setReadOnly(true);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(addBtn,0);
layout->addWidget(text);
connect(addBtn,SIGNAL(clicked()),SLOT(launchModule()));
}
Q_SLOT void launchModule() {
QString program = "C:/A2Q2-build-desktop/debug/A2Q1.exe";
QProcess *myProcess = new QProcess(this);
connect(myProcess, SIGNAL(finished(int)), SLOT(finished()));
connect(myProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(finished()));
myProcess->start(program);
}
Q_SLOT void finished() {
QProcess *process = qobject_cast<QProcess*>(sender());
QString out = process->readAllStandardOutput(); // will be empty if the process failed to start
text->append(out);
delete process;
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Widget w;
w.show();
app.exec();
}
#include "main.moc"
It's crashing because you're deleting the sender object while inside a slot. Instead of delete process, you should
process->deleteLater();
For logging purposes you should be using QPlainTextEdit instead of a QTextEdit. The former is faster. You're prematurely pessimizing by using the latter. Alas, even QPlainTextEdit becomes abysmally slow if you're sending about 100 lines/s (at least on Qt 4.8). If you want a really fast log view, you'll need to use QListWidget, with a caveat, or roll your own.
I have a complete example of how to send to and receive from a process in another answer.
The process is crashing because you're deleting the parent from within the finished slot.
Also, it's probably easier to do something like this:
QObject::connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(getOutput()));
instead of using the finished() slot. But that's more personal preference than anything.