(drunk drivers -> flying cars) |
(changes in code) |
||
| (2 intermediate revisions by one user not shown) | |||
| Line 6: | Line 6: | ||
name=Search| | name=Search| | ||
| − | pre=[[Projects/Marble/Runners/DisplayGeoDataPlacemark|Tutorial | + | pre=[[Projects/Marble/Runners/DisplayGeoDataPlacemark|Tutorial 6 - Displaying Places the proper way (via GeoDataDocuments)]]| |
| − | next=[[Projects/Marble/ | + | next=[[Projects/Marble/OnlineServices|Tutorial 8 - Creating new online services]]| |
}} | }} | ||
| Line 61: | Line 61: | ||
// | // | ||
// Copyright 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com> | // Copyright 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com> | ||
| − | |||
// | // | ||
| Line 91: | Line 90: | ||
signals: | signals: | ||
| − | |||
| − | |||
void coordinatesChanged(GeoDataCoordinates coord); | void coordinatesChanged(GeoDataCoordinates coord); | ||
| Line 126: | Line 123: | ||
} | } | ||
| − | |||
| − | |||
void CarWorker::iterate() | void CarWorker::iterate() | ||
{ | { | ||
| − | |||
qreal lon = m_city.longitude(GeoDataCoordinates::Degree) + m_radius * qCos(m_alpha * DEG2RAD); | qreal lon = m_city.longitude(GeoDataCoordinates::Degree) + m_radius * qCos(m_alpha * DEG2RAD); | ||
qreal lat = m_city.latitude(GeoDataCoordinates::Degree) + m_radius * qSin(m_alpha * DEG2RAD); | qreal lat = m_city.latitude(GeoDataCoordinates::Degree) + m_radius * qSin(m_alpha * DEG2RAD); | ||
| Line 137: | Line 131: | ||
emit coordinatesChanged(coord); | emit coordinatesChanged(coord); | ||
| − | |||
m_alpha += m_speed; | m_alpha += m_speed; | ||
} | } | ||
| Line 236: | Line 229: | ||
} | } | ||
| − | // Main | + | // Main (start point) |
int main(int argc, char** argv) | int main(int argc, char** argv) | ||
{ | { | ||
| Line 250: | Line 243: | ||
</source> | </source> | ||
| + | Before running qmake, create a [http://doc.trolltech.com/qmake-tutorial.html qmake project file] | ||
| + | <syntaxhighlight lang="text"> | ||
| + | TEMPLATE = app | ||
| + | TARGET = vehicletracking | ||
| + | DEPENDPATH += . | ||
| + | INCLUDEPATH += . | ||
| + | SOURCES += vehicletracking.cpp | ||
| + | LIBS += -lmarblewidget | ||
| + | </syntaxhighlight> | ||
| + | Save this file as "vehicletracking.pro" in the same directory and run | ||
| + | <source lang = "bash"> | ||
| + | qmake | ||
| + | make | ||
| + | </source> | ||
| + | then execute <tt>./vehicletracking</tt>. If everything goes fine, marble widget will appear as shown in the screenshot at the start of the tutorial. | ||
'''Find your way and explore the world! ;)''' | '''Find your way and explore the world! ;)''' | ||
| Tutorial Series | Marble C++ Tutorial |
| Previous | Tutorial 6 - Displaying Places the proper way (via GeoDataDocuments) |
| What's Next | Tutorial 8 - Creating new online services |
| Further Reading | n/a |
We want to present you another C++ example of Marble Runnable usage, here on KDE TechBase. It's about the cars. Imagine that there are two flying cars in the air. They are driving around ukranian city Kiev (EURO-2012). They are driving circles around it with set radius and speed. At the end of tutorial you will have an application with smth like:
Let's think about implementation a bit:
We need to change coordinates with really small interval (~10 ms). In this way, to not block GUI thread we need to use multi-threading. For that we suggest that model:
Then we will have three threads - GUI, Calculations #1, Calculations #2. To connect calc. threads with GUI we should use Qt S/S (Signal/Slot) connection.
| Important |
|---|
| Use here connection type Qt::BlockingQueuedConnection, another way GUI thread will be blocked |
Now we have a nice implementation plan. Let's start coding.
Then each CarWorker creates a QTimer with interval x. On timeout() it calls the CarWorker::iterate() - function to calculate new position of placemark and notify Window (another thread) about it.
Everything is going on until the application finishes.
The code with comments:
// // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com> // #include <QtGui/QApplication> #include <QtCore/QThread> #include <QtCore/QTimer> #include <QtCore/QHash> #include <QtCore/qmath.h> #include <QtCore/QDebug> #include <QtGui/QVBoxLayout> #include <marble/MarbleWidget.h> #include <marble/MarbleGlobal.h> #include <marble/GeoDataDocument.h> #include <marble/GeoDataPlacemark.h> #include <marble/GeoDataLineString.h> #include <marble/GeoDataTreeModel.h> #include <marble/MarbleModel.h> using namespace Marble; // CarWorker Class class CarWorker : public QObject { Q_OBJECT public: CarWorker(const GeoDataCoordinates& city, qreal radius, qreal speed); signals: void coordinatesChanged(GeoDataCoordinates coord); public slots: void startWork(); void finishWork(); private slots: void iterate(); private: QTimer *m_timer; GeoDataCoordinates m_city; qreal m_radius; qreal m_speed; qreal m_alpha; }; CarWorker::CarWorker(const GeoDataCoordinates &city, qreal radius, qreal speed) : QObject(), m_timer(new QTimer(this)), m_city(city), m_radius(radius), m_speed(speed), m_alpha(0.0) {} void CarWorker::startWork() { m_timer->setInterval(0); connect(m_timer, SIGNAL(timeout()), this, SLOT(iterate())); m_timer->start(); } void CarWorker::iterate() { qreal lon = m_city.longitude(GeoDataCoordinates::Degree) + m_radius * qCos(m_alpha * DEG2RAD); qreal lat = m_city.latitude(GeoDataCoordinates::Degree) + m_radius * qSin(m_alpha * DEG2RAD); GeoDataCoordinates coord(lon, lat, 0.0, GeoDataCoordinates::Degree); emit coordinatesChanged(coord); m_alpha += m_speed; } void CarWorker::finishWork() { m_timer->stop(); } // Window Class class Window : public QWidget { Q_OBJECT public: Window(QWidget *parent = 0); void startCars(); public slots: void setCarCoordinates(const GeoDataCoordinates &coord); private: MarbleWidget *m_marbleWidget; CarWorker *m_firstWorker; CarWorker *m_secondWorker; GeoDataPlacemark *m_carFirst; GeoDataPlacemark *m_carSecond; QThread *m_threadFirst; QThread *m_threadSecond; }; Window::Window(QWidget *parent) : QWidget(parent), m_marbleWidget(new MarbleWidget) { QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(m_marbleWidget); setLayout(layout); // Load the OpenStreetMap map m_marbleWidget->setMapThemeId("earth/openstreetmap/openstreetmap.dgml"); m_marbleWidget->setProjection( Mercator ); setGeometry(80, 60, 1000, 800); GeoDataCoordinates Kiev(30.523333, 50.45, 0.0, GeoDataCoordinates::Degree); m_marbleWidget->centerOn(Kiev); m_marbleWidget->setZoom(2300); m_carFirst = new GeoDataPlacemark("Bus"); m_carSecond = new GeoDataPlacemark("Car"); GeoDataDocument *document = new GeoDataDocument; document->append(m_carFirst); document->append(m_carSecond); m_marbleWidget->model()->treeModel()->addDocument(document); show(); } void Window::startCars() { GeoDataCoordinates Kiev(30.523333, 50.45, 0.0, GeoDataCoordinates::Degree); m_threadFirst = new QThread; m_firstWorker = new CarWorker(Kiev, (qreal)0.1, (qreal)0.7); m_firstWorker->moveToThread(m_threadFirst); connect(m_firstWorker, SIGNAL(coordinatesChanged(GeoDataCoordinates)), this, SLOT(setCarCoordinates(GeoDataCoordinates)), Qt::BlockingQueuedConnection); m_threadSecond = new QThread; m_secondWorker = new CarWorker(Kiev, (qreal)0.2, (qreal)-0.5); m_secondWorker->moveToThread(m_threadSecond); connect(m_secondWorker, SIGNAL(coordinatesChanged(GeoDataCoordinates)), this, SLOT(setCarCoordinates(GeoDataCoordinates)), Qt::BlockingQueuedConnection); connect(m_threadFirst, SIGNAL(started()), m_firstWorker, SLOT(startWork())); connect(m_threadFirst, SIGNAL(finished()), m_firstWorker, SLOT(finishWork())); connect(m_threadSecond, SIGNAL(started()), m_secondWorker, SLOT(startWork())); connect(m_threadSecond, SIGNAL(finished()), m_secondWorker, SLOT(finishWork())); m_threadFirst->start(); m_threadSecond->start(); } void Window::setCarCoordinates(const GeoDataCoordinates &coord) { CarWorker *worker = qobject_cast<CarWorker*>(sender()); if (worker == m_firstWorker) { m_carFirst->setCoordinate(coord); m_marbleWidget->model()->treeModel()->updateFeature(m_carFirst); } else if (worker == m_secondWorker) { m_carSecond->setCoordinate(coord); m_marbleWidget->model()->treeModel()->updateFeature(m_carSecond); } } // Main (start point) int main(int argc, char** argv) { QApplication app(argc,argv); Window window; window.startCars(); return app.exec(); } #include "vehicletracking.moc"
Before running qmake, create a qmake project file
TEMPLATE = app TARGET = vehicletracking DEPENDPATH += . INCLUDEPATH += . SOURCES += vehicletracking.cpp LIBS += -lmarblewidget
Save this file as "vehicletracking.pro" in the same directory and run
qmake
makethen execute ./vehicletracking. If everything goes fine, marble widget will appear as shown in the screenshot at the start of the tutorial.
Find your way and explore the world! ;)