Undefined element when tab is not active - qt

When I try to access a son of a Tab element, if it is not active QML throws an error saying is undefined.
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.0
ApplicationWindow {
TabView {
Tab {
id: mytab1
}
Tab {
id: myTab2
Rectangle {
//(...)
}
}
}
Connections {
target: eventManager
onData: {
var scene = myTab2.children[0];
console.log(scene);
}
}
}
So, if myTab2 is active, I can see in the console QQuickItem_QML_15(0x27f1e2e0). If myTab2 is not active, then qml throws TypeError: Cannot read property 'children' of undefined.
Why is undefined if the tab is not active and how can I fix it?
Thanks!

From the Qt documentation website, I've found a solution.
Tabs are lazily loaded; only tabs that have been made current (for
example, by clicking on them) will have valid content. You can force
loading of tabs by setting the active property to true:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.0
ApplicationWindow {
TabView {
Tab {
id: mytab1
active: true
}
Tab {
id: myTab2
active: true
Rectangle {
//(...)
}
}
}
Connections {
target: eventManager
onData: {
var scene = myTab2.children[0];
console.log(scene);
}
}
}
So, I've added the property active: true in both tabs, and It works fine!

A TabView doesn't create its content items until a tab is activated.
Your example begins at tab 1, at this point the rectangle in tab 2 doesn't exist, so you get undefined. If you activate tab 2 the rectangle will be created, and then if you go back to tab 1 you will not get undefined.
A Tab inherits a Loader, and comes with an active property. I suppose there is an optimization that exists back in the Loader component to delay loading until the element becomes visible. If you set active: true in your Tab it will be loaded before the tab is activated. Note that this will not make the tab view open with the second tab active.
Tab {
id: t2
active: true
Rectangle {
}
}

Related

qt qml errors with QtQuick Dialogs 1

I"m using Qt 5.15.2. Below is the qml snippet that is failing for me.
Upon launching everything is ok. However when i try to open new windows, 1 window will be ok, but second (and subsequent) one will start showing something like this in the console.
bin/archdatadir/qml/QtQuick/Dialogs/DefaultFileDialog.qml:99: TypeError: Value is null and could not be converted to an object
bin/archdatadir/qml/QtQuick/Controls/SplitView.qml:630: TypeError: Property 'terminateItemConnections' of object QObject_QML_93(0x600003397f20) is not a function
bin/archdatadir/qml/QtQuick/Controls/SplitView.qml:630: TypeError: Property 'terminateItemConnections' of object QObject_QML_93(0x600003394500) is not a function
bin/archdatadir/qml/QtQuick/Dialogs/DefaultFileDialog.qml:436: TypeError: Cannot read property 'bottom' of undefined
....
If I remove the FileDialog definition completely, which is actually unused here, then these errors disappear completely. Would there be any suggestions on what I am doing incorrectly?
thanks!
import QtQuick 2.15
import QtQuick.Controls 2.4
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: root
onClosing: {
console.log("closing window")
}
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&New")
icon.name: "document-new"
onTriggered: newDocument()
}
}
}
FileDialog {
id: openDialog
title: "Open"
folder: shortcuts.home
onAccepted: {
console.log("open file dialog")
}
}
function newWindow() {
console.log("new window")
var component = Qt.createComponent("reproducer.qml")
var window = component.createObject()
return window
}
function newDocument() {
console.log("new document")
var window = newWindow()
window.show()
}
}
VK

QML progress bar is NOT showing up on UI

I have this QML progress bar:
import QtQuick.Controls 2.0 as QQC20
Item {
QQC20.ProgressBar {
id: progressbar_id
visible: false // even if "true", the progress bar does NOT show up on UI
from: editorScene.progressbarMin
to: editorScene.progressbarMax
value: editorScene.progressbarVal
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
I can confirm that the progress bar value and visibility are changed by the methods onValueChanged and onVisibleChanged.
However, the problem is that the progress bar does NOT show up on the UI! How can I actually show the progress bar on the UI? Can anybody give me a hint?
Right now, all you're doing is creating a QML type which you can use as part of your API. To actually see it, you need to create an instance of it under a ApplicationWindow or Window (or anything else equivalent, e.g. Canvas or Felgo's GameWindow).
There are two ways you can accomplish this. You can
Directly add your item as a child of a window.
Put your item in a separate file, and create an instance of that file under a window.
Lé Code
Method 1: Directly Adding as Child
Directly insert your codeblock as a child of an ApplicationWindow.
// Main.qml
import QtQuick 2.0 // for `Item`
import QtQuick.Window 2.0 // for `ApplicationWindow`
import QtQuick.Controls 2.0 // as QQC20 // no need to label a namespace unless disambiguation is necessary
ApplicationWindow {
width: 480 // set the dimensions of the application window
height: 320
// here's your item
Item {
anchors.centerIn: parent // place in centre of window
ProgressBar {
id: progressbar_id
anchors.horizontalCenter: parent.horizontalCenter // horizontally align the progress bar
from: 0 // don't know what editorScene is
to: 100 // so I'm using raw values
value: 5
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
// side note: I'm not getting any output from this handler
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
// provide user-interaction for changing progress bar's value
MouseArea {
anchors.fill: parent // clicking anywhere on the background
onClicked: progressbar_id.value += 5; // increments the progress bar
// and triggers onValueChanged
}
}
Method 2: Using a Separate File
Save your item into a new qml file.
// MyProgressBar.qml
import QtQuick 2.0 // for `Item`
import QtQuick.Controls 2.0 // for `ProgressBar`
// here is your item, it has grown up to be in a file of its own 🚼
Item {
property alias value: progressbar_id.value // for user-interaction
ProgressBar {
id: progressbar_id
anchors.horizontalCenter: parent.horizontalCenter // centre horizontally
from: 0
to: 100
value: 5
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
Note that you still need the import statements.
Then call it from a window in Main.qml. We'll use an ApplicationWindow here.
// Main.qml
import QtQuick 2.0
import QtQuick.Window 2.0 // for `ApplicationWindow`
// import "relative/path/to/progressbar" // use this if MyProgressBar.qml is not in the same folder as Main.qml
ApplicationWindow {
width: 480
height: 320
MyProgressBar {
id: progressbar_id
}
MouseArea {
anchors.fill: parent
onClicked: progressbar_id.value += 5;
}
}
If your qml files aren't in the same directory, make sure you add an import "relative/path" at the top of the Main.qml file among the other import statements.
For example, if
Your Qml project is in /Users/Lorem/Project,
The full path to your Main.qml is /Users/Lorem/Project/qml/Main.qml, and
The full path to your MyProgressBar.qml is /Users/Lorem/Project/qml/myControls/MyProgressBar.qml...
Then use import "myControls" in Main.qml to import the items from the myControls subdirectory. Remember, you only need to import the directory, not the file itself.
Result
This is what the result resembles when I run it from a macOS.
At startup.
After 3 clicks on the background.
There is also console/debug output after each click:
Progressbar value changed: 10
Progressbar value changed: 15
Progressbar value changed: 20

How to keep a single instance of the window?

I have the next QML:
import Qt.labs.platform 1.0
SystemTrayIcon {
visible: true
iconSource: "qrc:/icons/ic_tray.png"
menu: Menu {
MenuItem {
text: qsTr("Settings")
onTriggered: {
// Don't create a new object if it exists, just show
var settings = Qt.createComponent("main.qml")
var form = settings.createObject(this)
form.show()
}
}
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit() // Just hide an existing
}
}
}
How to create main.qml one time only and after just show/hide?
P.S. I'm learning Qt including QtQuick 2 only
Depending on how your application is structured, the best way could be to pass in the window that the tray icon shall control as a property from "further up" your user interface structure.
First, extend your tray icon component and add a "window" property to it:
import QtQuick 2.9
import QtQuick.Window 2.2
import Qt.labs.platform 1.0
SystemTrayIcon {
id: trayIcon
// this property holds the window the tray icon controls:
property Window window
visible: true
iconSource: "qrc:/icons/ic_tray.png"
menu: Menu {
MenuItem {
text: qsTr("Settings")
onTriggered: {
trayIcon.window.show();
}
}
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit() // Just hide an existing
}
}
}
Now, you could instantiate your tray icon e.g. in your main window like this:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: mainWindow
width: 800
height: 600
TrayIcon { window: mainWindow }
}
In this case, the tray icon would control the main window itself; however, you can easily create a single instance of a settings window within the main window and pass that one to the tray icon.
You can create the component in the onCompleted.
SystemTrayIcon {
visible: true
iconSource: "qrc:/icons/ic_tray.png"
menu: Menu {
MenuItem {
text: qsTr("Settings")
property var form
onTriggered: {
form.show()
}
Component.onCompleted: {
// Don't create a new object if it exists, just show
var settings = Qt.createComponent("Test.qml")
form = settings.createObject(this)
}
}
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit() // Just hide an existing
}
}
}

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.

Can't access QML item by id inside SplitView

I've began learning QML and I'm getting the following error:
ReferenceError: chatTextArea is not defined
I have a global function that does something on an item within the same QML file, by id.
For some reason I can't access via the ID of my TextArea, or any item inside of the SplitView. But I am able to manipulate the properties of TabView and each Tab.
My broken code:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
Rectangle {
id: lobby
function appendChatMsg(msg) {
chatTextArea.append(msg) //causes: ReferenceError: chatTextArea is not defined
}
TabView {
id: frame
Tab { //I CAN access this item via ID.
id: controlPage
SplitView {
anchors.fill: parent
TableView {
Layout.fillWidth: true
}
GridLayout {
columns: 1
TextArea { //This item I CANNOT access via ID.
id: chatTextArea
Layout.fillHeight: true
Layout.fillWidth: true
}
TextField {
placeholderText: "Type something..."
Layout.fillWidth: true
}
}
}
}
}
}
Any idea why chatTextArea is out of scope of my function? Thanks in advance.
Change the starting portion of your code to smth like this:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
Rectangle {
id: lobby
function appendChatMsg(msg) {
controlPage.chatArea.append(msg) //causes: ReferenceError: chatTextArea is not defined
}
TabView {
id: frame
Tab { //I CAN access this item via ID.
id: controlPage
property Item chatArea: item.chatArea
SplitView {
property Item chatArea: chatTextArea
Reason this works, is that Tab turns out to behave like a Loader (per the docs), loading whichever Component you give it; thus, the SplitView in your code is a Component specification, and that component is instantiated by the Tab in a separate QML context (parented to that of the document root item). Which is why everything inside that context can see things up the scope chain (like the appendMessage() function), but not the reverse :)

Resources