Okay I'm using Qt Designer to build a GUI. I've managed to figure out how to make the menuBar and I've added some actions to the bar, but now I need to connect the actions to make them do something.
Specifically, on my file menu, I have the simple open action. I want this action to run a function that calls my QFileDialog and so on, but I don't know how to do this.
So, how do I connect my actionOpen to my static function?
I am using the latest Qt, 5.0.2
I'm a little frustrated here. This is obviously one of the most basic things someone might need to do, yet I cannot find any real solution to this anywhere on the web. From the lacking Qt wiki, to other people's questions, nobody really seems to have a clear answer. There are answers for older versions of Qt, yet in those old versions apparently signals couldn't connect to static functions, so those are irrelevant. And nobody seems to know how to do this through the Qt Designer. Also, nobody ever clarifies where to put what.
I have this line in my main.cpp file:
QObject::connect(actionOpen, &actionOpen::triggered, fileOpen)
I have an object called 'actionOpen' made in Qt Designer, there is a signal called triggered, and I have a function defined just below my main inside main.cpp called 'fileOpen'. This seems to follow the proper syntax, yet it throws many errors.
Also, I can repeatedly click build in Qt Creator and every single time it comes up with a different number of errors, disappearing and reappearing, without me even touching the code. I'm starting to think this IDE is sort of a POS.
EDIT:
Here are my files. Maybe this will help somewhat.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void fileOpen();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QObject::connect(ui->actionOpen, &QAction::triggered, &MainWindow::fileOpen);
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void fileOpen()
{
/*
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
if (!fileName.isEmpty()) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
return;
}
QTextStream in(&file);
ui->textEdit->setText(in.readAll());
file.close();
}
*/
cout << "Hello!";
}
The second argument is incorrect. You should specify the class name, not object name. So it should be:
QObject::connect(actionOpen, &QAction::triggered, fileOpen);
Complete working example (tested):
void fileOpen() {
qDebug() << "ok";
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMenu menu;
QAction* actionOpen = menu.addAction("test");
QObject::connect(actionOpen, &QAction::triggered, fileOpen);
menu.show();
return a.exec();
}
1.) Create regular slot and call the static function.
OR
2.) I suppose you could create a singleton Q_OBJECT class and connect to it - of course like option 1 you'd then make the call to whatever static/global function.
/**
* Pseudo-code!, the singleton should be in its own header file
* not in the global main.cpp file.
*/
class Singleton : public QObject
{
Q_OBJECT
public:
Singleton() : QObject() {}
static Singleton* getInstance() {
if(!_instance) _instance = new Singleton();
return _instance;
}
public slots:
void mySlot() { qDebug() << __FILE__ << " " << __FUNCTION__ << __LINE__;
}
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = NULL;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
Singleton* instance = Singleton::getInstance();
QObject::connect(&w, SIGNAL(destroyed()), instance, SLOT(mySlot()));
return a.exec();
}
I'm just trying my best to answer the question, but like many of the comments above - this isn't something that I've needed ever needed to do. I can say that QtCreator/Designer is a really amazing tool and as you overcome some of the learning curves it'll be less frustrating.
Related
Update:
If I delete Q_OBJECT, and I do not use SLOT SINGAL, just use connect() like this:
connect(this, &QWidget::destroyed, this, &QWidget::myslot),
my code will run well without any warnings and errors.
I want to write a little code to instruct some classes, so I try to simplify my code. But I encountered some strange things. I cannot write a simple widget in my main.cpp. If I write the widget in mywidget.cpp and mywidget.h, the program runs well. If I want to write the widget in the main.cpp, what should I do?
This is my code.
#include <QApplication>
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
}
Widget::~Widget()
{
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
And the error information is:
Add following line to the end of main.cpp and rerun qmake:
#include "main.moc"
That will invoke moc tool for your main.cpp. It generates meta-object function definitions for your Widget class resolving your linker errors when you rebuild.
As stated in documentation:
Whenever qmake is run, it parses the project's header files and
generates make rules to invoke moc for those files that contain a
Q_OBJECT macro.
Thus, put the class declaration in a header file (e.g. widget.h) and the class definition in a source file with the same name (e.g. widget.cpp).
If you still want to make it work with signals you can remove the Q_OBJECT macro and use the QObject::connect()
for example:
#include <QApplication>
#include <QWidget>
#include <iostream>
class Widget : public QWidget
{
//Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
Widget::Widget(QWidget *parent) : QWidget(parent)
{
// say bey
QObject::connect(this,&Widget::destroyed,
[](){std::cout<<"bye"<<std::endl;});
}
Widget::~Widget()
{
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
I test it on Qt5.9.4
This is a tutorial code for Qt:
Header file:
#include <QMainWindow>
namespace Ui {
class Notepad;
}
class Notepad : public QMainWindow
{
Q_OBJECT
public:
explicit Notepad(QWidget *parent = 0);
~Notepad();
private:
Ui::Notepad *ui;
};
Source file:
#include "notepad.h"
#include "ui_notepad.h"
Notepad::Notepad(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Notepad)
{
ui->setupUi(this);
}
Notepad::~Notepad()
{
delete ui;
}
And in main,
#include "notepad.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Notepad w;
w.show();
return a.exec();
}
So when we do Notepad w, an object is already created on the stack, and why does the code still create another object on the heap using new and assign to a member?
The one on the stack is of type Notepad, and it's the application's main window. The dynamic one is of type Ui::Notepad. That's a class automatically generated by Qt's uic tool; it contains the widgets created in UI creator as data members.
In a way, you could say that Notepad is concerned with the logic and uses an instance of Ui::Notepad to provide the GUI for it.
I am aware that to use the signals and slots mechanism of Qt inside a class, the class must include the Q_OBJECT macro, but I am attempting to use signals and slots in main(), without using any class.
Here is my code so far:
#include <QApplication>
#include <QWidget>
#include <QTextEdit>
#include <QtGui>
void saveText();
int main(int argv, char **args)
{
QApplication app(argv, args);
QTextEdit textEdit;
QPushButton saveButton("Save!");
QPushButton exitButton("Exit!");
QObject::connect(&exitButton,SIGNAL(clicked()),qApp,SLOT(quit()));
QObject::connect(&saveButton,SIGNAL(clicked()),qApp,SLOT(saveText()));
QVBoxLayout vlyt;
vlyt.addWidget(&textEdit);
vlyt.addWidget(&exitButton);
vlyt.addWidget(&saveButton);
QWidget mainWindow;
mainWindow.setLayout(&vlyt);
mainWindow.show();
return app.exec();
}
void saveText()
{
exit(0);
}
Here is the GUI window generated:
From the above code, the exit button is connected to quit(), which is a Qt function, when clicked it works. The save button assigned to the function saveText(), is configured to exit, but does not do so.
Please tell me where I have gone wrong in understanding signals and slots in Qt.
Qt4...
All classes that inherit from QObject or one of its subclasses (e.g.,
QWidget) can contain signals and slots.1
So, you can not use slots where placed outside of QObject children.
You can connect signals to the slots which are in classes where derived from QObject. Put your slot in a class which is in a separated .h/.cpp file:
class MyClass : public QObject
{
Q_OBJECT
...
public slots:
void saveText();
};
According to Qt5: New Signal Slot Syntax in Qt 5. You can connect to those type of global functions. (Thanks to #thuga's comments)
I'll just put example here.
main.cpp:
#include <QCoreApplication>
#include <iostream>
#include <QObject>
#include "siggen.h"
void handler(int val){
std::cout << "got signal: " << val << std::endl;
}
int main(int argc, char *argv[])
{
SigGen siggen;
QObject::connect(&siggen, &SigGen::sgAction, handler);
siggen.action();
QCoreApplication a(argc, argv);
std::cout << "main prog start" << std::endl;
return a.exec();
}
siggen.h:
#ifndef SIGGEN_H
#define SIGGEN_H
#include <QObject>
class SigGen : public QObject
{
Q_OBJECT
public:
explicit SigGen(QObject *parent = 0);
void action(void);
signals:
void sgAction(int value);
};
#endif // SIGGEN_H
siggen.cpp:
#include "siggen.h"
SigGen::SigGen(QObject *parent) : QObject(parent)
{}
void SigGen::action()
{
emit sgAction(42);
}
QObject::connect(&saveButton, &QPushButton::clicked, [](){saveText();}); // qt5.9.6
or as mentioned in the masoud's answer
QObject::connect(&saveButton, &QPushButton::clicked, saveText); // qt5.9.6
It is possible to connect a signal to function inside main function.
This has been tested in Qt5.15. Here is the simple example where the QPushButton 'Clicked' signal is used to trigger a function (here I used lamda's, but regular functions can also be used).
int main(int argc, char *argv[])
{
// Created QApplication
QApplication a(argc, argv);
// Created the splashscreen(which is QObject)
QPixmap pixmap(":/images/Sample.png");
QSplashScreen splash(pixmap);
// Created the pushbutton and added to splashscreen
QPushButton b(&splash);
b.setGeometry(50,50, 100, 50);
b.setText("FPS");
// variable to be modified inside a lamda function
int i = 0;
// Connection for button clicked signal executes lamda function
QObject::connect(&b, &QPushButton::clicked,
[i = static_cast<const int&>(i), &splash = static_cast<QSplashScreen&>(splash)]()mutable
{i = i+1; splash.showMessage("clicked: "+ QString::number(i));});
// Adding properties and displaying the splashscreen
splash.setGeometry(0,0, 1920, 1080);
splash.setEnabled(true);
splash.show();
a.processEvents();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void test();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
void MainWindow::test()
{
close(); // Line 1
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
close(); // Line 2
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
w.test();
return a.exec();
}
If I comment out the close() in line 2, the close() in line 1 closes the window and leaves the application running.
If I comment out the close() in line 1, the close() in line 2 closes the window and ends the application.
Why is there this difference in behaviour?
Reason for this is that w.test() call is called before event loop has been started (a.exec()), so it will not cause any other event to happen which could be related to close() operation of QMainWindow.
In the second approach, your close() function will be called after event loop has been started, better said, within your event loop (when you click the button, you will be dispatched to on_pushButton_clicked() method, and close() will inform your application that it needs to be ended.
In order to run test() function in event loop, use single-shot timer:
//Qt 4 - in Qt 5, you can work this out without helper slot:
class MainWindow: public QMainWindow{
//...
void test();
//...
public slots:
void testHelper(){ test(); }
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QTimer::singleShot(0, &w, SLOT(testHelper()));
return a.exec();
}
Edit
It seems that Qt5's new connect syntax will not help you here to avoid additional slot [1]
Qt5 having a new connection syntax which allows for the use of lambdas and QtConcurrent already beeing capable of using lambdas QTimer or more specifically QTimer::singleShot() is lacking both.
It would be convenient (and consistent) to have function pointer syntax and lambda support for QTimer::singleShot() as well.
Edit 2
To be more precise, when you call QMainWindow::close(), QMainWindow::closeEvent(QCloseEvent *) event is raised, and normally it should be processed in the current event loop. Since there is no event loop in the point of calling close, this event is not caught up and nothing will cause application to exit.
I want to have this code in one file, but can't figure out how to. I know it might not be good practice to do so but I am trying to learn qt, and would find it easier to understand the information if it were in one file.
This is the main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.showMaximized();
return app.exec();
}
This is the mainwindow.cpp
#include "mainwindow.h"
#include <QCoreApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// Create the button, make "this" the parent
m_button = new QPushButton("My Button", this);
// set size and location of the button
m_button->setGeometry(QRect(QPoint(100, 100),
QSize(200, 50)));
// Connect button signal to appropriate slot
connect(m_button, SIGNAL(released()), this, SLOT(handleButton()));
}
void MainWindow::handleButton()
{
// change the text
m_button->setText("Example");
// resize button
m_button->resize(100,100);
}
this is the mainwindow.h
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private slots:
void handleButton();
private:
QPushButton *m_button;
};
By just copying everything in one file.
#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private slots:
void handleButton();
private:
QPushButton *m_button;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// Create the button, make "this" the parent
m_button = new QPushButton("My Button", this);
// set size and location of the button
m_button->setGeometry(QRect(QPoint(100, 100),
QSize(200, 50)));
// Connect button signal to appropriate slot
connect(m_button, SIGNAL(released()), this, SLOT(handleButton()));
}
void MainWindow::handleButton()
{
// change the text
m_button->setText("Example");
// resize button
m_button->resize(100,100);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.showMaximized();
return app.exec();
}
It is usually not a good idea to define new classes in same file as your main. Generally you want new classes each in their own file or you would want to put several related classes together in a seperate file. There are a tonne of resources you can google related to best practices for this. I'd suggest you spend some time reading.
But since you asked... below is how you would do it for your example. If you do not define your class above the main, the compiler will complain because it won't know what a "MainWindow" is.
#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private slots:
void handleButton();
private:
QPushButton *m_button;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.showMaximized();
return app.exec();
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// Create the button, make "this" the parent
m_button = new QPushButton("My Button", this);
// set size and location of the button
m_button->setGeometry(QRect(QPoint(100, 100),
QSize(200, 50)));
// Connect button signal to appropriate slot
connect(m_button, SIGNAL(released()), this, SLOT(handleButton()));
}
void MainWindow::handleButton()
{
// change the text
m_button->setText("Example");
// resize button
m_button->resize(100,100);
}
#include essentially takes the contents of whatever file you choose and copy/pastes it at that location. The compiler then starts at the top of the file and works its way down to the bottom.
Knowing that, you should be able to just copy-paste the contents of the files in the order they are included.
mainwindow.h
mainwindow.cpp
main.cpp
The short answer is don't do this, you should define a class in its own header file and #include it to the main when you want to run it in the main. This allows you to reuse the class as you see fit throughout the program, its one of the tenets of Object Oriented programming, reusable code. For example
class A
{
public:
A();
~A();
void somePublicMethod();
private:
void somePrivateMethod();
};
If you include that class in your main when you compile that class to object code(assuming you know about implementing that class in a .cpp file) then when all the object files are linked to create the program the linker basically makes one big file with all the object code included in file to be fully compiled. I suggest you read up more on compiling and linking, essentially it boils down to three phases, preprocessing, compiling and linking. Learn more about Object Oriented programming and read up why it's a bad idea to just shove them all into one file. Every class should be in its own self contained .h file(unless its a tightly coupled class) so you can include them as you see fit. Hope this helps, have fun with Qt :)