Is it possible to inject CSS into QML WebView? - qt

I have been searching for any info about injecting CSS into QML WebView. Doc says QML WebView is a light version of Qt Widgets WebView. So I assume it is not possible to inject CSS into WebView. Is not it?

A possible solution is to inject css through javascript (as I pointed out in this solution):
import QtQuick 2.14
import QtQuick.Window 2.14
import QtWebView 1.14
Window {
visible: true
width: 640
height: 480
QtObject{
id: internals
property string css: "div { background-color: salmon;}"
}
WebView{
anchors.fill: parent
url: "https://stackoverflow.com"
onLoadingChanged: {
if(loadRequest.status == WebView.LoadSucceededStatus){
runJavaScript(loadCSS("foo", internals.css))
}
}
}
function loadCSS(name, css){
var script = "
(function() {
css = document.createElement('style');
css.type = 'text/css';
css.id = '" + name + "'; " +
"document.head.appendChild(css);
css.innerText ='" + css +"'" +
"})()";
return script;
}
}

Related

Unable to assign QList<QUrl> to QString

I'm encountering this error:
Unable to assign QList<QUrl> to QString
when trying to directly assign the result of drop.urls (obtained from DropArea's onDropped handler) to a Label's text property in Python.
Based on this doc, I tried Qt.resolvedUrl (to convert the type to a string) as shown in the following code. However, it results in an empty text label. The urls I'm working with start with "file:///".
What am I doing wrong?
import QtQuick.Window 2.2
import QtQuick 2.2
import QtQuick.Controls 2.14
Window {
id: root
visible: true
width: 640
height: 480
title: "Drop Test"
property var attachments: "empty"
DropArea {
id: dropArea;
anchors.fill: parent
onEntered: {
root.color = "gray";
console.log("You entered drop area")
drag.accept (Qt.LinkAction);
}
onDropped: {
console.log("You dropped " + drop.urls)
attachments = Qt.resolvedUrl(drop.urls)
}
}
Label {
id: mLableId
text: attachments
}
}
Assigning a URL to a string seems like such an obvious question, but if it has already been asked in the context of Python and Qt Quick, I have not find any such existing questions after searching since yesterday.
urls is a list of url so you will have to iterate and concatenate:
onDropped: {
console.log("You dropped " + drop.urls)
var str = ""
for(var i in drop.urls){
var url = drop.urls[i]
str += Qt.resolvedUrl(url)
}
attachments = str
}

QML: How to custom a component and use it in same file

Is there some syntax in QML to define and use a component in same file like this?
import QtQuick 2.6
import QtQuick.Window 2.2
var MyButton = Rectangle { width : 100; height : 60; color : "red" } // define it
Window {
visible: true
MyButton // use it
}
You can't really use an inline component directly, but you could use a loader:
Component {
id: btn
Button { width = 100; height = 60; background = "red" }
}
Loader {
sourceComponent: btn
}
Another downside is this way you cannot directly specify properties for the created object.
You can also use the component as a delegate for views and repeaters and such.
This is IMO one of the big omissions of QML.
Update: I just noticed this answer a bit out of date. Qt has had inline components for a while. Keep in mind they still have many bugs, there's stuff that will work in a regular component that will not work in an inlined one, especially around inline component properties in other inline components, property aliases and such. If you get some weird behavior, just remember to test it out standalone as well:
component Custom : Item { ...new stuff... }
... in the same source
Custom { }
Also note that it has to be put inside some qml object, it cannot be just a source code global as with JS files.
Powered by #dtech
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Component { id: btn; Rectangle { width : 100; height : 100; color : "red" } }
Column {
spacing: 10
Loader { sourceComponent: btn }
Loader { sourceComponent: btn; width: 300 }
Loader { sourceComponent: btn; width: 1000 }
}
}
And the result:

Creating QML Item via function defined in js library doesn't set attached properties

Note: the function in QtQuickUtils.js in the following testcase is just to abstract away the boilerplate involved in creating a QML object from a component URL.
Testcase:
main.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import "QtQuickUtils.js" as QtQuickUtils
Window {
visible: true
width: 640
height: 480
GridLayout {
anchors.fill: parent
id: container
columns: 1
}
Component.onCompleted: {
QtQuickUtils.createObjectFromComponent("qrc:///MyItem.qml", container, {
"Layout.fillWidth": true, "Layout.fillHeight": true
// "width": 100, "height": 100
});
}
}
MyItem.qml:
import QtQuick 2.0
Rectangle {
color: "red"
}
QtQuickUtils.js:
.import QtQml 2.0 as Qml
.pragma library
function createObjectFromComponent(componentUrl, parent, properties) {
var component = Qt.createComponent(componentUrl);
function finishCreation() {
console.log("finishCreation");
if (component.status === Qml.Component.Ready) {
var obj = component.createObject(parent, properties);
if (obj === null) {
console.log("Error creating object");
return;
}
console.log("success in creating obj");
} else if (component.status === Qml.Component.Error) {
console.log("Error loading component:", component.errorString());
return;
}
}
if (component.status === Qml.Component.Ready) {
finishCreation();
} else {
component.statusChanged.connect(function() { finishCreation(); });
}
}
This shows nothing (but "finishCreation" and "success in creating obj" are printed).
If I comment out the "Layout.fillWidth": true, "Layout.fillHeight": true line and uncomment the one after that, the item is displayed.
Also if I move the function from the JS file to main.qml, the item is displayed.
I tried moving the function from the JS file into a new qml file (tried both making this QML file singleton and not), but that didn't fix it.
Any idea what I'm doing wrong, and a proper fix?
The JS file doesn't know what a Layout is, therefore it cannot set it.
// in a JS file, shared or not
function foo(item) { console.log(item.Layout) } // undefined
If you were to:
.import QtQuick.Layouts 1.3 as L // you can't import without "as" in .JS
function foo(item) { console.log(item.L.Layout) } // and it suddenly works
So adding a minor inconvenience, you can achieve the goal by simply using:
"L.Layout.fillWidth": true, "L.Layout.fillHeight": true
However, it will work with a singleton without that extra step, because there you can do an "anonymous" import QtQuick.Layouts 1.3, and suddenly life is easy again. As I mentioned in the comments, I really don't see a reason to use a pragma library, unless maybe you are using some third party JS library. For everything else QML singletons are much more usable, you get the "shared" part, plus support for QML objects, properties with notifications, signals, bindings and all that good stuff.

Interaction between two QML files

I want to use some qml file(main.qml) but before that I need to get some authentication info from user(login, pass). So I want to do this in the second window(login.qml). I saw Qt.createComponent for opening second qml file, but I can't get any information from this type of window.
So how can I use some information from the second window in the first window?
Or how can I dynamically load these items(main.qml, login.qml) in the parent qml file?
So how can I use some information from the second window in the first
window?
This is just one way of doing it:
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
ApplicationWindow {
width: 400
height: 400
visible: true
ColumnLayout {
id: logItems
height: 200
Button {
id: loginButton
onClicked: loginForm.visible = true
text: "Log in"
}
Login {
anchors.top: loginButton.bottom
id: loginForm
visible: false
onLoginInfo: {
logInfo.text = "User:" + user + " password: " + password
}
}
}
Text {
id: logInfo
anchors.top : logItems.bottom
}
}
Login.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
Item {
signal loginInfo(string user, string password)
ColumnLayout {
RowLayout {
TextField {
id: user
}
TextField {
id: password
}
}
Button {
text: "Submit"
onClicked: loginInfo(user.text, password.text)
}
}
}
How can I dynamically load QML items from separate files/resources in
another QML file?
Qt.CreateComponent allows you to use JavaScript to dynamically create and use QML code from other files.

How to repaint Qt window or tabview in window

I want to click a button, then popup a dialog box. The Dialog is from Qml, has a label which is made of a variable number from another JavaScript file. When the variable changes, the dialog should repaint, the new number will display on the dialog box.
MyDlg.qml:
import "MyJs.js" as MyJs
Window {
id: myDialog
width: 300
height: 300
TabView {
id:myTabView
width: parent.width
height: parent.height
Tab {
title: "tab 1"
id: myTab1
text: MyJs.displayText
}
}
}
MyJs.js:
var displayText = "0";
Bindings don't work between QML and separate JavaScript files, only inline JavaScript expressions. I can't find any documentation that explicitly states this, but it has also been mentioned in previous answers.
If you don't want to step into C++, use a QML singleton (another answer to that question). Here's an example of how you'd use it:
MyDialog.qml
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Window 2.0
import "."
Window {
id: myDialog
width: 300
height: 300
TabView {
id:myTabView
width: parent.width
height: parent.height
Tab {
title: MySingleton.displayText
id: myTab1
Button {
text: "Click to change singleton property"
onClicked: MySingleton.displayText = "Hello"
}
}
}
}
MySingleton.qml
pragma Singleton
import QtQml 2.0
QtObject {
property string displayText: ""
}
qmldir
singleton MySingleton MySingleton.qml
More information:
http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType-3
http://doc.qt.io/qt-5/qtqml-modules-qmldir.html#contents-of-a-module-definition-qmldir-file

Resources