Viadeo Twitter Google Bookmarks ! Facebook Digg del.icio.us MySpace Yahoo MyWeb Blinklist Netvouz Reddit Simpy StumbleUpon Bookmarks Windows Live Favorites 
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

Part 3 - Listening to satellites

Another useful part of the Location API is the ability to receive updates of the user's present geographic location from methods such as GPS or network positioning. We're going to add support to our MapsDemo for using these methods to update the "my location" marker we've already added in parts 1 and 2 of this tutorial.

But first we need an attractive way to present status messages to the user while they are busy looking at the map. We're going to do this using an animated translucent rectangle at the bottom of the display.

Animated status bar

First, set up the map to resize automatically:

 class MapsWidget : public QWidget
 {
     ...
 private:
     void resizeEvent(QResizeEvent *event);
     void showEvent(QShowEvent *event);
 };

 void MapsWidget::resizeEvent(QResizeEvent *event)
 {
     if (d->view && d->map) {
         d->view->resize(size());
         d->map->resize(size());
         d->view->centerOn(d->map);
     }
 }

 void MapsWidget::showEvent(QShowEvent *event)
 {
     if (d->view && d->map) {
         d->view->resize(size());
         d->map->resize(size());
         d->view->centerOn(d->map);
     }
 }

And now we add our new StatusBarItem class:

 class StatusBarItemPrivate;
 class StatusBarItem : public QObject, public QGraphicsRectItem
 {
     Q_OBJECT
     Q_PROPERTY(int offset READ offset WRITE setOffset)

 public:
     StatusBarItem();
     ~StatusBarItem();

     int offset() const;
     void setRect(qreal x, qreal y, qreal w, qreal h);

 public slots:
     void setText(QString text);

     void showText(QString text, quint32 timeout=3000);
     void show();
     void hide();

     void setOffset(int offset);

 private:
     StatusBarItemPrivate *d;
 };

Note that the order of base classes here is very important: QObject and then QGraphicsRectItem. Re-ordering the base classes will cause the code not to compile, as QGraphicsRectItem does not have a meta-object (for more details consult the documentation in Qt).

The offset property here is added so that when we come to animating our status bar, we can handle the case where the bar is sliding in and the window is being resized simultaneously. If we simply animated the y property of the GraphicsItem instead we would have difficulty handling this case.

Now add a pointer to one of these in MapsWidgetPrivate (and matching accessor methods):

 class MapsWidgetPrivate
 {
 public:
     ...
     StatusBarItem *statusBarItem;
 };

And we're ready for the implementation. The constructor is not terribly exciting, but sets the defaults for everything:

 class StatusBarItemPrivate
 {
 public:
     int offset;
     QGraphicsSimpleTextItem *textItem;
 };

 StatusBarItem::StatusBarItem() :
     d(new StatusBarItemPrivate)
 {
     d->offset = 0;

     setPen(QPen(QBrush(), 0));
     setBrush(QBrush(QColor(0,0,0,120)));

     d->textItem = new QGraphicsSimpleTextItem(this);
     d->textItem->setBrush(QBrush(Qt::white));

     setText("");
 }

The setText function, however, is more interesting;

 void StatusBarItem::setText(QString text)
 {
     d->textItem->setText(text);
     QRectF rect = d->textItem->boundingRect();
     QPointF delta = this->rect().center() - rect.center();
     d->textItem->setPos(delta.x(), delta.y());
 }

This re-centers the textItem inside its parent (the StatusBarItem) every time the text changes.

Also, the setRect method is used to update the size and position of the status bar:

 void StatusBarItem::setRect(qreal x, qreal y, qreal w, qreal h)
 {
     QGraphicsRectItem::setRect(x, y + d->offset, w, h);
     setText(d->textItem->text());
 }

Here we see the use of the offset property for the first time. The idea is to call setRect to specify a rectangle that is below the bottom of the visible area in the QGraphicsView. Then offset is used to bump the status bar up into the visible area when needed.

Whenever we change the offset we should re-calculate our own y value using the rect and the offset together:

 void StatusBarItem::setOffset(int offset)
 {
     this->setY(this->y() - d->offset + offset);
     d->offset = offset;
 }

And now finally, the animations:

 void StatusBarItem::show()
 {
     QPropertyAnimation *anim = new QPropertyAnimation(this, "offset");
     anim->setStartValue(0);
     anim->setEndValue(-1 * rect().height());
     anim->setDuration(500);
     anim->start(QAbstractAnimation::DeleteWhenStopped);
 }

 void StatusBarItem::hide()
 {
     QPropertyAnimation *anim = new QPropertyAnimation(this, "offset");
     anim->setStartValue(d->offset);
     anim->setEndValue(0);
     anim->setDuration(500);
     anim->start(QAbstractAnimation::DeleteWhenStopped);
 }

You can see here that we simply use QPropertyAnimations on the offset property we just defined. This produces a nice linear slide in and out whenever show() or hide() are called.

Lastly, one convenience method:

 void StatusBarItem::showText(QString text, quint32 timeout)
 {
     setText(text);
     show();
     QTimer::singleShot(timeout, this, SLOT(hide()));
 }

This lets us more easily display a status message when we only want it to appear and disappear soon afterwards.

Then we have only to add this into our MapsWidget:

 void MapsWidget::initialize(QGeoMappingManager *manager)
 {
     QGraphicsScene *sc;
     ...
     d->statusBarItem = new StatusBarItem;
     sc->addItem(d->statusBarItem);
 }

 void MapsWidget::resizeEvent(QResizeEvent *event)
 {
     if (d->view && d->map) {
         ...
         d->statusBarItem->setRect(0, height(), width(), 20);
     }
 }

 // and similarly in MapsWidget::showEvent()

Getting GPS data

Now we move on to the focus of this section: GPS data and how to get it. The QGeoPositionInfoSource class gives a convenient interface to receive position updates. We're going to add one to our MainWindow:

 class MainWindow : public QMainWindow
 {
 private:
     QGeoPositionInfoSource *positionSource;

 private slots:
     // slot to receive updates
     void updateMyPosition(QGeoPositionInfo info);
 };

And in initialize() we'll set it up. We're just using whatever the default position source for the platform happens to be, at an update interval of 1000ms, which is plenty for a basic maps application. Once set up, we call the source's startUpdates() method to begin receiving position updates.

 void MainWindow::initialize()
 {
     ...
     if (positionSource)
         delete positionSource;

     positionSource = QGeoPositionInfoSource::createDefaultSource(this);

     if (!positionSource) {
         mapsWidget->statusBar()->showText("Could not open GPS", 5000);
         mapsWidget->setMyLocation(QGeoCoordinate(-27.5796, 153.1));
     } else {
         positionSource->setUpdateInterval(1000);
         connect(positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)),
                 this, SLOT(updateMyPosition(QGeoPositionInfo)));
         positionSource->startUpdates();
         mapsWidget->statusBar()->showText("Opening GPS...");
     }
 }

Here we also make use of the StatusBarItem to display a message when we are able or unable to open the QGeoPositionInfoSource.

And then in the slot updateMyPosition, we use this to set the myLocation marker.

 void MainWindow::updateMyPosition(QGeoPositionInfo info)
 {
     if (mapsWidget) {
         mapsWidget->setMyLocation(info.coordinate());
     }
 }

So, running the code as is, we have a moving marker for "My Location" that follows our actual GPS or network-sourced location. If you start driving your car with this app running however, you'll quickly notice the fact that the viewport does not pan to follow you as you leave the map area.

We could simply add a call to setCenter() on the map object in the updateMyPosition slot, but in the interests of prettiness, we are going to make a nice smoothly animated transition instead.

Following and animated panning

First, add a new boolean member variable to MainWindow, called tracking, to keep track of whether the viewport is currently following the My Location marker:

 class MainWindow : public QMainWindow
 {
 private:
     bool tracking;
     ...
 };

Our intended design is that initially, the viewport will be in tracking mode. It will continue this way until the view is manually panned by the user, at which point tracking will stop. Then, if the user clicks the "My Location" menu option to re-center the map, we resume tracking once again.

So we will need a way to notify the MainWindow that the user has panned the view. Add a new signal mapPanned() to MapsWidget, and a corresponding signal panned() to GeoMap, as we did for clicked().

 class MapsWidget : public QWidget
 {
 signals:
     void mapPanned();
     ...
 };

 class GeoMap : public QGraphicsGeoMap
 {
 signals:
     void panned();
     ...
 };

 void MapsWidget::initialize(QGeoMappingManager *manager)
 {
     ...
     connect(geoMap, SIGNAL(panned()),
             this, SIGNAL(mapPanned()));
     ...
 }

And now we simply emit it when a user pan takes place:

 void GeoMap::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
 {
     if (panActive) {
         ...
         emit panned();
     }
     ...
 }

Back up in MainWindow, we create a slot disableTracking and hook up the new signal to it:

 class MainWindow : public QMainWindow
 {
     ...
 private slots:
     ...
     void disableTracking();
     ...
 };

 void MainWindow::initialize()
 {
     ...
     connect(mapsWidget, SIGNAL(mapPanned()),
             this, SLOT(disableTracking()));
     ...
 }

And finally in the slot itself we simply set the flag we created earlier:

 void MainWindow::disableTracking()
 {
     tracking = false;
 }

Next we want animated panning to be available. Add a new method on MapsWidget:

 class MapsWidget : public QWidget
 {
 public:
     ...
     void animatedPanTo(QGeoCoordinate center);
     ...
 };

To do animations in Qt, it's always easiest if we can make use of a QPropertyAnimation, and to do this you need a Q_PROPERTY to act upon. We'll use two animations in parallel, one moving latitude and one moving longitude, so we need two Q_PROPERTIES:

 class GeoMap : public QGraphicsGeoMap
 {
     Q_OBJECT

     Q_PROPERTY(double centerLatitude READ centerLatitude WRITE setCenterLatitude)
     Q_PROPERTY(double centerLongitude READ centerLongitude WRITE setCenterLongitude)

 public:
     ...
     double centerLatitude() const;
     void setCenterLatitude(double lat);
     double centerLongitude() const;
     void setCenterLongitude(double lon);
     ...
 };

These functions simply adjust the corresponding value on center() and then call setCenter() with the new QGeoCoordinate.

Now we can implement our animatedPanTo() method:

 void MapsWidget::animatedPanTo(QGeoCoordinate center)
 {
     if (!d->map)
         return;

     QPropertyAnimation *latAnim = new QPropertyAnimation(d->map, "centerLatitude");
     latAnim->setEndValue(center.latitude());
     latAnim->setDuration(200);
     QPropertyAnimation *lonAnim = new QPropertyAnimation(d->map, "centerLongitude");
     lonAnim->setEndValue(center.longitude());
     lonAnim->setDuration(200);

     QParallelAnimationGroup *group = new QParallelAnimationGroup;
     group->addAnimation(latAnim);
     group->addAnimation(lonAnim);
     group->start(QAbstractAnimation::DeleteWhenStopped);
 }

To bring it all together, we make the last few changes in MainWindow:

 void MainWindow::goToMyLocation()
 {
     mapsWidget->animatedPanTo(markerManager->myLocation());
     tracking = true;
 }

 void MainWindow::updateMyPosition(QGeoPositionInfo info)
 {
     if (mapsWidget) {
         mapsWidget->setMyLocation(info.coordinate());
         if (tracking)
             mapsWidget->animatedPanTo(info.coordinate());
     }
 }

And now we have the simple location tracking functionality we set out to implement.

Publicité

Best Of

Actualités les plus lues

Semaine
Mois
Année
  1. « Quelque chose ne va vraiment pas avec les développeurs "modernes" », un développeur à "l'ancienne" critique la multiplication des bibliothèques 53
  2. Les développeurs ignorent-ils trop les failles découvertes dans leur code ? Prenez-vous en compte les remarques des autres ? 17
  3. BlackBerry 10 : premières images du prochain OS de RIM qui devrait intégrer des widgets et des tuiles inspirées de Windows Phone 0
  4. Apercevoir la troisième dimension ou l'utilisation multithreadée d'OpenGL dans Qt, un article des Qt Quarterly traduit par Guillaume Belz 0
  5. Quelles nouveautés de C++11 Visual C++ doit-il rapidement intégrer ? Donnez-nous votre avis 10
  6. Adieu qmake, bienvenue qbs : Qt Building Suite, un outil déclaratif et extensible pour la compilation de projets Qt 17
  7. La rubrique Qt a besoin de vous ! 1
Page suivante

Le blog Digia au hasard

Logo

Déploiement d'applications Qt Commercial sur les tablettes Windows 8

Le blog Digia est l'endroit privilégié pour la communication sur l'édition commerciale de Qt, où des réponses publiques sont apportées aux questions les plus posées au support. Lire l'article.

Communauté

Ressources

Liens utiles

Contact

  • Vous souhaitez rejoindre la rédaction ou proposer un tutoriel, une traduction, une question... ? Postez dans le forum Contribuez ou contactez-nous par MP ou par email (voir en bas de page).

Qt dans le magazine

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt qtmobility-1.2
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
 
 
 
 
Partenaires

Hébergement Web