Как вызвать слот из другого виджета
Перейти к содержимому

Как вызвать слот из другого виджета

  • автор:

Сигналы и Слоты

Сигналы и слоты используются для связи между объектами. Механизм сигналов и слотов — это основная особенность Qt и, вероятно, основная часть Qt, которая больше всего отличается от особенностей других структур.

Введение

При GUI-программировании, мы часто хотим сообщать одним элементам об изменении других элементов управления. Более обобщенно можно сказать, что мы хотим обеспечить связь между объектами любых видов. Например, если пользователь нажимает кнопку Close, мы, вероятно, хотим, чтобы была вызвана функция окна close().

Более старые инструментарии обеспечивают подобную связь с помощью отзывов. Обратный вызов, это указатель на функцию. Если Вы хотите чтобы функция обработки уведомила Вас о некотором событии, Вы передаете ей указатель на другую функцию (отзыв). Функция обработки вызовет функцию отзыва, когда это будет уместно. Отзыва имеют два фундаментальных недостатка: Во-первых, они не типобезопасны. Мы некогда не можем проверить, что функция обработки вызывает отзыв с правильными аргументами. Во-вторых, отзыв жестко связан с функцией обработки, так как функция обработки должна знать, какой отзыв вызывать.

Сигналы и Слоты

В Qt мы ввели технику, альтернативную отзывам: Мы используем сигналы и слоты. Сигнал испускается, когда происходит определенное событие. Виджеты Qt имеют множество предопределенных сигналов, и Вы всегда можете создать их подклассы, чтобы добавить свои сигналы. Слот — это функция, вызываемая в ответ на определенный сигнал. Виджеты Qt имеют множество предопределенных слотов, но Вы, и это стало общеиспользуемой практикой, можете создавать подклассы виджетов и добавлять свои слоты для того, чтобы обрабатывать поступающие сигналы, как Вы того хотите.

Механизм сигналов и слотов типобезопасен: Сигнатура сигнала должна соответствовать сигнатуре получающего сигнал слота. (Фактически слот может иметь более короткую сигнатуру, чем сигнал, который он получает, поскольку может игнорировать лишние аргументы.) Так как сигнатуры совместимы, компилятор может помочь в обнаружении несоответсвия типов. Вы вольны в соединении сигналов и слотов: Класс, испускающий сигналы, не знает, и не интересуется, который из слотов получит сигнал. Механизм сигналов и слотов Qt гарантирует, что, если Вы соединили сигнал со слотом, слот будет вызываться с параметрами сигнала в нужный момент. Сигналы и слоты могут иметь любое количество аргументов любых типов. Они полностью типобезопасны.

Все классы, наследующие QObject или одни из его подклассов (например, QWidget) могут содержать сигналы и слоты. Сигналы испускаются при изменении объектом своего состояния, если это изменение может быть интересно другим объектам. Все объекты делают это для связи с другими объектами. Их не заботит, получает-ли кто-нибудь испускаемые ими сигналы. Это истинная инкапсуляция информации, и такая инкапсуляция гарантирует, что объекты могут использоваться как компоненты программного обеспечения.

Слоты могут получать сигнал, но также они являются обыкновенными функциями-членами. Также, как объект не знает, получает-ли кто-нибудь сигналы, испускаемые им, слоты не знают, существуют-ли сигналы с ними связанные. Это гарантирует, что можно создать полностью независимые компоненты Qt.

Вы можете присоединять к одному слоту столько сигналов, сколько Вам будет нужно, и один сигнал может быть соединен со столькими слотами, сколько Вам требуется. Даже возможно соединять сигнал непосредственно с другим сигналом. (Второй сигнал будет испускаться немедленно всякий раз, когда испускается первый.)

Вместе, сигналы и слоты представляют собой мощный механизм компонентного программирования.

Небольшой пример

Минимальная декларация класса C++ может выглядеть следующим образом:

class Counter < public: Counter() < m_value = 0; >int value() const < return m_value; >void setValue(int value); private: int m_value; >;

Небольшой класс, основанный на QObject, может выглядеть так:

#include class Counter : public QObject < Q_OBJECT public: Counter() < m_value = 0; >int value() const < return m_value; >public slots: void setValue(int value); signals: void valueChanged(int newValue); private: int m_value; >;

Версия класса, основанная на QObject, имеет то-же самое внутреннее состояние, и предоставляет открытые методы для доступа к состоянию, но, в дополнение, она поддерживает компонентное программирование с использованием сигналов и слотов. Этот класс, испустив сигнал valueChanged(), может сообщать во вне, что его состояние изменилось, и имеет слот, которому другие объекты могут посылать сигналы.

Все классы, содержащие сигналы и слоты, должны упомянуть макрос Q_OBJECT наверху декларации. Также они должны происходить (прямо или косвенно) от QObject.

Слоты реализуются прикладным программистом. Вот возможная реализация слота Counter::setValue():

void Counter::setValue(int value) < if (value != m_value) < m_value = value; emit valueChanged(value); >>

Строка, содержащая emit, заставляет объект испустить сигнал valueChanged() с новым значением, переданным в аргументе.

В следующем отрывке кода мы, используя QObject::connect() создаем два объекта Counter и соединяем, используя QObject::connect(), сигнал valueChanged() первого объекта со слотом setValue() второго объекта:

Counter a, b; QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int))); a.setValue(12); // a.value() == 12, b.value() == 12 b.setValue(48); // a.value() == 12, b.value() == 48

Вызов a.setValue(12) заставляет a испустить сигнал valueChanged(12), который будет получен слотом setValue() объекта b, т.е. будет вызвано b.setValue(12). Затем b сам испустит сигнал valueChanged(), но с сигналом valueChanged() объекта b не связан ни один слот, и сигнал будет проигнорирован.

Обратите внимание на то, что функция setValue() устанавливает значение и испускается только в том случае, если value != m_value. Это уберегает от зацикливаний при циклических соединениях (например, если бы b.valueChanged() был соединен с a.setValue()).

Сигнал испускается для каждого соединения, которое было создано; если сигнал соединен с двумя слотами, то он будет испущен дважды. Также Вы можете разорвать соединение с помощью QObject::disconnect().

Данный пример иллюстрирует совместную работу объектов, которые ничего не знают друг о друге. Для ее достижения объекты должны быть соединены с помощью вызова простой функции QObject::connect(), или с помощью возможности автоматического связывания uic.

Сборка Примера

Препроцессор C++ заменяет или удаляет ключевые слова signals, slots и emit для того, чтобы компилятору был предоставлен стандартный код на C++.

moc обрабатывает определения классов, содержащих сигналы и слоты и генерирует файлы реализации C++, которые будут скомпилированы и связаны с другими объектными файлами приложения. Если Вы используете qmake, то в make-файл будет автоматически добавлен вызов moc.

Сигналы

Сигналы испускаются объектом, когда изменяется его внутреннее состояние, и если это может быть интересно его клиентам или владельцу. Только классы, содержащие определения сигналов, и их подклассы могут испускать сигналы.

При испускании сигнала, слоты с ним связанные исполняются немедленно, также, как при обычном вызове функции. Когда это случается, механизм сигналов и слотов полностью независим от цикла обработки сообщений GUI. Выполнение кода, следующего за выражением emit, продолжится, как только завершится выполнение всех слотов. При использовании связи с очередями ситуация несколько отлична; в этом случае, исполнение кода, следующего за ключевым словом emit, продолжится немеленно, а слоты будут исполнены несколько позже.

Если несколько слотов связаны с одним сигналом, то, при испускании сигнала, они будут выполенны один за другим в произвольном порядке.

Сигналы автоматически генерируются moc, и Вы не должны включать их реализацию в .cpp-файлы. Они не должны иметь возвращаемых типов (т.е. использовать void).

Примечание о сигналах: Наш опыт показывает, что сигналы и слоты более широко используются, если они не используют специальных типов. Если сигнал QScrollBar::valueChanged() должен использовать специальный тип, такой как гипотетический QScrollBar::Range, он может быть соединен только со слотами, которые работают QScrollBar. Что-либо столь-же простое, как программа Tutorial 5, в этом случае было-бы невозможно.

Слоты

Слот вызывается как только испускается соединенный с ним сигнал. Слоты — это обычные функции C++, и могут вызываться обычным образом; их единственная особенность — это то, что к ним могут быть присоединены сигналы. Слоты не могут иметь значений аргументов по умолчанию и использование в качестве типов аргументов Ваших собственных типов редко является мудрым решением.

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

  • Секция public slots содержит слоты, с которыми может быть соединен любой сигнал. Это очень удобно для компонентного программирования: Вы создаете объекты, которые ничего не знают друг о друге, соединяете их сигналы с слоты так, чтобы информация правильно проходила между ними, и, подобно модели железной дороги, запускаете и оставляете ее работать.
  • Секция protected slots содержит слоты, с которыми могут быть соединены сигналы этого класса и его подклассов. Эта секция предназначена для сигналов, которые скорее являются частью реализации класса, чем его интерфейсов с внешним миром.
  • Секция private slots содержит слоты, с которыми могут быть соединены только сигналы этого класса. Эта секция предназначена для очень строгих классов, которые не доверяют связь с некоторыми слотами даже своим потомкам.

Вы также можете определять виртуальные слоты, что мы находим очень полезным на практике.

По сравнению с отзывами, сигналы и слоты немного медленнее в связи с большей гибкостью, которую они предоставляют, но для реальных приложений это различие незначительно. Вообще, испускание сигнала, связанного с некоторыми слотами, примерно в десять раз медленнее, чем вызов невиртуальной функции приемника непосредственно. Так присходит потому, что требуется безопасно перебрать все соединения (т.е. проверить, чтобы поледующие приемники не были разрушени во время испускания сигнала) и передать параметры положенным образом. Хотя «десять вызовов невиртуальных функций» кажется долгим, это меньше чем, например, операция new или delete. Если Вы обрабатываете строку, вектор или список операция, которые требуют вызова new или delete, обработка сигналов и слотов становятся не самыми активными потребителями времени.

То-же самое происходит, когда система вызывает слот или косвенно вызываются более десятка функций. На i586-500 Вы можете генерировать около 2,000,000 сигналов, связанных с одним слотом, в секунду, или около 1,200,000 сигналов, связанных с двумя слотами, в секунду. Простой и гибкий механизм сигналов и слотов является хорошей оболочкой для внутренней реализации, которую пользователи даже не будут замечать.

Обратите внимание на то, что другие библиотеки, определяющие переменные с именем signals или slots, могут вызвать предупреждения и ошибки при компиляции с приложением, созданным на основе Qt. Решить эту проблему может директива препроцессора #undef.

Метаобъектная Информация

Метаобъектный компилятор (moc) просматривает декларацию класса в файле C++ и генерирует код C++ инициализирующий метаобъект. Метаобъект содержит имена всех сигналов и слотов и указатели на их функции.

Метаобъект содержит дополнительную информация, такую как имя класса объекта. Также Вы можете проверить, является-ли объект наследником определенного класса, например:

if (widget->inherits("QAbstractButton")) < QAbstractButton *button = static_cast(widget); button->toggle(); >

Информация метаобъекта также используется qobject_cast(), который подобен QObject::inherits(), но менее подвержен ошибкам:

if (QAbstractButton *button = qobject_cast(widget)) button->toggle();

Для получения более подробной информации см. Метаобъектная Система.

Реальный Пример

Далее приведен простой пример виджета с комментариями.

#ifndef LCDNUMBER_H #define LCDNUMBER_H #include class LcdNumber : public QFrame < Q_OBJECT

LcdNumber наследует QObject, который имеет понятие о сигналах и слотах, через QFrame и QWidget. Он немного похож на встроенный виджет QLCDNumber.

При расширении препроцессором, макрос Q_OBJECT декларирует несколько функций-членов, которые реализуются moc; если Вы получили сообщения об ошибках компилятора, подобные "undefined reference to vtable for LcdNumber", Вы, вероятно, забыли запустить moc или включить продукцию moc в команду link.

public: LcdNumber(QWidget *parent = 0);

moc явно не требует этого, но, если Вы наследуете QWidget, Вы, почти наверняка, захотите иметь аргумент parent в Вашем конструкторе, и передать его в конструктор базового класса.

Некоторые деструкторы и функции-члены здесь опущены; moc игнорирует функции-члены.

signals: void overflow();

LcdNumber испускае сигнал, когда его просят показать неверное значение.

Если Вы не заботитесь о том, выходит-ли значение за установленные пределы, или считаете, что оно не может за них выйти, Вы можете игнорировать сигнал overflow(), т.е. не соединять с ним ни какой слот.

Если Вы напротив, хотите вызвать две функции при выходе значения за пределы диапазона, содините сигнал с двумя слотами. Qt вызовет оба их (в произвольном порядке).

public slots: void display(int num); void display(double num); void display(const QString &str); void setHexMode(); void setDecMode(); void setOctMode(); void setBinMode(); void setSmallDecimalPoint(bool point); >; #endif

Слот обычно используется для получения информации об изменении состояния других виджетов. LcdNumber использует эту возможность, код, приведенный выше, показывает, как отобразить измененное значение. Так как display() является частью интерфейса между классом и остальной частью программы, то он является открытым слотом (public).

В некоторых из примеров программ сигнал valueChanged() соединяется со слотом display() объекта QScrollBar, в результате LCD-номер непрерывно отображает значение полосы прокрутки.

Обратите внимание на то, что display() перегружена; Qt выберет соответствующую версию во время соединения сигнала со слотом. При использовании отзывов Вы должны были бы завести пять различных названий и отслеживать используемые типы самостоятельно.

Некоторые несущественные функции-члены в данном примере были опущены.

Вызов одного виджета из другого виджета

Author24 — интернет-сервис помощи студентам

добрый день, есть 2 формы, сделанные в дизайнере. По нажатию на кнопку из формы 1 хочу чтобы выводилась форма 2.Создал слот, слот отзывается на нажатие кнопки, но не хочет рисовать второе окно.

вот такой банальный слот.

1 2 3 4 5 6 7 8 9 10
void Browser::ShowMsgSenderBox() { MsgSender w(this) ; w.show(); qDebug()"you suck" ; }

если этот код вывести в мейн то окно будет рисоваться. Подскажите пожалуйста решение, заранее спасибо!

Добавлено через 7 минут
он создается и сразу исчезает.

Добавлено через 7 минут
тут решение :

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Как добраться до виджета, который находится внутри другого виджета?
Есть QStackWidget, внутри которого на одной из страниц-виджетов находится QTabWidget.

Как навести мышку на x,y внутри виджета относительно 0,0 виджета (левого верхнего угла виджета)?
Как навести мышку на x,y внутри виджета относительно 0,0 этого виджета (левого верхнего угла.

Вызов виджета из формы
Я в qt немного новичок, но отчаянно пытаюсь перестать таковым быть и написать хотя бы какую-нибудь.

Размещение виджета поверх другого
Доброго времени суток. Имею форму с компоновкой Grid Layout. На ней размещен Graphics View.

Регистрация: 05.12.2013
Сообщений: 215
Наверное, проблема в классе MsgSender?
77 / 77 / 30
Регистрация: 21.05.2015
Сообщений: 257
Nelkor, или создавать окно куче:

MsgSender *w = new MsgSender (this) ; w->show();

или окно должно быть диалогом и вызывать так:

MsgSender w(this) ; w.exec();

Регистрация: 07.07.2014
Сообщений: 5
Так как проблема решается? У меня аналогично всё =(
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
VaderSDv, создавать виджет на куче.
Регистрация: 07.07.2014
Сообщений: 5

ЦитатаСообщение от icpu Посмотреть сообщение

VaderSDv, создавать виджет на куче.
Вот так?
Dialog *pDialog = new Dialog (this);
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь

наложение виджета поверх другого
Подскажите как наложить виджет(например чекбокс) поверх другого виджета на экране? гуглил, гуглил.

Изменение значения виджета из другого потока
Доброго времени суток. Продолжаю серию нубовских вопросов. Есть основной поток с окошком.

Виджет не рисует поверх другого виджета
Здравствуйте! Столкнулся с проблемой, нужно вставить один виджет поверх другого(абсолютное.

Перенос данных с одного виджета в другой
Доброго времени суток. У меня есть один объект класса QPlainTextEdit, в который я ввожу текст. Как.

Или воспользуйтесь поиском по форуму:

Как изменить свойства виджета Qt из другого виджета?

Есть два кастомных виджета. Требуется из одного виджета поменять свойства другого.
Виджеты не вложены друг в друга, у них общий родитель.
Т.е:
MainComponent
1. Widget1
1.1Image
2. Widget2
2.1 Widget21
Требуется из виджета 2.1 поменять свойство у 1.1
Виджеты созданы не через интерфейс Qt Creator, поэтому обратиться ui->something не получится.

  • Вопрос задан более года назад
  • 72 просмотра

Комментировать
Решения вопроса 0
Ответы на вопрос 1
you don't choose c++. It chooses you
Используйте сигналы и слоты.
Ответ написан более года назад
Нравится 1 2 комментария
Марат Нагаев @nagayev Автор вопроса
Спасибо.
А можете пожалуйста накидать как именно?
Нужно изменить QImage.

Тут очень подробно.

Вам лишь нужно объявить спецификатор доступа signal в классе. Создать переменную. И в классе, который владеет классом с сигналом написать функцию с точно такой же сигнатурой, как и у сигнала и с помощью макроса connect привязать одну функцию к другой. В вашем случае нужен один такой сигнал. Саму функцию сигнал, естественно, нужно в какой-то момент вызвать (в классе, котором она определена)

Ваш ответ на вопрос

Войдите, чтобы написать ответ

cpp

  • C++
  • +1 ещё

Как исправить ошибку uint(i) < uint(size()) при работе с QAxObject (docx file)?

  • 1 подписчик
  • 26 февр.
  • 90 просмотров

Qt: Как получить доступ к ui из другого класса?

Всем доброго времени суток. Похожая тема уже звучала, но я так и не нашел в ней ответа на главный вопрос. Ситуация такая: подключил библиотеку QCustomPlot через элемент QWidget, создал отдельный класс-наследник от QCustomPlot, соответственно. Теперь я хочу реализовать всю работу с этой библиотекой в этом классе, оставив в MainWindow только создание элемента объекта этого класса и один метод, который будет только отправлять массив данных в этот объект. Усё. Тут у меня начинается головоломка:

-нужно обращаться к элементу в форме через ui->, которого нет в моем классе, он там не наследован и по ссылке не передан, и никто его там не видел, и никто там его не узнает) как это сделать понятия не имею. Пробовал: множественное наследование и создание там отдельного объекта - не вышло. Не исключено, что в силу моих ошибок. Сделать метод в MainWindow, возвращающий ссылку на объект ui - не вышло, хотя, народ пишет, что это вполне возможно, но нет нигде кода, у кого бы это вышло. Сейчас все работает допотопно: я создаю объект моего класса в MainWindow, и дергаю там методы моего класса. А мне нужно реализовать все там. Возможно, я могу передавать эту ссылку как-то в конструктор при создании объекта, но я не ведаю , как это сделать. Помогите, люди добрые, кто чем может) Код привожу ниже:

Это .cpp моего класса

Charts::Charts(QWidget *parent) : QCustomPlot(parent) <> void Charts::setupgraph(QCustomPlot *customPlot) < // set dark background gradient: QLinearGradient gradient(0, 0, 0, 400); gradient.setColorAt(0, QColor(90, 90, 90)); gradient.setColorAt(0.38, QColor(105, 105, 105)); gradient.setColorAt(1, QColor(70, 70, 70)); customPlot->setBackground(QBrush(gradient));
#include #include class MainWindow; class Charts : public QCustomPlot < Q_OBJECT signals: void getData(const QByteArray &data); public: explicit Charts(QWidget *parent = nullptr); protected: private slots: public: void setupgraph(QCustomPlot *customPlot); private: QCPGraph *graphic; // Объявляем график >;

В MainWindow все в штатном режиме, как Qt прописал.

private: Ui::MainWindow *ui;

Заранее благодарен каждому, кто испытает малейшее желание оказать помощь

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *