QWebEngine переход к синхронности
От: coder9999  
Дата: 21.08.21 18:27
Оценка:
Привожу в порядок один свой старый проект (в том числе портирую с QWebKit на QWebEngine), с тем чтобы опубликовать его на гитхабе.
Проект — wysiwyg редактор на базе html движка в режиме editable.

О том как портировать есть некая информация здесь: https://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.html
Проблема в следующем. Теперь вызовы некоторых функций (в частности runJavaScript) стали асинхронные, и вместо возврата результата они вызывают колбэк, возможно даже в другом потоке — понятия не имею как оно устроено. Хочется минимизировать изменения в коде и по простому превратить это в обычные синхронные вызовы, как раньше.

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

Поскольку вся работа локальная (без обращения к сети) то все происходит быстро и никаких тормозов не должно быть; но в идеале хорошо бы туда еще на сякий случай поставить таймаут. Если 10 секунд нет ответа — значит действительно что-то подвисло в движке, и нет смысла ждать дальше.

Что-то вроде:

QVariant WebEditView::execScript(const QString &script)
{
    QVariant res;
    page()->runJavaScript(script, [&res](const QVariant &result) {
        res = result;
        GenerateEvent(); //<<< генериурем событие готовности данных
    });
    WaitForEvent(10000); //<<< ждем 10сек. или до события готовности данных
    return res;
}


Что конкретно в Qt позволит это сделать?
Re: QWebEngine переход к синхронности
От: Zhendos  
Дата: 21.08.21 18:34
Оценка:
Здравствуйте, coder9999, Вы писали:

C>Что-то вроде:


C>
QVariant WebEditView::execScript(const QString &script)
C>{
C>    WaitForEvent(10000); //<<< ждем 10сек. или до события готовности данных
C>    return res;
C>}


C>Что конкретно в Qt позволит это сделать?


https://doc.qt.io/qt-5/qcoreapplication.html#processEvents

но ее использование обычно является причиной кучи багов.
Правильнее просто использовать QTimer.
Re[2]: QWebEngine переход к синхронности
От: coder9999  
Дата: 21.08.21 18:52
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>https://doc.qt.io/qt-5/qcoreapplication.html#processEvents

Z>но ее использование обычно является причиной кучи багов.

Я припоминаю что нечто связанное с processEvents использовал в других проектах, правда там для нестандартной обработки GUI.
Класс назывался QEventLoop. Посмотрел сейчас на доки — кажется подходит. Но проверить пока не могу, куча кода еще не компилируется

QVariant WebEditView::execScript(const QString &script)
{
    QVariant res;
    QEventLoop loop;
    page()->runJavaScript(script, [&res, &loop](const QVariant &result) {
        res = result;
        loop.quit();
    });
    loop.exec();
    return res;
}


Как считаете, будет такое работать?
Правда непонятно, что будет если сама runJavaScript вдруг будет выполнять лямбду синхронно.
Re: QWebEngine переход к синхронности
От: Igore Россия  
Дата: 22.08.21 07:15
Оценка:
Здравствуйте, coder9999, Вы писали:

C>Привожу в порядок один свой старый проект (в том числе портирую с QWebKit на QWebEngine), с тем чтобы опубликовать его на гитхабе.

C>Проект — wysiwyg редактор на базе html движка в режиме editable.

C>О том как портировать есть некая информация здесь: https://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.html

C>Проблема в следующем. Теперь вызовы некоторых функций (в частности runJavaScript) стали асинхронные, и вместо возврата результата они вызывают колбэк, возможно даже в другом потоке — понятия не имею как оно устроено. Хочется минимизировать изменения в коде и по простому превратить это в обычные синхронные вызовы, как раньше.
Нет, callback будет вызываться в том же потоке, переделка то механическая

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


C>Поскольку вся работа локальная (без обращения к сети) то все происходит быстро и никаких тормозов не должно быть; но в идеале хорошо бы туда еще на сякий случай поставить таймаут. Если 10 секунд нет ответа — значит действительно что-то подвисло в движке, и нет смысла ждать дальше.

В идеале нужно переписать асинхронно
код приблизительный.
void WebEditView::execScript(const QString &script, const QJSValue& callback )
{
    page()->runJavaScript(script, [&res](const QVariant &result) {
        callback.call( result );
        GenerateEvent(); //<<< генериурем событие готовности данных
    });
}

//js Как то так
editView.execScript( "function(){ return 42; }", function(result){ console.log( result ); } );



C>Что конкретно в Qt позволит это сделать?

Можешь еще посмотреть на QMetaObject::invokeMethod + Qt::BlockingQueuedConnection, выполняемый слот в другом потоке вернет управление после завершения и вызывающий код пишется как синхронный, но если метод долгий, ui подвисает и лучше всё таки переписать на асинхронный вариант.
Приблизительно так
QVariant WebEditView::execScript(const QString &script)
{
    QVariant result;
    QMetaObject::invokeMethod( page(), "runJavaScript", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG( QString, script) );
    GenerateEvent(); //<<< генериурем событие готовности данных
    return result;
}
Re: QWebEngine переход к синхронности
От: wl. Россия  
Дата: 24.08.21 14:28
Оценка:
Здравствуйте, coder9999, Вы писали:


C>Что конкретно в Qt позволит это сделать?


у нас было сделано как-то так (для QNetworkReply правда):

void waitMax(int msec)
{
    QTimer timer;
    QEventLoop loop;
    connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
    connect(this, SIGNAL(requestProcessingFinished()), &loop, SLOT(quit()));
    timer.start(msec);
    loop.exec();
}
Отредактировано 24.08.2021 14:33 wl. . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.