I have multiple QML files. I just want to link them. Then I want to return to my home page from every page I navigate to. I am using loader in every page
Here is my code.
import QtQuick 1.1
Rectangle{
id:welcome
width:480
height:272
Loader{
id:loader
focus:false
anchors.fill: parent
}
gradient: Gradient {
GradientStop { position: 0.0; color: "light blue" }
GradientStop { position: 1.0; color: "blue" }
}
Text{
text:"\n\t\tPRESS ENTER"
font.bold:true
font.pointSize: 17
}
Button {
id: wel
height:30;
x:parent.width/2-30
y:parent.height/2-30
focus:true
border.color:"black"
opacity: activeFocus ? 1.0 : 0.5
Text{
text:"WELCOME"
anchors.horizontalCenter:wel.horizontalCenter;
anchors.verticalCenter:wel.verticalCenter;
}
Keys.onReturnPressed: {
wel.focus=false
loader.focus=true;
anchors.fill=parent
loader.source="Home.qml";
//welcome.visible=false;
}
}
}
My question is whenever I click on the button its loading new file. But the welcome page will not go. Both the file's will be overlapped. When I did visible=false complete UI will go. I get a white screen.
Can any one help me fix this problem?
How to load other file?
To load multiple pages you will need to use Connections element to handle signal from the page you have loaded.
Loader {
id: windowLoader
source: "Welcome.qml"
focus: true
property bool valid: item !== null
}
Button {
Keys.onReturnPressed: {
windowLoader.source = "Page1.qml"
}
}
Connections {
ignoreUnknownSignals: true
target: windowLoader.valid? windowLoader.item : null
onChangeToPage2: { windowLoader.source = "Page2.qml" }
onPageExit: { windowLoader.source = "Welcome.qml" }
}
And from the pages you load you will need to emit the "changeToPage2" and "pageExit" signals. The emitted signals will be handled by the Connections element.
Page1.qml:
Rectangle {
id: page1
signal changeToPage2
signal pageExit
Button {
id: exitButton
Keys.onReturnPressed: { page1.pageExit() }
}
}
Related
Currently I have a window openning in the following way:
property variant win
Button {
id: testButton
MouseArea {
onClicked: {
var component = Qt.createComponent("test.qml");
win = component.createObject(testButton);
win.show();
}
}
}
Is it ok to create a window like this or there is a better way to do it (from QML, not from C++)?
When I close this additional window (just by clicking "x" button), I want to connect it to another event (for example, changing color of the button). How to do it?
Thanks.
It is usually nicer to have it more declarative. If you want your button to only open one window, the usage of a Loader might be right for you.
I think this is what you want, as you store it in one variable, and if you click the button multiple times, you would lose access to your instance. If you need a larger amount of Windows created by the same Button, you might use a ListModel and a Instantiator to create the instances.
With the Loader this might look like this:
Button {
id: ldbutton
onClicked: winld.active = true
Rectangle {
id: ldindic
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: height
color: winld.active ? 'green' : 'red'
}
Loader {
id: winld
active: false
sourceComponent: Window {
width: 100
height: 100
color: 'green'
visible: true
onClosing: winld.active = false
}
}
}
In this code is also already the answer to your second question: The signal you are looking for is called closing - connect to it to do what ever is necessary.
In the case of the Loader it is necessary to unload the window, so it can be loaded again later, maybe. If you have the window created by a Instantiator, you need to remove the corresponding index from the Instantiator's ListModel.
This might look like this:
Button {
id: rpbutton
onClicked: rpmodel.append({})
text: 'Open Windows ' + rpmodel.count
ListModel {
id: rpmodel
}
Instantiator { // from QtQml 2.0
model: rpmodel
delegate: Window {
width: 100
height: 100
color: 'blue'
visible: true
onClosing: rpmodel.remove(index)
}
}
}
In your code you could connect to it, either by using a Connection-object, that connects to your property win, or by changing the JS onClicked like so:
onClicked: {
var component = Qt.createComponent("test.qml");
win = component.createObject(testButton);
win.closing.connect(function() { console.log('do something') })
win.show();
}
I am a beginner in QMl and have worked more on StackWidget in QT C++.In QML i am confused to use stackView and have written following code:
Window {
visible: true
width: 640
height: 480
title: qsTr("Stack view")
MainForm {
StackView {
id: stackView
x: 0
y: 0
width: 360
height: 360
initialItem: page1
Rectangle {
id: page1
//anchors.fill: parent
color: "lightgreen"
Button {
id: buttonPage1
text: "back to 2"
anchors.centerIn: parent
onClicked: {
stackView.pop() //**Is THIS CORRECT**
stackView.push(page2) //**Is THIS CORRECT**
}
}
TextEdit {
id: te1
width: 105
height: 40
text: "enter"
}
}
Rectangle {
id: page2
//anchors.fill: parent
color: "lightblue"
Button {
id: buttonPage2
text: "back to 1"
anchors.centerIn: parent
onClicked: {
stackView.pop() //**Is THIS CORRECT**
}
}
TextEdit {
id: te2
width: 109
height: 29
text: "enter"
}
}
}
}
}
Below are the questions:
In StackWidget i was using setCurrentIndex to set the desired page and I know that in QML i should use push and pop. In that case how to use push and pop to navigate between page1 and page2 based on some selection. ?
Initially, can I load all the pages to the stackView?
How to save the content in the page when I pop an item from stackView?
I know that I will not exactly answer your question on how to use the StackView, that is because I think you don't want to have a StackView following your description.
The use-case of a StackView is, when you have the pages - as the names suggests - on a stack. If you only want to switch between pages, where it is not determinable, which one is logically below another, the StackView is not what you want, and you might want to consider a SwipeView.
In the SwipeView the pages coexist in a side-by-side manner. Since Qt 5.9 they have a interactive property with which you might disable the swipe behaviour.
Here you can choose the page you want to show by setting the currentIndex.
However, the SwipeView will create its pages as needed, to reduce the memory and CPU load (effectively disabling bindings of unloaded pages). This might result in data loss, if the data is not stored in a model outside the page itself.
If you want to have all the pages loaded at the same time, and you only want to switch the visible one, you might go with a simple custom component:
Item {
property int currentIndex
Page1 { visible: parent.currentIndex === 0 }
Page2 { visible: parent.currentIndex === 1 }
Page3 { visible: parent.currentIndex === 2 }
...
}
Or you go like:
MyView.qml
Item {
id: root
property int currentIndex: 0
default property Item newContent
onNewContentChanged: {
newContent.parent = root
newContent.visible = Qt.binding(bindingsClosure(root.children.length - 1))
}
function bindingsClosure(index) { return function() { return root.currentIndex === index } }
}
main.qml
MyView {
Page1 { }
Page2 { }
Page3 { }
}
I want to ask simple question for quick solution. I have ListView and Button in main.qml and Component in closingtaskdelage.qml. Here is file main.qml:
ListView
{
id: grid
delegate:mydelegate
model: mymodel
spacing: 5
orientation:ListView.Vertical
Layout.preferredWidth: width_commmon
Layout.preferredHeight: height_body
Layout.maximumHeight: 800
Layout.alignment: Qt.AlignHCenter
// highlightFollowsCurrentItem: false
focus: true
ClosingTaskModel
{
id:mymodel
}
ClosingTaskDelegate
{
id:mydelegate
}
}
Button
{
id:finishbutton
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: width_commmon
Layout.preferredHeight:height_endbutton
text: "İş Emrini Tamamla"
onClicked:
{
// console.log((grid.children["border1"]).id);
}
}
and file ClosingTaskDelegate.qml:
Component
{
id:component1
Rectangle
{
signal sendMenuValuestoJS
height: 200
width: 200
Text
{
id:text1;
text:header;
height:parent.height/2
// width: parent.width
anchors.horizontalCenter:parent.horizontalCenter;
font.pointSize:20;
color:"red";
// height:parent.height/2
// width: parent.width
}
}
}
Don't care about details. My main problem is: When onClickButton() in main.qml occurs, signal sendMenuValuestoJS() must be triggered. This signal must send string value as parameter (i.e. send Text).
I can't access signal from main.qml. I have defined signal in the Component, but I get following runtime QML error:
Component objects cannot declare new signals.
How do I connect signal to some function?
What about to to use Connections?
Here is the idea,
/* main.qml */
Rectangle {
width: 200
height: 200
MouseArea {
id: mouse
anchors.fill: parent
}
Click {
id: click
targetto: mouse
}
}
/* Click.qml */
Item {
property variant targetto
Connections {
target: targetto
onClicked: console.log("CLICKED!");
}
}
Here is example from official Qt webiste in Signal Handler Attributes chapter.
I have a GridView with a delegate that is supposed to use a Loader to load and display components which are defined in the same QML file.
Let's say I have a GridView like this:
GridView {
delegate: Rectangle {
Loader { sourceComponent: model.pageContents }
}
model: ListModel {
ListElement { /* how do I reference any of the components defined below from here, so the Loader can actually load and display it... ? */ }
}
}
Component {
id: page_01
Rectangle {
color: "red"
// Page contents for page 1 go here.
}
}
Component {
id: page_02
Rectangle {
color: "red"
// Page contents for page 2 go here.
}
}
I know I can create QML objects and components from Strings, external files and URLs. But I'd like to ideally do something like this:
ListModel {
ListElement { pageContents: page_01 }
ListElement { pageContents: page_02 }
}
I'd prefer to keep everything in a single QML file, so I can easily transfer and store it on the device without having to worry about resolving external dependencies, etc.
How do I refer to components in the same QML file from within ListElements?
Due to ListElement values limitation you cannot just put item id here. But you easily can use some external storage, for example property to store pointers to your pages. In example below I use array with pages ids and an index to wanted page as ListElement data:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
visible: true
width: 600
height: 600
GridView {
id: grid
anchors.fill: parent
property var pages: [page_01, page_02]
model: ListModel {
ListElement { pageIndex: 0 }
ListElement { pageIndex: 1 }
}
delegate: Loader { sourceComponent: grid.pages[pageIndex] }
}
Component {
id: page_01
Rectangle {
color: "red"
width: 100
height: 100
Component.onCompleted: console.log("page_01 was created")
}
}
Component {
id: page_02
Rectangle {
color: "blue"
width: 100
height: 100
Component.onCompleted: console.log("page_02 was created")
}
}
}
I encounter a problem which is that the pop-up window cannot get the focus when it is shown. I tried to use the activefocus function in main window, but it doesn't work. It is supposed that if I press the enter key, the pop-window will be closed. How can I get the focus for the pop-up window? Thanks.
...
GridView {
id:grid_main
anchors.fill: parent
focus: true
currentIndex: 0
model: FileModel{
id: myModel
folder: "c:\\folder"
nameFilters: ["*.mp4","*.jpg"]
}
highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
delegate: Item {
width: 100; height: 100
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
text: fileName
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
}
}
}
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show();
subWindow.forceActiveFocus();
event.accepted = true;
grid_main.focus = false;
}
}
}
Window {
id: subWindow
Keys.onPressed: {
if (event.key == 16777220) {//press enter
subWindow.close();
}
}
}
...
Let's start with some basics:
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show()
...
event.accepted = true
}
}
Not to mention how error-prone it is, just for the sake of readability, please don't hard-code enum values like 16777220. Qt provides Qt.Key_Return and Qt.Key_Enter (typically located on the keypad) and more conveniently, Keys.returnPressed and Keys.enterPressed signal handlers. These convenience handlers even automatically set event.accepted = true, so you can replace the signal handler with a lot simpler version:
Keys.onReturnPressed: {
subWindow.show()
...
}
Now, the next thing is to find the correct methods to call. First of all, the QML Window type does not have such method as forceActiveFocus(). If you pay some attention to the application output, you should see:
TypeError: Property 'forceActiveFocus' of object QQuickWindowQmlImpl(0x1a6253d9c50) is not a function
The documentation contains a list of available methods: Window QML type. You might want to try a combination of show() and requestActivate().
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
Then, you want to handle keys in the sub-window. Currently, you're trying to attach QML Keys to the Window. Again, if you pay attention to the application output, you should see:
Could not attach Keys property to: QQuickWindowQmlImpl(0x1ddb75d7fe0) is not an Item
Maybe it's just the simplified test-case, but you need to get these things right when you give a testcase, to avoid people focusing on wrong errors. Anyway, what you want to do is to create an item, request focus, and handle keys on it:
Window {
id: subWindow
Item {
focus: true
Keys.onReturnPressed: subWindow.close()
}
}
Finally, to put the pieces together, a working minimal testcase would look something like:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
width: 300
height: 300
visible: true
GridView {
focus: true
anchors.fill: parent
// ...
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
}
Window {
id: subWindow
Item {
focus: true
anchors.fill: parent
Keys.onReturnPressed: subWindow.close()
}
}
}
PS. Key events rely on focus being in where you expect it to be. This may not always be true, if the user tab-navigates focus elsewhere, for example. Consider using the Shortcut QML type for a more reliable way to close the popup.