(Created) |
m (Fixes) |
||
| Line 11: | Line 11: | ||
}} | }} | ||
| − | We want to present you another C++ example of Marble Runnable usage, here on KDE TechBase. It's about the cars. | + | 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 completely drunk car drivers. 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: |
[[File:Marble-VehicleTracking-1.png]] | [[File:Marble-VehicleTracking-1.png]] | ||
| Line 24: | Line 24: | ||
| − | + | 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: | |
# Create [http://qt-project.org/doc/qt-5.0/qtcore/qthread.html QThread] #1 | # Create [http://qt-project.org/doc/qt-5.0/qtcore/qthread.html QThread] #1 | ||
| Line 36: | Line 36: | ||
{{Warning|2=Important|1=Use here connection type Qt::BlockingQueuedConnection, another way GUI thread will be blocked}} | {{Warning|2=Important|1=Use here connection type Qt::BlockingQueuedConnection, another way GUI thread will be blocked}} | ||
| − | Now we have nice implementation plan | + | Now we have a nice implementation plan. Let's start coding. |
| − | # main() creates object of the Window (our main QWidget-based window) | + | # main() creates an object of the Window (our main QWidget-based window) |
# Window creates the [http://api.kde.org/stable/kdeedu-apidocs/marble/html/classMarbleWidget.html MarbleWidget] and sets two GeoDataPlacemarks there | # Window creates the [http://api.kde.org/stable/kdeedu-apidocs/marble/html/classMarbleWidget.html MarbleWidget] and sets two GeoDataPlacemarks there | ||
# main() calls Window::startCars() | # main() calls Window::startCars() | ||
| Line 46: | Line 46: | ||
# Threads are started | # Threads are started | ||
| + | Then each CarWorker creates a [http://qt-project.org/doc/qt-5.0/qtcore/qtimer.html 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. | |
| − | + | ||
| − | Everything is going on until application finishes. | + | |
'''The code with comments:''' | '''The code with comments:''' | ||
| Tutorial Series | Marble C++ Tutorial |
| Previous | Tutorial 12 - Displaying Places the proper way (via GeoDataDocuments) |
| What's Next | Tutorial 14 - Yet missing |
| 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 completely drunk car drivers. 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> // Copyright 2012 Torsten Rahn <tackat@kde.org> // #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: /// This signal will be emitted when we need to /// move the placemark. 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(); } /// Single timer loop iteration: /// Calculates new coordinates for the car void CarWorker::iterate() { /// A bit of math 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); /// Iteration is finished. We need to increase angle 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); } else if (worker == m_secondWorker) { m_carSecond->setCoordinate(coord); m_marbleWidget->model()->treeModel()->updateFeature(m_carSecond); } } // Main int main(int argc, char** argv) { QApplication app(argc,argv); Window window; window.startCars(); return app.exec(); } #include "vehicletracking.moc"
Find your way and explore the world! ;)