QStringList services = QGeoServiceProvider::availableServiceProviders(); if (services.isEmpty()) { // }
QGeoMappingManager *manager = service.mappingManager();
In order to query the manager to search for objects on the map or route, you need to call searchManager () or routingManager () . QGraphicsScene scene; QGraphicsView view; view.setScene(&scene); QGraphicsGeoMap *geoMap = new QGraphicsGeoMap(manager); scene.addItem(geoMap);
class MyClass : public QObject { Q_OBJECT public: MyClass(QObject *parent = 0) : QObject(parent) { QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(this); if (source) { connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo))); source->startUpdates(); } } private slots: void positionUpdated(const QGeoPositionInfo &info) { qDebug() << "Position updated:" << info; } };
// Emit updates every 30 seconds if available QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(someParent); if (source) source->setUpdateInterval(30000);
class GeoMap : public QGraphicsGeoMap { Q_OBJECT Q_PROPERTY(double centerLatitude READ centerLatitude WRITE setCenterLatitude) Q_PROPERTY(double centerLongitude READ centerLongitude WRITE setCenterLongitude) public: GeoMap(QGeoMappingManager *manager, QGraphicsItem *parent = 0); void animatedPanTo(const QGeoCoordinate& center); double centerLatitude() const { return center().latitude(); } void setCenterLatitude(double lat); double centerLongitude() const { return center().longitude(); } void setCenterLongitude(double lon); //...
void GeoMap::animatedPanTo(const QtMobility::QGeoCoordinate ¢er) { QGeoCoordinate curStart(this->center()); if (curStart == center) return; // setStatusBarText(QString("Panning to %1").arg(center.toString(QGeoCoordinate::Degrees))); QPropertyAnimation *latAnim = new QPropertyAnimation(this, "centerLatitude"); latAnim->setEndValue(center.latitude()); latAnim->setDuration(300); QPropertyAnimation *lonAnim = new QPropertyAnimation(this, "centerLongitude"); lonAnim->setEndValue(center.longitude()); lonAnim->setDuration(300); QParallelAnimationGroup *group = new QParallelAnimationGroup; group->addAnimation(latAnim); group->addAnimation(lonAnim); group->start(QAbstractAnimation::DeleteWhenStopped); } void GeoMap::setCenterLatitude(double lat) { QGeoCoordinate c = center(); c.setLatitude(lat); setCenter( c); } void GeoMap::setCenterLongitude(double lon) { QGeoCoordinate c = center(); c.setLongitude(lon); setCenter( c); }
Now, when you call animatedPanTo (), the map will smoothly move to the specified coordinate and at the right angle, i.e. if the new coordinate is higher relative to the current center, then the map will move up and so on. Since QPropertyAnimation does not work with QGeoCoordinate by default , I added two properties to the map that the animation can work with ( list of supported types). Of course, you could register your interpolator and register QGeoCoordinate for QVariant , but with the properties it seems to me much clearer and more elegant. bool GeoMap::sceneEvent(QEvent *event) { switch (event->type()) { case QEvent::Gesture: { if (QGestureEvent *gesture = static_cast<QGestureEvent *>(event)) { if (QPinchGesture *pinch = static_cast<QPinchGesture *>(gesture->gesture(Qt::PinchGesture))) { qreal scale = qLn(pinch->scaleFactor())/qLn(2); qreal zoom = 0; zoom = scale > 0 ? 1 : -1; setZoomLevel(zoomLevel() + zoom); return true; } } } default: break; } return QGraphicsGeoMap::sceneEvent(event); }
protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); void wheelEvent(QGraphicsSceneWheelEvent *event); void keyPressEvent(QKeyEvent * event); void keyReleaseEvent(QKeyEvent * event); bool sceneEvent(QEvent *event); private slots: void kineticTimerEvent(); private: void panFloatWrapper(const QPointF & delta); void applyPan(const Qt::KeyboardModifiers & modifiers); void setStatusBarText(const QString &text); private: bool panActive; bool panDecellerate; // Fractional pan, used by panFloatWrapper QPointF remainingPan; // current kinetic panning speed, in pixel/msec QPointF kineticPanSpeed; QPoint panDir; QTimer kineticTimer; QTime lastMoveTime; // An entry in the mouse history. first=speed, second=time typedef QPair<QPointF, QTime> MouseHistoryEntry; // A history of the last (currently 5) mouse move events is stored in order to smooth out // movement detection for kinetic panning QList<MouseHistoryEntry> mouseHistory; StatusBarItem *m_statusBar; };
//////////////////////////////////////////////////////////////////////////////// // TODO: Some of these could be exposed in a GUI and should probably be put elsewhere in that case // (and made non-const) // Kinect annimation properties static const bool enableKineticPanning = true; // time until kinetic panning speed slows down to 50%, in msec static const qreal kineticPanningHalflife = 160.0; // keyboard panning speed without modifiers, in pixels/msec static const qreal panSpeedNormal = 0.3; // keyboard panning speed with shift, in pixels/msec static const qreal panSpeedFast = 1.0; // minimum panning speed, in pixels/msec static const qreal kineticPanSpeedThreshold = 0.005; // temporal resolution. Smaller values take more CPU but improve visual quality static const int kineticPanningResolution = 20; // maximum time between last mouse move and mouse release for kinetic panning to kick in static const int holdTimeThreshold = 100; ////////////////////////////////////////////////////////////////////////////////
I think nothing needs to be explained, everything is clear from the comments. void GeoMap::mousePressEvent(QGraphicsSceneMouseEvent *event) { setFocus(); if (event->button() == Qt::LeftButton) { panActive = true; // When pressing, stop the timer and stop all current kinetic panning kineticTimer.stop(); kineticPanSpeed = QPointF(); lastMoveTime = QTime::currentTime(); } event->accept(); } void GeoMap::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (panActive) { setCursor(Qt::ClosedHandCursor); // Calculate time delta QTime currentTime = QTime::currentTime(); int deltaTime = lastMoveTime.msecsTo(currentTime); lastMoveTime = currentTime; // Calculate position delta QPointF delta = event->lastPos() - event->pos(); // Calculate and set speed if (deltaTime > 0) { kineticPanSpeed = delta / deltaTime; mouseHistory.push_back(MouseHistoryEntry(kineticPanSpeed, currentTime)); mouseHistory.pop_front(); } // Pan map panFloatWrapper(delta); } event->accept(); } void GeoMap::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { if (event->button() == Qt::LeftButton && panActive) { panActive = false; setCursor(Qt::OpenHandCursor); if (!enableKineticPanning || lastMoveTime.msecsTo(QTime::currentTime()) > holdTimeThreshold) { return; } kineticPanSpeed = QPointF(); int entries_considered = 0; QTime currentTime = QTime::currentTime(); foreach(MouseHistoryEntry entry, mouseHistory) { // first=speed, second=time int deltaTime = entry.second.msecsTo(currentTime); if (deltaTime < holdTimeThreshold) { kineticPanSpeed += entry.first; entries_considered++; } } if (entries_considered > 0) kineticPanSpeed /= entries_considered; lastMoveTime = currentTime; // When releasing the mouse button/finger while moving, // start the kinetic panning timer kineticTimer.start(); panDecellerate = true; } event->accept(); } void GeoMap::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) { setFocus(); animatedPanTo(screenPositionToCoordinate(event->pos())); event->accept(); } // ... void GeoMap::kineticTimerEvent() { QTime currentTime = QTime::currentTime(); int deltaTime = lastMoveTime.msecsTo(currentTime); lastMoveTime = currentTime; if (panDecellerate) kineticPanSpeed *= qPow(qreal(0.5), qreal(deltaTime / kineticPanningHalflife)); QPointF scaledSpeed = kineticPanSpeed * deltaTime; if (kineticPanSpeed.manhattanLength() < kineticPanSpeedThreshold) { // Kinetic panning is almost halted -> stop it. kineticTimer.stop(); return; } panFloatWrapper(scaledSpeed); } // Wraps the pan(int, int) method to achieve floating point accuracy, // which is needed to scroll smoothly. void GeoMap::panFloatWrapper(const QPointF & delta) { // Add to previously stored panning distance remainingPan += delta; // Convert to integers QPoint move = remainingPan.toPoint(); // Commit mouse movement pan(move.x(), move.y()); // Store committed mouse movement remainingPan -= move; }
I dropped the implementation of the keyboard processing, you can get acquainted with it by downloading the entire project . The implementation of the inertial movement is taken slightly less than completely from this example, thanks to the developers for the excellent documentation and excellent examples. // An animated status bar item that appears at the bottom // of the map class StatusBarItem : public QObject, public QGraphicsRectItem { Q_OBJECT Q_PROPERTY(int offset READ offset WRITE setOffset) public: explicit 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: int m_offset; QGraphicsSimpleTextItem *m_textItem; };
First, the item itself is a rectangle that contains the QGraphicsSimpleTextItem and simply controls it. Let's look at the implementation of this class: StatusBarItem::StatusBarItem() { m_offset = 0; setPen(QPen(QBrush(), 0)); setBrush(QBrush(QColor(0,0,0,120))); m_textItem = new QGraphicsSimpleTextItem(this); m_textItem->setBrush(QBrush(Qt::white)); setText(""); } StatusBarItem::~StatusBarItem() { } void StatusBarItem::setText(QString text) { m_textItem->setText(text); QRectF rect = m_textItem->boundingRect(); QPointF delta = this->rect().center() - rect.center(); m_textItem->setPos(delta.x(), delta.y()); } int StatusBarItem::offset() const { return m_offset; } void StatusBarItem::setRect(qreal x, qreal y, qreal w, qreal h) { QGraphicsRectItem::setRect(x, y + m_offset, w, h); QFont f; f.setFixedPitch(true); f.setPixelSize(h/1.1); m_textItem->setFont(f); setText(m_textItem->text()); } void StatusBarItem::setOffset(int offset) { this->setY(this->y() - m_offset + offset); m_offset = offset; } void StatusBarItem::showText(QString text, quint32 timeout) { setText(text); show(); QTimer::singleShot(timeout, this, SLOT(hide())); } 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(m_offset); anim->setEndValue(0); anim->setDuration(500); anim->start(QAbstractAnimation::DeleteWhenStopped); }
The animation controls the position of the element and, depending on the method, either pulls the element up or down. Qt 's motto is “Code Less. Create More. " Works in full. // // , 0 m_geoMap->setZoomLevel(m_geoMap->zoomLevel() + zoomLevel(event->pos()));
#ifndef QT_NO_OPENGL setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); viewport()->setMouseTracking(true); setRenderHint(QPainter::HighQualityAntialiasing, true); #endif
And set the flag for QGraphicsView , since QGLWidget does not support partial screen updates: graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
Also, if you have a lot of animated or moving elements in the scene, then for better performance, you can disable indexing of elements: graphicsScene->setItemIndexMethod(QGraphicsScene::NoIndex);
Indexing is great for static scene elements, it enhances the search for scene elements in functions such as items () and itemAt () . You may also consider optimizing flags for QGraphicsView .Source: https://habr.com/ru/post/134289/
All Articles