невозможно добавить MapCircles в QML Map на MouseClick - PullRequest
1 голос
/ 22 марта 2019

Я пытаюсь создать некоторые маркеры, которые будут динамически перемещаться на карте QML. Однако до этого мне нужно нанести их все на карту с помощью мыши, чтобы я мог обновить их позже. Я использовал pyqtProperty для отправки нужных мне координат в QML, но когда я пытаюсь добавить их в MapItemView, они не определены. Следующий код демонстрирует то, что я надеюсь достичь. Проблема в том, что я передаю pyqtProperty в QML от другого объекта python, который не был добавлен с setContextProperty(), как в main.py? Или я неправильно использую делегирование MapItemView?

main.qml

import QtQuick 2.7
import QtQml 2.5
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.2
import QtPositioning 5.9
import QtLocation 5.3
import QtQuick.Dialogs 1.1

ApplicationWindow {
    id: root
    width: 640
    height: 480
    visible: true

    ListModel {
        id: markers
    }

    Plugin {
        id: mapPlugin
        name: "osm" //"mapboxgl" "osm" "esri"
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: mapPlugin
        center: atc.location
        zoomLevel: 14

        MapItemView {
            model: markers
            delegate: MapCircle {
                radius: 50
                color: 'red'
                center: markLocation //issue here? 
            }
        }

        MapCircle {
            id: home
            center: atc.location
            radius: 40
            color: 'white'
        }

        MouseArea {
            id: mousearea
            anchors.fill: map
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            hoverEnabled: true
            property var coord: map.toCoordinate(Qt.point(mouseX, mouseY))

            onDoubleClicked: {
                if (mouse.button === Qt.LeftButton)
                {
                    //Capture information for model here
                    atc.plot_mark(
                        "marker",
                        mousearea.coord.latitude,
                        mousearea.coord.longitude)
                    markers.append({
                        name: "markers",
                        markLocation: atc.get_marker_center("markers")
                    })
                }
            }
        }
    }
}

atc.py

import geocoder
from DC import DC
from PyQt5.QtPositioning import QGeoCoordinate
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot

class ATC(QObject):
    #pyqt Signals
    locationChanged = pyqtSignal(QGeoCoordinate)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._location = QGeoCoordinate()
        self.dcs = {}
        g = geocoder.ip('me')
        self.set_location(QGeoCoordinate(*g.latlng))


    def set_location(self, coordinate):
        if self._location != coordinate:
            self._location = coordinate
            self.locationChanged.emit(self._location)


    def get_location(self):
        return self._location


    #pyqt Property
    location = pyqtProperty(QGeoCoordinate,
        fget=get_location,
        fset=set_location,
        notify=locationChanged)


    @pyqtSlot(str, str, str)
    def plot_mark(self, mark_name, lat, lng):
            dc = DC(mark_name)
            self.dcs[mark_name] = dc
            self.dcs[mark_name].set_location(
                QGeoCoordinate(float(lat), float(lng)))


    @pyqtSlot(str)
    def get_marker_center(self, mark_name):
        return self.dcs[mark_name].location

DC.py

from PyQt5.QtPositioning import QGeoCoordinate
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot

class DC(QObject):
    #pyqt Signals
    locationChanged = pyqtSignal(QGeoCoordinate)

    def __init__(self, name, parent=None):
        super().__init__(parent)
        self.name = name
        self._location = QGeoCoordinate()        


    def set_location(self, coordinate):
        if self._location != coordinate:
            self._location = coordinate
            self.locationChanged.emit(self._location)


    def get_location(self):
        return self._location


    #pyqt Property
    location = pyqtProperty(QGeoCoordinate,
        fget=get_location,
        fset=set_location,
        notify=locationChanged)

main.py

from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty
from PyQt5.QtPositioning import QGeoCoordinate

from ATC import ATC

if __name__ == "__main__":
    import os
    import sys

    app = QGuiApplication(sys.argv)

    engine = QQmlApplicationEngine()

    atc = ATC()

    engine.rootContext().setContextProperty("atc", atc)

    qml_path = os.path.join(os.path.dirname(__file__), "main.qml")
    engine.load(QUrl.fromLocalFile(qml_path))

    if not engine.rootObjects():
        sys.exit(-1)

    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

1 Ответ

1 голос
/ 22 марта 2019

Вместо того, чтобы создавать модель в QML, вы должны создать ее в python, чтобы иметь возможность обрабатывать ее непосредственно для нее, которую вы должны наследовать от QAbstractListModel.Для плавного перемещения вы должны использовать QxxxAnimation в качестве QPropertyAnimation.

В следующем примере каждый раз, когда вставляется маркер, будет вызываться функция on_markersInserted, которая будет перемещать маркер к центру окна.

main.py

from functools import partial
from PyQt5 import QtCore, QtGui, QtQml, QtPositioning
import geocoder

class Marker(QtCore.QObject):
    locationChanged = QtCore.pyqtSignal(QtPositioning.QGeoCoordinate)

    def __init__(self, location=QtPositioning.QGeoCoordinate(), parent=None):
        super().__init__(parent)
        self._location = location

    def set_location(self, coordinate):
        if self._location != coordinate:
            self._location = coordinate
            self.locationChanged.emit(self._location)

    def get_location(self):
        return self._location

    location = QtCore.pyqtProperty(QtPositioning.QGeoCoordinate,
        fget=get_location,
        fset=set_location,
        notify=locationChanged)

    def move(self, location, duration=1000):
        animation = QtCore.QPropertyAnimation(
            targetObject=self, 
            propertyName=b'location',
            startValue=self.get_location(),
            endValue=location,
            duration=duration,
            parent=self
        )
        animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)

    def moveFromTo(self, start, end, duration=1000):
        self.set_location(start)
        self.move(end, duration)

class MarkerModel(QtCore.QAbstractListModel):
    markersInserted = QtCore.pyqtSignal(list)

    PositionRole = QtCore.Qt.UserRole + 1000

    def __init__(self, parent=None):
        super().__init__(parent)
        self._markers = []
        self.rowsInserted.connect(self.on_rowsInserted)

    def rowCount(self, parent=QtCore.QModelIndex()):
        return 0 if parent.isValid() else len(self._markers)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid() and 0 <= index.row() < self.rowCount():
            if role == MarkerModel.PositionRole:
                return self._markers[index.row()].get_location()
        return QtCore.QVariant()

    def roleNames(self):
        roles = {}
        roles[MarkerModel.PositionRole] = b'position'
        return roles

    @QtCore.pyqtSlot(QtPositioning.QGeoCoordinate)
    def appendMarker(self, coordinate):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        marker = Marker(coordinate)
        self._markers.append(marker)
        self.endInsertRows()
        marker.locationChanged.connect(self.update_model)

    def update_model(self):
        marker = self.sender()
        try:
            row = self._markers.index(marker)
            ix = self.index(row)
            self.dataChanged.emit(ix, ix, (MarkerModel.PositionRole,))
        except ValueError as e:
            pass

    @QtCore.pyqtSlot(QtCore.QModelIndex, int, int)
    def on_rowsInserted(self, parent, first, end):
        markers = []
        for row in range(first, end+1):
            markers.append(self.get_marker(row))
        self.markersInserted.emit(markers)


    def get_marker(self, row):
        if 0 <= row < self.rowCount():
            return self._markers[row]

class ManagerMarkers(QtCore.QObject):
    locationChanged = QtCore.pyqtSignal(QtPositioning.QGeoCoordinate)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._center= Marker(parent=self)
        self._model = MarkerModel(self)  
        g = geocoder.ip('me')
        self.moveCenter(QtPositioning.QGeoCoordinate(*g.latlng)) 

    def model(self):
        return self._model     

    def center(self):
        return self._center

    def moveCenter(self, position):
        self._center.set_location(position)

    center = QtCore.pyqtProperty(QtCore.QObject, fget=center, constant=True)
    model = QtCore.pyqtProperty(QtCore.QObject, fget=model, constant=True)


# testing
# When a marker is added
# it will begin to move toward
# the center of the window
def on_markersInserted(manager, markers):
    end = manager.center.get_location()
    for marker in markers:
        marker.move(end, 5*1000)

if __name__ == "__main__":
    import os
    import sys

    app = QtGui.QGuiApplication(sys.argv)
    manager = ManagerMarkers()
    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("manager", manager)
    qml_path = os.path.join(os.path.dirname(__file__), "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(qml_path))
    if not engine.rootObjects():
        sys.exit(-1)
    manager.model.markersInserted.connect(partial(on_markersInserted, manager))
    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.7
import QtQuick.Controls 2.2
import QtPositioning 5.9
import QtLocation 5.3

ApplicationWindow {
    id: root
    width: 640
    height: 480
    visible: true

    Plugin {
        id: mapPlugin
        name: "osm" // "mapboxgl" "osm" "esri"
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: mapPlugin
        center: manager.center.location
        zoomLevel: 14

        MapCircle {
            id: home
            center: manager.center.location
            radius: 40
            color: 'white'
        }

        MapItemView {
            model: manager.model
            delegate: MapCircle {
                radius: 50
                color: 'red'
                center: model.position
            }
        }

        MouseArea {
            id: mousearea
            anchors.fill: map
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            onDoubleClicked: if (mouse.button === Qt.LeftButton)  
                                manager.model.appendMarker(map.toCoordinate(Qt.point(mouseX, mouseY)))
        }
    }
}
...