This question already has answers here:
C++ Qt signal and slot not firing
(3 answers)
Closed 8 years ago.
I am very new to Qt and it looks like i have some very basic misunderstanding about how this library works. I am currently reading a book by m. Schlee but i do not want to continue until i do understand how to make this simple program work.
#include <QtWidgets>
#include <QApplication>
#include <QStackedWidget>
#include <QPushButton>
#include <QObject>
struct wizard : public QObject
{
QStackedWidget* p;
wizard(QStackedWidget* pp) : p(pp) { }
public slots:
void change()
{
int to = p->currentIndex();
if (to == p->count() - 1)
to = 0;
else
++to;
emit chIndex(to);
}
signals:
void chIndex(int);
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStackedWidget qsw;
QPushButton qpb("magic");
qsw.resize(500, 500);
qsw.move(500, 300);
qsw.setWindowTitle("test qsw");
qpb.move(330, 300);
qpb.setWindowTitle("test qpb");
QWidget* pw1 = new QWidget();
QPalette pal1;
pal1.setColor(pw1->backgroundRole(), Qt::blue);
pw1->setPalette(pal1);
pw1->resize(500, 500);
pw1->setAutoFillBackground(true);
QWidget* pw2 = new QWidget();
QPalette pal2;
pal2.setColor(pw2->backgroundRole(), Qt::yellow);
pw2->setPalette(pal2);
pw2->resize(500, 500);
pw2->setAutoFillBackground(true);
qsw.addWidget(pw1);
qsw.addWidget(pw2);
wizard stupidity(&qsw);
QObject::connect(&qpb, SIGNAL(clicked()), &stupidity, SLOT(change()));
QObject::connect(&stupidity, SIGNAL(chIndex(int)), &qsw, SLOT(setCurrentIndex(int)));
qpb.show();
qsw.show();
return a.exec();
}
The idea is to launch 2 separate windows: one with painted background, and another with button that changes color (blue->yellow->blue->.. etc).
They appear, but nothing happens if i press the button. Please help.
Except for struct being a class and a missing Q_OBJECT macro the code is fine
Try the following:
create a main.h file having this content:
#ifndef MAIN_H
#define MAIN_H
#include <QObject>
#include <QStackedWidget>
class wizard : public QObject
{
Q_OBJECT
public:
QStackedWidget* p;
wizard(QStackedWidget* pp) : p(pp) { }
public slots:
void change()
{
int to = p->currentIndex();
if (to == p->count() - 1)
to = 0;
else
++to;
emit chIndex(to);
}
signals:
void chIndex(int);
};
#endif // MAIN_H
change your main.cpp to the following content:
#include <QtWidgets>
#include <QApplication>
#include <QPushButton>
#include <main.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStackedWidget qsw;
QPushButton qpb("magic");
qsw.resize(500, 500);
qsw.move(500, 300);
qsw.setWindowTitle("test qsw");
qpb.move(330, 300);
qpb.setWindowTitle("test qpb");
QWidget* pw1 = new QWidget();
QPalette pal1;
pal1.setColor(pw1->backgroundRole(), Qt::blue);
pw1->setPalette(pal1);
pw1->resize(500, 500);
pw1->setAutoFillBackground(true);
QWidget* pw2 = new QWidget();
QPalette pal2;
pal2.setColor(pw2->backgroundRole(), Qt::yellow);
pw2->setPalette(pal2);
pw2->resize(500, 500);
pw2->setAutoFillBackground(true);
qsw.addWidget(pw1);
qsw.addWidget(pw2);
wizard stupidity(&qsw);
QObject::connect(&qpb, SIGNAL(clicked()), &stupidity, SLOT(change()));
QObject::connect(&stupidity, SIGNAL(chIndex(int)), &qsw, SLOT(setCurrentIndex(int)));
qpb.show();
qsw.show();
return a.exec();
}
qmake doesn't work very well with Q_OBJECT macro directly in cpp file. Btw. run qmake after changes applied.
Related
Consider following main.cpp (the only file of the whole Qt-project):
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include <QObject>
#pragma once
class collectedFunctions : public QObject
{
Q_OBJECT
public:
collectedFunctions(QObject* parent = 0) {};
~collectedFunctions() {};
public slots:
void setFunc() {
//Calculate 2+2 and change text of the label accordingly
}
};
#include "main.moc"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
QWidget window;
window.resize(200, 200);
window.setWindowTitle("Test-GUI");
QVBoxLayout layout(&window);
QPushButton button(&window);
button.setText("Test");
button.show();
QLabel *label = new QLabel(&window);
label->setText("Hello");
collectedFunctions testingFuncs;
QObject::connect(&button, &QPushButton::clicked, &testingFuncs, &collectedFunctions::setFunc);
layout.addWidget(&button);
layout.addWidget(label);
window.show();
return app.exec();
}
My goal with this code is to create a slot that runs a function which the programmer defines (similar to the pyqt-implementation of the signals and slots system). The code above successfully compiles, not throwing any errors and so far meeting my expectations. Yet as soon I want to manipulate a widget (which in this case is the label), the setfunc-slot does not find the defined label. How can I manipulate a widget using this slot (e.g. using setText() at a label or addItems() at a combobox) or generally have them being recognized in the slot.
Update:
Thanks to the solution of this question, I figured that I could simply use a lambda instead of the Q_OBJECT makro, so I removed the header-code and then changed the connect from the Button to following:
QObject::connect(&button, &QPushButton::clicked, [&label](){
int num = 2 + 2;
string text = "2+2 = ";
text += std::to_string(num);
QString qtext = QString::fromStdString(text);
label->setText(qtext); });
you shouldn't use the Q_OBJECT macro in main.cpp.
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.
The moc tool reads a C++ header file. If it finds one or more class
declarations that contain the Q_OBJECT macro, it produces a C++ source
file containing the meta-object code for those classes. Among other
things, meta-object code is required for the signals and slots
mechanism, the run-time type information, and the dynamic property
system.
The C++ source file generated by moc must be compiled and linked with
the implementation of the class.
When you have your class defined in .cpp file instead of .h file moc fails to process it properly.
you need a separate file for class:
in collectedfunctions.h
#pragma once
#include <QObject>
class collectedFunctions: public QObject
{
Q_OBJECT
public:
collectedFunctions(QObject *parent = 0);
~collectedFunctions();
public slots:
void setFunc();
};
and in collectedfunctions.cpp
#include "collectedfunctions.h"
#include <QDebug>
collectedFunctions::collectedFunctions(QObject *parent)
{
}
collectedFunctions::~collectedFunctions()
{
}
void collectedFunctions::setFunc()
{
qDebug() << "2+2 = " << 2 + 2;
}
and in your main.cpp
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include "collectedfunctions.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.resize(200, 200);
window.setWindowTitle("Test-GUI");
QVBoxLayout layout(&window);
QPushButton button(&window);
button.setText("Test");
button.show();
QLabel *label = new QLabel(&window);
label->setText("Hello");
collectedFunctions testingFuncs;
QObject::connect(&button, &QPushButton::clicked, &testingFuncs, &collectedFunctions::setFunc);
layout.addWidget(&button);
layout.addWidget(label);
window.show();
return app.exec();
}
don't forget to add QT +=core gui widgets in your .pro file.
Result:
If you want to change QLabel Text :
in collectedfunctions.h
#pragma once
#include <QObject>
class collectedFunctions: public QObject
{
Q_OBJECT
public:
explicit collectedFunctions(QObject *parent = 0);
~collectedFunctions();
signals:
void updateLable(int num);
public slots:
void setFunc();
private:
int num = 0;
};
and in collectedfunctions.cpp
#include "collectedfunctions.h"
#include <QDebug>
collectedFunctions::collectedFunctions(QObject *parent)
{
}
collectedFunctions::~collectedFunctions()
{
}
void collectedFunctions::setFunc()
{
qDebug() << "2+2 = " << 2 + 2;
num++;
emit updateLable(num);
}
and in your main.cpp
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include "collectedfunctions.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.resize(200, 200);
window.setWindowTitle("Test-GUI");
QVBoxLayout layout(&window);
QPushButton button(&window);
button.setText("Test");
button.show();
QLabel *label = new QLabel(&window);
label->setText("Hello");
collectedFunctions testingFuncs;
QObject::connect(&button, &QPushButton::clicked, &testingFuncs, &collectedFunctions::setFunc);
QObject::connect(&testingFuncs, &collectedFunctions::updateLable, [&label](int num)
{
qDebug() << "Number = " << num;
label->setText(QString::number(num));
});
layout.addWidget(&button);
layout.addWidget(label);
window.show();
return app.exec();
}
you can see:
On Linux I can use this pipeline QMimeDatabase::mimeTypeForFile > QMimeType::iconName > QIcon::fromTheme to get icons for files. Afaik the latter works on Linux only.
How can I get icons for mimetype on macOS? Do I have to use icon(for:) and create the pixmap on my own, or are there better ways?
Solution
To get the icon for a file use QFileIconProvider::icon with QFileInfo like that:
QIcon fileIcon = QFileIconProvider().icon(QFileInfo(fileName));
Example
Here is a simple example I wrote for you to demonstrate the proposed solution:
#include <QApplication>
#include <QFileIconProvider>
#include <QFileDialog>
#include <QFileInfo>
#include <QBoxLayout>
#include <QPushButton>
class Widget : public QWidget
{
public:
Widget(QWidget *parent = nullptr) : QWidget(parent) {
auto *l = new QVBoxLayout(this);
auto *btnOpen = new QPushButton(tr("Open File"), this);
btnOpen->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
l->addWidget(btnOpen);
connect(btnOpen, &QPushButton::clicked, this, &Widget::onFileOpen);
resize(300, 300);
}
private slots:
void onFileOpen() {
const QString &fileName(QFileDialog::getOpenFileName(this, tr("Open File")));
if (fileName.isEmpty())
return;
auto *btnOpen = static_cast<QPushButton *>(sender());
btnOpen->setIcon(QFileIconProvider().icon(QFileInfo(fileName)));
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
I am doing a qt project, my goal is to draw something based on my input. The signal is in the main, and the drawing is in another object, I am not able to connect them.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Frequency fre; //this is the class that does the drawing
QWidget *window = new QWidget;
QGridLayout *grid = new QGridLayout;
QGroupBox *groupBox = new QGroupBox(QObject::tr("Volume"));
QSpinBox *spinBox = new QSpinBox;
spinBox->setRange(0, 5);
QObject::connect(spinBox, SIGNAL(valueChanged(int)),&fre, SLOT(setVolume(int)));
QVBoxLayout *Vbox = new QVBoxLayout;
Vbox->addWidget(spinBox);
groupBox->setLayout(Vbox);
grid->addWidget(groupBox, 4,7,1,1);
window->setLayout(grid);
window->show();
return app.exec();
}
This is how I set up the Frequency class :
in h file
class Frequency : public QGLWidget
{
Q_OBJECT
public slots:
void setVolume(int value);
in cpp file
void Frequency :: setVolume(int val) {
vol = val;
updateGL();
}
void Frequency :: paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw();
}
Since I can't put all this in the comment section, I'm going to put it here:
I copied your code and I'm not able to reproduce the error with Qt version 5.14.0, because when I change the value in any way, the method, void MyObject::setVolume(int value) is being called.
This code should work for you. Possibly, you are running it in release mode, and used a breakpoint to check if it was called (which doesn't always work on release mode).
// #include "QtWidgetsApplication4.h"
#include <QtWidgets/QApplication>
#include "MyObject.h"
#include <QGridLayout>
#include <QGroupBox>
#include <QSpinBox>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// QtWidgetsApplication4 w;
// w.show();
// return a.exec();
MyObject object;
QWidget* window = new QWidget;
QGridLayout* grid = new QGridLayout;
QGroupBox* groupBox = new QGroupBox(QObject::tr("Volume"));
QSpinBox* spinBox = new QSpinBox;
spinBox->setRange(0, 5);
QObject::connect(spinBox, SIGNAL(valueChanged(int)), &object, SLOT(setVolume(int)));
QVBoxLayout* Vbox = new QVBoxLayout;
Vbox->addWidget(spinBox);
groupBox->setLayout(Vbox);
grid->addWidget(groupBox, 4, 7, 1, 1);
window->setLayout(grid);
window->show();
return a.exec();
}
#pragma once
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public slots:
void setVolume(int value);
private:
int m_volume;
};
#include "MyObject.h"
void MyObject::setVolume(int value)
{
m_volume = value;
}
I have a main window which opens a new window and connects a button from the new window to a "close" function. The problem arises when that new window has more than one button; it will always connect the last created button instead of the explicited one. Here is a sample working code:
"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 "screen_char_info.h"
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
QPushButton *button_show_char_info;
Screen_Char_Info *screen_char_info;
QWidget *mainwidget;
QVBoxLayout *layout_main;
MainWindow(QWidget *parent = 0) : QMainWindow(parent) {
button_show_char_info = new QPushButton("Character info", this);
layout_main = new QVBoxLayout();
mainwidget = new QWidget(this);
screen_char_info = NULL;
QObject::connect (button_show_char_info, &QPushButton::clicked, [this]{
if (screen_char_info == NULL) {
screen_char_info = new Screen_Char_Info();
screen_char_info->show();
QObject::connect (screen_char_info->button_return, &QPushButton::clicked, [=] {
screen_char_info->close();
screen_char_info = NULL;
});
}
});
layout_main->addWidget(button_show_char_info);
mainwidget->setLayout(layout_main);
setCentralWidget(mainwidget);
}
~MainWindow()
{
}
};
#endif // MAINWINDOW_H
"screen_char_info.h"
#ifndef SCREEN_CHAR_INFO_H
#define SCREEN_CHAR_INFO_H
#include <QString>
#include <QMenu>
#include <QMenuBar>
#include <QLabel>
#include <QTextEdit>
#include <QPushButton>
#include <QWidget>
#include <QLineEdit>
#include <QGridLayout>
class Screen_Char_Info : public QWidget {
Q_OBJECT
public:
QPushButton *buttons_modify_attributes[15];
QPushButton *button_return;
QGridLayout *layout;
Screen_Char_Info () {
this->setAttribute(Qt::WA_DeleteOnClose);
this->setWindowTitle("Character Info");
layout = new QGridLayout(this);
for (int i = 0; i <= 15; i++) {
buttons_modify_attributes[i] = new QPushButton((i%2 ? "-" : "+"), this);
connect(buttons_modify_attributes[i], &QPushButton::clicked, [this] {
});
layout->addWidget(buttons_modify_attributes[i], (i / 2), (i % 2), 1, 1);
}
layout->addWidget(button_return = new QPushButton("Return", this), 8, 0, 1, 1);
this->setLayout(layout);
}
};
#endif // SCREEN_CHAR_INFO_H
However, if i put the line layout->addWidget(button_return... before the for loop, the button that closes the window is the last "-" button, and not the return one.
The way you do the connect does not appear to be conventional. Try using traditional Qt way:
connect(pButtonToPress, SIGNAL(clicked()), pObjectToHandle, SLOT(onClicked));
Provided QPushButton* pButtonPress actually pointing to QPushButton and pObjectToHandle to some object (can be 'this' pointer):
class ObjHandler
{
public slot:
void onClicked();
};
... should satisfy. SIGNAL and SLOT are macro that work with some help of Qt meta object compiler. That is why having slot: statement is highly critical.
Found the bug, I was declaring a button matrix with 15 elements, but iterating over a 16 element loop. The 16th element was the return button, and was overwritten in the loop.
Alright so is there any way to make this program randomly change the variables x and y every time the button is clicked i am new to programming...
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QtGUI>
#include <QWidget>
#include <cstdlib>
#include <ctime>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *window = new QWidget;
srand(time(0));
int x = 1+(rand()%900);
int y = 1+(rand()%400);
QPushButton *MainInter = new QPushButton("Push me!",window);
QPropertyAnimation *animation = new QPropertyAnimation(MainInter, "pos");
animation->setDuration(0);
animation->setEndValue(QPoint(x,y));
Object::connect(MainInter,SIGNAL(released()),animation,SLOT(start()));
window->resize(900,500);
window->show();
return a.exec();
}
What you can do is, instead of connecting the released() signal of your button directly to your animations start() SLOT, you would create your own custom SLOT. Then you connect the button to it, handle the action, and call the animation.
First read up on how to create a custom QWidget, instead of creating top level object in your main(). Simple example here
A custom widget might look like this:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class QPushButton;
class QPropertyAnimation;
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
private:
QPushButton *button;
QPropertyAnimation *animation;
public slots:
void randomizeAnim();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QPushButton>
#include <QPropertyAnimation>
#include <ctime>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
button = new QPushButton("Push me!", this);
animation = new QPropertyAnimation(button, "pos");
animation->setDuration(0);
QObject::connect(button, SIGNAL(released()), this, SLOT(randomizeAnim()));
}
void MyWidget::randomizeAnim()
{
srand(time(0));
int x = 1+(rand()%900);
int y = 1+(rand()%400);
animation->setEndValue(QPoint(x,y));
animation->start();
}
And now your main.cpp can be reduced to the boilerplate code:
#include <QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *window = new MyWidget;
window->resize(900,500);
window->show();
return a.exec();
}
Every time you click, your custom slot will handle the action and do the animation.