I am having difficulty in my Qt program with connecting button signals to my slots. My code is:
Main.cpp
#include <QtGui/QApplication>
#include "MainWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWidget mainWidget;
mainWidget.show();
return app.exec();
}
MainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
class MainWidget : public QWidget
{
public:
MainWidget();
public slots:
void bAdvice_clicked();
void bWeather_clicked();
void bNextMeeting_clicked();
void bQuit_clicked();
};
#endif // MAINWIDGET_H
MainWidget.cpp
#include "MainWidget.h"
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
MainWidget::MainWidget()
{
QLayout *layout = new QVBoxLayout();
this->setLayout(layout);
QTextEdit *message = new QTextEdit();
layout->addWidget(message);
QPushButton *bAdvice = new QPushButton("Advice");
connect(bAdvice, SIGNAL(clicked()), this, SLOT(bAdvice_clicked()));
layout->addWidget(bAdvice);
QPushButton *bWeather = new QPushButton("Weather");
connect(bWeather, SIGNAL(clicked()), this, SLOT(bWeather_clicked()));
layout->addWidget(bWeather);
QPushButton *bNextMeeting = new QPushButton("Next Meeting");
connect(bNextMeeting, SIGNAL(clicked()), this, SLOT(bNextMeeting_clicked()));
layout->addWidget(bNextMeeting);
QPushButton *bQuit = new QPushButton("Quit");
connect(bQuit, SIGNAL(clicked()), this, SLOT(bQuit_clicked()));
layout->addWidget(bQuit);
}
void MainWidget::bAdvice_clicked()
{
}
void MainWidget::bWeather_clicked()
{
}
void MainWidget::bNextMeeting_clicked()
{
QMessageBox::information(this, "Next Meeting", "Today", QMessageBox::Ok);
}
void MainWidget::bQuit_clicked()
{
this->close();
}
The program outputs the following:
Starting C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe...
Object::connect: No such slot QWidget::bAdvice_clicked() in MainWidget.cpp:16
Object::connect: No such slot QWidget::bWeather_clicked() in MainWidget.cpp:20
Object::connect: No such slot QWidget::bNextMeeting_clicked() in MainWidget.cpp:24
Object::connect: No such slot QWidget::bQuit_clicked() in MainWidget.cpp:28
C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe exited with code 0
The code seems right, no compiler warnings. Just this output at runtime. But it looks like I hooked the signals and slots up correctly.
Add Q_OBJECT to your class, like this:
class MainWidget : public QWidget
{
Q_OBJECT
You also have to run moc to generate some helper code. qmake does that automatically for your, but if you compile this yourself, you need to run moc.
When I started with Qt, I had this problem a lot. As I see it your slots are defined wrong. If you look at the signature for the signal (Qt Clicked Signal Docs), you will see that the argument list is (bool clicked = false).
The way Qt's signal & slots connect work at run time, is that it will only connect the signal and slot if they have the exact same signatures. If they don't match exactly, no connection.
so in MainWidget.h
public slots:
void bAdvice_clicked(bool);
In MainWidget.cpp
connect(bAdvice, SIGNAL(clicked(bool)), this, SLOT(bAdvice_clicked(bool)));
Things will start working for you.
Edited:
Compiled your code and all the slots were correctly called.
It was just the Q_OBJECT macro that was missing.
Related
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
I'm developing C++ application in QT with GUI. To make the GUI always respond, I create a thread for other blocking process. However, the application is waiting for the blocking process and therefore the GUI didn't respond.
Is creating a thread for blocking process is a wrong way?
Or it doesn't work in QT? If so, how to make the GUI respond? Please give me example.
This is a simple example of multithreaded application with responsive GUI:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QThread>
class Worker : public QThread
{
protected:
/// Wait 3s which simulates time demanding job.
void run() { sleep(3); }
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
public slots:
void doJob(bool);
void jobFinished();
private:
Worker worker;
QLineEdit *line;
QPushButton *button;
int counter;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
line(new QLineEdit()),
button(new QPushButton()),
counter(0)
{
line->setDisabled(true);
line->setAlignment(Qt::AlignRight);
line->setText(QString::number(counter));
button->setText("Push");
connect(button, SIGNAL(clicked(bool)), this, SLOT(doJob(bool)));
connect(&worker, SIGNAL(finished()), this, SLOT(jobFinished()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(line);
layout->addWidget(button);
QWidget *window = new QWidget();
window->setLayout(layout);
setCentralWidget(window);
}
void MainWindow::doJob(bool)
{
// Only one thread is running at a time.
// If you want a thread pool, the implementation is up to you :)
worker.start();
// Uncomment to wait. If waiting, GUI is not responsive.
//worker.wait();
}
void MainWindow::jobFinished()
{
++counter;
line->setText(QString::number(counter));
}
Qt has a very good multithreading support. You probably do something wrong and we can't help you if you don't provide any code. There are a lot of ways of implementing "responsive" GUI! (Including a lot of ways how to implement another thread!)
So this is the code of the main.cpp:
#include <QApplication>
#include <QtGui>
#include <mainwidget.h>
int main(int argc, char **argv)
{
QPushButton button ("Hello world!");
button.show();
mainWidget widget;
widget.show();
return app.exec();
}
I want a button from the "widget" to close the "Hello world!" window. I have already added that button in "widget" and made a function for it in mainwidget.h (which also shows a message box) and connected them. I just want to know how can that same button close the Hello World window. I guess I have to add something to the function in mainwidget.h but I don't know what it is. The instructor says we should use the QWidget close() function.
I will answer to your question but before I'll explain you how a basic Qt application looks like.
In Qt, a basic main is like :
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Here as you can see a QApplication is created then a MainWindow.
So What's the MainWindow class ?
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
};
#endif // MAINWINDOW_H
It's the MainWindow.h. As you can see MainWindow inherit to the QMainWindow class. It's the main window class to create a graphic user interface.
the Q_OBJECT define is for qmake. Indeed qmake I'll create a moc_mainwindow.cpp for this class to manage the Qt signals.
Now you get an empty window if you create an Empty constructor and destructor like:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
}
MainWindow::~MainWindow()
{
}
then after you wish to write "Hello world !" in the window so in Qt to write a text you can use a QLabel. So to write "Hello World !" you get:
#include "mainwindow.h"
#include <QLabel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget *widget = new QLabel("Hello world !", this);
}
MainWindow::~MainWindow()
{
}
then after to create a button as you made you ll use the QPushButton class.
so you get:
#include "mainwindow.h"
#include <QLabel>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget *widget = new QLabel("Hello world !", this);
setCentralWidget(widget);
QPushButton *button = new QPushButton("close", this);
}
MainWindow::~MainWindow()
{
}
(I Choose to set the QLabel to the central Widget to don't get the label behind the button but after and in real Qt application a QMainWindow's central widget is mostly usualy a QWidget I'll explain you why after)
now you have a button. But when you click on it nothing append.
Why ? Because nothing link the Button and the Window. to link it in Qt we use the connect function. [http://qt-project.org/doc/qt-4.8/qobject.html][1]
so with connect to close the window when you click on the button you get:
#include "mainwindow.h"
#include <QLabel>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget *widget = new QLabel("Hello world !", this);
setCentralWidget(widget);
QPushButton *button = new QPushButton("close", this);
connect(button, SIGNAL(clicked()), this, SLOT(close()));
}
MainWindow::~MainWindow()
{
}
As you can see with connect. In first parameter, we put the object which ll send the signal here the button. in second parameter, we put the signal to link here it's clicked() to do this we write SIGNAL(clicked()). In third, the object which will receive the signal, here the window to close. In fourth parameter the function to launch when the signal is received. We write this SLOT(close()).
Why SIGNAL and SLOT ? because in Qt to create a signal we use signal: in the .hh and to create a slot et use (public or protected or private) slots:
example:
Class Object
{
Q_OBJECT
...
signals:
void aSignal();
public slots:
void aSlot();
...
};
NOTE: the signal and the slot must have same return and parameters.
after to organize your objects you ll use the labels in the QWidget in the centralWidget so you have:
#include "mainwindow.h"
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget* centralWid = new QWidget(this);
setCentralWidget(centralWid);
QVBoxLayout *layout = new QVBoxLayout(centralWid);
QWidget *widget = new QLabel("Hello world !", this);
QPushButton *button = new QPushButton("close", this);
layout->addWidget(widget);
layout->addWidget(button);
centralWid->setLayout(layout);
connect(button, SIGNAL(clicked()), this, SLOT(close()));
}
MainWindow::~MainWindow()
{
}
I am trying to program an App that fetches files from a server.
I have a 'Window' class(mainwindow.cpp, which is a widget class that would be the UI) and then I have a 'Backend' class(Backend.cpp).
The GUI has a push button and two radio buttons. If the radio button "remote" is seleted, then upon clicking the push button will lead to fetching files from server.
However, there is some problem in the 'connect' call in Backend.cpp which I can't figure out. The error I get is: no matching function call to 'QObject::connect(QNetworkReply*&), const char[13], Backend* const, const char[20])'
Here are the codes:
ANSWER: Avoid circular inclusions!!!!
Here are the updated codes:
mainwindow.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include <QRadioButton>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>
#include <QFile>
#include <QUrl>
#include "Backend.h"
class QGroupBox;
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
QTcpSocket *conn;
QFile *file;
QUrl url;
Backend backend_inst;
private:
QRadioButton *button_local;
QRadioButton *button_remote;
QGroupBox *createPushButtonGroup();
private slots:
void onClick_button1();
void onCheck_local();
void onCheck_remote();
};
#endif
mainwindow.c
#include <QtGui>
#include "mainwindow.h"
Window::Window(QWidget *parent)
: QWidget(parent)
{
QGridLayout *grid = new QGridLayout;
grid->addWidget(createPushButtonGroup(), 1, 1);
setLayout(grid);
setWindowTitle(tr("File-Fetch App"));
resize(480, 420);
}
QGroupBox *Window::createPushButtonGroup()
{
QGroupBox *groupBox = new QGroupBox();
QPushButton *pushButton1 = new QPushButton(tr("Fetch Files!!"));
button_local = new QRadioButton(tr("&Download Files from Local Storage"));
button_remote = new QRadioButton(tr("&Download Files from a Web-Server"));
button_local->setChecked(1);
connect(pushButton1,SIGNAL(clicked()), this, SLOT(onClick_button1()));
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(pushButton1);
vbox->addSpacing(50);
vbox->addWidget(button_local);
vbox->addWidget(button_remote);
vbox->addStretch(1);
groupBox->setLayout(vbox);
return groupBox;
}
void Window::onClick_button1()
{
QTextStream out(stdout);
out << "fetch button clicked\n";
if (button_local->isChecked()){
onCheck_local();
}
else if (button_remote->isChecked()){
onCheck_remote();
}
}
void Window::onCheck_local()
{
QTextStream out(stdout);
out << "local update button checked\n";
}
void Window::onCheck_remote()
{
QTextStream out(stdout);
out << "remote update button checked\n";
QString pathname= "http://192.168.1.1:8000/example.txt";
QUrl webaddr = pathname;
backend_inst.FetchFile(webaddr);
}
Backend.h
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QTextStream>
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=0);
void FetchFile(QUrl fpath);
public slots:
void getBytesFromFile();
private:
QNetworkReply *reply;
QNetworkAccessManager qnam;
};
#endif // BACKEND_H
Backend.cpp
#include "Backend.h"
Backend::Backend(QObject* parent)
: QObject(parent)
{
}
void Backend::FetchFile(QUrl fpath)
{
reply = qnam.get(QNetworkRequest(fpath));
QObject::connect(reply, SIGNAL(readyRead()),this, SLOT(getBytesFromFile()));
//qnam = new QNetworkAccessManager;
//QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile()));
}
void Backend::getBytesFromFile(){
QByteArray downloadedData;
QTextStream out(stdout);
out << "we are loading data from URL\n";
downloadedData =reply->readAll();
out << downloadedData;
delete reply;
}
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
To use signals and slots, your classes (both signaling and slotting) must derive from QObject, i.e.
#include "mainwindow.h"
#include <QObject>
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=0);
[...]
Backend::Backend(QObject* parent)
: QObject(parent)
{
}
You posted this:
class Backend
{
// Q_OBJECT
public:
Backend();
void FetchFile(QUrl fpath);
public slots:
void getBytesFromFile();
private:
QNetworkReply *reply;
QNetworkAccessManager qnam;
};
Q_OBJECT is still commented if yes remove it.. you are using signal and slots..
EDIT :
try to avoid circular inclusion:
you included Backend in mainwindow and viceversa..
The commented out lines:
qnam = new QNetworkAccessManager;
QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile()));
Were what's causing your issue. Connect excepts a pointer, not a pointer-to-pointer. qnam is a pointer was a pointer in the previous version of the code and using the address-of operator on it would turn it into a pointer-to-pointer. Second mistake is that you need to have the same signature for your signal and slot in order to get it called (otherwise you get a runtime error). So, correctly:
connect(qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile(QNetworkReply*)));
(and obviously, change the actual signature of the getBytesFromFile method).
As to why your error persists despite commenting the code out: I think you are running an old build and the new one is failing to build due to the vtable issue (as you described in the comment thread). My guess is that qmake is glitching out, which I have experienced when adding the Q_OBJECT macro to already existing classes. Go to your build folders and delete Makefile* everywhere. Then go back to Qt creator and rebuild the project, that should force qmake to generate the Makefiles again.
It seems you have some problems with build mechanism. With posted code and un-commented
QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile())); in Backend::FetchFile function, all your code works. Running and with checked "Download Files from Web-Server" it prints the "we are loading data from URL" from getBytesFromFile - isn't this the slot you want to be called ?
I am having difficulty in my Qt program with connecting button signals to my slots. My code is:
Main.cpp
#include <QtGui/QApplication>
#include "MainWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWidget mainWidget;
mainWidget.show();
return app.exec();
}
MainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
class MainWidget : public QWidget
{
public:
MainWidget();
public slots:
void bAdvice_clicked();
void bWeather_clicked();
void bNextMeeting_clicked();
void bQuit_clicked();
};
#endif // MAINWIDGET_H
MainWidget.cpp
#include "MainWidget.h"
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
MainWidget::MainWidget()
{
QLayout *layout = new QVBoxLayout();
this->setLayout(layout);
QTextEdit *message = new QTextEdit();
layout->addWidget(message);
QPushButton *bAdvice = new QPushButton("Advice");
connect(bAdvice, SIGNAL(clicked()), this, SLOT(bAdvice_clicked()));
layout->addWidget(bAdvice);
QPushButton *bWeather = new QPushButton("Weather");
connect(bWeather, SIGNAL(clicked()), this, SLOT(bWeather_clicked()));
layout->addWidget(bWeather);
QPushButton *bNextMeeting = new QPushButton("Next Meeting");
connect(bNextMeeting, SIGNAL(clicked()), this, SLOT(bNextMeeting_clicked()));
layout->addWidget(bNextMeeting);
QPushButton *bQuit = new QPushButton("Quit");
connect(bQuit, SIGNAL(clicked()), this, SLOT(bQuit_clicked()));
layout->addWidget(bQuit);
}
void MainWidget::bAdvice_clicked()
{
}
void MainWidget::bWeather_clicked()
{
}
void MainWidget::bNextMeeting_clicked()
{
QMessageBox::information(this, "Next Meeting", "Today", QMessageBox::Ok);
}
void MainWidget::bQuit_clicked()
{
this->close();
}
The program outputs the following:
Starting C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe...
Object::connect: No such slot QWidget::bAdvice_clicked() in MainWidget.cpp:16
Object::connect: No such slot QWidget::bWeather_clicked() in MainWidget.cpp:20
Object::connect: No such slot QWidget::bNextMeeting_clicked() in MainWidget.cpp:24
Object::connect: No such slot QWidget::bQuit_clicked() in MainWidget.cpp:28
C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe exited with code 0
The code seems right, no compiler warnings. Just this output at runtime. But it looks like I hooked the signals and slots up correctly.
Add Q_OBJECT to your class, like this:
class MainWidget : public QWidget
{
Q_OBJECT
You also have to run moc to generate some helper code. qmake does that automatically for your, but if you compile this yourself, you need to run moc.
When I started with Qt, I had this problem a lot. As I see it your slots are defined wrong. If you look at the signature for the signal (Qt Clicked Signal Docs), you will see that the argument list is (bool clicked = false).
The way Qt's signal & slots connect work at run time, is that it will only connect the signal and slot if they have the exact same signatures. If they don't match exactly, no connection.
so in MainWidget.h
public slots:
void bAdvice_clicked(bool);
In MainWidget.cpp
connect(bAdvice, SIGNAL(clicked(bool)), this, SLOT(bAdvice_clicked(bool)));
Things will start working for you.
Edited:
Compiled your code and all the slots were correctly called.
It was just the Q_OBJECT macro that was missing.