How to repaint Qt window or tabview in window - qt

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

Related

How to make some reusable QML object, which can inject another QML object?

How to make some reusable QML object, which can inject another object?
I've ever tried to use Component & Loader , but seems not what I want. (It still encapsulate the whole QML type and lacks of elasticity, hard to reuse)
Usage example:
Card.qml
import QtQuick 2.0
import QtQuick.Layouts 1.3
Rectangle {
default property var innerObject
property string titleText: "[Hello Untitled Title]"
id: root
color: "#fff"
ColumnLayout {
anchors.fill: parent
Rectangle {
id: header
height: 10
width: parent.width
color: "#666"
RowLayout {
Text { text: titleText; color: "#fff" }
}
}
// How to inject innerObject in here ?
}
}
main.qml
import QtQuick 2.0
import QtQuick.Layouts 1.3
Card {
titleText: "Image Information"
ColumnLayout { /* .......*/ } // innerObject
}
Card {
titleText: "Image Viewer"
Rectangle { /* .......*/ } // innerObject
}
The answer I linked works like this:
Main.qml
Card {
titleText: "Image Viewer"
innerObject: Rectangle {
Component.onCompleted: {
console.log(parent.objectName)
}
}
}
Card.qml
Rectangle {
property string titleText: "[Hello Untitled Title]"
default property alias innerObject : innercolumn.children
id: root
color: "#fff"
ColumnLayout {
id: innercolumn
objectName: "column"
anchors.fill: parent
Rectangle {
id: header
height: 10
width: parent.width
color: "#666"
RowLayout {
Text { text: titleText; color: "#fff" }
}
}
}
}
I also want to suggest a solution based on default property and reparenting:
The Item which can embed another Item:
MyItem.qml
import QtQuick 2.7
import QtQuick.Layouts 1.2
Rectangle {
id: root
default property Item contentItem: null
border {
width: 1
color: "#999"
}
ColumnLayout {
anchors.fill: parent
Rectangle {
Layout.fillWidth: true
height: 30
color: "lightgreen"
}
Item {
id: container
Layout.fillWidth: true
Layout.fillHeight: true
}
}
onContentItemChanged: {
if(root.contentItem !== null)
root.contentItem.parent = container;
}
}
Can be used as below:
main.qml
import QtQuick 2.7
import QtQuick.Window 2.0
Window {
visible: true
width: 600
height: 600
MyItem{
width: 400
height: 400
anchors.centerIn: parent
Text {
text: "Hello!"
anchors.centerIn: parent
}
}
}
But I still agree with #ddriver that Loader is the best solution for this case
It is not mandatory that you use a Loader with a component. You can just go:
Loader {
source: "Something.qml"
}
When the source is something that can be loaded synchronously, you can directly use the loader's item for stuff like bindings, without worrying about whether or not it is created. If you load over network, you have to delay the bindings until the item is completed and use either a Binding element or Qt.binding() to do it respectively in a declarative or imperative manner.
In your case, a loader would be appropriate, and the property for the inner dynamic object outta be a Component. This way you can populate it either with an inline component, or with Qt.createComponent() from existing source.
property Component innerObject
...
innerObject: Component { stuff }
...
innerObject: Qt.CreateComponent(source)
Of course, there are even more advanced ways to do it, for example, the "generic QML model object" I have outlined here. It allows to quickly and easily create arbitrary data structure trees both declaratively and imperatively, and since the object is also a model, you can directly use listviews or positioner elements with repeaters to layout the gui without actually writing the UI code each and every time.
Also, from your main.qml code example - you cannot have more than one root element in a qml file.
Edit: The default property approach actually works if the element is moved to its own qml file, so also basically you could just:
default property alias innerObject: innerColumn.children
where innerColumn is the id of your ColumnLayout. Also, innerObject could be whatever legal name, since as a default property, it will not actually be used.
There is also the option to not use a default property, which is useful when the root item still needs to have its own children, but still have the ability to redirect declarative objects to be children of a sub-object:
property alias content: innerColumn.children
// and then
content: [ Obj1{}, Obj2{}, Obj3{} ] // will become children of innerColumn

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 add File name at recently opened file File menu

I want to write a QML app that adds the latest opened files from FileDialog to the main menu. I'm currently following this documentation example but the problem is that I can't understand how to pass the file name of an opened file.
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar : MenuBar
{
Menu
{
id: recentFilesMenu
Instantiator
{
model: recentFilesMenu
MenuItem
{
text: model.fileName // I neeed to pass name of opned file here
}
onObjectAdded: recentFilesMenu.insertItem(index, object)
}
title: "File"
MenuItem
{
text: "Open"
onTriggered: fileDialog.visible = true
}
MenuItem
{
text: "Exit"
}
}
}
FileDialog
{
id: fileDialog
title: "Oooopen"
onAccepted:
{
// Here is problem
recentFilesMenu.objectName = fileDialog.fileUrls
}
}
}
According to the documentation, Instantiator accepts the most common types of models - both C++ and QML ones. In the documentation example such an information is missing, probably to not force the usage of a specific one. An actual implementation can relay on ListModel. In this case the model would expose a fileName role used as the actual menu item.
Following this approach the result would be something like the following code. Mind that the urls are prepended with information which can be easily removed (see for instance this answer).
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar : MenuBar {
Menu {
id: recentFilesMenu
title: "File"
MenuItem {
text: "Open"
onTriggered: fileDialog.visible = true
}
MenuSeparator { }
Instantiator {
model: ListModel { id: files }
MenuItem { text: fileName }
onObjectAdded: recentFilesMenu.insertItem(index, object)
onObjectRemoved: recentFilesMenu.removeItem(object)
}
MenuSeparator { visible: files.count > 0 }
MenuItem { text: "Exit" }
}
}
FileDialog {
id: fileDialog
title: "Open"
onAccepted: {
for(var i = 0; i < fileDialog.fileUrls.length; ++i)
files.append({fileName: fileDialog.fileUrls[i]})
}
}
}
There is a widgets version of this kind of feature:
http://doc.qt.io/qt-5/qtwidgets-mainwindows-recentfiles-example.html
But the descriptive help is non-existent. Looking through the code here:
http://doc.qt.io/qt-5/qtwidgets-mainwindows-recentfiles-mainwindow-cpp.html
You will see that it stores a QStringList of a list of recent files in QSettings, and loads everything into an array of QActions.
Follow through the mainWindow.cpp for all the references to
enum { MaxRecentFiles = 5 };
QAction *recentFileActs[MaxRecentFiles];
And you should have some good ideas about how to do something similar in QML.
Hope that helps.
You probably have a finite number of recent files that you want to display. That being said, you can implement x number of MenuItems and set the text to QStringList[i] implemented as a Q_PROPERTY in a C++ class. Then, you can manipulate the QStringList elements(size, order) on your C++ class.

ReferenceError while trying to call function of Item within Tab

I tried to call functions of qml file from another qml file user component id but i am facing some issues. could some one help me out of this.
here is my code.
Browser.qml:
import QtQuick 2.0
Item {
function callme(message) {
console.log(message)
}
}
main.qml:
import QtQuick 2.3
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.0
ApplicationWindow {
visible: true
width: 640
height: 100
TabView {
id: tabView
width: 640
height: 50
Tab {
width: 100
title: "Sample1.html"
onVisibleChanged: {
browser1.callme("hi")
}
Browser {
id: browser1
}
}
Tab {
width: 100
title: "Sample2.html"
onVisibleChanged: {
browser2.callme("bye")
}
Browser {
id: browser2
}
}
}
}
Error reported:
ReferenceError: browser1 is not defined
If you want access to items inside Tab control, you have to use its item property. I have changed your signal handler and it works:
...
onVisibleChanged: {
item.callme("hi")
}
Browser{
id: browser1
}
...
Tab control inherits from Loader component. It takes its children as delegate and they are only created with the tab is activated. Most of the behavior is the same then the Loader component.
Experimentation for the record
What happend if we define two or more components inside a Tab? Loader component only accepts a delegate and the component created is accessed by item property. Tab component maps children property to delegate and you can define more than one, but I realized that only the last child is created.

How to use QtQuick.Window element in Qt5 and QML?

I recently installed the Qt5 RC2 for Mac OS X and started developing some QML applications. After looking at the new elements, I especially wanted to try the Window and Screen Element. (http://qt-project.org/doc/qt-5.0/qtquick/qmlmodule-qtquick-window2-qtquick-window-2.html)
So I set the imports at the top of the file like this:
import QtQuick 2.0
import QtQuick.Window 2.0
The import is found, but I can use neither Window nor Screen. Everytime I type Screen or Window an error appears which says "Unknown component (M300)"
Has anyone an idea what the problem is?
Sometimes QtCreator doesn't recognize some types/properties, mainly the ones that were not present in Qt4.x, but that doesn't mean you can't use them, so yes, 'Window' is unknown just like properties 'antialiasing', 'fontSizeMode' or 'active' are, but you can use them, here is an example of use for QtQuick.Window 2.0 :
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: win1;
width: 320;
height: 240;
visible: true;
color: "yellow";
title: "First Window";
Text {
anchors.centerIn: parent;
text: "First Window";
Text {
id: statusText;
text: "second window is " + (win2.visible ? "visible" : "invisible");
anchors.top: parent.bottom;
anchors.horizontalCenter: parent.horizontalCenter;
}
}
MouseArea {
anchors.fill: parent;
onClicked: { win2.visible = !win2.visible; }
}
Window {
id: win2;
width: win1.width;
height: win1.height;
x: win1.x + win1.width;
y: win1.y;
color: "green";
title: "Second Window";
Rectangle {
anchors.fill: parent
anchors.margins: 10
Text {
anchors.centerIn: parent
text: "Second Window"
}
}
}
}
You just need to have a Window item as root object, and you can embed other Window items into it.

Resources