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 Т.Ж.

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

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

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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