Evgenii Legotckoi
Evgenii LegotckoiҚаз. 3, 2015, 12:56 Т.Қ.

Qt/C++ - Сабақ 026. Кері шақыру функциясын пайдалану

Деректерді Qt ішінде тасымалдау үшін сигналдар мен слоттар жүйесі пайдаланылады, бірақ бұл ескі дәлелденген әдісті, атап айтқанда CallBack функцияларын пайдалану мүмкін емес дегенді білдірмейді. Мәселе мынада: Кері қоңырау функциясын пайдалану сигналдар мен ұяшықтарға қарағанда біршама жылдамырақ. Сондай-ақ оны пайдалану оңайырақ болуы мүмкін, өйткені сигнал жіберетін нысан бағдарламада жойылып, енді пайдаланылмай қалған кезде сигналдарды ұялардан ажыратқан жөн. Бұл тармақ әсіресе C++ тілінде Java немесе C# сияқты қоқыс жинаушы болмағандықтан маңызды.

Қайта шақыру функциясы қалай жұмыс істейді

CallBack функциясы қалай жұмыс істейді Нәтижені қайтаратын сыныпта CallBack функцияларын пайдалану үшін CallBack функциясы ретінде пайдаланылатын функциямен бірдей қолтаңбасы бар функция көрсеткішін жариялау керек. Функцияға көрсеткіш орнату үшін сол көрсеткішті орнату үшін класс әдісін пайдалану керек. Яғни, функцияға көрсеткіш осы әдіске жіберіледі, ол CallBack әрекетінің нәтижесін қайтаратын класс көрсеткішіне орнатылады. Сонымен қатар, осы сыныпта бұл көрсеткіш ағымдағы сыныпта CallBack функциясы ретінде орнатылған сыныпта көрсетілген әрекеттерді орындайтын тұрақты функция ретінде пайдаланылады.

Мысалы, графикалық көріністе шаршы сызатын және W, A, S, D пернелері арқылы басқарылатын класс пайдаланылады.Жылжыған кезде шаршы өзінің координаталары туралы мәліметтерді өзі құрылған классқа жіберуі керек. Яғни, ол осы класстың функциясын өзінің CallBack функциясы ретінде шақыруы керек.


Жоба құрылымы

Жоба құрылымы CallBack функциясының жұмысымен танысу үшін біз келесі құрылымы бар жобаны қолданамыз:

  • mainwindow.h - қолданбаның негізгі терезесінің тақырып файлы;
  • mainwindow.cpp - қолданбаның негізгі терезесіне арналған бастапқы код файлы;
  • square.h - нысаны CallBack функциясын пайдаланатын сыныптың тақырып файлы.
  • square.cpp - осы сынып үшін бастапқы код файлы;

mainwindow.ui

Дизайнердегі негізгі терезеде біз графикалық көрініс, және координаталары болатын QLineEdit класының нысандарын тастаймыз. көрсетілген, біз осы терезеде қолмен жасаймыз және орнатамыз. Өйткені бұл нысандар статикалық ретінде жариялануы керек. Дәл осындай шарт Кері шақыру функциясына да қолданылуы керек. Ол сондай-ақ статикалық ретінде жариялануы керек.

mainwindow.h

Сондай-ақ негізгі терезенің тақырып файлында Square. класының объектісін жариялаймыз.

#ifndef MAINWINDOW\_H
#define MAINWINDOW\_H

#include <QMainWindow>
#include <QGraphicsScene>
#include <QLineEdit>

#include <square.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q\_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QGraphicsScene *scene;  // Объявляем графическую сцену
    Square *square;         // Объявляем квадрат, в который будем передавать callback функцию
    static QLineEdit *line1;    // Объявляем static QLineEdit, с которым будет работать callback функция
    static QLineEdit *line2;    // Объявляем static QLineEdit, с которым будет работать callback функция

private:
    // Объявляем callback функцию
    static void getPosition(QPointF point);
};

#endif // MAINWINDOW\_H

mainwindow.cpp

Статикалық QLineEdit нысандарын жариялаумен қатар, оларды бастапқы код файлында функциялар ретінде іске асыру қажет, әйтпесе компилятор қате жариялайды. Мәселе мынада, статикалық нысандар инициализациялануы керек.

#include "mainwindow.h"
#include "ui\_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // Инициализируем объекты QLineEdit
    line1 = new QLineEdit();
    line2 = new QLineEdit();

    // Устанавлвиваем их в gridLayout
    ui->gridLayout->addWidget(line1,0,1);
    ui->gridLayout->addWidget(line2,0,2);

    scene = new QGraphicsScene();       // Инициализируем графическю сцену
    ui->graphicsView->setScene(scene);  // Устанавливаем сцену в graphicsView
    scene->setSceneRect(0,0,300,300);   // Устанавливаем область сцены
    square = new Square();              // Инициализируем квадрат
    square->setCallbackFunc(getPosition);   // Устанавливаем в квадрат callback функцию
    square->setPos(100,100);            // Устанавливаем стартовую позицию квадрата
    scene->addItem(square);             // Добавляем квадрат на графическую сцену
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* callback функция получает позицию квадрата
 * и помещает его координаты в line1 и line2
 * */
void MainWindow::getPosition(QPointF point)
{
    line1->setText(QString::number(point.x()));
    line2->setText(QString::number(point.y()));
}

QLineEdit * MainWindow::line1;
QLineEdit * MainWindow::line2;

шаршы сағ

Бұл класс QGraphicsItem ішінен мұра алады және CallBack функциясы үшін көрсеткішті, сондай-ақ оны орнату функциясын жариялайды. Қайтару мәндерінің функциясы міндетті түрде көрсетілуі керек.

#ifndef SQUARE\_H
#define SQUARE\_H

#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QTimer>
#include <QPointF>

class Square : public QObject, public QGraphicsItem
{
    Q\_OBJECT
public:
    explicit Square(QObject *parent = 0);
    ~Square();
    // Функция для установки callback функции
    void setCallbackFunc(void (*func) (QPointF point));

signals:

public slots:

protected:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private:
    QTimer *timer;  // игровой таймер
    // Указатель на callback функцию
    void (*callbackFunc)(QPointF point);

private slots:
    void slotTimer();   // слот игрового таймера

};

#endif // SQUARE\_H

шаршы.cpp

Бұл мысал механикада ойынға ұқсас, сондықтан біз ойын таймерінің болуына таң қалмаймыз. Таймерден келетін сигналға қосылған ұяшықты пайдалана отырып, біз графикалық көріністе шаршының қозғалысын жүзеге асырамыз және оның координаттарын CallBack функциясына береміз. Ал мақсатты түймелердің күйін тексеру үшін WinAPI. функциясын қолданамыз

#include "square.h"
#include <windows.h>

Square::Square(QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    // Инициализируем и настраиваем игровой таймер
    timer = new QTimer();
    connect(timer, &QTimer::timeout, this, &Square::slotTimer);
    timer->start(1000/33);
}

Square::~Square()
{

}

QRectF Square::boundingRect() const
{
    return QRectF(-15,-15,30,30);
}

void Square::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::black);
    painter->setBrush(Qt::green);
    painter->drawRect(-15,-15,30,30);

    Q\_UNUSED(option);
    Q\_UNUSED(widget);
}

void Square::slotTimer()
{
    // В зависимости от нажатых кнопок перемещаем квадрат по сцене
    if(GetAsyncKeyState('A')){
        this->setX(this->x() - 2);
    }
    if(GetAsyncKeyState('D')){
        this->setX(this->x() + 2);
    }
    if(GetAsyncKeyState('W')){
        this->setY(this->y() - 2);
    }
    if(GetAsyncKeyState('S')){
        this->setY(this->y() + 2);
    }
    // Вызываем callback функцию для передачи координат квадрата
    callbackFunc(this->pos());
}

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

Барлығы

Нәтижесінде сіз жасыл шаршы басқарылатын және оның координаттары туралы деректер Кері қоңырау функциясы арқылы негізгі терезе класына жіберілетін Қолданбаны алуыңыз керек.

Бейне оқулық сонымен қатар түсіндірмелер береді және Қолданбаның жұмысын көрсетеді.

CallBack функциясы бар қолданба

Бейне оқулық

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

ЛП
  • Наурыз 27, 2017, 4:17 Т.Ж.

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

QLineEdit * MainWindow::line1;
Evgenii Legotckoi
  • Наурыз 27, 2017, 4:45 Т.Ж.

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

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

ЛП
  • Наурыз 27, 2017, 6:59 Т.Ж.

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

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

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() , например )

ЛП
  • Наурыз 27, 2017, 10:35 Т.Ж.

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

callbackFunc(this->pos())
? Я просто меняю значение аргументов у указателя?
Evgenii Legotckoi
  • Наурыз 27, 2017, 11:46 Т.Ж.

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

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

c
  • Сәуір 21, 2018, 11:53 Т.Ж.

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
  • Сәуір 22, 2018, 6:34 Т.Ж.

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
  • Сәуір 22, 2018, 7:26 Т.Ж.
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
  • Сәуір 22, 2018, 7:30 Т.Ж.

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++.

А
  • Ақп. 12, 2019, 3:19 Т.Ж.
  • (өңделген)

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

А
  • Ақп. 12, 2019, 4:26 Т.Ж.

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

R
  • Там. 3, 2020, 11:56 Т.Ж.

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

Evgenii Legotckoi
  • Там. 4, 2020, 5:24 Т.Ж.

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

R
  • Там. 4, 2020, 5:53 Т.Ж.

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз