本节主要记录一下qt开发过程中离线地图瓦片的加载方式,瓦片加载选择graphicsView控件,同时为了不影响主线程事件和其他操作,这里采用了异步线程的操作,将地图瓦片加载的步骤放到了异步子线程之中。注:本记录仅为本人笔记记录。
一、效果展示

二、代码展示
1、思路
设计中,我们先选择graphicsView放置与ui设计界面,以作为瓦片的存放容器。
创建子线程:线程功能实现为筛选对应坐标编号的地图瓦片信息并传递给主线程进行显示
主线程:开启子线程的运行,并获取子线程传递的对应瓦片地图信息进行显示。
2、子线程代码实现
cpp
#ifndef MAPWORK_H
#define MAPWORK_H
#include <QObject>
#include <QPixmap>
#include <QFile>
class mapWork : public QObject
{
Q_OBJECT
public:
explicit mapWork(int zoom, int tileSize, QObject *parent = nullptr)
: QObject(parent), m_zoom(zoom), m_tileSize(tileSize) {}
public slots:
//自定义函数,是实现瓦片的查找和加载
void loadTiles(int xStart, int xEnd, int yStart, int yEnd, const QString& basePath) {
//X层级和y层级代表地图瓦片编号相对应的层级
for(int x = xStart; x < xEnd; ++x) {
for(int y = yStart; y < yEnd; ++y) {
//我的瓦片图文件存放在程序目录下的mapabc目录,satellite目录对应的是卫星地图瓦片,overlay目录对应的是街道瓦片数据
//先加载卫星瓦片地图
QString filename_1 = QString("%1/mapabc/satellite/%2/%3/%4.jpg")
.arg(basePath)
.arg(m_zoom)
.arg(x)
.arg(y);
if(QFile::exists(filename_1)) {
QPixmap pixmap(filename_1);
if(!pixmap.isNull()) {
emit tileLoaded(x, y, pixmap);//发送瓦片数据信息 对应的层级和图片文件
}
//加载街道地图瓦片
QString filename_2 = QString("%1/mapabc/overlay/%2/%3/%4.png")
.arg(basePath)
.arg(m_zoom)
.arg(x)
.arg(y);
if(QFile::exists(filename_2)) {
QPixmap pixmap2(filename_2);
if(!pixmap.isNull()) {
emit tileLoaded(x, y, pixmap2);//发送瓦片数据信息 对应的层级和图片文件
}
}
}
}
emit finished();
}
}
signals:
void tileLoaded(int x, int y, const QPixmap& pixmap); //定义信号,传递主线程对应的瓦片信息
void finished();
private:
int m_zoom; //瓦片层级
int m_tileSize; //瓦片的大小 256*256,此处为256
};
#endif // MAPWORK_H
3、主函数代码功能实现
mainwindow.h文件
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene> //场景
#include <QGraphicsView> //视图
#include <QGraphicsItem> //图元
#include <QDir>
#include "mapwork.h"
#include <QThread>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
void setMap();
private slots:
void addTileToScene(int x, int y, const QPixmap& pixmap);
private:
void setupThread();
Ui::MainWindow *ui;
QGraphicsScene *myScene;
//map 线程加载
mapWork *m_worker;
QThread *m_workerThread;
//map相关
int zoom = 14;//地图层级
int wap_X_start = 13373; //x层级瓦片和y层级瓦片的开始结束标号
int wap_X_end = 13604;
int wap_Y_start = 6123;
int wap_Y_end = 6290;
//程序路径
QDir currentDir = QDir::current();
int tileSize = 256;//每个瓦片的像素边长 256*256
};
#endif // MAINWINDOW_H
mainwindow.cpp文件
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPixmap>
#include <QTimer>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow),
zoom(14),
currentDir(QDir::current()),
tileSize(256)
{
ui->setupUi(this);
setupThread();//进入线程加载图片
//场景设置
myScene = new QGraphicsScene(this);
//初始化视图
ui->graphicsView->setScene(myScene);
ui->graphicsView->setRenderHint(QPainter::SmoothPixmapTransform);
ui->graphicsView->setCacheMode(QGraphicsView::CacheBackground);
setMap();
}
MainWindow::~MainWindow()
{
delete ui;
if(m_workerThread && m_workerThread->isRunning()) {
qDebug() << "Stopping worker thread...";
m_workerThread->quit();
if(!m_workerThread->wait(3000)) {
qCritical() << "线程未正常退出,强制终止";
m_workerThread->terminate();
}
}
}
void MainWindow::setupThread()
{
m_workerThread = new QThread(this);
m_worker = new mapWork(zoom, tileSize);
m_worker->moveToThread(m_workerThread);
// 完整信号连接
connect(m_worker, &mapWork::tileLoaded,
this, &MainWindow::addTileToScene);
connect(m_workerThread, &QThread::finished,
m_worker, &QObject::deleteLater);
m_workerThread->start();
}
void MainWindow::setMap()
{
const QString basePath = currentDir.absolutePath();
//线程安全
QMetaObject::invokeMethod(m_worker, "loadTiles", Qt::QueuedConnection,
Q_ARG(int, wap_X_start),
Q_ARG(int, wap_X_end),
Q_ARG(int, wap_Y_start),
Q_ARG(int, wap_Y_end),
Q_ARG(QString, basePath));
}
//插入图片元素
void MainWindow::addTileToScene(int x, int y, const QPixmap& pixmap)
{
QGraphicsPixmapItem* item = myScene->addPixmap(pixmap);
item->setPos(x * tileSize, y * tileSize);
}