PDA

View Full Version : Embedded Map in QWidget c++



froeben
15th February 2016, 14:20
Hi Community,

i would like to embed an QML-Map into a widget.
From general: i have successfully embedded a text (QML) with the following code:



QQmlEngine *engine = new QQmlEngine(this);
QQuickWidget *view = new QQuickWidget(engine, this);
view->setSource(QUrl("qrc:/test.qml"));
this->setCentralWidget(view);


test.qml is taken from an example.

Embedding the following Map.qml does yield to syntax error at line 13. I doubt the reason is that i don't have a "parent".


import QtQuick 2.0
import QtLocation 5.3

Plugin {
name: "osm"
PluginParameter { name: "osm.useragent"; value "MapViewer"}
PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
PluginParameter { name: "osm.mapping.copyright"; value: "All mine" }
PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" }
}

Map {
id: map

plugin: osm

zoomLevel: map.minimumZoomLevel

center {
// The Qt Company in Oslo
latitude: 59.9485
longitude: 10.7686
}
}

The code shall be embedded in a qwidget because it is part of an existing library that is loaded into a main application.
Could someone give me a hint where i can find a good example? The mapviewer example vom QT-documentation does not hold enough background information how to create a qwidget holding a QML-Map.

Thx!!!!

anda_skoa
15th February 2016, 15:34
A QML file has one and only one top level element, yours has two.

Cheers,
_

P.S.: if you are looking for a widget based map view, look here https://inqlude.org/libraries/marble.html

froeben
15th February 2016, 16:32
I will have a look at the mentioned library. Btw. ist "Item" the best container for what i am supposed to do?

Added after 5 minutes:

The project seems to be much to complex. Do they make use of QML?


A QML file has one and only one top level element, yours has two.

Cheers,
_

P.S.: if you are looking for a widget based map view, look here https://inqlude.org/libraries/marble.html

The project seems to be much to complex. Do they make use of QML?

Added after 48 minutes:

After searching the web, i found something which seems to yield to a valid qml-file. But i need some advice if i am on the right track.


import QtQuick 2.0
import QtPositioning 5.6
import QtLocation 5.6

Item{
id: container

Plugin {
id: myplugin

name: "osm"
PluginParameter { name: "osm.useragent"; value: "My great Qt OSM application" }
PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
PluginParameter { name: "osm.mapping.copyright"; value: "All mine" }
PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" }
}

Map {
id: map

plugin: myplugin
center {
latitude: -27
longitude: 153
}
zoomLevel: map.minimumZoomLevel
gesture.enabled: true
}
}


Is the way of bringing data into the widget correct?



QQmlEngine *engine = new QQmlEngine(this);
QQuickWidget *view = new QQuickWidget(engine, this);

view->setSource(QUrl("qrc:/map.qml"));

this->setCentralWidget(view);


I don't see any error during build ....


Advice appreciated!

Thx!

anda_skoa
15th February 2016, 16:37
The project seems to be much to complex. Do they make use of QML?

Marble provides a map widget. I just thought I mention it since you have a widget based application.



After searching the web, i found something which seems to yield to a valid qml-file. But i need some advice if i am on the right track.

Yes, that looks ok.
You could also try to have the Map as the root element and put the plugin into it.


import QtPositioning 5.6
import QtLocation 5.6

Map {
id: map

plugin: Plugin {

name: "osm"
PluginParameter { name: "osm.useragent"; value: "My great Qt OSM application" }
PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
PluginParameter { name: "osm.mapping.copyright"; value: "All mine" }
PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" }
}


//...
}




Is the way of bringing data into the widget correct?

Don't know, never worked with the QtLocation API before.





QQmlEngine *engine = new QQmlEngine(this);
QQuickWidget *view = new QQuickWidget(engine, this);

view->setSource(QUrl("qrc:/map.qml"));

this->setCentralWidget(view);


I don't see any error during build ....

That looks fine. You don't even need to explictly create the QQmlEngine.

Cheers,
_

froeben
15th February 2016, 17:04
Marble provides a map widget. I just thought I mention it since you have a widget based application.


Yes, that looks ok.
You could also try to have the Map as the root element and put the plugin into it.


import QtPositioning 5.6
import QtLocation 5.6

Map {
id: map

plugin: Plugin {

name: "osm"
PluginParameter { name: "osm.useragent"; value: "My great Qt OSM application" }
PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
PluginParameter { name: "osm.mapping.copyright"; value: "All mine" }
PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" }
}


//...
}



Don't know, never worked with the QtLocation API before.


That looks fine. You don't even need to explictly create the QQmlEngine.

Cheers,
_

So, the qml is loaded correctly, the QQmlError list is empty (have checked, code returns "OK"):


QQmlEngine *engine = new QQmlEngine(this);
QQuickWidget *view = new QQuickWidget(engine, this);
view->setSource(QUrl("qrc:/map.qml"));

this->setCentralWidget(view);

QList<QQmlError> err = view->errors();
for (QList<QQmlError>::iterator i = err.begin(); i != err.end(); ++i)
{
qDebug() << *i;
}
if (err.isEmpty())
{
qDebug() << "OK";
}


But I don't see an image!

So i assume some possible problems:
1. The size of the map is not set and thus, the object is not drawn.
-- When using text, the size is given by "font.pointSize". Will check tomorrow? Any idea, anyone?
2. Do i need to set the Map to visible?
-- ???
3. My OSM plugin is trying to contact the wrong server.
-- Can anyone check? I didnot find different server information for "OSM" server url.
4. My firewall is blocking access.
-- Which port does Qt use for requesting data from OSM server? Is it port 80? Can it be configured?

Regards,
Frank

anda_skoa
15th February 2016, 17:26
1. The size of the map is not set and thus, the object is not drawn.
-- When using text, the size is given by "font.pointSize". Will check tomorrow? Any idea, anyone?



view->setResizeMode(QQuickWidget::SizeRootObjectToView);




2. Do i need to set the Map to visible?
-- ???

That should not be necessary.



3. My OSM plugin is trying to contact the wrong server.
-- Can anyone check? I didnot find different server information for "OSM" server url.

No idea.



4. My firewall is blocking access.
-- Which port does Qt use for requesting data from OSM server? Is it port 80? Can it be configured?

If you provide http URLs it will be port 80 unless the URL has a port number.

Cheers,
_

froeben
16th February 2016, 09:00
So, taking a look at my possible issues:

1. The size of the map is not set and thus, the object is not drawn.
Setting


view->setResizeMode(QQuickWidget::SizeRootObjectToView);

did not change anything. No image visualized. :-(

2. Do i need to set the Map to visible?
Setting


view->setVisible(true);

did not yield to a displayed map. :-/

3. My OSM plugin is trying to contact the wrong server.
I currently don't find any better servers as mentioned in the examples. Can anyone provide working tile server from Open Streetmap?

4. My firewall is blocking access.
Since it shall be a port 80 request, the firewall should not be a problem.


Further ideas:
How about additional debugging options for the qml plugin object? Are there any debug options that could give me more information about what might get wrong.

Thx.
Frank

anda_skoa
16th February 2016, 09:47
1. The size of the map is not set and thus, the object is not drawn.
Setting


view->setResizeMode(QQuickWidget::SizeRootObjectToView);

did not change anything. No image visualized. :-(

And Map is your top level element in the QML file?



2. Do i need to set the Map to visible?
Setting


view->setVisible(true);

did not yield to a displayed map. :-/

view is the window's central widget, it is visible when the window is unless you explicitly hide it.


Further ideas:
How about additional debugging options for the qml plugin object? Are there any debug options that could give me more information about what might get wrong.

Have you investigates the values of the Map's error and errorString properties?

Cheers,
_

froeben
16th February 2016, 10:03
Looking into the example (http://doc.qt.io/qt-5/qtlocation-mapviewer-main-cpp.html), is see a difference in loading the information:


engine.addImportPath(QStringLiteral(":/imports"));
engine.load(QUrl(QStringLiteral("qrc:///mapviewer.qml")));


In my code, i create a new QQuickWidget and assign the source of the widget:


QQmlEngine *engine = new QQmlEngine(this);
QQuickWidget *view = new QQuickWidget(engine, this);
view->setSource(QUrl("qrc:/map.qml"));


Is the effect the same or is there something i do not get from the examples?

Added after 8 minutes:

My QML-file currently is this:



import QtQuick 2.3
import QtPositioning 5.6
import QtLocation 5.6

Item{
id: container

Plugin {
id: myplugin

name: "osm"
PluginParameter { name: "osm.useragent"; value: "My great Qt OSM application" }
PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
PluginParameter { name: "osm.mapping.copyright"; value: "All mine" }
PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" }
}
Map {
id: map

plugin: myplugin
center {
latitude: -27
longitude: 153
}
zoomLevel: map.minimumZoomLevel
gesture.enabled: true
}
}


From my understanding of the example, "plugin" cannot be a child of Map.
If i modify it to have the plugin as a child of Map, the project crashes during runtime.

anda_skoa
16th February 2016, 10:36
Is the effect the same or is there something i do not get from the examples?

Yes, that's basically the same.


My QML-file currently is this:



import QtQuick 2.3
import QtPositioning 5.6
import QtLocation 5.6

Item{
id: container

Plugin {
id: myplugin

name: "osm"
PluginParameter { name: "osm.useragent"; value: "My great Qt OSM application" }
PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
PluginParameter { name: "osm.mapping.copyright"; value: "All mine" }
PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" }
}
Map {
id: map

plugin: myplugin
center {
latitude: -27
longitude: 153
}
zoomLevel: map.minimumZoomLevel
gesture.enabled: true
}
}


Ah, so Map is not your top level element, then of course it still has no size.
You need to make the Map element follow the size of the Item, since that is the element resized by the view.



Map {
anchors.fill: parent
}




From my understanding of the example, "plugin" cannot be a child of Map.

Ok. Putting it directly into the plugin property should still have worked.

Can I ask which features you are targetting that makes you choose an embedded QtQuick scene over the widget solution of Marble?

Cheers,
_

froeben
16th February 2016, 11:53
Yes, that's basically the same.


Ah, so Map is not your top level element, then of course it still has no size.
You need to make the Map element follow the size of the Item, since that is the element resized by the view.



Map {
anchors.fill: parent
}



Ok. Putting it directly into the plugin property should still have worked.

Can I ask which features you are targetting that makes you choose an embedded QtQuick scene over the widget solution of Marble?

Cheers,
_

I have had a look at Marble, which seems to be a nice tool. But i want to understand from scratch how it works. Using another framework to get something working that you don't understand is also a pain in the ass. The problem remains: if something goes wrong, how do you find the problem...

The features are available in QT and shall be usable. The question to me is, am i doing it correctly? :-D Do I have a wrong understanding of the tools?
If it turns out, that the official documentation does not give you enough information to get it running, than something is weird.

anda_skoa
16th February 2016, 12:05
Well, I was just asking because you are mixing different UI technologies which increases the complexity of any error analysis.
So I assumed you needed QtQuick for something in particular, e.g. certain effects, animations, etc.

Cheers,
_

froeben
16th February 2016, 12:44
Nope, don't need QtQuick but even if you delete it, there is no image :-D

anda_skoa
16th February 2016, 12:49
I am not sure I follow.

If you remove the QQuickWidget, what image to you expect to see?

Cheers,
_

froeben
16th February 2016, 13:42
Forget my last post ...

Small changes to QML (without success):


import QtQuick 2.3
import QtPositioning 5.6
import QtLocation 5.6

Map {
id: map
anchors.fill: parent;

plugin: Plugin {
name: "osm"
}

center {
latitude: -27
longitude: 153
}
zoomLevel: map.minimumZoomLevel
gesture.enabled: true
}

anda_skoa
16th February 2016, 14:23
anchors.fill: parent only makes sense if there is a parent.

I guess you need to look at the error properties.

maybe something like



onErrorChanged: console.log("error=" + error);
onErrorStringChanged: console.log("errorString=" + errorString);


Cheers,
_

froeben
16th February 2016, 16:37
So, network proxy was the problem.
Needed to add qnetwork and needed to add


QNetworkProxyFactory::setUseSystemConfiguration(tr ue);

to get the system proxy settings.

Only small problem left:
i do get exceptions from sysfer.dll which happen if the following is executed:


view->setSource(QUrl("qrc:/map.qml"));


Here are the files:
QML:


import QtQuick 2.3
import QtPositioning 5.6
import QtLocation 5.6

Map {
id: map
width: 640
height:480

plugin: Plugin {
name: "osm"
}

center {
latitude: -27
longitude: 153
}
zoomLevel: 10
gesture.enabled: true

Component.onCompleted: {
console.log("Dimensions: ", width, height)
}
}


Mainwindow.cpp:


MainWindow::MainWindow() : QMainWindow ()
{
// embed resources into binary
// infos: http://doc.qt.io/qt-5/resources.html
Q_INIT_RESOURCE(qmlsources);

this->resize(1024, 768);

createActions();
createMenus();

QQmlEngine *engine = new QQmlEngine(this);
QQuickWidget *view = new QQuickWidget(engine, this);
view->setSource(QUrl("qrc:/map.qml"));
this->setCentralWidget(view);
view->setResizeMode(QQuickWidget::SizeRootObjectToView);


main.cpp:


int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// load resources from binary
// infos: http://doc.qt.io/qt-5/resources.html
Q_INIT_RESOURCE(qmlsources);

QNetworkProxyFactory::setUseSystemConfiguration(tr ue);
MainWindow mw;
mw.show();

return a.exec();
}