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)
Related
In my code every marker that I clicked are selected(turn into green from red). I want just 1 can change. When I click another marker the marker I clicked before turns red again. Or When I click an empty area the marker I clicked before turns red again.
In qml my Item's code:
Component {
id: hazardous_img
MapQuickItem {
id: hazardousitem
anchorPoint.x: image.width/4
anchorPoint.y: image.height
coordinate: position
property bool isClicked: false
MouseArea {
anchors.fill: parent
onDoubleClicked: {
mainwindow.hazardousIconClicked(mapview.toCoordinate(Qt.point(mouse.x,mouse.y)))
}
onClicked: {
if (isClicked === false) {
image.source = "qrc:/grn-pushpin.png"
isClicked = true
} else {
image.source = "qrc:/red-pushpin.png"
isClicked = false
}
}
}
sourceItem: Image {
id: image
source: "qrc:/red-pushpin.png"
}
}
}
In QML this is usually done with using a ButtonGroup, but as you're not using AbstractButtons you need to write it yourself. Here is my solution for it.
I've used the ListModel to not only store the coordinates of each marker, but also a selected flag which is set to false by default. In the delegate I'm using the selected data role to show if a marker is selected or not.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
ApplicationWindow {
id: window
width: 640
height: 480
visible: true
title: qsTr("Map")
ListModel { id: markerModel }
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MouseArea {
anchors.fill: parent
onDoubleClicked: {
var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
var jsonObject = JSON.parse(JSON.stringify(coordinate))
jsonObject["selected"] = false
markerModel.append(jsonObject)
}
onClicked: map.deselectAll()
}
MapItemView {
model: markerModel
delegate: markerDelegate
}
function deselectAll() {
for (var i = 0; i < markerModel.count; ++i)
markerModel.setProperty(i, "selected", false)
}
Component {
id: markerDelegate
MapQuickItem {
id: markerItem
required property int index
required property real latitude
required property real longitude
required property bool selected
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: QtPositioning.coordinate(latitude, longitude)
sourceItem: Rectangle {
id: waypointMarker
width: 20
height: 20
radius: 20
border.width: 1
border.color: mouseArea.containsMouse ? "red" : "black"
color: markerItem.selected ? "red" : "gray"
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: {
map.deselectAll()
markerModel.setProperty(markerItem.index, "selected", true)
}
}
}
}
}
}
I came up with yet another solution without looping over all items in the model. It just stores the index of the selected marker in a dedicated property. This has the drawback that if the model order changes the index can become invalid, also potential multi selection is hard to handle, but on the other hand it is faster because it doesn't need to iterate over all items.
I experimented a lot with DelegateModel, it seems to be a perfect match if one could use it in combination with MapItemView, because of the groups and the attached properties like inGroupName.
After that I've tried ItemSelectionModel, but it seems it is only intended to be used in combination with a view, e.g. TreeView. I couldn't find out how to generate a QModelIndex in QML without a TreeView.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
title: qsTr("Map")
property int selectedMarker: -1
Map {
id: map
anchors.fill: parent
plugin: Plugin {
id: mapPlugin
name: "osm"
}
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MouseArea {
anchors.fill: parent
onDoubleClicked: {
var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
markerModel.append(JSON.parse(JSON.stringify(coordinate)))
}
onClicked: root.selectedMarker = -1
}
MapItemView {
model: ListModel { id: markerModel }
delegate: markerDelegate
}
Component {
id: markerDelegate
MapQuickItem {
id: markerItem
required property int index
required property real latitude
required property real longitude
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: QtPositioning.coordinate(latitude, longitude)
sourceItem: Rectangle {
id: waypointMarker
width: 20
height: 20
radius: 20
border.width: 1
border.color: mouseArea.containsMouse ? "red" : "black"
color: markerItem.index === root.selectedMarker ? "red" : "gray"
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: root.selectedMarker = markerItem.index
}
}
}
}
}
I promise this is the last answer on that question.
This one is using an ItemSelectionModel and a few undocumented functions, e.g. ListModel.index(row, col).
itemSelectionModel.hasSelection is used in the color binding to trigger a reevaluation in order to call isRowSelected and set the color accordingly whenever the selection has changed.
If the user clicks on the background the clear() is called to clear the selection.
I think out of the three this is the best solution. It can be easily upgraded to allow multi selection as shown below. Also the ItemSelectionModel can be used by other views to show the data and selection.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
import QtQml.Models 2.15
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
title: qsTr("Map")
Map {
id: map
anchors.fill: parent
plugin: Plugin {
id: mapPlugin
name: "osm"
}
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MouseArea {
anchors.fill: parent
onDoubleClicked: function(mouse) {
markerModel.append(map.toCoordinate(Qt.point(mouse.x, mouse.y)))
}
onClicked: itemSelectionModel.clear()
}
MapItemView {
model: ListModel { id: markerModel }
delegate: markerDelegate
}
ItemSelectionModel {
id: itemSelectionModel
model: markerModel
}
Component {
id: markerDelegate
MapQuickItem {
id: markerItem
required property int index
required property real latitude
required property real longitude
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: QtPositioning.coordinate(latitude, longitude)
sourceItem: Rectangle {
id: waypointMarker
width: 20
height: 20
radius: 20
border.width: 1
border.color: mouseArea.containsMouse ? "red" : "black"
color: {
itemSelectionModel.hasSelection
return itemSelectionModel.isRowSelected(markerItem.index) ? "red" : "gray"
}
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: itemSelectionModel.select(markerModel.index(markerItem.index, 0),
ItemSelectionModel./*ClearAnd*/Select)
}
}
}
}
}
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
I've written this simple qml app that allows to paint pixels over a grid:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Grid {
id: grid
anchors.fill: parent
rows: 32
columns: 64
Repeater {
model: grid.columns * grid.rows;
delegate: delegateGridImage
}
}
Component {
id: delegateGridImage
Item {
id: gridItem
property int currentColumn: index % grid.columns
property int currentRow: Math.floor(index / grid.rows);
// Resize to screen size
width: grid.width / grid.columns
height: grid.height / grid.rows
Rectangle {
id: pixel
anchors.fill: parent
property bool pixel_state: true
color: if (pixel_state == true ) { "white" } else { "black" }
MouseArea {
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: console.log(index)
onPressed: pixel.pixel_state ^= true
}
}
}
}
}
This works fine:
I would like to be able to paint multiple pixels with a single mouse click pressed.
I've tried the onEntered event, but it only listens to the active mouse area until the click button is released. Is there a way to not block the events from the other mouse areas?
You can use a global MouseArea and deduct the current item below the cursor via childAt(...) of the Grid.
Window {
... // remove the MouseArea of pixel
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
property bool pixel_activate: true
onPressed: {
var child = grid.childAt(mouse.x, mouse.y)
child.pixel_state ^= true
pixel_activate = child.pixel_state
}
onPositionChanged: {
if (!pressed) return;
var child = grid.childAt(mouse.x, mouse.y)
child.pixel_state = pixel_activate
}
}
}
You just have to decide what action you want to perform once you hold the button pressed (currently it performs first action activate/deactivate and all following). Also take a look at the MouseEvent passed by the signals pressed and positionChanged so you can differentiate what key was pressed.
Lasall's solution works great. This is the final result:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Grid {
id: grid
anchors.fill: parent
rows: 32
columns: 64
Repeater {
model: grid.columns * grid.rows;
delegate: delegateGridImage
}
}
Component {
id: delegateGridImage
Item {
id: gridItem
property int currentColumn: index % grid.columns
property int currentRow: Math.floor(index / grid.rows);
property bool pixel_state: false
// Resize to screen size
width: grid.width / grid.columns
height: grid.height / grid.rows
Rectangle {
id: pixel
anchors.fill: parent
color: if (gridItem.pixel_state == true ) { "white" } else { "black" }
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
property bool pixel_activate: true
onPressed: {
var child = grid.childAt(mouse.x, mouse.y)
child.pixel_state ^= true
pixel_activate = child.pixel_state
}
onPositionChanged: {
if (!pressed) return;
var child = grid.childAt(mouse.x, mouse.y)
child.pixel_state = pixel_activate
}
}
}
I'm trying to handle mouse clicks on table cells. I need to respond to the right button only if I clicked in a certain cell. but I can't find how to catch the column number. A TableView from QtQuick 2.12 is used. Or maybe you can do without a column number? Tableview from QtQuickControls I, for various reasons, can not use.
TableView {
id: table
boundsBehavior: Flickable.StopAtBounds
anchors.fill: parent
columnSpacing: 0
rowSpacing: 0
anchors.rightMargin: 2
anchors.leftMargin: 5
anchors.bottomMargin: 5
anchors.topMargin: 70
clip: true
model: TableModel {
id: model
Component.onCompleted: {
model.init()
}
}
delegate: Rectangle {
id: tableDelegate
implicitWidth: textDelegate.implicitWidth + textDelegate.padding * 2
implicitHeight: 30
border.width: 0
TextField {
id: textDelegate
text: tabledata
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
clip: true
horizontalAlignment: TextField.AlignHCenter
verticalAlignment: TextField.AlignVCenter
enabled: true
background: Rectangle {
border.color: "#e61d6887"
color: "#e6ffffff"
border.width: 1
}
selectByMouse: true
MouseArea {
id: mad
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
switch(mouse.button){
case Qt.RightButton:
console.log("right button")
dialog.open()
break
case Qt.LeftButton:
console.log("left button")
break
default:
break
}
}
}
}
}
}
You have to use model.row and model.column (or row and column) to get the row or column in the delegate, respectively.
// ...
MouseArea {
id: mad
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
console.log(model.row, model.column)
// or
// console.log(row, column)
// ...
}
// ...
I have reported the bug in the documentation: QTBUG-76529.
I am having a QML code which shows map, it has a MapQuickItem for an image.
MapQuickItem {
id: transMarker
sourceItem: Image {
id: transImage
width: 50
height: 50
source: "trans.png"
}
}
When I clicks on the map, it should paste that image on the map, I could achieve that by below code
transMarker.coordinate = map.toCoordinate(Qt.point(mouse.x,mouse.y))
I want to save the position permanently, but the problem is I am trying to print map.toCoordinate(Qt.point(mouse.x,mouse.y))
It prints in degree and minutes (Coordinate: 8° 29' 21.4" N, 76° 57' 41.9" E)
I want to get that as decimal latitude and longitude (Coordinate: 76.9616344 8.4892798).
How this can be accomplished?
You have to use the latitude and longitude properties of coordinate:
Map {
id: map
anchors.fill: parent
plugin: Plugin {
name: "osm"
}
center: QtPositioning.coordinate(59.91, 10.75)
zoomLevel: 10
MapQuickItem {
id: transMarker
sourceItem: Image {
id: transImage
width: 50
height: 50
source: "trans.png"
}
}
MouseArea{
anchors.fill: parent
onClicked: {
var coord = map.toCoordinate(Qt.point(mouse.x,mouse.y));
transMarker.coordinate = coord;
console.log(coord.latitude, coord.longitude)
}
}
}
Output:
qml: 59.969159320456804 10.824157714841107
qml: 59.98427615215763 10.895568847649372
qml: 59.989771470871446 10.780212402338407
qml: 59.965722714293186 10.652496337891108