How can I interact with html elements use QT - c++

For example, I have a simple HTML page with button and label (or something else). How can I change the text in label (or something else) and catch the button click use QT.
I try to use QWebEngineView to show html, but I don`t know how to interact with elements from QT modul, just change the url, but I dont think its a better way

To be able to interact with HTML rendered with QWebEngine you need to use QWebChannel. You can find the basic guidelines at Qt WebChannel JavaScript API page.
To implement intercommunication with JavaScript in your HTML page you need:
Add Qt += webchannel in your project file
Implement a QObject derived class that should be a proxy between C++ and JavaScript. The simpliest way to make it usable in JavaScript is to create getters, setters and signals for the values you intend to use in JavaScript, and expose them as Q_PROPERTY.
Example:
class ProxyClass: public QObject
{
Q_OBJECT
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit ProxyClass(QObject *parent = nullptr);
QString value() const;
void setValue(const QString &aValue);
signals:
void valueChanged(const QString &aValue);
};
Set HTML to QWebEngineView with QWebEngineView::setHtml, instantiate your "proxy" class and create QWebChannel for the page (note that you can register multiple objects in QWebChannel). Example:
//create QWebEngineView and set HTML from resources
auto webView = new QWebEngineView;
QFile htmlFile(":/page.html");
htmlFile.open(QIODevice::ReadOnly);
QString html = htmlFile.readAll();
webView->setHtml(html);
//create and set up the instance of your "proxy" class
auto proxy = new ProxyClass;
//create QWebChannel and set it for the page
QWebChannel *webChannel = new QWebChannel(webView->page());
webChannel->registerObject(QStringLiteral("proxy"), proxy);
webView->page()->setWebChannel(webChannel);
Embed qwebchannel.js file in HTML. File is located at <Qt installation directory>/Examples/Qt-<version>/webchannel/shared directory. You can include it in application resources and embed in HTML with <script type="text/javascript" src="qrc:/qwebchannel.js"></script>
Create onload event handler in HTML and initialize QWebChannel in it. Example:
function loaded() {
new QWebChannel(qt.webChannelTransport, function (channel) {
<!--get and assign "proxy"-->
window.proxy = channel.objects.proxy;
<!--now you can-->
<!--get values-->
let proxyValue = proxy.value;
<!--connect signals-->
proxy.valueChanged.connect(() => {
...
});
});
}
function someFunction(newValue) {
<!--set values-->
proxy.value = newValue;
}
...
<body onload="loaded()">...</body>
Once you initialized a web channel and assigned "proxy" object to the window object, you are free to use it everywhere in JavaScript.

Related

Custom qml view

Can I create some custom view? I have some model(written in C++ inheriting QAbstractListModel), which I use in ListView and GridView. When I do some changes in C++, I can just emit dataChanged signal to update my views. Can I connect some QML properties to data from model? Image.source for example?
EDIT 1
I have QAbstractListModel subclass. It contains data about some files. I have created ListView in the left of my main window. Click on some row should change view in the right working area. Working area contains detailed information about each file. So, how can I implement this?
You can access whatever part of your C++ class you expose as a property:
class MyModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString source READ source WRITE setsource NOTIFY source Changed)
public:
QString source();
void setSource(QString s);
signals:
void sourceChanged();
};
Register the type:
qmlRegisterType<MyModel>("Stuff", 1, 0, "MyModel");
MyModel model
QQmlContext *ctxt = rootContext();
ctxt->setContextProperty("myModel", &model);
Then in QML:
import Stuff 1.0
Image {
source: myModel.source
}

Get webview document title when HTML is loaded

I have the following Qt webview:
QWebView *view = new QWebView();
view->load(QUrl("http://example.com"));
I want to get the title of document when load is finished, and use it to set the main window title.
From what I suppose view->loadFinished() returns true if page was loaded or not.
For setting the window title I use webView->setWindowTitle(newTitle). So, I need that newTitle variable that I want to be the document title.
How can I do this?
QWebView::loadFinished is a signal. You can subscribe to it to know when the page is loaded:
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(onLoaded()));
To access HTML title you can use QWebView::title property.
void onLoaded()
{
window->setWindowTitle(view->title());
}
Rather then using loadFinished it may be more appropriate to use signal titleChanged(const QString& title) to apply a new title to the window:
connect(view, SIGNAL(titleChanged(QString)), this, SLOT(setWindowTitle(QString)));
EDIT:
Example:
QWebView* webView = new QWebView();
connect(webView, SIGNAL(titleChanged(QString)), webView, SLOT(setWindowTitle(QString)));
webView->load(QUrl("http://yahoo.com"));
webView->show();

How to modify the -webkit-scrollbar styles in a QWebView

Webkit provides special css properties for styling scrollbars, for example:
::-webkit-scrollbar-track {
background-color:white;
}
Normally I'd put these inside a <style> tag inside <head>, but unfortunately QWebElement doesn't seem to be able to modify anything inside <head>. I can use the setHtml() function to specify initial styling, but not modify it later. Is there an alternative way to apply CSS styles to the scrollbars in a QWebFrame?
Is possible using QWebSettings::setUserStyleSheetUrl, see example:
const QString path = PATH_OF_CSS_FILE;
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(QUrl(path));
Example
If dynamic CSS is a string, you can create a method and use QTemporaryFile, like this:
void MainWindow::setStyle(const QString data)
{
QTemporaryFile file;
if (file.open()) {
const QString path = file.fileName();
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(QUrl(path));
}
}
Usage:
setStyle("::-webkit-scrollbar-track { background-color:white;}")
If needs load dynamic file, you can create an alternative method like this:
void MainWindow::setStyle(const QUrl url)
{
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(url);
}
Using QRC
Is part of the answer is just a tip for other implementations;
You can use resources (QRC) in your project for put default stylesheet for all QWebViews, see example:
Click right button in your project > Add New ... > Qt > Qt Resource File > Put name "resources.qrc"
Click right button in "resources.qrc" > Open in Editor
Put a CSS file with name scrollbar.css (css file must be in the same folder as your project).
Put this in your "main.cpp":
#include <QWebSettings>
#include <QUrl>
...
const QString path = "qrc:/scrollbar.css";
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(QUrl(path));

Qt - Any guideline on how to implement navigation between UI Forms?

I'm learning Qt right now, trying to make a simple application. What I'm trying to implement is a simple 'Welcome' screen, with two buttons ('Register' and 'Login'). The problem is on the page redirection to the other two pages.
Also, I have the screens already setup using the QtCreator (in .ui Forms format).
The only solution I could come up with, until now, was based on an example from Qt itself, which uses a QStackedWidget, adding QWidgets as pages. The problem is that those pages in the example are mounted programmatically (and I want to use the Forms that I have).
If I try this:
MainWindow::MainWindow() :
ui_home(new Ui::HomeView),
ui_register(new Ui::RegisterView) {
ui_home->setupUi(this);
ui_register->setupUi(this);
pagesWidget = new QStackedWidget;
pagesWidget->addWidget(ui_home->centralWidget);
pagesWidget->addWidget(ui_register->centralWidget);
...
}
It 'kind of works', but the result is horrible. The 'centralWidget' from my Forms is added to the 'pagesWidget', but the 'setupUi' before that really renders the 'Home' and 'Register' pages all at once, messing everything up.
So, real question is:
Is there any guideline on how to implement navigation between UI Forms?
Secondly:
How can I retrieve the QWidget from my UI Form and add to a QStackedWidget, without rendering it?
I am using Ubuntu 12.04, with QtCreator 3.2.1.
Thanks in advance.
You do it wrong. You should create a "MainWidget" with 2 items: a) QStackedWidget, b) navigation panel (your buttons). Then you should set "MainWidget" as a central widget.
After it you can connect signals from navigation panel (clicked signals of "Registed" or "Login" buttons) to corresponding slots, that will select necessary widget on QStackedWidget
So, in your case, you need next 3 ui forms:
MainWindow (QStackWidget + 2 buttons)
LoginWidget
RegisterWidget
Pseudocode:
// RegisterWidget.cpp + you should have RegisterWidget.ui
RegisterWidget::RegisterWidget()
: public QWidget(NULL)
, ui( new Ui::RegisterWidget() )
{}
// LoginWidget.cpp + you should have LoginWidget.ui
LoginWidget::LoginWidget()
: public QWidget(NULL)
, ui( new Ui::LoginWidget() )
{}
// MainWindow.cpp + you should have MainWindow.ui
MainWindow::MainWindow()
: public QMainWindow()
, ui( new Ui::MainWindow() )
{
ui->setupUi(this);
// !!!!!!!!!!!!!!!!!!!
// Creating widgets here.
// Possible - setup communication between widgets with signals / slots
// !!!!!!!!!!!!!!!!!!!
m_loginForm = new LoginWidget();
m_regForm = new RegisterWidget();
ui->stackedWidget->addWidget( m_loginForm );
ui->stackedWidget->addWidget( m_regForm );
connect( ui->loginBtn, &QPushButton::clicked, this, &MainWindow::onLogin );
connect( ui->regBtn, &QPushButton::clicked, this, &MainWindow::onReg );
}
// private slots:
void MainWindow::onLogin()
{
ui->stackedWidget->setCurrentWidget( m_loginForm );
}
void MainWindow::onReg()
{
ui->stackedWidget->setCurrentWidget( m_regForm );
}
And don't forget about layouts. Because you may see nothing if your widgets will have 1x1px size.
How can I retrieve the QWidget from my UI Form and add to a QStackedWidget, without rendering it?
You may use QWidget::hide() method.

Add widget with editable properties through code

I've created a custom widget plugin that is a container( it overloads the qframe). In this container i would like to add children through the code. I've succeeded in doing this. However the children aren't editable or clickable in the designer. I know for children to be editable through the designer, they should be added as code to the XML, but i don't know how to do this.
Would anybody know if it's at all possible to do this?
The code I have, with arrowedFrame as my custom widget plugin class:
arrowedFrame::arrowedFrame(QWidget *parent, Qt::WindowFlags f) : (parent, f)
{
QLabel* testLabel = new QLabel(this);
}
this adds a new Label, as member (sorry i can't yet post pictures, but imagine a box with a label in it). But as i said this label isn't at all editable through the designer.
The solution I found to this is by taking this guide. And adding some things in the initialize function:
void PlotContainerPlugin::initialize(QDesignerFormEditorInterface *formEditor)
{
if (initialized)
return;
QExtensionManager *manager = formEditor->extensionManager();
myFormEditor = formEditor;
Q_ASSERT(manager != 0);
manager->registerExtensions(factory, Q_TYPEID(QDesignerContainerExtension));
initialized = true;
}
The first part just gets a manager:
QExtensionManager *manager = formEditor->extensionManager();
And then we use this manager to,
manager->registerExtensions(factory, Q_TYPEID(QDesignerContainerExtension));
register the plugin(a plotter I my case) with the designer.
Hope this helps:)