I am using QQmlApplicationEngine and therefore have one root element and Item-s. Because I have one root file, to which all the methods connected from C++ (but still accessible in whole .qml directory) I can't make the other Item-s be independet from the dimension settings of the root. So if I make minimumwidth of the value 300, so the other Item-s going to have the same minimumWidth too.
The quesstion is, whether it's possible to make them independent from main.qml and be able set others dimensions.
Here is some code to make you idea what I am talking about:
main.cpp:
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.rootContext()->setContextProperty("mqttClient",mqttClient);
engine.rootContext()->setContextProperty("math",math);
engine.load(url);
main.qml:
Window {
id:root
width: 400
height: 300
minimumWidth: 960
minimumHeight: 420
visible: true
title: qsTr("Dampfmaschine Client")
StackView {
id: stackView
initialItem: "LoginPage.qml"
anchors.fill: parent
}
Component{
id: login
LoginPage{}
}
Component{
id: measure_page2
MeasurePage2{}
}
function load_page(page){
switch(page){
case 'MeasurePage':
stackView.push(measure_page2);
break;
}
}
}
You can use the Binding type in QML to set a binding which can be optional. I propose to add some property in your pages, for example:
LoginPage {
property int windowWidth : 200
property int windowHeight : 300
}
Then in main, you put the Binding
Window {
id:root
width: 400
height: 300
minimumWidth: 960
minimumHeight: 420
visible: true
title: qsTr("Dampfmaschine Client")
Binding {
target: root
property: "width"
value: stackView.currentItem.windowWidth
}
Binding {
target: root
property: "height"
value: stackView.currentItem.windowHeight
}
StackView {
id: stackView
initialItem: "LoginPage.qml"
anchors.fill: parent
}
Component{
id: login
LoginPage{}
}
Component{
id: measure_page2
MeasurePage2{}
}
function load_page(page){
switch(page){
case 'MeasurePage':
stackView.push(measure_page2);
break;
}
}
}
This way, the window will always be bound to the current item of the stackView and you don't have to look after it.
Optionally you can also use implicitWidth and implicitHeight from the pages, leading to even less maintenance (but possibly some headaches with weird pages)
Related
Why the below code is not working when clicked on cancel button ie., parameterValueText.text is not setting to value 0.
I am assuming both val and parameterValueText.text are binded eachother. If I am wrong, please correct me
Please find below steps
Step 1: Edit a value in Text Input, for ex: 1
Step 2: on Keyboard accepted, parameterValueText.onAccepted is called
Step 3: Click on Save, saveBtn.onClicked is called
Step 4: Click on Cancel, cancelBtn.onClicked is called but the parameterValueText.text is not changed to value 0.
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.VirtualKeyboard 2.1
ApplicationWindow {
id: window
width: 480
height: 272
visible: true
title: qsTr("Keyboard")
property int val: 0
Column {
Item {
id: itemId
height: 20
width: window.width
Rectangle{
width: 100
height: itemId.height
border.color:"black"
TextInput {
id: parameterValueText
text: val //Assuming text and val are binded ??
inputMethodHints: Qt.ImhDigitsOnly
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
onAccepted: console.log("Value = ", parameterValueText.text)
}
}
}
Row {
spacing: 10
Button {
id: saveBtn
text: "Save"
onClicked: console.log("Save = ", parameterValueText.text)
}
Button {
id: cancelBtn
text: "Cancel"
onClicked: val = 0 //Why the value is not changing in parameterValueText.text ?
}
}
}
InputPanel
{
id:inputPanel
y:parent.height
width: window.width
//Background for Virtual Keyboard
Component{
id:keyboardBackground
Rectangle{
color:"#f4f6f3"//ScreenConfiguration.backGroundCanvas
}
}
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: parent.height - inputPanel.height
}
PropertyChanges {
target: inputPanel
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 200
easing.type: Easing.InOutQuad
}
}
}
Component.onCompleted: {
keyboard.style.keyboardBackground = keyboardBackground; // the keyboard background
}
}
}
main.cpp
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
Add below change in .pro file for enabling virtual keyboard
QT += quick virtualkeyboard
The console log ouputs are below
qml: Value = 1, val = 0
qml: Save = 1, val = 0
The binding isn't maintained after the user changes the text using the TextInput (as commenters suggested).
To achieve similar results to what you were attempting you could set val in the TextInput's onAccepted slot, and similarly update both val and parameterValueText.text in the onClicked slots for your buttons as appropriate. For example:
TextInput {
...
onAccepted: {
val = text
}
}
//...
Button {
id: cancelBtn
text: "Cancel"
onClicked: {
val = 0;
parameterValueText.text = val;
}
}
This won't maintain the initial bindings because the values are assigned in imperative javascript, but should achieve resetting values as expected.
As continuation on the answer of #dabbler, you can also recreate the binding in the onAccepted handler, but yeah it's still weird.
parameterValueText.text = Qt.binding(function() { return val; })
As another solution, you could have the val property bind to the text of the input (which then starts with "0"):
readonly property int val: parseInt(parameterValueText.text)
But that only works if the val property is intended to be used as readonly (otherwise you will loose the binding again). In case you want the val property to be set from outside, you could do the following:
onValChanged: parameterValueText.text = val
Note that this is assignment, not binding!
I'm working on a project that gets news with API. I can clearly get the news from api and load them to the listview.
I simplify the code for telling my problem clearly.
Here is a 2 questions...
1 - I need to slide this list from top to the bottom basic sliding animation with given time. (eg. y from 0 to en of list with 5secs). The important point is the item count of the list can be changeable.
2 - When the animation reachs to the end of the list, I need to see the first item after the last item. But it has to be like this; after the last item of list, the first item has to shown( like infinite list) while the sliding process going on.
Here are my codes;
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStringList news = { "news01",
"news02",
"news03",
"news04",
"news05",
"news06",
"news07",
"news08",
"news09",
"news10",
"news11",
"news12",
"news13",
"news14",
"news15",
"news16",
"news17",
"news18",
"news19",
};
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("listNews",news);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
Window {
id:pencere
visible: true
width: 640
height: 480
title: qsTr("Hello World")
color: "black"
ListView{
id: newsListView
implicitWidth: parent.width
implicitHeight: parent.height
model:listNews
spacing: 5
delegate: Rectangle {
id: delegateBackground
color:"#505051"
radius: 10
width: parent.width
height: contentContainer.height + 20
Item {
id: contentContainer
width: parent.width - 20
height: column.height
anchors.centerIn: delegateBackground
RowLayout {
width: parent.width
Rectangle {
id: newsicon
width: 16
height: 16
color: "steelblue"
Layout.alignment: Qt.AlignTop
}
ColumnLayout {
id: column
Layout.fillWidth: true
spacing: 100
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
id: messageText
text: modelData
wrapMode: TextEdit.WordWrap
verticalAlignment: index %2 == 0 ? Text.AlignBottom : Text.AlignTop
color: "white"
}
}
}
}
}
}
}
For the first question you could add something like the following to your ListView. This will trigger an animation if you press the arrow key up/down. It isn't perfect, but it explains how to use NumberAnimations. The key to move the ListView content is the property contentY. If you want to scroll all the way to the bottom of the news feed you could calculate the position by using contentHeight of the ListView and the Window height.
ListView {
id: newsListView
property bool scrollUp: false
property bool scrollDown: false
focus: true
Keys.onUpPressed: newsListView.scrollUp = true
Keys.onDownPressed: newsListView.scrollDown = true
NumberAnimation on contentY {
running: newsListView.scrollDown
from: 0
to: newsListView.contentHeight
duration: 1000
onFinished: newsListView.scrollDown = false
}
NumberAnimation on contentY {
running: newsListView.scrollUp
from: newsListView.contentHeight
to: 0
duration: 1000
onFinished: newsListView.scrollUp = false
}
...
}
For the first question as proposed by #iam_peter, you can try using NumberAnimation to animate scrolling. For second query, in my opinion you can try researching on PathView as it is much easier to get circular list behavior in PathView without cumbersome index calculations.
Also, please have a look at this topic ListView Scrolling.
I'm using QQmlApplicationEngine то load my main window.
QQmlApplicationEngine engine;
engine.load("GUI.qml");
Where GUI.qml is my main GUI file. How I can create and destruct a new window from code? As far as I can see, if I write engine.load("SecondWindow.qml");, how I can close it? Or I should create and destruct such objects from QML itself?
Option 1: You can do it from QML, see this sample:
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: "Window 1"
CheckBox {
id: cb
text: "Show Window #2"
}
Loader {
active: cb.checked
sourceComponent: Component {
ApplicationWindow { // Or "SecondWindow"
visible: true
width: 640
height: 480
title: "Window 2"
}
}
}
}
Option 2: Also, you can control it from C++ side, for example like this:
QML
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: "Window 1"
Loader {
active: showWindowFlag
// Instead of "sourceComponent" you can use
// source: "SecondWindow.qml"
sourceComponent: Component {
ApplicationWindow {
visible: true
width: 640
height: 480
title: "Window 2"
}
}
}
}
C++
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
bool showWindowFlag = false;
engine.rootContext()->setContextProperty("showWindowFlag", showWindowFlag); // !!!!
auto timer = new QTimer(&engine); // Parent will delete timer
QObject::connect(timer, &QTimer::timeout, [&](){
showWindowFlag = !showWindowFlag;
engine.rootContext()->setContextProperty("showWindowFlag", showWindowFlag);
});
timer->setInterval(1000);
timer->setSingleShot(false);
timer->start();
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
I have a form where i've done a custom field that only accepts the top, right and bottom arrows.
But to accept the tab navigation i'm using the following chain of functions:
nextItemInFocusChain().forceActiveFocus()
The problem is that this is working on windows but not on mac...
I've a formulary to illustrate the problem where next to that "code text field" i have a comboBox where i want, that when the user clicks on tab in the "code text field" to navigate to.
It seems that it only navigates to other textFields, and a spinBox, like i have on the example seems to have a textField as a contentItem.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
Column{
width:parent.width
spacing:10
TextField {
id:textField
KeyNavigation.tab: spinBox1
implicitHeight: 30
font.bold: true
}
SpinBox {
id: spinBox1
width: 100
height: 30
editable: true
Component.onCompleted: contentItem.KeyNavigation.tab = userCodeField
}
PanelCodeField {
id: userCodeField
width: 100
height: 30
KeyNavigation.tab: comboBox
}
ComboBox {
id:comboBox
anchors.topMargin: 10
model: [ "Banana", "Apple", "Coconut" ]
KeyNavigation.tab: spinBox2
}
SpinBox {
id: spinBox2
width: 100
height: 30
editable: true
Component.onCompleted: contentItem.KeyNavigation.tab = textField
}
}
}
}
PanelCodeField.qml
import QtQuick 2.0
PanelTextField {
height: 479.9
visible: true
maximumLength: 5
font.pointSize: 12
property bool jumpOnTab: false
Keys.onPressed: {
var c
switch (event.key) {
case Qt.Key_Up:
c = String.fromCharCode(0x25B2)
break
case Qt.Key_Down:
c = String.fromCharCode(0x25BC)
break
case Qt.Key_Right:
c = String.fromCharCode(0x25B6)
break
case Qt.Key_Tab:
if(jumpOnTab)
nextItemInFocusChain().nextItemInFocusChain().forceActiveFocus()
else
nextItemInFocusChain().forceActiveFocus()
event.accepted = true
break
default:
event.accepted = true
break
}
if (!event.accepted) {
var s = text.concat(c)
text = s.substr(Math.max(0,s.length-maximumLength), maximumLength)
event.accepted = true
}
}
}
PanelTextField.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
TextField {
property var linkedData
implicitHeight: 30
font.bold: true
implicitWidth:parent.width
}
Am i doing something wrong for the mac os x, or is there a workaround?
Open System Preferences > Keyboard > Shortcuts and select All Controls. By default macOS only allows tab navigation between "Text boxes and lists only".
I want to create an animation to move (or resize) my application Window built using QML.
I have the following code (most of it created by default when we create a QT quick Controls application:
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
MainForm.ui.qml
import QtQuick 2.6
import QtQuick.Controls 1.5
import QtQuick.Layouts 1.3
Item {
width: 640
height: 480
property alias button1: button1
property alias button2: button2
RowLayout {
anchors.centerIn: parent
Button {
id: button1
text: qsTr("Press Me 1")
}
Button {
id: button2
text: qsTr("Press Me 2")
}
}
}
main.qml
import QtQuick 2.6
import QtQuick.Controls 1.5
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: mainWindow
visible: true
width: 640
height: 480
title: qsTr("Hello World")
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("&Open")
onTriggered: console.log("Open action triggered");
}
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
MainForm {
anchors.fill: parent
button1.onClicked: Qt.quit();
button2.onClicked: state = "other";
}
transitions: [
Transition {
from: "*"
to: "other"
NumberAnimation { properties: "x,y"; easing.type: Easing.InOutQuad; duration: 2000 }
}
]
states: [
State {
name: "other"
PropertyChanges {
target: mainWindow
x: x + 200
}
}
]
MessageDialog {
id: messageDialog
function show(caption) {
messageDialog.text = caption;
messageDialog.open();
}
}
}
With this code, I was simply trying to move my window 200 pixels to the right. When I try to run it, I get qrc:/main.qml:42 Cannot assign to non-existent property "states". I believe that is weird, because when I start typing "states" and choose to auto-complete, it builds to me the whole structure, so I thought it should exist...
I'm new to QML, and I'm not fully familiar with the several options of animations that exists. This one I've tried based on the example that comes with QT creator (animation.pro - code from transitions).
I believe it should be quite simple, right? Could you help me with this?
Qt Creator has a feature where you can insert code snippets using certain keywords. You can see which snippets are available by going to Tools > Options > Text Editor > Snippets.
Snippets will show up as red in the auto completion popup, and regular properties (or types, as is the case below) will show up as green:
So, ApplicationWindow doesn't have a states property. If you're ever in doubt, go to the documentation for the type you're interested in (e.g. http://doc.qt.io/qt-5/qml-qtquick-controls-applicationwindow.html) and click on the link that says "List of all members, including inherited members". This will show you all properties, functions, etc. belonging
to that type.
If you want to animate the window's position, you can use a NumberAnimation without using states:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.5
ApplicationWindow {
id: window
width: 400
height: 400
visible: true
NumberAnimation {
id: xyAnimation
target: window
properties: "x,y"
easing.type: Easing.InOutQuad
duration: 2000
to: 500
}
Button {
text: "Start"
onClicked: xyAnimation.start()
}
}
state is a property in Item, however ApplicationWindow is not an Item. To add state/transition to an non-Item type, use StateGroup:
ApplicationWindow {
id: mainWindow
//your code...
MainForm {
button2.onClicked: { myWindowStates.state = "other";}
}
StateGroup {
id: myWindowStates
transitions: [
Transition {
from: "*"; to: "other"
NumberAnimation {
properties: "x,y"; easing.type: Easing.Linear;
duration: 2000
}
}
]
states: [
State {
name: "other"
PropertyChanges {
target: mainWindow
x: mainWindow.x + 200
explicit: true //Remember to set this
}
}
]
}
}
Remember to set PropertyChange.explict to true, otherwise the state behavior is wrong and your window will disappear after the transition finished.