Russian Qt Forum
Ноябрь 22, 2024, 16:01 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Вопрос по примеру QThread из офф. документации  (Прочитано 13576 раз)
mad
Гость
« : Июнь 05, 2016, 20:49 »

В офф.документации есть такой пример (http://doc.qt.io/qt-5/qthread.html#details):

Код:
class Worker : public QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString &parameter) {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }

signals:
    void resultReady(const QString &result);
};

class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller() {
        Worker *worker = new Worker;
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();
    }
    ~Controller() {
        workerThread.quit();
        workerThread.wait();
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);
};

В принципе почти все ясно, хочу свои догадки подтвердить. Плохо, что в примере не показывается, как именно по их задумке должен использоваться класс Controller в реальной практике. Смущает сигнал operate. Т.е. для запуска операции в другом потоке нужно выработать сигнал внутри себя?
Это такой красивый лайфхак? Чтобы уйти от использования не красивого invokeMethod? Я вообще правильно понимаю, что connect и invokeMethod по сути делают одно и то же? Как этот класс подразумевается использовать в реальной ситуации? Добавить мембер функцию, которая внутри класса испускает сигнал

Код:
void Controller::doOperate(const QString & str)
{
...
emit operate(str);
}


или дергать напрямую сигнал инстанса этого класса извне:
Код:
int main(int argc, char *argv[])
{
...

Controller * cntrl = new Controller ();
emit cntrl->operate ("somestring");

}

Второй вариант как-то хитро выглядит, хотя и легитимный, судя по изучению ресурса stackoverflow. Насколько хорошо вырабатывать сигнал другого объекта?
Как бы вы предложили использовать этот класс, какая лучшая практика для этого существует?


ЗЫ Да, я понимаю, что emit это пустой макрос. Но в рамках философии Qt нормально ли "чужой" сигнал дергать?
Таки для чего это так сделано в примере через сигнал, если сигнал operate убрать и не использовать сигнал started у класса QThread, то кроме как через methodInvoke уже не выкрутиться, чтобы doWork выполнился именно в другом потоке, правильно понимаю? Всего лишь хочу понять логику писавшего этот пример Улыбающийся







« Последнее редактирование: Июнь 05, 2016, 21:04 от mad » Записан
_OLEGator_
Гость
« Ответ #1 : Июнь 05, 2016, 21:02 »

Это такой красивый лайфхак? Чтобы уйти от использования не красивого invokeMethod? Я вообще правильно понимаю, что connect и invokeMethod по сути делают одно и то же?

Читайте внимательно документацию. Connect также разруливает вызов слота между потоками (в контексте какого потока будет вызван слот).
Записан
mad
Гость
« Ответ #2 : Июнь 05, 2016, 21:32 »

Ну так это я понимаю, просто в голове логически не укладывается вызывать сигнал другого объекта. Я в Qt новичок и начал сразу с 5.6 версии.
Судя по всему раньше чужие сигналы вне класса нельзя было вызывать(Qt4), а теперь это работает
Код:
emit cntrl->operate("somestr");
т.к.
Код:
#     define signals public

для чего это сделано и хорошо ли это использовать?)
« Последнее редактирование: Июнь 05, 2016, 21:45 от mad » Записан
_OLEGator_
Гость
« Ответ #3 : Июнь 05, 2016, 22:46 »

Не знаю для чего это сделано, но выглядит плохо, это так называемый запах - вроде все работает, никто не запрещает так делать - но выглядит костыльно.
Я бы не стал так делать - это косвенный показатель плохой архитектуры. Объект сам должен информировать других сигналами, странно выглядит когда внешний объект их начинает испускать  Улыбающийся
Записан
Bepec
Гость
« Ответ #4 : Июнь 05, 2016, 22:48 »

Чужой сигнал дёргать плохо. Ибо сигналы могут вызывать реакцию, которая приведёт к падению программы или некорректной работе Улыбающийся Вот если вы точно знаете что сигнал можно дёргать - тогда пожалуйста Веселый

Свой сигнал дёргать есть хорошо Улыбающийся Можно дёргать нежно, можно дёргать жёстко, кому как нравится.

В данном примере для "разруливания" синхронизации используется сигнал-слотовая система. Т.е. вызов сигнала означает, что все проблемы "синхронизации", отправки и приёма сигнала в разные потоки берёт на себя сигнал-слотовая система. В ином случае вам бы пришлось составлять конструкции из мьютексов, общий контейнер и тому подобные вещи.

Connect соединяет сигналы и слоты. InvokeMethon вызывает сигналы и слоты.  Тёплое с мягким не путайте Улыбающийся

PS в идеале да, должна быть паблик функция, которая заставляет controller вызывать свой сигнал.

PPS а сделаны пабликами сигналы скорее всего для улучшения отладки. Чтобы не приходилось портить тестируемый класс дебажными вставками Улыбающийся
Записан
mad
Гость
« Ответ #5 : Июнь 06, 2016, 00:05 »

Спасибо за ответы, учту Улыбающийся
Цитировать
Connect соединяет сигналы и слоты. InvokeMethon вызывает сигналы и слоты.  Тёплое с мягким не путайте

Эх, получается я криво спросил, имелось ввиду, что выработка сигнала с последующим вызовом подключенного слота это тоже самое, что и вызов через invokeMethod, т.е. их механизм работы в недрах Qt одинаков?

Еще мысли....по примеру.
А как же тогда скрыть сигнал (сделать приватным), если хочется использовать его для внутренних целей класса. А так получается торчит он наружу, соблазняет с ним делать всякое нехорошее)) Хотя чисто внутренний сигнал - кажется бредовой идеей. Конечно, его (сигнал operate) можно соединить с другим сигналом где-либо в программе, и это будет красивее с точки зрения Qt, но это ведь не будет чем-то более лучшим, чем emit cntrl->operate("somestr"); Будет emit anothersignal("somestr").

Хм, есть некий хак QPrivateSignal

очень полезные статьи:
https://habrahabr.ru/post/214379/
https://habrahabr.ru/post/215181/
« Последнее редактирование: Июнь 06, 2016, 00:31 от mad » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Июнь 06, 2016, 07:41 »

Но в рамках философии Qt нормально ли "чужой" сигнал дергать?
Да, нормально. В данном случае Controller не располагает никаким интеллектом чтобы решать когда надо начинать вычисления, он только может их начать. Значит тот кто обеспечивает параметры сигнала operate и должен "эмиттить".

Эх, получается я криво спросил, имелось ввиду, что выработка сигнала с последующим вызовом подключенного слота это тоже самое, что и вызов через invokeMethod, т.е. их механизм работы в недрах Qt одинаков?
Да, только нет никакой "выработки", после того как отработал moc, сигнал - такой же метод как если бы Вы его написали сами.

А как же тогда скрыть сигнал (сделать приватным), если хочется использовать его для внутренних целей класса. А так получается торчит он наружу, соблазняет с ним делать всякое нехорошее)) Хотя чисто внутренний сигнал - кажется бредовой идеей. Конечно, его (сигнал operate) можно соединить с другим сигналом где-либо в программе, и это будет красивее с точки зрения Qt, но это ведь не будет чем-то более лучшим, чем emit cntrl->operate("somestr"); Будет emit anothersignal("somestr").
Слишком много концептуальности/философии  Улыбающийся  Да, иногда хотелось бы подчеркнуть что сигнал чисто "местный", ну это дело невеликое, достаточно просто примечаний. А борцы за идейную чистоту могут напр ограничить доступ к самому классу Controller  Улыбающийся
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #7 : Июнь 06, 2016, 08:25 »

Но в рамках философии Qt нормально ли "чужой" сигнал дергать?

В рамках любой философии дергать "чужой" сигнал не нормально.

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

В случае внешнего вызова сигнала причинно-следственная связь нарушается, так как сам объект фактически ни как не реагировал на какое-либо событие, и систему можно привести в несогласованное состояние. Например, вызывая сигнал у QSlider valueCanged(int) все взаимосвязанные объекты изменят свое состояние, а сам QSlider нет.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Июнь 06, 2016, 09:26 »

Подразумевается, что через механизм сигнал-слот взаимодействия обеспечивается некоторая согласованность данных в программном обеспечении. То есть изменение состояния одного объекта может быть сигнализировано другим с помощью сигнала, на основании которого связанные с ним объекты могут поменять свое состояние им т.п. Таким образом взаимосвязанные объекты будут находится в согласованном состоянии - в единой модели причинно следственных связей.
Эти (разумные) соображения столь же справедливы для любого метода класса (должен ли он быть public/private/protected). Считать сигнал "чем-то особенным" нет оснований.

В случае внешнего вызова сигнала причинно-следственная связь нарушается, так как сам объект фактически ни как не реагировал на какое-либо событие,
А на что может среагировать Controller в примере? У него же ничего нет чтобы он мог что-то решать. И почему сигнал рассматривается только как "реакция" (на что-то) ? Это может быть по сути просто (piblic) "командой"
Записан
Bepec
Гость
« Ответ #9 : Июнь 06, 2016, 10:52 »

Не спорьте, вы оба правы.
В случае с примером у нас нет чужого класса. У нас есть свой класс с, пусть нелепой, но допустимой механикой.
Но вот когда класса не знаешь, лучше так не делать Улыбающийся
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #10 : Июнь 06, 2016, 12:10 »

Эти (разумные) соображения столь же справедливы для любого метода класса (должен ли он быть public/private/protected).

Я согласен с этим. Просто для сигналов в Qt не оставлено другого выбора кроме public. И получается, что по такому принципу можно дернуть любой сигнал, а это уже косяк архитектуры.

Считать сигнал "чем-то особенным" нет оснований.

Понятие сигнала  достаточно абстрактное и в общем случае может быть реализовано не только в виде методов класса, как в Qt. Но это большая тема для другого обсуждения, если хотите  Улыбающийся.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #11 : Июнь 06, 2016, 13:42 »

Не так давно была тема про области видимости для сигналов.

Если коротко, то объект должен предоставлять возможность подключаться к своим сигналам и отключаться от них другим объектам, а возможность "вызывать" их должен иметь только он сам. То, что в Qt сигналы объявлены в публичной секции, чтобы можно было подключаться и отключаться от них - это нормально, а то, что кто угодно может "вызвать" эти сигналы - ненормально, и это косяк архитектуры.

Эх, получается я криво спросил, имелось ввиду, что выработка сигнала с последующим вызовом подключенного слота это тоже самое, что и вызов через invokeMethod, т.е. их механизм работы в недрах Qt одинаков?

Я тоже думаю, что механизм одинаковый, подробности можно в исходниках посмотреть.

Как бы вы предложили использовать этот класс, какая лучшая практика для этого существует?
...
Таки для чего это так сделано в примере через сигнал, если сигнал operate убрать и не использовать сигнал started у класса QThread, то кроме как через methodInvoke уже не выкрутиться, чтобы doWork выполнился именно в другом потоке, правильно понимаю? Всего лишь хочу понять логику писавшего этот пример Улыбающийся

Мне тоже логика этого класса не очень понятна Улыбающийся. Разве что демонстрация, что "можно так вот делать". Может Controller будет базовым классом для других контроллеров, но всё равно это криво. Должен быть публичный метод, который будет вызывать этот сигнал operate.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Июнь 06, 2016, 14:11 »

Мне тоже логика этого класса не очень понятна Улыбающийся. Разве что демонстрация, что "можно так вот делать". Может Controller будет базовым классом для других контроллеров, но всё равно это криво. Должен быть публичный метод, который будет вызывать этот сигнал operate.
А по-моему очень неплохой пример. Обычно никакого контроллера-то и нет, (и воркера тоже), наследуются от QThread - и все дела. А здесь показано как сделать грамотно, все разложено  по полочкам. Воркер делает содержательную работу не заботясь в какой он нитке. Контроллер связывает его с QThread, которую умеет запускать/завершать, сигналами ему можно скармливать задачи и получать рез-т. Чего еще надо? Посвящать контроллер в подробности уже какой-то конкретной задачи - явно лишнее
Записан
Bepec
Гость
« Ответ #13 : Июнь 06, 2016, 17:33 »

Логика этого класса в том, что в сигнал слотовой системе можно подключать СИГНАЛЫ к СЛОТАМ и(sic!) СИГНАЛАМ.

По сути сделан connect сигнала своего класса к сигналу operate - и вот уже полноценный контроллер с воркерами пошёл в работу Улыбающийся

PS по сути он рассчитан на работу с "классом". А вызов функции invoke - это уже издержки Веселый
Записан
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #14 : Июнь 07, 2016, 20:26 »

Обычно никакого контроллера-то и нет, (и воркера тоже), наследуются от QThread - и все дела. А здесь показано как сделать грамотно, все разложено  по полочкам. Воркер делает содержательную работу не заботясь в какой он нитке.
Это, если вся функциональность (с созданием объектов) реализована в run(). Если вам нужно вызвать слот в объекте, созданном в другом потоке, тогда подобный контроллер - очевидное (наверное, не единственное) решение. Для этого и служит operate - в каком-нибудь методе контроллера испускается сигнал, при этом в другом потоке выполняется слот. Можно таким образом передавать данные из потока в поток.
И наоборот, из этого потока данные (или управление) передаются через сигнал resultReady в главный поток.
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.063 секунд. Запросов: 23.