How to change textfield property from 'onEditingFinished' in Qt QML - qt

I am trying to alter the border color and width of my TextField depending on a vaildation check (javascript function that returns true if OK). If the user enters an invalid date of birth I wish to colour the Textfield box border red. Here is my qml definition:
TextField{
id: dob
placeholderText: qsTr("dd-mm-yyyy")
font.pixelSize: 14
Layout.fillWidth: true
style: TextFieldStyle {
textColor: "black"
background: Rectangle {
id:dobstyle
radius: 2
implicitWidth: 100
implicitHeight: 24
border.color: "black"
border.width: 1
}
}
inputMethodHints: Qt.ImhDate
onActiveFocusChanged: {
inputMask= "00-00-0000"
}
onEditingFinished: {
var datesplit = dob.text.split("-");
var jsDOB = new Date(datesplit[2],datesplit[1]-1,datesplit[0]);
var logichk = (isValidDateLogical(jsDOB));
var validchk = (isValidDate(dob.text))
if(!validchk || !logichk)
{
dobstyle.border.color="red"
dobstyle.border.width=3
}
}
selectByMouse: true
}
I am getting 'ReferenceError: dobstyle is not defined' Any idea how best to achieve this?

TextFieldStyle is a component, and what is inside does not exist for outside, what you must do is create a property in the TextField and make a binding, then only modify the property:
...
Layout.fillWidth: true
property color bordercolor: "black" // <---
property int borderwidth: 1 // <---
style: TextFieldStyle {
textColor: "black"
background: Rectangle {
id:dobstyle
radius: 2
implicitWidth: 100
implicitHeight: 24
border.color: dob.bordercolor // <---
border.width: dob.borderwidth // <---
}
}
...
onEditingFinished: {
var datesplit = dob.text.split("-");
var jsDOB = new Date(datesplit[2],datesplit[1]-1,datesplit[0]);
var logichk = isValidDateLogical(jsDOB));
var validchk = (isValidDate(dob.text))
if(!validchk || !logichk)
{
dob.bordercolor ="red" // <---
dob.borderwidth = 3 // <---
}
}

Related

QtLocation QML: MouseArea stop Updating on hover

I have a qml map application and I added a marker as MapQuickItem.
I have a lat lon display connected to map Mousearea, when I move the mouse on the map i read lat/lon in realtime, and all works well.
marker.qml is a mapquickItem with its Mousearea, when the mouse is "hover" the marker I write a string into the "labelLatLon" of the map.
ALL Works well, but when I exited the mouse from the marker, the "labelLatLon" is no more been updated with the mouse coordinate, it stops updating lat lon. I move the mouse but no more lat/lon update... It seems the main MouseArea stop "hearing" mouse onhover..
This is the snipping code to test: the CROSS IMAGE is from resources.
Rectangle {
id: mainWindow
visible: true
Plugin {
id: mapPlugin
name: "osm"
PluginParameter {
name: "osm.mapping.providersrepository.disabled"
value: "true"
}
PluginParameter {
name: "osm.mapping.providersrepository.address"
value: "http://maps-redirect.qt.io/osm/5.6/"
}
}
function addMarker(latitude,longitude) {
var Component = Qt.createComponent("qrc:///qml/marker.qml")
var item = Component.createObject(Item, {
coordinate: QtPositioning.coordinate(latitude, longitude)
})
map.addMapItem(item);
}
function setLatLonBox(coordinate) {
labelLatLon.text= "Lat: %1; Lon:%2".arg(coordinate.latitude).arg(coordinate.longitude)
}
Map {
id: map
gesture.enabled: true
copyrightsVisible : true
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(44.0, 9.3) // La Spezia
zoomLevel: 10
Component.onCompleted:addMarker(44.0, 9.3)
MouseArea {
id: mapMouseArea
property int pressX : -1
property int pressY : -1
property int jitterThreshold : 10
property int lastX: -1
property int lastY: -1
property var coordinate: map.toCoordinate(Qt.point(mouseX, mouseY))
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled : true
ColumnLayout {
id: layout
spacing: 5
z: 5 // ordine alto
Rectangle {
id: latLonArea
z: 5 // ordine alto
width: 320
height: 40
color: "grey"
opacity: 0.7
border.color: "black"
border.width: 1
Label {
id: labelLatLon
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.bold: true
color: "black"
text: "Lat: %1; Lon:%2".arg(mapMouseArea.coordinate.latitude).arg(mapMouseArea.coordinate.longitude)
}
}
}
}
}
}
and marker.qml
MapQuickItem {
id: marker
z: 2 //ordine basso
anchorPoint.x: marker.width / 2
anchorPoint.y: marker.height /2
property int idx
sourceItem: Image{
id: icon
source: "../symbols/Red_Cross.png"
sourceSize.width: 40
sourceSize.height: 40
opacity: markerMouseArea.pressed ? 0.6 : 1.0
}
MouseArea {
id: markerMouseArea
property int pressX : -1
property int pressY : -1
property int jitterThreshold : 10
property int lastX: -1
property int lastY: -1
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled : true
drag.target: marker
onEntered: {
var coordinate = map.toCoordinate(Qt.point(mouseX, mouseY));
setLatLonBox(coordinate);
}
}
}
Your problem is the label text property
text: "Lat: %1; Lon:%2".arg(mapMouseArea.coordinate.latitude).arg(mapMouseArea.coordinate.longitude)
Once you overwrite it with setLatLonBox, all the bindings on it are gone.
You have to re-set it after you exit the MQI mouse area (or you re-enter map mouse area)

is this possible to drag forward or reverse in qml charts, if possible can anybody share me the logic

ChartView {
id: chartView
width: 900
height: 250
// anchors.fill: parent
x: 250
y: 0
animationOptions: ChartView.NoAnimation
antialiasing: true
backgroundColor: "#1f1f1f"
Rectangle{
id: rectang
color: "black"
opacity: 0.6
visible: false
}
MouseArea{
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.AllButtons
onPressed: {
rectang.x = mouseX; rectang.y = mouseY; rectangle.visible = true
}
onMouseXChanged: {rectang.width = mouseX - rectang.x}
onMouseYChanged: {rectang.height = mouseY - rectang.y}
onReleased: {
if(!isTimerStoped)
{
// objMv.stopTimer();
isTimerStoped=true;
chartView.zoomIn(Qt.rect(rectang.x, rectang.y, rectang.width, rectang.height))
rectang.visible = false;
}
else
{
chartView.zoomReset();
isTimerStoped=false;
// objMv.startTimer();
}
}
}
ToolTip {
id: id_tooltip
contentItem: Text{
color: "#21be2b"
text: id_tooltip.text
}
background: Rectangle {
border.color: "#21be2b"
}
}
ValueAxis {
id: axisY1
min: -10
max: 100
gridVisible: false
color: "#ffffff"
labelsColor: "#ffffff"
labelFormat: "%.0f"
}
ValueAxis {
id: axisX
min: 0
max: 100
gridVisible: false
color: "#ffffff"
labelsColor: "#ffffff"
labelFormat: "%.0f"
// tickCount: 25
}
SplineSeries{
id: lineSeries1
name: "signal 1"
color: "white"
axisX: axisX
axisY: axisY1
onHovered: {
var p = chartView.mapToPosition(point)
var text = qsTr("x: %1, y: %2").arg(point.x).arg(point.y)
id_tooltip.x = p.x
id_tooltip.y = p.y - id_tooltip.height
id_tooltip.text = text
//id_tooltip.timeout = 1000
id_tooltip.visible = true
}
}
}
is this possible to drag forward or reverse in qml charts, if possible can anybody share me the logic
any mouse event i have to use?
please give us some idea
below was my code which only functioning for chart and tooltip, I require is if chart is going left to right if user wants to stop on that time and he wants see history of the graph by drag option

Dynamic validator for TextField / Change validator at runtime

I have small problem/requirement about validators, which i'm currently not able to implement.
I have a simple TextField (QtQuick.Control 2) which is decorated with different colors depending on a state. It should also use a different RegExpValidator (with another regular expression) depending on the current state.
Does anyone have an idea how i can switch/change/modify a RegExpValidator at runtime? (e.g. when a PushButton is pressed or onEditingFinished-Event is triggered)
My current qml code is:
import QtQuick 2.3
import QtQuick.Controls 2.2 as Quick
import QtQuick.Layouts 1.3
RowLayout {
id: layout
property color modeColor: "whitesmoke"
property color modeTextColor: "gray"
spacing: 0
Rectangle {
id: rect
Layout.fillWidth: true
Layout.minimumWidth: 100
Layout.preferredWidth: 100
Layout.maximumWidth: 100
Layout.preferredHeight: layout.implicitHeight
color: modeColor
border.width: 1
border.color: modeColor
Text {
id: recttext
anchors.centerIn: parent
text: "Enter key"
color: modeTextColor
}
}
Quick.TextField {
id: input
Layout.fillWidth: true
placeholderText: "Text"
background: Rectangle {
color: "whitesmoke"
border.width: 1
border.color: modeColor
}
validator: RegExpValidator { regExp: /.*:$/ }
onEditingFinished: {
recttext.text = input.text
if (layout.state == "keyinput") {
layout.state = "valinput"
// should change to another regExp validator
} else {
layout.state = "keyinput"
// should change to another regExp validator
}
input.clear()
}
Keys.onPressed: {
if (event.key == Qt.Key_Escape) {
layout.state = "keyinput"
recttext.text = "Enter key"
input.clear()
}
}
}
states: [
State {
name: "keyinput"
PropertyChanges { target: layout; modeColor: "whitesmoke"; modeTextColor: "gray" }
},
State {
name: "valinput"
PropertyChanges { target: layout; modeColor: "red"; modeTextColor: "white" }
}
]
state: "keyinput"
}
Best Regards, Chris
edit:
The endgoal is an inputline for entering key-value pairs (where i could use a specific RegExpValidator for a specific key.)
Here it should use RegExpValidator for entering a key e.g. "author:" + Pressing Enter (keyinput-mode)
#
After switching into the valueinput-mode, it should use another RegExpValidator:
btw. It's a desktop application :)
I hope this can help you!
property var valid1 : IntValidator { bottom:0; top: 2000}
property var valid2 : IntValidator { bottom:2000; top: 4000}
...
validator: if(condition) { valid1 }
else { valid2 }

QML-Move item with arrow keys

I'm using Qt version 5.6.0 from windows 8.1. I draw a shape that has 4 sides and the ability to drag its apexes.I want to move apexes of shape with arrow keys.I use this code but it does not work.
Point.qml:
Item {
id: root
signal dragged()
property alias color :point.color
Rectangle {
id:point
anchors.centerIn: parent
width: 20
height: 20
opacity: 0.2
MouseArea {
anchors.fill: parent
drag.target: root
onPositionChanged: {
if(drag.active) {
dragged()
}
}
onClicked: point.focus=true;
}
// Keys.onPressed: {
// console.log("move");
// if (event.key === Qt.Key_Left) {
// console.log("move left");
// event.accepted = true;
// point.x-=1;
// }
// }
// Keys.onRightPressed: {
// console.log("move");
// point.x+=1;
// }
// Keys.onLeftPressed: {
// console.log("move");
// point.x-=1;
// }
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Left: point.x-=1;
break;
case Qt.Key_Right: point.x+=1;
break;
case Qt.Key_Up: point.y-=1;
break;
case Qt.Key_Down: point.y+=1;
break;
}
}
focus: true;
}}
main.qml:
Point {
id: pointA
x: 50
y: 50
}
Point {
id: pointB
x: 250
y: 50
}
Point {
id: pointC
x: 250
y: 250
}
Point {
id: pointD
x: 50
y: 250
}
Item {
anchors.fill: parent
Canvas {
id: canvas
anchors.fill: parent
onPaint: {
var ctx = canvas.getContext('2d');
ctx.moveTo(pointA.x, pointA.y);
ctx.lineTo(pointB.x, pointB.y);
ctx.lineTo(pointC.x, pointC.y);
ctx.lineTo(pointD.x, pointD.y);
ctx.lineTo(pointA.x, pointA.y);
ctx.stroke();
}
Component.onCompleted: {
pointA.dragged.connect(repaint)
pointB.dragged.connect(repaint)
pointC.dragged.connect(repaint)
pointD.dragged.connect(repaint)
}
function repaint() {
var ctx = getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
requestPaint()
}
}
}
Update:
main.qml:
PhotoPreview {
id : photoPreview
anchors.fill : parent
//focus:true //visible
visible: capture
}
photopreview.qml:
Item {
id:mywin
property real defaultSize:mywin.width
property var currentFrame: undefined
property real surfaceViewportRatio: 1.5
focus: true
ScrollView {
anchors.fill: parent
flickableItem.interactive: true
frameVisible: true
highlightOnFocus: true
Flickable {
id: flick
anchors.fill: parent
contentWidth: parent.width
contentHeight: parent.height
property alias source :image.source
signal closed
Rectangle {
id: photoFrame
width: parent.width
height: parent.height
color:"transparent"
scale:defaultSize / parent.width
Behavior on scale { NumberAnimation { duration: 200 } }
Behavior on x { NumberAnimation { duration: 200 } }
Behavior on y { NumberAnimation { duration: 200 } }
smooth: true
antialiasing: true
Image {
id:image
anchors.fill: parent
fillMode: Image.PreserveAspectFit
smooth: true
}
PinchArea {
anchors.fill: parent
pinch.target: photoFrame
pinch.minimumRotation: -360
pinch.maximumRotation: 360
pinch.minimumScale: 0.1
pinch.maximumScale: 10
pinch.dragAxis: Pinch.XAndYAxis
property real zRestore: 0
onSmartZoom: {
if (pinch.scale > 0) {
photoFrame.rotation = 0;
photoFrame.scale = Math.min(mywin.width, mywin.height) / Math.max(image.sourceSize.width, image.sourceSize.height) * 0.85
photoFrame.x = flick.contentX + (flick.width - photoFrame.width) / 2
photoFrame.y = flick.contentY + (flick.height - photoFrame.height) / 2
zRestore = photoFrame.z
photoFrame.z = ++mywin.highestZ;
} else {
photoFrame.rotation = pinch.previousAngle
photoFrame.scale = pinch.previousScale
photoFrame.x = pinch.previousCenter.x - photoFrame.width / 2
photoFrame.y = pinch.previousCenter.y - photoFrame.height / 2
photoFrame.z = zRestore
--mywin.highestZ
}
}
MouseArea {
id: dragArea
hoverEnabled: true
anchors.fill: parent
drag.target: photoFrame
scrollGestureEnabled: false // 2-finger-flick gesture should pass through to the Flickable
onPressed: {
photoFrame.z = ++mywin.highestZ;
}
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
photoFrame.rotation += wheel.angleDelta.y / 120 * 5;
if (Math.abs(photoFrame.rotation) < 4)
photoFrame.rotation = 0;
} else {
photoFrame.rotation += wheel.angleDelta.x / 120;
if (Math.abs(photoFrame.rotation) < 0.6)
photoFrame.rotation = 0;
var scaleBefore = photoFrame.scale;
photoFrame.scale += photoFrame.scale * wheel.angleDelta.y / 120 / 10;
}
}
}
}
Point {
id: pointA
x: image.width/4
y: image.height/4
color: "blue"
}
Point {
id: pointB
x: image.width/2
y: image.height/2
color: "blue"
}
Point {
id: pointD
x: image.width/4
y: image.height/2
color: "red"
}
Point {
id: pointC
x: image.width/2
y: image.height/4
color: "red"
}
Item {
anchors.fill: parent
Canvas {
id: canvas
anchors.fill: parent
onPaint: {
//...
}
Component.onCompleted: {
//...
}
function repaint() {
//..
}
}
}
}
}
}}
You are using ScrollView that inherit FocusScope, which needs to have focus:true to forward it.
You instead are setting focus to PhotoPreview, which is plain Item that are not supposed to be focused.
So you need to simple remove focus:visible and set it to ScrollView.
The better fix is to remove that redundant Item, and leave ScrollView as PhotoPreview root item, with all its properties and signals.
In Point.qml the key event handler tries to change the x, y of point, however point is a rectangle that anchors.centerIn: parent. The key event handler should change root x, y instead:
//in Point.qml
case Qt.Key_Left:
root.x-=1;
break;
Point now changes it's position when keyboard event is triggered. Next, Point needs to notify the Canvas in main.qml to repaint. Point can emit dragged signal after changing it's x, y:
//in Point.qml
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Left:
root.x-=1;
dragged();
break;
case Qt.Key_Right:
root.x+=1;
dragged();
break;
case Qt.Key_Up:
root.y-=1;
dragged();
break;
case Qt.Key_Down:
root.y+=1;
dragged();
break;
}
}

How to limit the size of drop-down of a ComboBox in QML

I am using a ComboBox in QML and when populated with a lot of data it exceeds my main windows bottom boarder. From googling I have learned that the drop-down list of a ComboBox is put on top of the current application window and therefore it does not respect its boundaries.
Ideally I would want the ComboBox to never exceed the main applications boundary, but I can not find any property in the documentation.
A different approach would be to limit the number of visible items of the drop-down list so that it do not exceed the window limits for a given window geometry. I was not able to find this in the documentation either and I have run out of ideas.
Take a look to the ComboBox source code, the popup is of a Menu type and it doesn't have any property to limit its size. Moreover, the z property of the Menu is infinite, i.e. it's always on top.
If you Find no way but to use the ComboBox of Qt you can create two models one for visual purpose, I will call it visual model, you will show it in your ComboBox and the complete one , it will be the reference model. Items count in your VisualModel wil be equal to some int property maximumComboBoxItemsCount that you declare . you'll need o find a way that onHovered find the index under the mouse in the visualmodel if it's === to maximumComboBoxIemsCount you do visualModel.remove(0) et visualModel.add(referenceModel.get(maximum.. + 1) and you'll need another property minimumComboBoxIemsCount, same logic but for Scroll Up , I dont know if it will work. but it's an idea
I think there is no solution using the built-in component and you should create your own comboBox. You can start from the following code.
ComboBox.qml
import QtQuick 2.0
Item {
id: comboBox
property string initialText
property int maxHeight
property int selectedItem:0
property variant listModel
signal expanded
signal closed
// signal sgnSelectedChoice(var choice)
width: 100
height: 40
ComboBoxButton {
id: comboBoxButton
width: comboBox.width
height: 40
borderColor: "#fff"
radius: 10
margin: 5
borderWidth: 2
text: initialText
textSize: 12
onClicked: {
if (listView.height == 0)
{
listView.height = Math.min(maxHeight, listModel.count*comboBoxButton.height)
comboBox.expanded()
source = "qrc:/Images/iconUp.png"
}
else
{
listView.height = 0
comboBox.closed()
source = "qrc:/Images/iconDown.png"
}
}
}
Component {
id: comboBoxDelegate
Rectangle {
id: delegateRectangle
width: comboBoxButton.width
height: comboBoxButton.height
color: "#00000000"
radius: comboBoxButton.radius
border.width: comboBoxButton.borderWidth
border.color: comboBoxButton.borderColor
Text {
color: index == listView.currentIndex ? "#ffff00" : "#ffffff"
anchors.centerIn: parent
anchors.margins: 3
font.pixelSize: 12
text: value
font.bold: true
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.height = 0
listView.currentIndex = index
comboBox.selectedItem = index
tools.writePersistence(index,5)
comboBoxButton.text = value
comboBox.closed()
}
}
}
}
ListView {
id: listView
anchors.top: comboBoxButton.bottom
anchors.left: comboBoxButton.left
width: parent.width
height: 0
clip: true
model: listModel
delegate: comboBoxDelegate
currentIndex: selectedItem
}
onClosed: comboBoxButton.source = "qrc:/Images/iconDown.png"
Component.onCompleted: {
var cacheChoice = tools.getPersistence(5);
listView.currentIndex = tools.toInt(cacheChoice)
selectedItem = listView.currentIndex
comboBoxButton.text = cacheModel.get(selectedItem).value
}
}
ComboBoxButton.qml
import QtQuick 2.0
Item {
id: container
signal clicked
property string text
property alias source : iconDownUp.source
property string color: "#ffffff"
property int textSize: 12
property string borderColor: "#00000000"
property int borderWidth: 0
property int radius: 0
property int margin: 0
Rectangle {
id: buttonRectangle
anchors.fill: parent
color: "#00000000"
radius: container.radius
border.width: container.borderWidth
border.color: container.borderColor
Image {
id: image
anchors.fill: parent
source: "qrc:/Images/buttonBackground.png"
Image {
id: iconDownUp
source: "qrc:/Images/iconDown.png"
sourceSize.height:20
sourceSize.width: 20
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
id:label
color: container.color
anchors.centerIn: parent
font.pixelSize: 10
text: container.text
font.bold: true
}
MouseArea {
id: mouseArea;
anchors.fill: parent
onClicked: {
container.clicked()
buttonRectangle.state = "pressed"
startTimer.start()
}
}
Timer{
id:startTimer
interval: 200
running: false;
repeat: false
onTriggered: buttonRectangle.state = ""
}
states: State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges { target: image; scale: 0.7 }
PropertyChanges { target: label; scale: 0.7 }
}
transitions: Transition {
NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
}
}
}
I've used it in some software of mine, hence it is possible that It could not work "out of the box". I use it like this:
ComboBox{
id:cacheChoice
initialText: "None"
anchors.top: baseContainer.top
anchors.topMargin: 2
anchors.right: baseContainer.right
maxHeight: 500
listModel: cacheModel
onExpanded: {
cacheChoice.height = 500
}
onClosed: {
cacheChoice.height = 20
}
}
In case you are working with ComboBox from Qt Quick Controls 2, here's the source code for it:
https://github.com/qt/qtquickcontrols2/blob/5.12/src/imports/controls/ComboBox.qml
Based on that, this override of the behavior works to limit the height to something reasonable:
myComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
return Math.min(250, myComboBox.popup.contentItem.contentHeight);
});
It is possible to access the hidden MenuStyle within the ComboBoxStyle component. There you can use all the things and hidden things you have within a MenuStyle, including its maximum height.
The thing looks roughly like this.
Not pretty but it works well enough.
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
ComboBox {
id: comboBox
style: ComboBoxStyle {
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: 400
__menuItemType: "comboboxitem" //not 100% sure if this is needed
}
}
As it came up resonantly in our team, here is a updated version of the idea shown above. The new version restricts the size automatically to the size of your application.
ComboBox {
id: root
style: ComboBoxStyle {
id: comboBoxStyle
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: Math.max(55, //min value to keep it to a functional size even if it would not look nice
Math.min(400,
//limit the max size so the menu is inside the application bounds
comboBoxStyle.control.Window.height
- mapFromItem(comboBoxStyle.control, 0,0).y
- comboBoxStyle.control.height))
__menuItemType: "comboboxitem" //not 100% sure if this is needed
} //Component __dropDownStyle: MenuStyle
} //style: ComboBoxStyle
} //ComboBox

Resources