Reputation: 111
I'm using PyQt5 to access QML code through a QQuickWidget in a UI file. My QML file creates a map and plots points. I want to add/modify these points from my python code. I'm able to access the Map object in QML in python, but PyQt treats it and the MapQuickItem as QQuickItems. I'm not sure how to actually create a new MapQuickItem in python and add it to the Map object. I've tried creating a QQuickItem with the necessary properties and then using the addMapItem method, but receive this error:
TypeError: unable to convert argument 0 of QQuickItem.addMapItem from 'QQuickItem' to 'QDeclarativeGeoMapItemBase*'"
I don't know how to create a QDeclarativeGeoMapItemBase
object in PyQt or if I should be going about this another way.
As you can see, I'm also having some trouble correctly referencing the object in the QML file. self.map
or self.map.rootObject()
gets me the QQuickWidget in the UI, with self.map.rootObject().children()[1]
getting me the Map object within the QML. I'd prefer to locate the items by their ID using findChild(), but haven't been able to. Is there a better method? Should a create a Python object which replicates the structure of my QML file?
This is a sample of the QML code. I've referenced this QML file as a QQuickWidget in a UI file.
Rectangle {
id:rectangle
Plugin {
id: osmPlugin
name: "osm"
}
property variant locationTC: QtPositioning.coordinate(44.951, -93.192)
Map {
id: map
anchors.fill: parent
plugin: osmPlugin
center: locationTC
zoomLevel: 10
MapQuickItem {
coordinate: QtPositioning.coordinate(44.97104,-93.46055)
anchorPoint.x: image.width * 0.5
anchorPoint.y: image.height
sourceItem:
Image { id: image; source: "marker.png" }
}
}
}
Below is a sample of the PyQt code where I attempt to create a MapQuickItem and add it to the map.
from PyQt5 import QtCore, uic, QtWidgets, QtPositioning, QtLocation, QtQml, QtQuick
form_class = uic.loadUiType("TTRMS.ui")[0]
class MainWindow(QtWidgets.QMainWindow, form_class):
'''
classdocs
'''
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
tmc = QQuickItem()
new_coordinate = QtPositioning.QGeoCoordinate()
new_coordinate.setLatitude(44.951)
new_coordinate.setLongitude(-93.192)
tmc.setProperty("coordinate",new_coordinate)
tmc.setProperty("anchorPoint",QtCore.QPointF(12.5, 32.0))
image = QQuickItem()
image.setProperty("source", QtCore.QUrl.fromLocalFile(("marker.png")))
tmc.setProperty("sourceItem", image)
image.setParent(tmc)
self.map.rootObject().children()[1].addMapItem(tmc)
I'm running everything on Windows 7. The PyQt5 development is done in Eclipse using PyDev and Python 3.4 (32 bit), the QML coding in Qt Creator 5.5 and the UI in Qt Designer 5.5.
Upvotes: 4
Views: 3558
Reputation: 244291
In the cases that you want to interact with C++/Python with QML it is better to expose some object to QML that allows to transmit the data of C++/Python to QML since the latter has a different life cycle.
In this particular case I will create a model that stores the data, send it to QML through setContextProperty(), and on the QML side use MapItemView with a delegate so you can have many markers.
main.py
import os
from PyQt5 import QtCore, QtWidgets, QtQuickWidgets, QtPositioning
class MarkerModel(QtCore.QAbstractListModel):
PositionRole, SourceRole = range(QtCore.Qt.UserRole, QtCore.Qt.UserRole + 2)
def __init__(self, parent=None):
super(MarkerModel, self).__init__(parent)
self._markers = []
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._markers)
def data(self, index, role=QtCore.Qt.DisplayRole):
if 0 <= index.row() < self.rowCount():
if role == MarkerModel.PositionRole:
return self._markers[index.row()]["position"]
elif role == MarkerModel.SourceRole:
return self._markers[index.row()]["source"]
return QtCore.QVariant()
def roleNames(self):
return {MarkerModel.PositionRole: b"position_marker", MarkerModel.SourceRole: b"source_marker"}
def appendMarker(self, marker):
self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
self._markers.append(marker)
self.endInsertRows()
class MapWidget(QtQuickWidgets.QQuickWidget):
def __init__(self, parent=None):
super(MapWidget, self).__init__(parent,
resizeMode=QtQuickWidgets.QQuickWidget.SizeRootObjectToView)
model = MarkerModel(self)
self.rootContext().setContextProperty("markermodel", model)
qml_path = os.path.join(os.path.dirname(__file__), "main.qml")
self.setSource(QtCore.QUrl.fromLocalFile(qml_path))
positions = [(44.97104,-93.46055), (44.96104,-93.16055)]
urls = ["http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_gray.png",
"http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_red.png"]
for c, u in zip(positions, urls):
coord = QtPositioning.QGeoCoordinate(*c)
source = QtCore.QUrl(u)
model.appendMarker({"position": coord , "source": source})
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MapWidget()
w.show()
sys.exit(app.exec_())
main.qml
import QtQuick 2.11
import QtPositioning 5.11
import QtLocation 5.11
Rectangle {
id:rectangle
width: 640
height: 480
Plugin {
id: osmPlugin
name: "osm"
}
property variant locationTC: QtPositioning.coordinate(44.951, -93.192)
Map {
id: map
anchors.fill: parent
plugin: osmPlugin
center: locationTC
zoomLevel: 10
MapItemView{
model: markermodel
delegate: MapQuickItem {
coordinate: model.position_marker
anchorPoint.x: image.width
anchorPoint.y: image.height
sourceItem:
Image { id: image; source: model.source_marker }
}
}
}
}
Upvotes: 2