How to get NavigationKey.up value in qml and c++? - qt

Im beginner in qml. I have set KeyNavigation.up (in item up) to an id of another item (down).
Why i could't retrieve KeyNavigation.up like this in qml ?!
var x = down.KeyNavigation.up
UPDATE:
This is an example. why i couldn't get a.KeyNavigation.up ?
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
TextField {
id: up
KeyNavigation.down: down
}
TextField {
id: down
KeyNavigation.up: up
}
function fun(){
var x = up.KeyNavigation.down
}
}

Your code actually works.
I don't see where you're calling fun(), but if you for example add the line onActiveFocusItemChanged: fun() after visible: true you will see the x variable is OK.
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
visible: true
width: 500; height: 500
onActiveFocusItemChanged: fun()
function fun(){
var x = up.KeyNavigation.down
print("x = up.KeyNavigation.down: " + x.text)
var y = x.KeyNavigation.up
print("y = x.KeyNavigation.up: " + y.text)
}
TextField {
id: up
y: 50
KeyNavigation.down: down
}
TextField {
id: down
y: 100
KeyNavigation.up: up
}
}

In qml, items have properties. And you should declare properties. Without your whole code i cannot really give you a more detailed answer. Just have a look at this

In QML, ids must begin with a lowercase letter. If you're using Qt Creator, you'd see this problem as soon as you typed it. When you run the program, you'd see this error:
IDs cannot start with an uppercase letter
So, either you typed your code in manually for this question (bad idea) and you mistyped the ids, or you didn't run it, which would be strange.
Using a lowercase id works:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
Component.onCompleted: print(a.KeyNavigation.up)
Item {
id: A
KeyNavigation.up: b
}
Item {
id: b
}
}
Output:
qml: QQuickItem(0x596dcc9180)

Related

Disable mouse wheel for QML Slider

I want to be able to scroll Flickable with mouse wheel (or two fingers on touchpad) without changing Sliders it may containt.
Sample code and result application:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
ApplicationWindow {
id: rootWindow
visible: true
width: 400
height: 200
title: qsTr("Hello World")
ScrollView {
anchors.fill: parent
flickableItem.flickableDirection: Flickable.VerticalFlick
Column {
Repeater {
model: 40
Slider {
width: rootWindow.width * 0.9
}
}
}
}
}
Looks like there was some attempt to fix this in the past, but not successful.
EDIT: this relates to Controls 1.x only, as controls doesn't seem to have this issue starting from 2.0 version.
You can place MouseAreas on the sliders to steal the mouse wheel event.
Something like this:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
ApplicationWindow {
id: rootWindow
visible: true
width: 400
height: 200
title: qsTr("Hello World")
ScrollView {
id: _scrollview
anchors.fill: parent
flickableItem.flickableDirection: Flickable.VerticalFlick
Column {
Repeater {
model: 40
Slider {
width: rootWindow.width * 0.9
property int scrollValue: 10
MouseArea {
anchors.fill: parent
onWheel: {
//check if mouse is scrolling up or down
if (wheel.angleDelta.y<0){
//make sure not to scroll too far
if (!_scrollview.flickableItem.atYEnd)
_scrollview.flickableItem.contentY += scrollValue
}
else {
//make sure not to scroll too far
if (!_scrollview.flickableItem.atYBeginning)
_scrollview.flickableItem.contentY -= scrollValue
}
}
onPressed: {
// forward mouse event
mouse.accepted = false
}
onReleased: {
// forward mouse event
mouse.accepted = false
}
}
}
}
}
}
}
Using the onWheel - event to forward any scrolling to the ScrollView. The other mouse events, such as clicking, can be forwarded to the parents (in this case the sliders) by setting mouse.accepted = false; for any mouse event you wish to have forwarded.
Edit: Oh, I just saw now that you don't want any changes in the sliders contents. You can also to try to place a MouseArea all over the ScrollView and do the same forwarding.
The easiest, if feasible for you, would probably be, to change from QtQuick.Controls 1.4 which can be either considered deprecated, not maintained, or low-performing to the new QtQuick.Controls 2.0
In this version your issue has been adressed.
To adress your need of QtQuick.Controls 1.4 we'll import the QtQuick.Controls 2.0 with an alias:
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0 as NewCtrl
Column {
Slider {
id: oldslider // old slider from QtQuick.Controls 1.4 with your issue
width: 500
height: 250
}
NewCtrl.Slider {
id: newsli // new slider without your issue. Both side by side
width: 500
height: 30
wheelEnabled: false // use this to enable or disable the wheel
}
}
Of course you can also alias the old controls and use the new one as the basic... Or alias both. As you like
Hack from here did it for me.
Slider {
id: slider
Component.onCompleted: {
for (var i = 0; i < slider.children.length; ++i) {
if (slider.children[i].hasOwnProperty("onVerticalWheelMoved") && slider.children[i].hasOwnProperty("onHorizontalWheelMoved")) {
slider.children[i].destroy()
}
}
}
}
This may not work with other controls (e.g. ComboBox).

QtCreator shows 'Build Problems' when instantiating Object in property-assignment

I want to have an QtObject accessible through a property of another QtObject.
In this concrete case, a QtObject serves as a model for various Items.
TestObj.qml
QtObject {
id: root
property real position: 0
readonly property NumberAnimation animation: NumberAnimation { // <-*1
target: root
property: 'position' //<-*2
from: 0
to: 1
duration: 10000 * (1 - position)
onRunningChanged: console.log('running', running)
}
}
main.qml
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
width: 700
height: 700
visible: true
Button {
x: 600 * model.position
y: 400 * model.position
onClicked: model.animation.running = true
}
TestObj {
id: model
}
}
It runs flawless, as expected. However the QtCreator finds some "Build Problems" at the two marked lines (<-*) in TestObj.qml.
*1: It expects the token: ','
*2: It expects the token: '}'
What's up with the QtCreator (4.0.2, Windows)? Is it right or wrong?
When I remove the readonly, the problems disappear, but I want it to be readonly.
For me it would be sufficient, to have the running-property exposed, but as QtObjects can't have (visual) children for it has no default property, I can't see any other way to do this.
I don't see this with Creator 4.1.0, so it could be that it is a bug that has been fixed.

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.

QML: move to next control in form

How can I move focus from one control to next one inside QML form?
By default it works with Tab button but I need to change it to Enter.
All the control are ordered with Gridlayout with 2 columns.
I've defined a new component, TextFieldMoveOnReturn.qml
import QtQuick 2.0
import QtQuick.Controls 1.1
TextField {
Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus()
}
If you use this one instead of TextField, you get the required behaviour
edit a better solution: define a new component GridLayoutNextOnReturn.qml
import QtQuick 2.0
import QtQuick.Layouts 1.1
GridLayout {
Keys.onReturnPressed: {
for (var i = 0; i < children.length; ++i)
if (children[i].focus) {
children[i].nextItemInFocusChain().forceActiveFocus()
break
}
}
}
and use normal TextField inside - works like a charm
In order to make it more robust and flexible, you should make same behaviour
for Tab and Enter/Return keys.
Handle keyPressed event and use KeyNavigation.tab instead of nextItemInFocusChain to focus next element as follow:
import QtQuick 2.12
import QtQuick.Controls 1.12
Column {
TextField {
id: field1
KeyNavigation.tab: field2
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
TextField {
id: field2
KeyNavigation.tab: field3
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
TextField {
id: field3
KeyNavigation.tab: field1
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
}
So you controlled order of focus and users can use both tab and return keys interchangeably which results better UX.
Whenever you want to change order, just change KeyNavigation.tab values :)
Note: I extremely suggest you to avoid using nextItemInFocusChain because of future changes and flexibility
You can use onEditingFinished:
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 400
height: 400
GridLayout {
anchors.fill: parent
columns: 2
Label {
text: "Name"
}
TextField {
onEditingFinished: addressEdit.focus = true
}
Label {
text: "Address"
}
TextField {
id: addressEdit
}
}
}
Use Keys.onReturnPressed and forceActiveFocus()
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 400
height: 400
GridLayout {
anchors.fill: parent
columns: 2
Label {
text: "Name"
}
TextField {
Keys.onReturnPressed: organizationEdit.forceActiveFocus()
}
Label {
text: "Organization"
}
TextField {
id: organizationEdit
Keys.onReturnPressed: addressEdit.forceActiveFocus()
}
Label {
text: "Address"
}
TextField {
id: addressEdit
}
}
}

Resources