Russian Qt Forum

Qt => Общие вопросы => Тема начата: Azazello от Ноябрь 02, 2018, 00:08



Название: Ожидание сигнала
Отправлено: Azazello от Ноябрь 02, 2018, 00:08
QEventLoop ожидает сигнал, но при этом все события обрабатываются.

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


Название: Re: Ожидание сигнала
Отправлено: zhbr от Ноябрь 02, 2018, 06:33
не стартовать QEventLoop, а на ваш сигнал директом повесить слот, который запустит QEventLoop?


Название: Re: Ожидание сигнала
Отправлено: ssoft от Ноябрь 02, 2018, 07:52
Не ясно, что конкретно требуется, но можно дождаться выполнение слота по месту вызова сигнала, если использовать Qt::BlockingQueuedConnection.


Название: Re: Ожидание сигнала
Отправлено: Igors от Ноябрь 02, 2018, 09:04
Есть ли возможность сделать полностью синхронное выполнение - т.е. пока мой сигнал не пришёл, ничего не делам (никакие события не выполняются, гуи подвисло и т.д.)
Нет, processEvents будет молотить все события очереди. Остается фильтровать отсекая нежелательные, лучшего я не нашел


Название: Re: Ожидание сигнала
Отправлено: Авварон от Ноябрь 02, 2018, 15:26
Пустить тред, в нем крутить эвентлуп из семи залуп, по сигналу выходить из него.
Ещё надо добавить ждун_условие и спать на нем в главном треде, после выхода из лупа будить.
Возможно что-то подобное есть в тест фреймворке.
А что мешает просто крутить луп, исключая юзер эвенты?


Название: Re: Ожидание сигнала
Отправлено: Igors от Ноябрь 02, 2018, 16:16
А что мешает просто крутить луп, исключая юзер эвенты?
Ничто не мешает, но будут обрабатываться и др события очереди, а это не все всегда хорошо/допустимо. Напр есть загрузка из 20 шагов, после каждого надо шлепнуть в UI текст "делаю то-то" и дождаться его отображения


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 05, 2018, 20:11
Пустить тред, в нем крутить эвентлуп из семи залуп, по сигналу выходить из него.
Ещё надо добавить ждун_условие и спать на нем в главном треде, после выхода из лупа будить.
Возможно что-то подобное есть в тест фреймворке.

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


Вообщем так и не поборол, что с одной стороны, что с другой (другую сторону придумайте сами)

Есть модель с асинхронной загрузкой данных. Rest.
Есть QGraphicsView(scene), который выступает view (в контексте MVC) для этой модели.

Смысл не отличается от обычной вью - скрол влево - данные подтянулись. Все ОК.

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

Код fetchMore():
Код:
while (mRequestRows > 0) {
        qCDebug(candleModelDebug) << "Send rest request";
        RestEventLoop eventLoop;
        connect(&mRest,&Rest::stateChanged,&eventLoop,&RestEventLoop::finish);
        mRest.sendRequest();
        eventLoop.exec();

        qCDebug(candleModelDebug) << "Data loaded";

        QList<BaCandleData> buffer = mRest.candleData();
Куда концептуально идти -  не понятно.

RestEventLoop - обычный EventLoop просто со своим слотом


Название: Re: Ожидание сигнала
Отправлено: Авварон от Ноябрь 05, 2018, 22:04
Так а чо дебаггер говорит? Могу предположить, что внутри эвентлупа вьюга запрашивает fetchMore заново и стек кончается.


Название: Re: Ожидание сигнала
Отправлено: Old от Ноябрь 05, 2018, 22:24
В resizeEvent вы только запускаете загрузку данных, но модель не обновляете и ничего не ждете, пусть рисуются только те объекты которые уже есть в модели. А на сигнал завершения загрузки вешаете слот модели, который обновит объекты и пошлет сигнал вьюшки на перерисовку.


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 05, 2018, 23:44
Так а чо дебаггер говорит? Могу предположить, что внутри эвентлупа вьюга запрашивает fetchMore заново и стек кончается.

Дебагер говорит о прохождении от функции маин (loop) до ексепшина. Т.е. проблема с сообщениями, какие-то друг другу противоречат. О переполнении стека говорить не приходится. Дебаг застрал после 1 часа втыкания- я понял  - застряну в дебаге надолго и даже знание проблемы не даст ничего.

В resizeEvent вы только запускаете загрузку данных, но модель не обновляете и ничего не ждете, пусть рисуются только те объекты которые уже есть в модели. А на сигнал завершения загрузки вешаете слот модели, который обновит объекты и пошлет сигнал вьюшки на перерисовку.

Я понял вашу мысль. Вы просто предлагаете разделить данные во времени (по сути). Но это ничего не даст, пользователь просто может залипнуть на ресайзе мышкой, и тогда ситуация ничем не будет отличаться от существующей.

Хотя попробую! Да, а нам то как раз и зависимость сигналов то не нужна.


Название: Re: Ожидание сигнала
Отправлено: Авварон от Ноябрь 05, 2018, 23:49
Ну old прав - обычно при запросе fetch more взводится флажок "фетчим" чтобы не перечерчивать и когда данные появляются, флажок сбрасывается и они вставляются в модель.


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 06, 2018, 11:44
Ну old прав - обычно при запросе fetch more взводится флажок "фетчим" чтобы не перечерчивать и когда данные появляются, флажок сбрасывается и они вставляются в модель.

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


Название: Re: Ожидание сигнала
Отправлено: Old от Ноябрь 06, 2018, 12:51
Это сработает, когда ресайз охватывает один фетч. А если несколько. Да, можно установить размер пакета, который перекрывает заранее окно. Но не в моем случае. У меня есть масштабирование, и при очень мелком масштабе при ресайзе может быть вызвано десятки фетчей.
Так ставьте запросы в очередь, по мере подгрузки данных сцена будет обновляться.


Название: Re: Ожидание сигнала
Отправлено: Igors от Ноябрь 06, 2018, 13:45
Есть модель с асинхронной загрузкой данных. Rest.
...
Код fetchMore():
Ну если "асинхронно", то надо полагать что RestEventLoop выполняется в др нитке (не главной). Это нормально, но трогать сцену напрямую эта нитка не должна. Нужно через QueuedConnection

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




Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 06, 2018, 14:12
resizeEvent - одно событие, прием новых данных сцены - другое, пересекаться они не должны. Юзер залип - ну новые данные не отобразятся (пока не отлипнет), это не катастрофа

Ладно. Реально я это не поборол, просто отодвинул.
Подытожу.
Условие:
Программа валится, когда происходит Resize вью и получение данных (fetchMore + EventLoop).
События эти во времени нельзя разделить, т.к. это получение данных у нас асинхронно - мы не знаем когда данные прийдут, а могут прийти когда мы делаем ресайз, повторно ресайз (данные с первого), ковыряемся в носу и т.д.

Решение. Пока такое:
Очередь запросов (просто их количество). И если я в своем вью для асинхонной модели могу при fetchMore проверить, что у нас в очереди для загрузки есть данные (т.е. не нужно делать fetchMore), и не выполнять запрос, то для стандартной вьюхи это не возможно, и любое fetchMore будет скачивать данные, понятно почему, rowCount изменится позже. Не очень критично, чтобы сейчас переписывать стандартный вью.

Вывод: Дизайн MVC (в Qt) не предназначен для асинхронной загрузки данных.


Ну, может псевдокод для понимания.
Код:

void GraphicsView::checkNeedData()
{
    int fetchCount = requestVisibleData();
    int needRowCount = fetchCount + model->rowCount();
    if (needRowCount > model->[b]requestRowCount[/b]()) {
        qCDebug(graphicsViewDebug) << "Data size: " << scene()->model()->rowCount();
        int fetchedCount = fetchData(fetchCount);
        qCDebug(graphicsViewDebug) << "Send request fetched data" << fetchedCount;

    }

void CaModel::fetchMore(const QModelIndex &)
{  
    .....

    if (mRequestRows == 0) {
        mRequestRows = mRest.limit();
        mRest.sendRequest();
    } else { //if data in progress
        mRequestRows += mRest.limit();
    }
}

void CaModel::finishFetch(Ba::ReplyNetworkState state)
{
    qCDebug(candleModelDebug) << "Data loaded";

    QList<BaCandleData> buffer = mRest.candleData();
    mRequestRows -= buffer.size();

    if (buffer.size() == 0)
        return;

    beginInsertRows(QModelIndex(),rowCount(),rowCount() + buffer.size() - 1);
    mData.append(std::move(buffer));
    endInsertRows();

    if (mRequestRows > 0)
        mRest.sendRequest();
}





Название: Re: Ожидание сигнала
Отправлено: Авварон от Ноябрь 06, 2018, 15:07
Посмотрите как QFileSystemModel работает, она грузит астнхронно


Название: Re: Ожидание сигнала
Отправлено: Old от Ноябрь 06, 2018, 15:24
Ну если "асинхронно", то надо полагать что RestEventLoop выполняется в др нитке (не главной).
Не надо так полагать. Асинхронность и многопоточность это не связанные вещи, несмотря на то, что одно может быть реализовано используя другое.


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 06, 2018, 16:05
Посмотрите как QFileSystemModel работает, она грузит астнхронно

Спасибо, но не поможет. Он грузит все данные без подкачки


Название: Re: Ожидание сигнала
Отправлено: Igors от Ноябрь 07, 2018, 08:12
...это получение данных у нас асинхронно - мы не знаем когда данные прийдут, а могут прийти когда мы делаем ресайз, повторно ресайз (данные с первого), ковыряемся в носу и т.д.
Вот как работает мой старенький LLDB отладчик. Жму на треугольник expand/collapse чтобы увидеть члены структуры. Tреугольник реагирует (поворачивается), но никаких новых (child) айтемов (пока) нету. UI полностью свободно, можно ресайзить и.т.п. - все что угодно. Ну ладно, закурил, налил чайку и, всего лишь через минуту.. вот они, члены структуры, появились! Ну конечно так не всякий раз, но частенько.

Да, минусы очевидны "я нажал, а оно..". Однако если (как Вы утверждаете) момент приема данных неизвестен, то другого корректного решения нет.  Асинхронно - значит асинхронно, и нечего крутить лупы и замораживать UI чтобы из нее сделать синхронку.

Вывод: Дизайн MVC (в Qt) не предназначен для асинхронной загрузки данных.
Да, не получается - значит Qt виновато (ну а кто же еще?)  :)


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 07, 2018, 09:38
Да, не получается - значит Qt виновато (ну а кто же еще?)  :)

Ну поговорим :). Начнем с того, что я не говорил, что у меня не получается, а говорил, что коробочный вариант Qt MVC не предусматривает асинхронную загрузку.

А с другой стороны, сколько, сколько раз (я не знаю, предполагаю) вы давали здесь советы - у вас неверная архитектура, пересмотрите её? Даже ваш последние топики посвещены переделу архитектуры.

Тогда получается, все, кто говорит про архитектуру изначально не прав. Ибо лукавит. Когда говорим про свою архитектуру - она может быть не правильна. Но Qt создавали боги, поэтому такое рассуждение кощунство. Или это из области -   дай мудрости не перепутать одно с другим? Но вопрос то стоит в другом - Qt:не трогай архитектуру, у тебя руки кривые. Свой проект - переделай архитектуру, у тебя руки кривые.


Название: Re: Ожидание сигнала
Отправлено: Igors от Ноябрь 07, 2018, 10:40
... Qt:не трогай архитектуру, у тебя руки кривые. Свой проект - переделай архитектуру, у тебя руки кривые.
Согласен, когда "слон слишком велик" - на него не лают. Хотя то же самое "в велосипедном исполнении" будет охаяно немедленно. Такова человеческая природа, ничего не попишешь  :) Однако все-таки вернемся к теме.

..я не говорил, что у меня не получается, а говорил, что коробочный вариант Qt MVC не предусматривает асинхронную загрузку.
Хмм... не понял, а что в данном конкретном случае он должен "предусматривать"? Какого чуда Вы ожидали "ис каропки"? Юзер давит бубочку  "expand" - UI должно ему "ответить ответ". Если "достаточно быстрый" ответ невозможен - надо отпускать UI, т.е. выходить в событийный цикл, и показать юзеру что, мол, "процесс пошел". А потом, когда данные придут, по событию уже вставить строки.

А Вы что? Из обработчика одного события зовете др события - верный способ нарваться на неприятности, и Вам это прекрасно известно. Так что все верно - у Вас "архитектура кривая"  :)




Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 07, 2018, 11:29
Хмм... не понял, а что в данном конкретном случае он должен "предусматривать"? Какого чуда Вы ожидали "ис каропки"? Юзер давит бубочку  "expand" - UI должно ему "ответить ответ". Если "достаточно быстрый" ответ невозможен - надо отпускать UI, т.е. выходить в событийный цикл, и показать юзеру что, мол, "процесс пошел". А потом, когда данные придут, по событию уже вставить строки.

А Вы что? Из обработчика одного события зовете др события - верный способ нарваться на неприятности, и Вам это прекрасно известно. Так что все верно - у Вас "архитектура кривая"  :)

Давайте приведу пример конктетный. Будем брать для упрощения таблицу, а не дерево.

Итак, используется асинхронная модель с вью.
Вы смотрите данные и листаете их. Доходите до конца.
В случае синхронной модели: Пошел запрос, гуи ждет данные, появились, .отобразились.
В случае асинхонной модели: Пошел запрос. Данных ещё нет. А вы то листаете все равно -т.е. посылаете запросы. Пошел запрос, данных ещё нет. А вы все листаете. А вью смотрит не на те данные которые будут, а на банальный rowCount. И в определенный момент у вас появится не один пакет данных, а несколько, сколько вы там мышкой накляцали до 1-го прихода данных.

Конечно, в вашем дереве это не будет происходить, т.к. скорее вся ветка закачивается один раз и запрашивается только один раз.

Другой пример: resizeEvent. Как не создавай виджет, а два ресайза будет, и он 2 раза скажет fetchMore, потому что после первого fetchMore данные банально не успеют загрузится. Итог - два пакета данных при старте. Критично? Ну кому как. У меня сервер Rest считает эти запросы и каждый запрос имеет свою цену, причем не виртуальную, а которая считается.

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



Название: Re: Ожидание сигнала
Отправлено: Авварон от Ноябрь 07, 2018, 13:54
Лол, потому и нужна очередь.
Пришёл фетчмор - смотрим, мы уже запрашивали этот кусок? Нет, запрашиваем и кладём в очередь идентификатор индекса (в терминах ФС имя папки).
Папка прогрузилась, вставляем строки, убираем путь из очереди.
Боде, ну откройте код qfilesystemmode уже


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 07, 2018, 15:24
Лол, потому и нужна очередь.
Пришёл фетчмор - смотрим, мы уже запрашивали этот кусок? Нет, запрашиваем и кладём в очередь идентификатор индекса (в терминах ФС имя папки).
Папка прогрузилась, вставляем строки, убираем путь из очереди.
Боде, ну откройте код qfilesystemmode уже

Так. Давайте закроем тему. А то меня не понимают, видно не стоит расширять вопросы, должны быть конкретные и не охватывать большУю область.
Блин. Еще раз повторюсь, qfilesystemmodel не делает подкачку данных, она их сразу ВСЕ скачивает. Да, он делает запрос на закачку ноды дерева, но ВСЕЙ ноды.
Я же вам говорю про другую подчкачку данных - в пределах таблицы, или в пределах одной ноды дерева. Как в QtSQL. Если база данных поддерживает курсор, SqlModel скачает первые 256 записей. Когда вы наступите на 256-ю, он закачает следущие 256. Где там в qfilesystemmodel - скачать следующие файлы/папки по списку. Не,там запрос на все файлы в папке.

Мой пример: вы открыли youtube и ведете скрол вниз, у вас подтягиваются данные.
Ваш пример: вы открыли youtube и кляцнули на категорию, и у вас тоже подтягиваются данные.

Но есть нюанс :)
https://pikabu.ru/story/anekdot_pro_nyuans__349792


Название: Re: Ожидание сигнала
Отправлено: Авварон от Ноябрь 07, 2018, 16:37
Как делать заначку не всей годы разобрано в примере fetchMore, если мне память не изменчет


Название: Re: Ожидание сигнала
Отправлено: Azazello от Ноябрь 07, 2018, 19:01
Как делать заначку не всей годы разобрано в примере fetchMore, если мне память не изменчет

Я так понял, вы сделали заначку. На все годы.... А потом память изменила и заначка пропала :))))))))))


Название: Re: Ожидание сигнала
Отправлено: Igors от Ноябрь 08, 2018, 09:59
В случае синхронной модели:
В случае асинхонной модели:
Никто не спорит, синхронка куда проще и приятнее, как для программиста, так и для юзера. Но ее возможности ограничены. Напр источник данных может (надолго) застрять или вообще отвалиться.
Пошел запрос. Данных ещё нет. А вы то листаете все равно -т.е. посылаете запросы. Пошел запрос, данных ещё нет. А вы все листаете. А вью смотрит не на те данные которые будут, а на банальный rowCount. И в определенный момент у вас появится не один пакет данных, а несколько, сколько вы там мышкой накляцали до 1-го прихода данных.
Что здесь значит "листаете"? Если данных еще нет - как (или что) я могу листать? Видимо об этом Вы упоминаете дальше
А можно при запросе создавать "пустые" данные, которые будут заменятся на рабочие. Но это все так усложняет.....
Думаю это неудачная затея, такие пустышки станут "источником постоянных забот", лучше обойтись без них, сценарий Вы же и рассказали
Я же вам говорю про другую подчкачку данных - в пределах таблицы, или в пределах одной ноды дерева. Как в QtSQL. Если база данных поддерживает курсор, SqlModel скачает первые 256 записей. Когда вы наступите на 256-ю, он закачает следущие 256.
Возможно Вам просто не хватает уверенности начать эти переделки, издалека все кажется таким огромным  :)