Evgenii Legotckoi
Oct. 3, 2015, 10:56 p.m.

Qt/C++ - Lesson 026. Using CallBack function

To transfer data in Qt uses signals and slots system, but this does not mean that you can not use the old proven method, namely the use CallBack functions. The fact that the use CallBack function is somewhat fast option than signals and slots. And also can be easier to use with regard to the fact that it is desirable to disconnect signals from the slots when an object sends a program signal is destroyed and is no longer used. This point is particularly relevant when one considers that, in C ++ there is no garbage collection, like Java or C# .

The principle of CallBack

The principle of CallBack To use CallBack functions in a class that will have to return a result, you must declare a pointer to a function with the same signature as the function that will be used as a CallBack function. But to place the pointer on the function you want to use a class method to apply this pointer. That is, this method is passed a pointer to a function, which is set in the CallBack class pointer, which will return the result of its activities. At the same time in this class, this pointer is used as an ordinary function that will perform the action specified in the class from which this feature has been installed as a CallBack function in the current class.

For example, the class will be used, which draws a square on the graphic scene and Control keys W, A, S, D. When moving square should send data on their coordinates in the class that was created. That is, should call this class function as its CallBack function.


Project Structure

Project Structure For a work function CallBack use project with the following structure:

  • mainwindow.h - The header of the main window;
  • mainwindow.cpp - source files of the main application window;
  • square.h - Header class file, the object of which is to use the CallBack function.
  • square.cpp - file source code of the class;

mainwindow.ui

In the main window in the designer throws a graphic scene , and objects QLineEdit class, which will display the coordinates manually create and set in this window. Since the data objects must be declared as static. The same condition should apply for CallBack function. It must also be declared as static .

mainwindow.h

Also, in the header of the main window to declare the object class Square.

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QGraphicsScene>
  6. #include <QLineEdit>
  7.  
  8. #include <square.h>
  9.  
  10. namespace Ui {
  11. class MainWindow;
  12. }
  13.  
  14. class MainWindow : public QMainWindow
  15. {
  16. Q_OBJECT
  17.  
  18. public:
  19. explicit MainWindow(QWidget *parent = 0);
  20. ~MainWindow();
  21.  
  22. private:
  23. Ui::MainWindow *ui;
  24. QGraphicsScene *scene;
  25. Square *square; // We declare the square, which will transmit the callback function
  26. static QLineEdit *line1; // We declare a static QLineEdit, which will run the callback function
  27. static QLineEdit *line2; // We declare a static QLineEdit, which will run the callback function
  28.  
  29. private:
  30. // Declare a callback function
  31. static void getPosition(QPointF point);
  32. };
  33.  
  34. #endif // MAINWINDOW_H

mainwindow.cpp

In addition to static objects QLineEdit their ads more necessary and implemented as functions in the source file, otherwise the compiler will announce an error. The fact that static objects necessarily need to be initialized.

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3.  
  4. MainWindow::MainWindow(QWidget *parent) :
  5. QMainWindow(parent),
  6. ui(new Ui::MainWindow)
  7. {
  8. ui->setupUi(this);
  9.  
  10. // Инициализируем объекты QLineEdit
  11. line1 = new QLineEdit();
  12. line2 = new QLineEdit();
  13.  
  14. // Устанавлвиваем их в gridLayout
  15. ui->gridLayout->addWidget(line1,0,1);
  16. ui->gridLayout->addWidget(line2,0,2);
  17.  
  18. scene = new QGraphicsScene(); // Init graphics scene
  19. ui->graphicsView->setScene(scene); // Set scene into the graphicsView
  20. scene->setSceneRect(0,0,300,300);
  21. square = new Square();
  22. square->setCallbackFunc(getPosition); // Set callback function into th square
  23. square->setPos(100,100);
  24. scene->addItem(square);
  25. }
  26.  
  27. MainWindow::~MainWindow()
  28. {
  29. delete ui;
  30. }
  31.  
  32. /* callback function receives a square position and places it in a position line1 and line2
  33. * */
  34. void MainWindow::getPosition(QPointF point)
  35. {
  36. line1->setText(QString::number(point.x()));
  37. line2->setText(QString::number(point.y()));
  38. }
  39.  
  40. QLineEdit * MainWindow::line1;
  41. QLineEdit * MainWindow::line2;

square.h

This class is inherited from QGraphicsItem and it is declared a pointer for the CallBack function and function for its installation. The function return values must be specified without fail.

  1. #ifndef SQUARE_H
  2. #define SQUARE_H
  3.  
  4. #include <QObject>
  5. #include <QGraphicsItem>
  6. #include <QPainter>
  7. #include <QTimer>
  8. #include <QPointF>
  9.  
  10. class Square : public QObject, public QGraphicsItem
  11. {
  12. Q_OBJECT
  13. public:
  14. explicit Square(QObject *parent = 0);
  15. ~Square();
  16. // Function for setting of callback function
  17. void setCallbackFunc(void (*func) (QPointF point));
  18.  
  19. signals:
  20.  
  21. public slots:
  22.  
  23. protected:
  24. QRectF boundingRect() const;
  25. void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
  26.  
  27. private:
  28. QTimer *timer;
  29. // Pointer to callback function
  30. void (*callbackFunc)(QPointF point);
  31.  
  32. private slots:
  33. void slotTimer();
  34. };
  35.  
  36. #endif // SQUARE_H

square.cpp

This example is similar in mechanics to the game, so no wonder the presence of the game timer. In the slot, connected to the signal from timer we implement movement square in the graphic scene and to transfer its coordinates in the CallBack function. And to check the status of target use functional buttons WinAPI .

  1. #include "square.h"
  2. #include <windows.h>
  3.  
  4. Square::Square(QObject *parent) :
  5. QObject(parent), QGraphicsItem()
  6. {
  7. timer = new QTimer();
  8. connect(timer, &QTimer::timeout, this, &Square::slotTimer);
  9. timer->start(1000/33);
  10. }
  11.  
  12. Square::~Square()
  13. {
  14.  
  15. }
  16.  
  17. QRectF Square::boundingRect() const
  18. {
  19. return QRectF(-15,-15,30,30);
  20. }
  21.  
  22. void Square::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  23. {
  24. painter->setPen(Qt::black);
  25. painter->setBrush(Qt::green);
  26. painter->drawRect(-15,-15,30,30);
  27.  
  28. Q_UNUSED(option);
  29. Q_UNUSED(widget);
  30. }
  31.  
  32. void Square::slotTimer()
  33. {
  34. // Depending on the button pressed move the square around the stage
  35. if(GetAsyncKeyState('A')){
  36. this->setX(this->x() - 2);
  37. }
  38. if(GetAsyncKeyState('D')){
  39. this->setX(this->x() + 2);
  40. }
  41. if(GetAsyncKeyState('W')){
  42. this->setY(this->y() - 2);
  43. }
  44. if(GetAsyncKeyState('S')){
  45. this->setY(this->y() + 2);
  46. }
  47. // Call the callback function to transmit the coordinates of a square
  48. callbackFunc(this->pos());
  49. }
  50.  
  51. void Square::setCallbackFunc(void (*func)(QPointF point))
  52. {
  53. // Set the pointer to the callback function
  54. callbackFunc = func;
  55. }

Result

As a result, you should get the app, which made management a green square, and through the CallBack feature available data on its coordinates in the class of the main window.

Video

Recommended articles on this topic

By article asked1question(s)

6

Do you like it? Share on social networks!

ЛП
  • March 27, 2017, 2:17 p.m.

Хорошо объяснено, но картинку я бы заменил =) А зачем создавать статические объекты?

QLineEdit * MainWindow::line1;

В данном конкретном случае сделать QLineEdit статическим является самым простым способом в принципе работать с этим объектом внутри статической функции. Если удалить static у QLineEdit, то метод getPosition выбросит ошибку, что невозможно чего-то там... точной формулировки не помню.

Недостаток статических методов в том, что они не будут работать с полями членами класса, если те не будут статическими. Есть конечно, ещё варианты несколько иначе получать id окна, с помощью API операционной системы, по нему кастовать полученный объект в MainWindow, а потом пройтись по child Объектам, найти нужный QLineEdit. Слишком геморно и вообще статья не о том.

ЛП
  • March 27, 2017, 4:59 p.m.

У меня еще вопросы! 1.

callbackFunc(this->pos());
"записали" координаты в указатель на функцию без реализации. 2.
 
void Square::setCallbackFunc(void (*func)(QPointF point))
{
    // Устанавливаем указатель на callback функцию
    func = callbackFunc ; // не логично ли такое присваивание?
}

1. Что значит без реализации? Это функция имеет реализацию. И реализацией это функции является метод:

void MainWindow::getPosition(QPointF point)

Да, это работать не будет, если не будет установлена соответствующая функция перед тем, как её использовать, но она в этом примере устанавливается:

square->setCallbackFunc(getPosition);   // Устанавливаем в квадрат callback функцию

2. Вообще никакой логики:

void Square::setCallbackFunc(void (*func)(QPointF point))
{
    // Устанавливаем указатель на callback функцию
    func = callbackFunc ; // не логично ли такое присваивание?
}

В корне неправильный подход. func - Это аргумент, который передаётся в класс для установки в качестве callback . А переменная callbackFunc - это просто указатель на функцию, в который нужно установить эту callback функцию, то есть аргумент func . Изначально там содержится nullptr , а если будет вызываться nullptr , то будет краш программы. А если Вы попытаетесь установить nullptr в указатель, который указывает на метод класса, который реализован, то будет краш программы.

Проще говоря, работать даже не будет. Это ещё с объектами такое можно будет сделать, когда например забрать указатель на какой-то объект класса извне. Да и то это в корне неправильный подход. Для такого существуют функции геттеры ( SomeClass* getSomeObject() , например )

ЛП
  • March 27, 2017, 8:35 p.m.

А что означает

callbackFunc(this->pos())
? Я просто меняю значение аргументов у указателя?

callbackFunc - это указатель на некую функцию, сигнатура которой указана в заголовочном файле класса Square

В качестве callbackFunc выступает метод void MainWindow::getPosition(QPointF point) . Посмотрите сигнатуру (сигнатура, то есть объявление идентично сигнатуре callbackFunc ) и реализацию этого метода. Вот что в нём реализовано то он и делает. А аргументы в функцию передаются, а что с ними уже происходит - это вопрос реализации.

c
  • April 21, 2018, 9:53 p.m.

I don’t understand in Mainwindow.cpp lines 40 + 41 what or how these lines work? They look like a declaration but they are in the implementation which doesnt make sense to me. Please explain:


  1. QLineEdit * MainWindow::line1;
  2. QLineEdit * MainWindow::line2;
Evgenii Legotckoi
  • April 22, 2018, 4:34 p.m.

There are static members of class. There in cpp file it isn`t declaration of these members, it`s implementation without assigning a value. Some value will be assigned to these members in constructor later.

c
  • April 22, 2018, 5:26 p.m.
That is what I thought however do not understand why it is necessary. I guess the format was new and unfamiliar to me. Found other examples online where the value was assigned to NULL. That worked as well.
Evgenii Legotckoi
  • April 22, 2018, 5:30 p.m.

It is especciality of workflow with static members.
And I think using of nullptr instead of NULL is better. Because of using of nullptr is modern standard of C++.

А
  • Feb. 12, 2019, 2:19 p.m.
  • (edited)

День добрый! Можешь выложить форму mainwindow.ui от урока? Заранее спасибо

А
  • Feb. 12, 2019, 3:26 p.m.

Сам разборался, спасибо.

R
  • Aug. 3, 2020, 9:56 p.m.

Добрый день, объясните, пожалуйста, почему функция объявлена статической?

Evgenii Legotckoi
  • Aug. 4, 2020, 3:24 p.m.

Если не объявлять статической, то не соберётся. Не получится сделать привязку метода.
Дело в том, что в процессе компиляции производится сборка с указанием конкретных участков кода в данном случае. А передача в качестве аргумента нестатического метода приводит к привязке к динамически вызываемой части кода, поскольку объект должен быть создан в процессе работы программы.
Обычные callback функции не должны быть частью класса, но статические методы являются глобальными для класса. Поэтому есть возможность их передавать в качестве callback.
Но вообще это вполне возможно сделать без статического объявления функции, если использовать в качестве передаваемого аргумента std::function объект . И в данном случае уже передавать лямбда функцию с замыканием на конкретный объект, то есть на объект MainWindow. Тогда всё можно будет сделать без статики.

R
  • Aug. 4, 2020, 3:53 p.m.

Спасибо огромное!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • AK
    April 1, 2025, 11:41 a.m.
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup