Сигнально-слотові з'єднання
Одною з фундаментальних можливостей у Qt є можливість взаємодії об'єктів з допомогою сигнально-слотових з'єднань. Розглянемо, як влаштувати взаємодію між об'єктами у програмі більш детально.
Оргазінація взаємодії між віджетами та класами у програмі
Будь-яка об'єктно-орієнтована програма складається з об'єктів, які взаємодіють між собою. Кожен з об'єктів володіє станом, який визначає сукупність даних, які зберігає об'єкт у даний момент. У відповідь на взаємодію з об'єктом, його стан може змінитися. Наприклад, об'єкт який реалізує мережеве з'єднання може отримати нові переслані дані, а об'єкт який реалізує кнопку на вікні користувацького інтерфейсу, може бути натиснутий користувачем. Таким чином об'єкт змінив свій стан — і він може повідомити про це інший об'єкт надсилаючи йому повідомлення про зміну. Практично, цю взаємодію, зазвичай, реалізують з допомогою виклику методу об'єкту, який необхідно повідомити. Цей метод має виконувати відповідні дії у відповідь на зміну. Кожен об'єкт виконує свою роль, а взаємодія між ними відбувається за рахунок взаємного виклику методів (прямого чи опосередкованого), кожен з яких має виконувати власну чітку функцію. Таким розділенням отримують зменшення складності у програмному коді — за рахунок чіткого розподілу відповідальності між об'єктами — а, отже, і гнучкість, здатність класів до повторного використання, простоту супроводження програмного коду. Дуже важливе хороше розуміння цих ключових ідей — розділення відповідальності між об'єктами у програмі та організації взаємодії між ними на основі чітко визначених інтерфейсів (наборів методів), які випливають з фундаментальних понять ООП: абстракції та інкапсуляції.
Отже, для реалізації такої взаємодії, програмісту необхідно викликати метод іншого об'єкту у відповідь на зміну стану. Об'єкт який змінив стан може для цього просто зберігати вказівник на інший об'єкт, метод якого має викликатися. У відповідь на зміну стану від буде доступатися через вказівник та викликати метод. У такому разі втрачається гнучкість, адже кожен об'єкт має містити поле для збереження вказівника на інший об'єкт, з яким він взаємодіє. Наприклад, у випадку з елементами управління на формі, доведеться програмувати кожен елемент окремо — успадковувати від класу елемента управління, додавати вказівник та перевизначити метод для обробки дій користувача задля взаємодії з іншими об'єктами у програмі. До того ж такий зв'язок задається жорстко на етапі компіляції.
Звичайно можна передавати вказівник на метод, який буде викликатися у відповідь (callback), але такий підхід теж володіє деякими недоліками (менш безпечно, завжди працює як прямий виклик методу, і т. п.). Саме тому у Qt використовується концепція сигнально-слотових з'єднань.
Поняття про сигнально-слотові з'єднання
Сигнально-слотові з'єднання є простим, але водночас важливим засобом кросплатформного інструментарію розробки Qt. Один об'єкт при зміні свого стану може повідомити інших за допомогою сигналу. Візуальні компоненти також надсилають сигнали, у відповідь на дії користувача (наприклад, натискання кнопки, встановлення прапорця, зміна положення слайдера, редагування тексту у полі вводу тощо). Інші об'єкти можуть приєднатися до сигналу слотом — спеціальним методом, який реалізує деяку функціональність, та викликається кожен раз, коли був випущений сигнал, приєднаний до нього. Як було відзначено раніше, сигнально-слотові з'єднання можуть бути використані як для взаємодії об'єктів навіть у багатопоточних програмах.
Для задання з'єднання використовують метод connect() класу QObject. Метод приймає п'ять параметрів:
вказівник на об'єкт, який посилає сигнал (sender);
назва сигналу (signal) та його параметри, які задаються з допомогою макроса SIGNAL();
вказівник на об'єкт, який отримує сигнал (receiver);
назва слота (slot) та його параметри, які задаються з допомогою макроса SLOT(), або ж назва іншого сигналу, який буде емітуватися (випускатися) у відповідь;
тип сигнально слотового з'єднання (має значення за замовчуванням Qt::AutoConnection).
У наступному прикладі ми демонструємо сигнально-слотове з'єднання між кнопкою lPushButton та віджетом-вікном lWindow. У відповідь на натискання кнопки (сигнал clicked()), викликається метод-слот close(), який закриває вікно. Зауважимо, що слоти мають специфікатор доступу (private/protected/public) та викликають їх як і звичайні методи класу. Цим вони не відрізняються від інших методів.
Передача параметрів
Також з допомогою з'єднань між слотами та сигналами може відбуватися передача параметрів. Наприклад, візуальний елемент QCheckBox випускає сигнал toggled() кожен раз при встановленні та знятті прапорця. Сигнал toggled() передає один параметр — булеве значення: true – якщо прапорець втрановлено, false – якщо ні. Ми зможемо з'єднати його з зі слотом setChecked() кнопки, який також приймає булеве значення та встановлює кнопку у ввімкнений (true) або вимкнений (false) стан. Зауважте: ми використали метод setCheckable(), який встановлює для кнопки режим перемикання між двома станами.
Сигнально-слотове з'єднання з передачею параметрів відбувається за правилами:
порядок та тип параметрів має співпадати у об'єкта який передає сигнал та у об'єкта-отримувача;
сигнал чи слот отримувача може опускати кілька або всі останні параметри, при цьому порядок та тип параметрів, які залишилися у отримувача має співпадати з першими параметрами сигналу, який передають.
Можливість перехресного сигнально-слотового з'єднання зображено у наступному прикладі.
Тут ми бачимо взаємодію між обома елементами управління. При встановленні\скиданні прапорця буде встановлюватися у ввімкнений стан чи скидатися кнопка, та навпаки.
Типи сигнально-слотових з'єднань
Сигнально-слотові з'єднання можуть різнитися за методом виклику слота, або за механізмом з'єднання. Тип з'єднання можна вказати при його створенні:
Qt::AutoConnection – тип з'єднання за замовчуваннням. При з'єднанні об'єктів в межах потоку поводить себе як Direct Connection, інакше — як Queued Connection;
Qt::DirectConnection — слот викликається негайно після того як було випущено сигнал. По-суті це нагадує звичайний виклик слота як метода;
Qt::QueuedConnection — слот виконується, як тільки управління перейде до черги обробки повідомлень потоку-отримувача. Також використовують для організації взаємодії об'єктів у багатопоточній програмі;
Qt::BlockingQueuedConnection — теж саме, що і Queued Connection, але потік, з якого було випущено сигнал блокується допоки виконання слота не буде завершено. Цей тип з'єднання має використовуватись тільки коли об'єкти, які взаємодіють, знаходятся у різних потоках.
Qt::UniqueConnection — цей тип з'єднання таки же як і Qt:AutoConnection, але з'єднання відбувається тільки тоді коли воно унікальне (тобто таке, яке не дублює інше вже існуюче).
Один і той же сигнал об'єкта може бути приєднаний до кількох різних сигналів та слотів іншого об'єкту, та навпаки. При цьому послідовність у якій будуть викликатися приєднані сигнали та слоти наперед не відома, тому не варто розраховувати на послідовність з'єднання. Також варто пам'ятати, що одне і те ж з'єднання може бути виконане кілька разів підряд. У такому разі під час виклику сигналу, слот спрацює стільки ж разів, скільки разів було повторно виконано з'єднання. Для того, щоб уникнути цього, необхідно передавати тип з'єднання Qt::UniqueConnection кожного разу у випадках, коли інший тип з'єднання не потрібний.
Використання вказівників на функції у з'єднаннях (Qt5)
todo
Last updated