Cannot connect slots from another class - c++

I currently try to write an little application for experiments and more. I write everything in its own file (Menu -> menu.cpp etc.). Now I want to create a menu action, this works but interacting with the action doesnt. Thats what I've done so far:
menu.cpp
#include "menu.h"
#include <QMenuBar>
#include <QMenu>
#include <QAction>
void Menu::setupMenu(QMainWindow *window) {
QMenuBar *mb = new QMenuBar();
QMenu *fileMenu = new QMenu("File");
QAction *newAct = new QAction("New...", window);
window->connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
fileMenu->addAction(newAct);
mb->addMenu(fileMenu);
window->setMenuBar(mb);
}
void Menu::newFile() {
printf("hello world!");
}
menu.h
#ifndef MENU_H
#define MENU_H
#include <QObject>
#include <QMainWindow>
#include <QWidget>
#include <QMenuBar>
class Menu : public QObject
{
public:
void setupMenu(QMainWindow *window);
private slots:
void newFile();
};
#endif // MENU_H
But its not printing out 'hello world', the only message I get is:
QObject::connect: No such slot QObject::newFile() in ../from Scratch written UI app C++/src/ui/menu.cpp:11
What can I do to fix this?
~ Jan

class Menu : public QObject
Menu is a QObject but is also needs to use the Q_OBJECT macro.
See the Qt5 - QObject documentation:
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
Next, there is some confusion in your connect call. Here is the signature of the static connect function.
Qt5 - static QObject::connect:
QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection)
You can see it takes 5 parameters (object pointer, signal, object pointer, signal/slot) and the 5th parameter is defaulted.
There is also a member function connect.
Qt5 - QObject::connect:
QObject::connect(const QObject * sender, const char * signal, const char * method, Qt::ConnectionType type = Qt::AutoConnection) const
This takes 4 parameters (object pointer, signal, signal/slot) and the 4th parameter is defaulted.
Your code:
window->connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
You're calling the the connect member function of window but you're passing parameters for the static connect function.
What can I do to fix this?
Figure out what you're trying to do and make the appropriate call.
For example: connect the QAction signal to a slot in Menu then either call the static function as follows.
connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
Or using the member function.
connect(newAct, SIGNAL(triggered()), SLOT(newFile()));

Related

C++ Qt4: Add slot with QObject implementation

I'm fairly new to Qt in general, and I would like to use some degree of reflection in writing a generic method for printing lists of objects.
I'm currently trying to hook up to the ui like so:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <algorithm>
#include <QStringListModel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
...
connect(ui->print_books_btn, SIGNAL(released()), this, SLOT(PrintList(BookList)));
connect(ui->print_clients_btn, SIGNAL(released()), this, SLOT(PrintList(ClientList)));
}
...
void MainWindow::PrintList(QList<QMetaObject*> list)
{
list.first()->className();
}
My relevant header files look like so:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
...
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
...
private:
Ui::MainWindow *ui;
QList<Book*> BookList;
QList<Client*> ClientList;
private slots:
...
void PrintList(QList<QMetaObject*> list);
};
#endif // MAINWINDOW_H
I believe I've set everything up with regards to the Q_OBJECT macro in the header files:
...
class Book : public QObject
{
Q_OBJECT
public:
Book();
...
}
Everything compiles without error, though when running I get this in the console:
QObject::connect: No such slot MainWindow::PrintList(BookList) in ..\LibraryManager\mainwindow.cpp:18
QObject::connect: (sender name: 'print_books_btn')
QObject::connect: (receiver name: 'MainWindow')
QObject::connect: No such slot MainWindow::PrintList(ClientList) in ..\LibraryManager\mainwindow.cpp:19
QObject::connect: (sender name: 'print_clients_btn')
QObject::connect: (receiver name: 'MainWindow')
Is it possible to work it this way?
Thanks!
UPDATE
I think its QMetaObject that I actually want to be using - so I can do list.first()->className() etc. Edited above ^
UPDATE 2
Required to be using Qt4.
released() signals of QPushButtons would not send any parameters like QMetaObject* as you like to your slots SLOT(PrintList(...))
so your implementation is wrong,
you should think another way for example you take the released() [or clicked()] signals of QPushButtons and connect them to another slot in your class like buttonsClicked() then in the implementation of buttonsClicked() slot you should manually emit a signal with parameters like booklist or clientlist as you like to be received in the other slot PrintList(QMetaObject*)
this is how signals and slots are working in Qt. Slots only receive parameters emitted by signal not any other parameters.
I think you should connect both signals to SLOT(PrintList(QList<QObject*>))).
In the PrintList(QList<QObject*> list) you can use dynamic_cast to check what type it is (BookList or ClientList) then you can print what you want depend on its type.
In Qt5, you can use lambda functions in the QObject::connect method to pass a parameter to your slot:
connect(ui->print_books_btn, &QPushButton::clicked, [=] { PrintList(BookList); });
You can also use the QSignalMapper class to do the same thing in Qt4 (see the doc). Or, you can create two specific slots:
connect(ui->print_books_btn, SIGNAL(released()), this, SLOT(printBookList));
void MainWindow::printBookList()
{
printList(bookList);
}
The last solution is maybe the best way: it's explicit and more readable.

Why does adding a function argument cause a SLOT() to be unrecognised?

I have a class as follows:
handler.h:
#ifndef HANDLER_H
#define HANDLER_H
#include <QObject>
class handler : public QObject
{
Q_OBJECT
public:
explicit handler(QObject *parent = nullptr);
~handler();
public slots:
void returnHandler(int input);
};
#endif // HANDLER_H
handler.cpp:
#include "handler.h"
#include "otherclass.h"
handler::handler(QObject *parent) : QObject(parent)
{
}
handler::~handler()
{
}
void handler::returnHandler(int input)
{
otherclass *otherclassPointer = otherclass::getInstance();
otherclassPointer->returnFunction(input);
}
As shown, this is a very simple class, which aims to receive an input and pass the input to a function in an external class ('otherclass'). In my main application ('main.cpp'), I create a QThread, and call the returnHandler slot when the QThread is started, as follows:
main.cpp:
QThread* newThread = new QThread();
handler* handlerPointer = new handler();
handlerPointer->moveToThread(newThread);
connect(newThread, SIGNAL(started()), handlerPointer, SLOT(returnHandler(someInput)));
newThread->start();
The issue I'm having is this:
I'm currently get the following error:
QObject::connect: No such slot handler::returnHandler(someInput) in ../app/main.cpp:100
However, if I remove the int input from the handler class (both the .h and .cpp files), the SLOT() is recognized and called successfully when the QThread is started.
Why does adding an argument cause the slot to no longer be recognized?
EDIT: Following some very informative and appreciated comments/answers below, I've modified the approach as follows:
Create a signal in the handler class, which matches the parameters of the returnHandler slot. E.g. void handlerSignal(int).
Used the handlerSignal() SIGNAL instead of the QThread::started() signal in the connect().
Emit the handlerSignal() once the QThread is started.
`
QThread* newThread = new QThread();
handler* handlerPointer = new handler();
handlerPointer->moveToThread(newThread);
connect(handlerPointer, SIGNAL(handlerSignal(int)), handlerPointer, SLOT(returnHandler(int)));
newThread->start();
emit handlerPointer->handlerSignal(someInput);
Thanks!
Two things:
Qt expects the signal and the slot to have the same parameter types.
In SLOT(), you have to provide types, and not names for the parameters.SLOT(returnHandler(int)) instead of SLOT(returnHandler(someInput))
Qt uses the signals and slots's names and argument list to identify them. I your case, Qt looks for a slot named 'returnHandler' and having only one parameter, from type 'someInput'.
connect takes strings as the identification of the signal & slot to connect. The macros SIGNAL and SLOT stringise their arguments (using the preprocessor functionality for that). The argument to SIGNAL or SLOT must therefore be the function name, with parameter types in the parentheses. You cannot do argument binding with them.
If you need to connect to a nullary signal, you need a nullary slot.

How can I return a value from SLOT in Qt5?

I have been new to the Qt environment. I recently started with a QtCreator project for a simple chat application (QMainWindow). I have nearly completed the project but stuck with the SIGNAL/SLOT problem.
Let me explain my problem in brief :
Due to the length of the code I am not able to paste it here.
I have two classes MainWindow and NetworkFile.
The function newServerConn() in NetworkFile connects the signal readyRead() to the slot readFromClient().
The string returned by client is stored in a QString in readFromClient() SLOT.
Problem:
I have to return the QString in the slot to the newServerConn() function and from there to a function in MainWindow class because only then I would be able to print the string to the plainLineEdit widget pointed by the ui object.
Question 1:
How can I return a value from the slot?
Question 2:
Or is there any way I could get a copy of the ui instance in the NetworkFile class so that I could use the widgets from there?
Thanks.
I would just emit the data as a signal and wire up the connection between that new signal and a slot where you add then string to your ui.
A quick self-contained example (which btw. "Due to the length of the code I am not able to paste it here." is just an excuse, you can pretty much always cut down your relevant code)
Header:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTextEdit>
class NetworkFile;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow(){}
public slots:
void addText(QString str)
{
edit->append(str);
}
private:
QTextEdit* edit;
NetworkFile* net;
};
class NetworkFile : public QObject
{
Q_OBJECT
public:
NetworkFile(QObject *parent = 0):
QObject(parent)
{
connect(&server, &QTcpServer::newConnection,
this, &NetworkFile::newConnection);
//TODO: check return value
server.listen(QHostAddress::Any,16001);
}
signals:
void dataRead(QString);
public slots:
void newConnection(){
//TODO: wire up socket disconnect with deleteLater, also check for null
QTcpSocket* socket = server.nextPendingConnection();
connect(socket, &QTcpSocket::readyRead,
this, &NetworkFile::dataAvailable);
}
void dataAvailable(){
//HACK: only for brevity, create a connection wrapper that contains the socket in real code
QTcpSocket* source = (QTcpSocket*)sender();
auto bytes = source->readAll();
if(bytes.size())
{
emit dataRead(QString(bytes));
}
}
private:
QTcpServer server;
};
#endif // MAINWINDOW_H
cpp file
#include "mainwindow.h"
#include <QApplication>
MainWindow::MainWindow(QWidget *parent ):
QMainWindow(parent)
{
edit = new QTextEdit(this);
net = new NetworkFile(this);
connect(net, &NetworkFile::dataRead,
this, &MainWindow::addText );
setCentralWidget(edit);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
As from the documentation:
Signals [...] can never have return types (i.e. use void).
Therefore slot return types are useless when invoked through a signal (even though you can still use them when you invoke a slot directly, ie if it's a member function).
That being said, you can capture ui or even better plainLineEdit by reference or by address in your slot (ie if it's a lambda) and set correctly the string from there.

Clickable QLabel class "multiply defined" functions

I'm having trouble moving from VC++ to Qt-style programming with slots/signals. I wanted to create a button with an image that, when clicked, changed to another image, and when released, changed back to its original. I created a class called ClickableQLabel that inherits from QLabel, but it's telling me that certain functions are re-defined. Specifically, the ones that are emitted.
MainProgram.cpp
#include "MainProgram.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
CodeVault w;
w.show();
return a.exec();
}
MainProgram.h
#ifndef MAINPROGRAM_H
#define MAINPROGRAM_H
#include <QMainWindow>
#include "clickableqlabel.h"
namespace Ui {
class MainProgram;
}
class MainProgram : public QMainWindow
{
Q_OBJECT
public:
explicit MainProgram(QWidget *parent = 0);
~MainProgram();
private:
Ui::MainProgram *ui;
};
#endif // MAINPROGRAM_H
clickableqlabel.h
#ifndef CLICKABLEQLABEL_H
#define CLICKABLEQLABEL_H
#include <QLabel>
#include <QMouseEvent>
using namespace Qt;
class ClickableQLabel : public QLabel {
Q_OBJECT
QPixmap pushed;
QPixmap unpushed;
public:
ClickableQLabel(QWidget *parent = 0);
void mousePressEvent(QMouseEvent *eve );
void mouseReleaseEvent(QMouseEvent *eve );
void setPushed(QPixmap &p);
void setUnpushed(QPixmap &p);
signals:
void leftButtonPressed(ClickableQLabel* sender);
void leftButtonReleased(ClickableQLabel* sender);
};
#endif // CLICKABLEQLABEL_H
clickableqlabel.cpp
#include "clickableqlabel.h"
ClickableQLabel::ClickableQLabel(QWidget *parent) : QLabel(parent){
// set up qlabel
}
void ClickableQLabel::setPushed(QPixmap &p){
pushed = p;
}
void ClickableQLabel::setUnpushed(QPixmap &p){
unpushed = p;
}
void ClickableQLabel::leftButtonPressed(ClickableQLabel* sender){
if(!pushed.isNull())
sender->setPixmap(pushed.scaledToWidth(sender->width()));
}
void ClickableQLabel::leftButtonReleased(ClickableQLabel* sender){
if(!unpushed.isNull())
sender->setPixmap(unpushed.scaledToWidth(sender->width()));
}
void ClickableQLabel::mousePressEvent(QMouseEvent *eve ){
if(eve->button() == Qt::LeftButton){
emit leftButtonPressed(this);
}
}
void ClickableQLabel::mouseReleaseEvent(QMouseEvent *eve ){
if(eve->button() == Qt::LeftButton){
emit leftButtonReleased(this);
}
}
What I receive is the following 3 errors:
moc_clickableqlabel.obj:-1: error: LNK2005: "public: void __cdecl ClickableQLabel::leftButtonPressed(class ClickableQLabel *)" (?leftButtonPressed#ClickableQLabel##QEAAXPEAV1##Z) already defined in clickableqlabel.obj
moc_clickableqlabel.obj:-1: error: LNK2005: "public: void __cdecl ClickableQLabel::leftButtonReleased(class ClickableQLabel *)" (?leftButtonReleased#ClickableQLabel##QEAAXPEAV1##Z) already defined in clickableqlabel.obj
debug\CodeVault.exe:-1: error: LNK1169: one or more multiply defined symbols found
The two functions that are causing the errors are the two signals in the clickableqlabel.h file. How am I supposed to set up the connect function and where?
You shouldn't provide an implementation for your signals. You just declare signals in the class header. the Qt moc provides an implementation that is responsible to call the slots connected to the signal when it is emitted, see this. Because you are providing an implementation and Qt moc is providing another one, you end up with two different implementations and the linker complains.
so, to make some piece of code execute when a signal is emitted, you can put it in some slot that is connected to that signal, or you can execute it manually before emitting the signal. . .
Another thing to note is that your signals have a parameter named sender. normally there is no need to do this, QObject::sender() provides similar functionality.
How am I supposed to set up the connect function and where?
When you instantiate your ClickableQLabel in your MainProgram window for example (You can do this by using ClickableQLabel in a .ui form file), you can connect its signals to slots of the MainProgram, like this.
mainprogram.h
#ifndef MAINPROGRAM_H
#define MAINPROGRAM_H
#include <QMainWindow>
#include "clickableqlabel.h"
namespace Ui {
class MainProgram;
}
class MainProgram : public QMainWindow
{
Q_OBJECT
public:
explicit MainProgram(QWidget *parent = 0);
~MainProgram();
public slots:
void labelPressed();
void labelReleased();
private:
Ui::MainProgram *ui;
};
#endif // MAINPROGRAM_H
then in the constructor connect the ClickableQLabel's signals to the MainProgram's slots , something like this:
connect(clickableLabel, SIGNAL(leftButtonPressed()), this, SLOT(labelPressed()));
where clickableLabel is your ClickableQLabel object.
You should not explicitly define signal "functions" - they are simply emitted (as you do in the mousePressEvent() and mouseReleaseEvent() functions).
If you want some action performed (such as setPixmap), you would either do that in the slot functions that are connected to those signals, or perhaps directly in the mousePressEvent and mouseReleaseEvent functions.
Unrelated, but you may also need to do something like qRegisterMetaType<ClickableQLabel>() to use that data type with the signal/slot mechanism.
You don't need to subclass QLabel to achieve this: put an onLabelButtonClicked slot (method) in your MainWindow class, which is connected to the QPushButton clicked() signal, and which calls setText / setIcon as required.
If you want to re-use this clickable element you can of course encapsulate it in a class, but subclassing and handling raw-events is only required in Qt when defining new kinds of widget; if you're simply composing standard functionality (being clicked, adjusting appearance) then slots on your main window / dialog class are usually sufficient.

undefined reference to function send

I'm a very beginnner at C++ /Qt programming. I've made this simple dialog box that check the QLineEdit, if the text entered is "bob" should enable the OK button.
I can't get it to compile successfully, it gives me:
dialog.cpp|31|undefined reference to `Dialogmio::send()'
What am I doing wrong?
This is dialog.h:
//dialog.h
#ifndef DIALOG_H_INCLUDED
#define DIALOG_H_INCLUDED
#include <QDialog>
class QPushButton;
class QLineEdit;
class Dialogmio : public QWidget
{
public:
Dialogmio(QWidget *parent =0);
signals:
void send ();
public slots:
void recip(QString &text);
private:
QLineEdit *linedit;
QPushButton *buttonOK;
};
#endif
This is dialog.cpp:
//dialog.cpp
#include <QtGui>
#include "dialog.h"
Dialogmio::Dialogmio(QWidget *parent)
: QWidget(parent)
{
linedit = new QLineEdit();
buttonOK = new QPushButton("OK");
buttonOK->setEnabled(FALSE);
connect( linedit, SIGNAL( textChanged(const QString &) ), this, SLOT( recip(const QString &) ));
connect (this,SIGNAL( send()), this, SLOT( buttonOK->setEnabled(true)) );
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(linedit);
layout->addWidget(buttonOK);
setLayout(layout);
}
void Dialogmio::recip(QString &text)
{
QString a = linedit->text();
if (a == "bob"){
emit send(); //here it gives me the error
}
}
This is main.cpp:
#include <QApplication>
#include "dialog.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Dialogmio *dialog = new Dialogmio;
dialog->show();
return app.exec();
}
I've inserted the Q_OBJECT macro as suggested, now I get one more errors on line 7:
dialog.cpp|7|undefined reference to `vtable for Dialogmio'|
You start by including the Qt file for QDialog, but then go on to inherit from QWidget. While inheriting from QWidget is not a problem, was your intention to actually inherit from QDialog(?), in which case you should define your class this way: -
class Dialogmio : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget* parent);
private slots:
void aSlotFunction();
}
The signals and slots mechanism are C++ extensions that are unique to Qt and in order for a class to use it, the class must include the Q_OBJECT macro, which adds all the necessary functionality. During the build stages, Qt parses the header and creates the code required for the extensions, including run-time type information, the dynamic property system and of-course the signals and slots.
As you state you're using codeblocks as the IDE, if it doesn't automatically run qmake before building, you'll need to do that whenever you add any signals or slots to a class in order for the moc (meta-object-compiler) to see them.
Another thing is that the call to connect signals and slots is wrong: -
connect (this,SIGNAL( send()), this, SLOT( buttonOK->setEnabled(true)) );
The parameter in the SLOT macro takes a slot function, so you need to create a slot and connect it to the send signal: -
connect(this, SIGNAL(send()), this, SLOT(aSlotFunction());
Inside the aSlotFunction, you can then call the set enabled for the button: -
void Dialogmio::aSlotFunction()
{
buttonOK->setEnabled(true);
}
If you're using Qt 5, there's an easier syntax of handling the connection: -
connect(this, &Dialogmio::send, this, &Dialogmio::aSlotFunction);
As this syntax accepts pointers to the functions that will be called, they don't actually have to be declared as slots to work. In addition, you do not provide the arguments, so if they change, you won't have to update the connect calls too.