Evgenii Legotckoi
Evgenii LegotckoiҚыр. 15, 2015, 11:44 Т.Ж.

Qt/C++ - 024-сабақ. Qt5-тегі сигналдар мен слоттар

Сигналдар мен слоттар Qt-дағы объектілер арасындағы байланыс үшін пайдаланылады. Сигнал және слот механизмі Qt-тің орталық ерекшелігі болып табылады және Qt-ны функционалдық жағынан басқа құрылымдардан ерекшелендіретін нәрсе болуы мүмкін. Сигналдар мен слоттар Qt. meta-object system арқылы мүмкін болады.

Кіріспе

GUI бағдарламалауда виджеттердің бірі өзгерген кезде, біз басқа виджеттердің хабарландыруын жиі қалаймыз. Жалпы, біз объектілердің бір-бірімен әрекеттесе алатынын қалаймыз. Мысалы, егер пайдаланушы Жабу түймесін басқан болса, біз window нысанының close(). функциясын шақырғанын қалар едік.

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


Сигналдар мен слоттар

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

]

Qt жүйесіндегі сигналдар мен ұялар Сигналдар мен слоттар типті қауіпсіз механизм болып табылады. Сигналдың қолтаңбасы қабылдау ұясының қолтаңбасына сәйкес болуы керек (Шын мәнінде, ұяшықта сигналдан қысқа қолтаңба болуы мүмкін, бірақ ұяшық сигналды қабылдайды, себебі ол қосымша аргументтерді елемейді). Қолтаңбалар үйлесімді болғандықтан, компилятор көрсеткішке негізделген синтаксисті пайдалану кезінде сәйкессіздіктерді анықтауға көмектеседі. Ал, SIGNAL және SLOT макростарына негізделген синтаксиспен түр сәйкессіздігін тек орындау уақыты процесінде анықтауға болады. Сигналдар мен слоттар еркін байланысқан: сигналды шақыратын класс сигналды қабылдайтын ұяшықты ғана біледі. Qt жүйесіндегі сигнал және ұяшық механизмі сигналды қажетті уақытта сигнал параметрлерімен шақырылатын ұяға қоссаңыз қамтамасыз етіледі. Сигналдар мен ұяшықтардың бірнеше аргументтері мен түрлері болуы мүмкін. Және олар түрге мүлдем қауіпсіз.

QObject немесе оның ішкі сыныптарынан (мысалы, QWidget) алынған барлық сыныптар -5/qwidget.html) сигналдар мен слоттарды қамтуы мүмкін. Сигналдарды басқа объектілерді қызықтыруы мүмкін күйін өзгертетін объектілер шақырады. Байланыс үшін нысанның барлығы осы. Ал нысан ол шығаратын сигналдарды кім қабылдайтыны маңызды емес. Бұл ақпараттың әділ инкапсуляциясы және объектіні бағдарламалық құрал құрамдас бөлігі ретінде пайдалану мүмкіндігін қамтамасыз етеді.

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

Бірнеше сигналды бір ұяға қосуға болады немесе сигналды бірнеше ұяшықтарға қосуға болады. Және тіпті сигналды басқа сигналға тікелей қосуға болады. (Бұл бірінші сигнал шақырылған кезде екінші сигналды қосады)

Сигналдар мен слоттар бірге құрамдас бағдарламалаудың қуатты механизмін жасайды.

Сигналдар

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

Сигнал шақырылғанда, оған қосылған ұяшық әдетте қалыпты функция сияқты бірден орындалады. Бұл мүмкін, себебі сигнал және слот механизмі GUI-дегі кез келген циклдардан тәуелсіз. Кодты орындау барлық слоттарды шақыратын emit директивасы арқылы шақырылуы керек. Қосылым кезегі пайдаланылған жағдайларда код сигнал береді және слоттар сәл кейінірек орындалады.

Егер бір сигналға бірнеше слоттар қосылса, онда ұялар сигнал шақырылған кезде қосылу ретімен бірінен соң бірі шақырылады.

Сигналдар moc ішінде автоматты түрде жасалады және оларды .cpp файлында анықтаудың қажеті жоқ және олар ешқашан нәтижені қайтармайды.

Ескертпе: Біздің тәжірибемізге сәйкес, сигналдар мен слоттар, егер олар арнайы түрлерде пайдаланылмаса, тиімдірек болады. Егер QScrollBar::valueChanged () гипотетикалық QScrollBar::Range сияқты арнайы түрі болса, ол тек келесіге қосыла алады. QScrollBar үшін арнайы жасалған ұяшық. Әртүрлі виджеттерді біріктіру мүмкін болмауы мүмкін.

Слоттар

Слот оған қосылған сигнал шақырылған кезде шақырылады. Слоттар қалыпты C++ функциясы болып табылады және оларды шақыруға болады; олар сигналдардың оларға қосылуында ғана ерекше.

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

Сондай-ақ, слоттарды виртуалды деп анықтауға болады, біз оны іс жүзінде өте пайдалы деп санаймыз.

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

Басқа кітапханалар сигналдар мен ұялар деп аталатын айнымалы мәндерді анықтайтынын және Qt негізіндегі қолданба құрастырылған кезде қателер мен ескертулерді тудыруы мүмкін екенін ескеріңіз. Бұл мәселелердің шешімі препроцессор үшін #undef директивасын пайдалану болып табылады.

Сигналды ұяға қосыңыз

Qt бесінші нұсқасына дейін сигналды ұяға қосу макростар арқылы жазылса, бесінші нұсқада көрсеткіштерге негізделген белгілер қолданыла бастады.

Макростармен жазу:

connect(button, SIGNAL(clicked()), this, SLOT(slotButton()));

Көрсеткіш негізіндегі жазба:

connect(button, &QPushButton::clicked, this, &MainWindow::slotButton);

Екінші нұсқаның артықшылығы мынада: қолтаңбаның сәйкессіздігі мен дұрыс емес ұяшық немесе сигнал атауын қолданбаны тестілеу процесінде емес, жобаны құрастыру сатысында анықтауға болады.

Сигналдарды және ұяшықтарды пайдалану мысалы

Сигналдарды және ұяшықтарды пайдалану мысалы үшін негізгі терезеде үш түймесі бар жоба жасалды, олардың әрқайсысында ұяшық қосылған және бұл слоттар басылған түйменің нөмірімен сигналды бір ұяшыққа жібереді.

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

Жоба құрылымы Сабақты оқытудың қалыптасқан дәстүріне сәйкес, мен мүлдем тривиальды және ұятқа қалдыратын жоба құрылымын қосамын, мен оған енгізілген сыныптар мен файлдарды сипаттамаймын.

mainwindow.h

Сонымен, әрекет келесідей: үш түйме - үш ұяшық, барлық үш түйме үшін бір сигнал, ол түйме ұяшықтарына беріледі және түйме нөмірін бір жалпы ұяшыққа жібереді, ол түйме нөмірі бар хабарлама береді.

#ifndef MAINWINDOW\_H
#define MAINWINDOW\_H

#include <QMainWindow>
#include <QPushButton>
#include <QMessageBox>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q\_OBJECT

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

signals:
    void signalFromButton(int buttonID);    // Сигнал для передачи номер нажатой кнопки

private:
    Ui::MainWindow *ui;

private slots:
    void slotButton1();     // Слоты-обработчики нажатий кнопок
    void slotButton2();
    void slotButton3();

    // Слоты вызывающий сообщение с номеро нажатой кнопки
    void slotMessage(int buttonID);
};

#endif // MAINWINDOW\_H

mainwindow.cpp

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

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

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

    /* Объявляем и инициализируем кнопки
     * */
    QPushButton *but\_1 = new QPushButton(this);
    QPushButton *but\_2 = new QPushButton(this);
    QPushButton *but\_3 = new QPushButton(this);

    /* Устанавливаем номера кнопок
     * */
    but\_1->setText("1");
    but\_2->setText("2");
    but\_3->setText("3");

    /* Добавляем кнопки на слой с вертикальной ориентацией
     * */
    ui->verticalLayout->addWidget(but\_1);
    ui->verticalLayout->addWidget(but\_2);
    ui->verticalLayout->addWidget(but\_3);

    /* Подключаем к кнопкам индивидуальные слоты
     * */
    connect(but\_1, SIGNAL(clicked()), this, SLOT(slotButton1()));
    connect(but\_2, SIGNAL(clicked()), this, SLOT(slotButton2()));
    connect(but\_3, SIGNAL(clicked()), this, SLOT(slotButton3()));

    /* Подключаем сигнал с передачей номера кнопки к слоту вывода сообщения
     * */
    connect(this, &MainWindow::signalFromButton, this, &MainWindow::slotMessage);
}

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

/* Слоты для обработки нажатия кнопок
 * */
void MainWindow::slotButton1()
{
    emit signalFromButton(1);
}

void MainWindow::slotButton2()
{
    emit signalFromButton(2);
}

void MainWindow::slotButton3()
{
    emit signalFromButton(3);
}

/* Слоты вывода сообщения
 * */
void MainWindow::slotMessage(int buttonID)
{
    QMessageBox::information(this,
                             "Уведомление о нажатой кнопке",
                             "Нажата кнопка под номером " + QString::number(buttonID));
}

Бейне оқулық

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

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

ИВ
  • Сәуір 29, 2020, 10:04 Т.Ж.

Простите , а если я например хочу сделать по кнопке клик не левой кнопкой ,а правой или вообще засекать перемещение мыши на кнопке, то как будет выглядить строка

connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::slotButton1);

Я так понял что сиглалы виджетов заранее предопределены в библиотеке и в данном случае &QPushButton::clicked это событие из стандартной библиотеки Qt. В справке сказано, что всего есть четыре сигнала clicked, pressed, released,toggled. Может я не ту справку читаю, от того и странные вопросы.

Evgenii Legotckoi
  • Сәуір 29, 2020, 10:09 Т.Ж.

Правильно всё читаете. Так и есть.

Если хотите отслеживать правую кнопку мыши, то вам следует наследоваться от QPushButton и переопределять методы

virtual void mouseDoubleClickEvent(QMouseEvent *event)
virtual void mouseMoveEvent(QMouseEvent *event)
virtual void mousePressEvent(QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event)

И переопределять поведение кнопки в случае правой, или левой кнопки мыши.

ИВ
  • Сәуір 29, 2020, 12:39 Т.Қ.

Извините, последний вопрос.
Написал маленький "проект", хочу стобы сцена ловила события мыши. Особенно когда мышь "отпустили". По сути это нужно для манипулирования с картинками на сцене.
Согласно справке за события на сцене отвечает QGraphicsSceneMouseEvent, однако попытка подружить слот и сигнал со сценой провалились, вот такой код привязки

connect (scene, &MainWindow::mousePressEvent, this, &MainWindow::Slot_mousePressEvent);

не работает, компилятор ругается на объект scene, если поставить this то все конечно компилируется, но это же и не должно работать как надо
Вот весь код проекта.
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtCore>
#include <QtGui>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void mousePressEvent(QGraphicsSceneMouseEvent *e);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *e);
    void mouseReleasEvent(QGraphicsSceneMouseEvent *e);
private:
    Ui::MainWindow *ui;
    QGraphicsScene *scene;
    QGraphicsEllipseItem *ellips;
private slots:
    void Slot_mousePressEvent();     // Слоты-обработчики нажатий кнопок
    void Slot_mouseMoveEvent();
    void Slot_mouseReleasEvent();
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsItem>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    scene = new QGraphicsScene(this);  //вот они наши сцены
    ui->graphicsView->setScene(scene);
    QBrush redBrash (Qt::red);
    QPen blackpen(Qt::black);
    blackpen.setWidth(6);
    ellips = scene->addEllipse(10,10,100,100,blackpen,redBrash);
    ellips->setFlag(QGraphicsItem::ItemIsMovable);

    //вот тут ошибки
    connect (this, &MainWindow::mousePressEvent, this, &MainWindow::Slot_mousePressEvent);
    connect (this, &MainWindow::mouseMoveEvent, this, &MainWindow::Slot_mouseMoveEvent);
    connect (this, &MainWindow::mouseReleasEvent, this, &MainWindow::Slot_mouseReleasEvent);
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
    emit Slot_mousePressEvent();
    Q_UNUSED(e);
}
void MainWindow::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
{
    emit Slot_mouseMoveEvent();
    Q_UNUSED(e);
}
void MainWindow::mouseReleasEvent(QGraphicsSceneMouseEvent *e)
{
    emit Slot_mouseReleasEvent();
    Q_UNUSED(e);
}
void MainWindow::Slot_mousePressEvent()
{
    QMessageBox msgBox;
    msgBox.setText("Hello Here");
    msgBox.exec();
}
void MainWindow::Slot_mouseMoveEvent()
{
    QMessageBox msgBox;
    msgBox.setText("Hello Here");
    msgBox.exec();
}
void MainWindow::Slot_mouseReleasEvent()
{
    QMessageBox msgBox;
    msgBox.setText("Hello Here");
    msgBox.exec();
}

Как можно привязать слот к сцене или либому другому виджету ? Может быть у вас есть видеоурок на этой теме? Особенно интересует обработка событий при столкновении двух пиксельных рисунков на сцене.

Evgenii Legotckoi
  • Сәуір 29, 2020, 12:45 Т.Қ.

какая жуть.... если вам нужно, чтобы сцена что-то делала при кликах мыши, то методы нужно переопределять в наследованном классе сцены, а не запихивать методы из графической сцены в класс окна приложения.

ИВ
  • Сәуір 29, 2020, 1:09 Т.Қ.

Да я уже нашел ваш проект, с рисованием мышью и понял что это действительно жуть.
Думаю надо отдохнуть, уже 12 учусь.

Дмитрий
  • Маусым 8, 2022, 3:24 Т.Қ.

Возник такой вопрос.
Разбираюсь с одной библиотекой. В ней применен паттерн Pimpl. И в коде есть вызовы метода connect(), но с помощью макросов:

Q_Q(QAmqpClient);
initSocket();
heartbeatTimer = new QTimer(q);
QObject::connect(heartbeatTimer, SIGNAL(timeout()), q, SLOT(_q_heartbeat()));
...

Q_Q(QAmqpClient); - это, как понял, получение q-указателя на основной класс.
В нем сигнал таймера, который находится в приватном классе коннектится к слоту, который находится в основном классе.
Как этот коннект записать без макросов?
В основном классе _q_heartbeat() объявлен как Q_PRIVATE_SLOT(d_func(), void _q_heartbeat()). Т.е., на сколько понял, вызывается приватный метод _q_heartbeat приватного класса. Причем он находится в секции public, а не public slots.

Попытки написать что-то типа

QObject::connect(heartbeatTimer, &QTimer::timeout, q, &QAmqpClient::_q_heartbeat);
или
QObject::connect(heartbeatTimer, &QTimer::timeout, q, &QAmqpClientPrivate::_q_heartbeat);

приводят к ошибке компиляции.
ошибка: '_q_heartbeat' is not a member of 'QAmqpClient'
или
ошибка: expected primary-expression before '/' token

Evgenii Legotckoi
  • Там. 24, 2022, 7:38 Т.Ж.

Без макросов никак. Приватные методы через указатели не коннектятся извне, что правильно, а вот макросы болт кладут на private и protected модификаторы.

Пікірлер

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

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

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

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

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

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

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

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