Can't Figure out QWebEngineView::findText()
-
I have a Form for processing purchases. It used to use a QWebView which is or is going to be deprecated. I updated to the latest Qt/Creator 5.7.1 in order to use the QWebEngineView..which is the QWebView replacement.
Creator does not have a QWebEngineView widget to just drop on a from, so a QWidget is added and then promoted to a QWebEngineView. The purpose of the view is to load a local html file that will transfer control to a https card processing portal on a button press. During processing at the on_webView_loadFinished signal, I would use the QWebView::findText() method to search the active webpage to monitor progress, check for errors, etc. It worked fine as a QWebView..not so much in QWebEngineView.
When I get the QWebEngineView::loadFinished signal the bool passed is “true” signifying all is good, page is loaded. I can access the webpage title QWebEngineView::title() property but I’m not having any luck using the QWebEngineView::findText() method. I just cannot figure it out.
I the QWebView version, I would have tests like..
if ( webView->findText( USERNOTCONNECTED ) )
{
..
}In the QWebEngineView version I can’t find any Qt examples on how to use it. Essentially I created a function to return a bool if the text being searched for is located in the webpage content nd “selected”. There is a findText() method in QWebEngineView for the view itself and also of the view->page(). Not sure which to use or if it make a difference. The QWebEngineView::selectedText() method always returns “”..so whatever is being searched for is never found.
I did find a similar problem thread..( https://forum.qt.io/topic/55362/solved-webengineview-findtext ).. but the “solution” seems incomplete and I could not get it to work.
What I tried..
@
void frmBuyIt::isFoundText( bool value )
{
// textFound is a frmBuyIt class bool member
textFound = value;
}bool frmBuyIt::FoundText( QWebEngineView *view, QString it )
{
// examine page content for a specific text string..#if DEBUG_BUYIT
qDebug() << "frmBuyIt::FoundText looking for: "<< it;
#endif// QWebEngineView doesnt' work..
// use C++11 to use lambdas. Add CONFIG += c++11 in your .pro file and recompile. <-did this//dnw
view->findText( it, QWebEnginePage::FindFlags(), [=](bool found) { frmBuyIt::isFoundText(found); qDebug()<<"view "<< it << textFound; });
// if the text was found, then it is "selected"..according to docs
QString vst = view->selectedText();//dnw
view->page()->findText( it, QWebEnginePage::FindFlags(), [=](bool found) { frmBuyIt::isFoundText(found); qDebug()<<"page "<< it << textFound; });
// if the text was found it is "selected"..according to docs
QString pst = view->page()->selectedText();// if the text was found, then it's "selected"..according to docs
bool found = ( vst == it );#if DEBUG_BUYIT
qDebug() << "frmBuyIt::FoundText: "<< it << found << vst << pst;
#endifreturn found;
}
@ -
Please read porting guide completely
http://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.htmlCommunication with embedded web engine is now asynchronous - so you don't know if your lambda will be executed before you try to evaluate Selected text - usually not
Try to move all debug print inside your lambda function or add two buttons just for testing - one to do findtext the other to get SelectedText and make sure you print it inside your lambda this time -
Please read porting guide completely
http://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.htmlCommunication with embedded web engine is now asynchronous - so you don't know if your lambda will be executed before you try to evaluate Selected text - usually not
Try to move all debug print inside your lambda function or add two buttons just for testing - one to do findtext the other to get SelectedText and make sure you print it inside your lambda this timePlease read porting guide completely
Thx, but I did that prior to posting this.Try to move all debug print inside your lambda function..
I've never used a Lambda before..but isn't that where the qDebug statement is in the example I posted?
@
view->findText( it, QWebEnginePage::FindFlags(), [=](bool found) { frmBuyIt::isFoundText(found); qDebug()<<"view "<< it << textFound; });
@ -
well next time please use code formatting and it will be nice if you can show us some debugging output too...
The problem is not with the lambda (if it compiles it will be run) it is just a matter of time - so on that line you basically just schedule you lambda to be run and then immediately execute next lineQString vst = view->selectedText();
without even giving your view a chance to execute 'findText' and hit your lambda
so even last line probably executes before Blink/WebEngine even get a chance to start executing commands, so by the time you hit final debug print - of course it is falsebool found = ( vst == it ); // executes before your lambdas qDebug() << "frmBuyIt::FoundText: "<< it << found << vst << pst; // before lambda
so I thought we were discussing this last debug print - you actually never showed a single line of debug output..
Other issues with your code - you call same functions twice (on view and page) but because WebEngine will execute these async there is no guarantee which one will complete first, and even if they are guaranteed to complete in order what if the view gets it right and then page invalidates result and gets it wrong - you final debug will print it wrong.
some general pointers
- once you 'schedule' something for execution by WebEngine you must wait until it completes
- if your lambdas grab some vars from local scope you must ensure they still exist by the time lambda is executed and tries to use them i.e. in that case do not go out of scope
NB! do not try to just Sleep arbitrary long interval thinking lambda has completed - Sleep will block you thread (GUI) and that was very bad thing esp. for older WebKit, not so sure about Blink...
The easiest test for you would be with two buttons :
Click button one =>view->findText( it, QWebEnginePage::FindFlags(), [=](bool found) { qDebug()<<"view findText "<< found; });
Wait until you see its debug output and then
Click button 2 =>qDebug()<<"view selectedText "<< view->selectedText();
and watch the debugger output
-
well next time please use code formatting and it will be nice if you can show us some debugging output too...
The problem is not with the lambda (if it compiles it will be run) it is just a matter of time - so on that line you basically just schedule you lambda to be run and then immediately execute next lineQString vst = view->selectedText();
without even giving your view a chance to execute 'findText' and hit your lambda
so even last line probably executes before Blink/WebEngine even get a chance to start executing commands, so by the time you hit final debug print - of course it is falsebool found = ( vst == it ); // executes before your lambdas qDebug() << "frmBuyIt::FoundText: "<< it << found << vst << pst; // before lambda
so I thought we were discussing this last debug print - you actually never showed a single line of debug output..
Other issues with your code - you call same functions twice (on view and page) but because WebEngine will execute these async there is no guarantee which one will complete first, and even if they are guaranteed to complete in order what if the view gets it right and then page invalidates result and gets it wrong - you final debug will print it wrong.
some general pointers
- once you 'schedule' something for execution by WebEngine you must wait until it completes
- if your lambdas grab some vars from local scope you must ensure they still exist by the time lambda is executed and tries to use them i.e. in that case do not go out of scope
NB! do not try to just Sleep arbitrary long interval thinking lambda has completed - Sleep will block you thread (GUI) and that was very bad thing esp. for older WebKit, not so sure about Blink...
The easiest test for you would be with two buttons :
Click button one =>view->findText( it, QWebEnginePage::FindFlags(), [=](bool found) { qDebug()<<"view findText "<< found; });
Wait until you see its debug output and then
Click button 2 =>qDebug()<<"view selectedText "<< view->selectedText();
and watch the debugger output
In your wtf.h class definition
protected slots: void handleHTML( QString sHtml ); void handleSearch( bool result ); signals: void gotHTML( QString sHtml ); void gotSearch( bool result );
In your wtf.cpp class definition
void wtf::handleHTML( QString sHtml ) { qDebug() << “wtf::handleHTML:” << sHtml; } void wtf::handleSearch( bool result ) { // searched for text string result qDebug() << “wtf::handleSearch: "<< result; // remove hi-lighted "found" text ui->webView->page()->findText(""); }
In your wtf class instantiation..
connect(this, SIGNAL(gotHTML(QString)), this, SLOT(handleHTML(QString))); connect(this, SIGNAL(gotSearch(bool)), this, SLOT(handleSearch(bool)));
Usage:
Assuming the QWebEngineView is a widget on your wtf.ui..
Somewhere in the wtf::on_webView_loadFinished( bool ok ) slot code put the following..
To scrape the html..
ui->webView->page()->toHtml([this](const QString& result) mutable {emit gotHTML(result);});
To find something..
QString idk = “something”; ui->webView->page()->findText(idk, QWebEnginePage::FindFlags(), [this](bool found) { if (true) emit handleSearch( found ); });
The findText() example from the QWebEnginePage class docs..(it would have been nice to see in QWebEngineView, too!!!)
ui->webView->page()->findText(QStringLiteral("Qt"), QWebEnginePage::FindFlags(), [this](bool found) { if (!found) QMessageBox::information(ui->webView, QString(), QStringLiteral("No occurrences found")); });
The concept of QWebEngineView::findText() is really, really weird. If you’re looking for specific text on a webpage, you want to know the searched for text is there NOW..not when some callback function gets around to it.
It would seem to make much more sense to scrape the page toHtml() or toString() and then use QString::contains() to do searches. Heck, if the page has been rendered to a view, the QWebEngineView has already read the page content..couldn’t it just save the page data as it goes..in a QString or QByteArray or a QWhatever..? JMO. Done complaining.
-
In your wtf.h class definition
protected slots: void handleHTML( QString sHtml ); void handleSearch( bool result ); signals: void gotHTML( QString sHtml ); void gotSearch( bool result );
In your wtf.cpp class definition
void wtf::handleHTML( QString sHtml ) { qDebug() << “wtf::handleHTML:” << sHtml; } void wtf::handleSearch( bool result ) { // searched for text string result qDebug() << “wtf::handleSearch: "<< result; // remove hi-lighted "found" text ui->webView->page()->findText(""); }
In your wtf class instantiation..
connect(this, SIGNAL(gotHTML(QString)), this, SLOT(handleHTML(QString))); connect(this, SIGNAL(gotSearch(bool)), this, SLOT(handleSearch(bool)));
Usage:
Assuming the QWebEngineView is a widget on your wtf.ui..
Somewhere in the wtf::on_webView_loadFinished( bool ok ) slot code put the following..
To scrape the html..
ui->webView->page()->toHtml([this](const QString& result) mutable {emit gotHTML(result);});
To find something..
QString idk = “something”; ui->webView->page()->findText(idk, QWebEnginePage::FindFlags(), [this](bool found) { if (true) emit handleSearch( found ); });
The findText() example from the QWebEnginePage class docs..(it would have been nice to see in QWebEngineView, too!!!)
ui->webView->page()->findText(QStringLiteral("Qt"), QWebEnginePage::FindFlags(), [this](bool found) { if (!found) QMessageBox::information(ui->webView, QString(), QStringLiteral("No occurrences found")); });
The concept of QWebEngineView::findText() is really, really weird. If you’re looking for specific text on a webpage, you want to know the searched for text is there NOW..not when some callback function gets around to it.
It would seem to make much more sense to scrape the page toHtml() or toString() and then use QString::contains() to do searches. Heck, if the page has been rendered to a view, the QWebEngineView has already read the page content..couldn’t it just save the page data as it goes..in a QString or QByteArray or a QWhatever..? JMO. Done complaining.
// remove hi-lighted "found" text ui->webView->page()->findText("");
NOTE: this only does 1 instance of the "found" text. If there are multiple found instances of a word, the findText("") will hight-light all of them..not just the first occurrence.
The call must be repeated to un-highlight all of them.
Probably a bug.
-
In your wtf.h class definition
protected slots: void handleHTML( QString sHtml ); void handleSearch( bool result ); signals: void gotHTML( QString sHtml ); void gotSearch( bool result );
In your wtf.cpp class definition
void wtf::handleHTML( QString sHtml ) { qDebug() << “wtf::handleHTML:” << sHtml; } void wtf::handleSearch( bool result ) { // searched for text string result qDebug() << “wtf::handleSearch: "<< result; // remove hi-lighted "found" text ui->webView->page()->findText(""); }
In your wtf class instantiation..
connect(this, SIGNAL(gotHTML(QString)), this, SLOT(handleHTML(QString))); connect(this, SIGNAL(gotSearch(bool)), this, SLOT(handleSearch(bool)));
Usage:
Assuming the QWebEngineView is a widget on your wtf.ui..
Somewhere in the wtf::on_webView_loadFinished( bool ok ) slot code put the following..
To scrape the html..
ui->webView->page()->toHtml([this](const QString& result) mutable {emit gotHTML(result);});
To find something..
QString idk = “something”; ui->webView->page()->findText(idk, QWebEnginePage::FindFlags(), [this](bool found) { if (true) emit handleSearch( found ); });
The findText() example from the QWebEnginePage class docs..(it would have been nice to see in QWebEngineView, too!!!)
ui->webView->page()->findText(QStringLiteral("Qt"), QWebEnginePage::FindFlags(), [this](bool found) { if (!found) QMessageBox::information(ui->webView, QString(), QStringLiteral("No occurrences found")); });
The concept of QWebEngineView::findText() is really, really weird. If you’re looking for specific text on a webpage, you want to know the searched for text is there NOW..not when some callback function gets around to it.
It would seem to make much more sense to scrape the page toHtml() or toString() and then use QString::contains() to do searches. Heck, if the page has been rendered to a view, the QWebEngineView has already read the page content..couldn’t it just save the page data as it goes..in a QString or QByteArray or a QWhatever..? JMO. Done complaining.
@PSI_lbc said in Can't Figure out QWebEngineView::findText():
The concept of QWebEngineView::findText() is really, really weird. If you’re looking for specific text on a webpage, you want to know the searched for text is there NOW..not when some callback function gets around to it.
This is by design - it affects all functions that interact with the browser, everything is asynchronous e.g. runJavaScript(code) too.