Відповіді:
Це найкраще пояснює документація Qt :
У Qt події - це об'єкти, похідні від абстрактного
QEvent
класу, які представляють речі, що відбулися або в рамках програми, або в результаті зовнішньої діяльності, про яку програма повинна знати. Події можуть бути отримані та оброблені будь-яким екземпляромQObject
підкласу, але вони особливо актуальні для віджетів. Цей документ описує спосіб доставки та обробки подій у типовому додатку.
Отже, події та сигнал / слоти - це два паралельні механізми, що здійснюють одне і те ж. Як правило, подія генерується зовнішнім об'єктом (наприклад, клавіатурою або коліщатком миші) і доставляється через цикл подій у QApplication
. Загалом, якщо ви не встановите код, ви не будете створювати події. Ви можете фільтрувати їх QObject::installEventFilter()
або обробляти події в підкласовому об'єкті, замінюючи відповідні функції.
Сигнали та слоти набагато простіше генерувати та отримувати, і ви можете підключити будь-які два QObject
підкласи. Вони обробляються через Metaclass (детальніше ознайомтеся з вашим файлом moc_classname.cpp), але більша частина міжкласового зв’язку, який ви будете виробляти, ймовірно, буде використовувати сигнали та слоти. Сигнали можуть бути доставлені негайно або відкладені через чергу (якщо ви використовуєте потоки).
Може генеруватися сигнал.
У Qt сигнали та події є реалізацією шаблону Observer . Їх використовують у різних ситуаціях, оскільки вони мають різні сильні та слабкі сторони.
Перш за все давайте визначимо, що ми точно маємо на увазі під поняттям "Qt подія": віртуальна функція в класі Qt, яку, як очікується, буде реалізовано у вашому базовому класі, якщо ви хочете обробити подію. Це пов'язано з шаблоном методу шаблону .
Зверніть увагу, як я вживав слово " ручка ". Дійсно, ось основна різниця між намірами сигналів та подіями:
Різниця полягає в тому, що, коли ви «обробляєте» подію, ви берете на себе відповідальність «реагувати» поведінкою, корисною поза класом. Наприклад, розглянемо додаток, на якому є кнопка з номером. Додаток повинен дозволити користувачеві сфокусувати кнопку та змінити номер, натискаючи клавіші клавіатури «вгору» та «вниз». В іншому випадку кнопка повинна функціонувати як звичайна QPushButton
(на неї можна клацнути тощо). У Qt це робиться шляхом створення власного маленького багаторазового "компонента" (підкласу QPushButton
), який повторно реалізується QWidget::keyPressEvent
. Псевдокод:
class NumericButton extends QPushButton
private void addToNumber(int value):
// ...
reimplement base.keyPressEvent(QKeyEvent event):
if(event.key == up)
this.addToNumber(1)
else if(event.key == down)
this.addToNumber(-1)
else
base.keyPressEvent(event)
Побачити? Цей код представляє нову абстракцію: віджет, який діє як кнопка, але має деяку додаткову функціональність. Цю функціональність ми додали дуже зручно:
keyPressEvent
сигнал, нам потрібно було б вирішити, чи успадковувати його, QPushButton
чи просто зовні підключити до сигналу. Але це було б нерозумно, оскільки в Qt від вас завжди очікується успадкування, коли ви пишете віджет із власною поведінкою (з поважної причини - повторне використання / модульність). Отже, роблячи keyPressEvent
подію, вони передають свої наміри, які keyPressEvent
є лише базовим елементом функціональності. Якби це був сигнал, це виглядало б як річ, орієнтована на користувачів, коли це не буде призначено.keyPressEvent
був сигналом.Дизайн Qt добре продуманий - вони змусили нас потрапити в яму успіху , спростивши зробити правильну справу і важко зробити неправильну справу (зробивши keyPressEvent подією).
З іншого боку, врахуйте найпростіше використання QPushButton
- просто миттєво встановити його та отримувати сповіщення про його натискання :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Це однозначно має бути зроблено користувачем класу:
QPushButton
кожного разу, коли ми хочемо, щоб якась кнопка сповіщала нас про клацання, це вимагало б багато підкласів без поважних причин! Віджет, який завжди відображає "Привіт світ" messagebox
при натисканні, корисний лише в одному випадку - тому його повністю не можна використовувати багаторазово. Знову ж таки, у нас не залишається іншого вибору, як зробити правильну справу - підключившись до неї зовні.clicked()
- або підключити кілька сигналів до sayHello()
. З сигналами немає суєти. З підкласом вам доведеться сісти і обдумати деякі схеми класів, поки ви не вирішите відповідний дизайн.Зверніть увагу, що одне з місць, що QPushButton
виділяється, clicked()
знаходиться у його mousePressEvent()
реалізації. Це не означає clicked()
і mousePressEvent()
є взаємозамінними - просто те, що вони пов’язані між собою.
Отже, сигнали та події мають різні цілі (але пов’язані між собою тим, що обидва дозволяють «підписатися» на сповіщення про те, що щось відбувається).
Наразі відповіді мені не подобаються. - Дозвольте зосередитись на цій частині питання:
Чи події є абстракцією сигналу / слотів?
Коротка відповідь: ні. Довга відповідь піднімає "краще" запитання: як пов'язані сигнали та події?
Неактивний основний цикл (наприклад, Qt) зазвичай «застряє» у виклику select () операційної системи. Цей виклик перетворює додаток у режим "сну", тоді як він передає купу ядер або файлів або будь-якого іншого ядра з проханням: якщо щось зміниться на них, нехай виклик select () повернеться. - І ядро, як господар світу, знає, коли це станеться.
Результатом цього вибору select () може стати: нові дані про підключення сокета до X11, прийшов пакет до порту UDP, який ми слухаємо, і т. Д. - Цей матеріал не є ні сигналом Qt, ні подією Qt, Основний цикл Qt самостійно вирішує, перетворює свіжі дані в ті, інші або ігнорує їх.
Qt може викликати метод (або кілька), наприклад keyPressEvent (), ефективно перетворюючи його на подію Qt. Або Qt випромінює сигнал, який фактично шукає всі функції, зареєстровані для цього сигналу, і викликає їх одну за одною.
Тут видно одну різницю цих двох концепцій: слот не має права голосу щодо того, чи будуть викликані інші слоти, зареєстровані на цей сигнал, чи ні. - Події більше схожі на ланцюжок, і обробник подій вирішує, перериває це ланцюжок чи ні. Сигнали в цьому відношенні схожі на зірку або дерево.
Подія може спрацьовувати або повністю перетворюватися на сигнал (просто видайте його, і не називайте “super ()”). Сигнал може бути перетворений на подію (зателефонуйте обробнику події).
Що абстрагує те, що залежить від випадку: натиснуте () - сигнал абстрагує події миші (кнопка опускається і знову вгору, не надто рухаючись). Події на клавіатурі - це абстракції з нижчих рівнів (такі речі як 果 або é - це кілька клавішних обрисів у моїй системі).
Можливо focusInEvent () є прикладом протилежного: він міг би використовувати (і, отже, абстрактно) сигнал, на який клацнули (), але я не знаю, чи насправді це робить.
Події розсилаються циклом подій. Кожна програма графічного інтерфейсу потребує циклу подій, незалежно від того, що ви пишете в Windows або Linux, використовуючи Qt, Win32 або будь-яку іншу бібліотеку графічного інтерфейсу. Також кожен потік має свій цикл подій. У Qt "GUI Event Loop" (який є основним циклом усіх програм Qt) прихований, але ви починаєте його викликати:
QApplication a(argc, argv);
return a.exec();
Повідомлення ОС та інших програм, що надсилаються до вашої програми, розсилаються як події.
Сигнали та слоти є механізмами Qt. У процесі компіляції з використанням moc (компілятор мета-об'єктів) вони змінюються на функції зворотного виклику.
У події повинен бути один приймач, який повинен відправити його. Ніхто інший не повинен отримати цю подію.
Всі слоти, підключені до випромінюваного сигналу, будуть виконані.
Не слід вважати Сигнали як події, тому що, як ви можете прочитати в документації Qt:
Коли видається сигнал, слоти, підключені до нього, зазвичай виконуються негайно, як звичайний виклик функції. Коли це трапляється, механізм сигналів і слотів абсолютно незалежний від будь-якого циклу подій графічного інтерфейсу.
Коли ви надсилаєте подію, вона повинна зачекати деякий час, поки цикл подій не відправить усі події, що відбулися раніше. Через це виконання коду після надсилання події або сигналу відрізняється. Код після надсилання події буде запущений негайно. Що стосується механізмів сигналів і слотів, це залежить від типу з'єднання. Зазвичай він буде виконаний після всіх слотів. Використовуючи Qt :: QueuedConnection, він буде виконуватися негайно, як і події. Перевірте всі типи з'єднання в документації Qt .
When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
Є стаття, в якій докладно обговорюється обробка подій: http://www.packtpub.com/article/events-and-signals
Тут обговорюється різниця між подіями та сигналами:
Події та сигнали - це два паралельні механізми, які використовуються для досягнення одного і того ж. Як загальна різниця, сигнали корисні при використанні віджета, тоді як події корисні при реалізації віджета. Наприклад, коли ми використовуємо віджет, такий як QPushButton, нас більше цікавить його сигнал click (), ніж натискання миші на низькому рівні або події натискання клавіш, які спричинили випромінювання сигналу. Але якщо ми реалізуємо клас QPushButton, нас більше цікавить реалізація коду для миші та ключових подій. Крім того, ми зазвичай обробляємо події, але отримуємо повідомлення про випромінювання сигналу.
Здається, це звичайний спосіб говорити про це, оскільки прийнята відповідь використовує одні й ті ж фрази.
Зауважте, будь ласка, дивіться корисні коментарі нижче до цієї відповіді від Kuba Ober, що змушує мене замислитись, чи це може бути трохи спрощено.
event
. Конкретно , сигнали та слоти - це методи, тоді як механізм з'єднання - це структура даних, яка дозволяє сигналу викликати один або кілька слотів, перелічених як пов'язані з ним. Я сподіваюся, ви бачите, що говорити про сигнал / слоти як про якийсь "підмножина" або "варіант" подій - це нісенітниця, і навпаки. Вони справді різні речі, які, як правило, використовуються для подібних цілей у контексті деяких віджетів. Це воно. Чим більше ви узагальнюєте, тим менш корисним ви стаєте ІМХО.
TL; DR: Сигнали та слоти - це непрямі виклики методу. Події - це структури даних. Тож вони зовсім різні тварини.
Єдиний час, коли вони збираються разом, це коли виклики слотів здійснюються через межі потоку. Аргументи виклику слотів упаковуються в структуру даних і надсилаються як подія до черги подій приймаючого потоку. У приймальному потоці QObject::event
метод розпаковує аргументи, виконує виклик і, можливо, повертає результат, якщо це було блокувальне з'єднання.
Якщо ми готові узагальнити забуття, можна подумати про події як спосіб виклику event
методу цільового об’єкта . Це непрямий виклик методу, після моди - але я не думаю, що це корисний спосіб думати про це, навіть якщо це правдиве твердження.
Події (у загальному розумінні взаємодії користувача / мережі), як правило, обробляються в Qt сигналами / слотами, але сигнали / слоти можуть робити багато іншого.
QEvent та його підкласи - це, в основному, лише трохи стандартизованих пакетів даних для фреймворку для зв'язку з вашим кодом. Якщо ви хочете якимось чином звернути увагу на мишку, вам потрібно лише переглянути API QMouseEvent, і дизайнерам бібліотеки не потрібно винаходити колесо кожен раз, коли вам потрібно розібратися, що робила миша в якомусь куточку API Qt.
Це правда, що якщо ви чекаєте якихось подій (знову ж у загальному випадку), ваш слот майже напевно прийме підклас QEvent як аргумент.
З огляду на це, сигнали та слоти, безсумнівно, можна використовувати без QEvents, хоча ви виявите, що початковим поштовхом для активації сигналу часто буде якась взаємодія користувача або інша асинхронна діяльність. Однак іноді ваш код просто досягає точки, коли спрацьовування певного сигналу буде правильним. Наприклад, спрацьовування сигналу, підключеного до індикатора прогресу під час тривалого процесу, не включає QEvent до цього моменту.
Я знайшов це запитання під час читання " Обробки подій" Леу Ві Ві Хенга. Також там сказано:
Жасмін Бланшетт каже:
Основною причиною, чому ви використовуєте події, а не стандартні виклики функцій, або сигнали та слоти, є те, що події можуть використовуватися як синхронно, так і асинхронно (залежно від того, чи викликаєте ви sendEvent () або postEvents ()), тоді як виклик функції або виклик слот завжди синхронний. Ще однією перевагою подій є те, що їх можна відфільтрувати.
Ще одне незначне прагматичне розгляд: випромінювання або отримання сигналів вимагає успадковування, QObject
тоді як об’єкт будь-якого успадкування може публікувати або надсилати подію (оскільки ви викликаєте QCoreApplication.sendEvent()
або postEvent()
) Це зазвичай не проблема, але: для використання сигналів PyQt дивним чином потрібно QObject
бути першим суперкласом, і ви можете не захотіти переставляти порядок спадкування, щоб мати змогу надсилати сигнали.)
На мій погляд, події абсолютно зайві і можуть бути викинуті. Немає жодної причини, через яку сигнали не могли бути замінені подіями чи подіями сигналами, за винятком того, що Qt вже налаштований таким, яким він є. Сигнали в черзі охоплені подіями, і події можуть бути обернені сигналами, наприклад:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Замінив би зручну mouseMoveEvent()
функцію, знайдену в QWidget
(але вже не в QQuickItem
), і обробляв би mouseMove
сигнали, які менеджер сцен видавав би для елемента. Той факт, що сигнал випромінюється від імені товару якоюсь стороннім об'єктом, є неважливим і трапляється досить часто у світі компонентів Qt, навіть якщо це нібито не дозволено (компоненти Qt часто обходять це правило). Але Qt - це конгломерат безлічі різноманітних дизайнерських рішень, який в значній мірі кидається в камінь через страх зламати старий код (що і так трапляється досить часто).
QObject
ієрархії батьків та дітей, коли це необхідно. З'єднання сигнал / слот - це лише обіцянка викликати функцію, прямо чи опосередковано, коли виконується певна умова. Не існує пов’язаної ієрархії обробки з сигналами та слотами.
accepted
опорний параметр до сигналу та слоти, що обробляють його, або у вас може бути структура як опорний параметр зaccepted
полем. Але Qt зробив вибір дизайну, який він зробив, і тепер вони встановлені в камені.