Qt小模块 - 缩放移动图片、插入巡航点

一、实现效果

上面是“实现效果”,可以实现放大、缩小、还原和移动地图图片,插入导航点,并且在缩放前后巡航点位置不变。


二、程序设计

2.1 showMap.h

#ifndef SHOWMAP_H
#define SHOWMAP_H

#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QMouseEvent>
#include <QDebug>

#define WINDOW_WIDTH 1024 //窗口宽度
#define WINDOW_HEIGHT 768 //窗口高度

namespace Ui {
class ShowMap;
}

class ShowMap : public QWidget
{
    Q_OBJECT

public:
    explicit ShowMap(QWidget *parent = nullptr);
    ~ShowMap();

private slots:
    void on_addBoardButton_clicked(); //放大背景图按钮-点击槽函
    void on_lessBoardButton_clicked(); //缩小背景图按钮-点击槽函数
    void on_resetBoardButton_clicked(); //复位背景图按钮-点击槽函数
    void doProssTimerOut(); //超时函数:超时后将当前鼠标按下坐标,赋给预设点绘制坐标

    void on_manualButton_clicked(); //手动按钮-点击槽函数
    void on_addItemButton_clicked(); //手动模式添加巡航点按钮-点击槽函数
    void on_cruiseGroupBox_returnBtn_clicked(); //巡航分组框的返回按钮-点击槽函数
    void on_lessItemButton_clicked(); //手动模式删除巡航点按钮-点击槽函数

private:
    Ui::ShowMap *ui;

    //地图相关
    QPixmap pix;
    QPoint OriginSize; //地图图片的尺寸大小
    QPoint CurrImgSize; //地图图片的尺寸大小
    QPoint ImgPaintOrigin; //地图图片的绘制原点
    int AddLessCount; //放大缩小次数

    //设定点相关
    QList<QPoint> Point_xy; //定义设定点列表
    QPoint PreSetPoint_xy; //设定点绘制坐标
    QPoint CentPoint_xy; //地图图片中心点
    QPoint MousePress_xy; //鼠标当前点
    QPoint MapRemov_Old; //鼠标不点击在摇杆大圆内时的鼠标点击坐标
    int MouseAllMove_x; //鼠标全部移动x坐标
    int MouseAllMove_y; //鼠标全部移动y坐标

    QTimer *timer; //预设点设置相关定时器

    void Init(); //初始化窗口
    void paintEvent(QPaintEvent *event); //绘图事件:绘制地图、预设点
    void mousePressEvent(QMouseEvent *e); //鼠标按下事件:获取摇杆所在的实时坐标
    void mouseMoveEvent(QMouseEvent *e); //鼠标移动事件:实现摇杆功能
};

#endif // SHOWMAP_H

上面是“显示地图程序”的头文件。

2.2 showMap.cpp

#include "showmap.h"
#include "ui_showmap.h"

ShowMap::ShowMap(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ShowMap)
{
    ui->setupUi(this);

    //初始化窗口
    Init();
}

ShowMap::~ShowMap()
{
    delete ui;
}

//初始化窗口
void ShowMap::Init()
{
    //设置窗口无边框且窗口显示在最顶层
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);

    //巡航点GroupBox
    ui->cruiseGroupBox->setGeometry(20,176,296,386);
    ui->cruiseGroupBox->hide();

    //初始化地图图片
    pix.load(":/new/prefix1/image/map.png");
    //pix.load(":/new/prefix1/image/map.pgm");
    //初始化地图图片的原始尺寸
    OriginSize.setX(pix.width());
    OriginSize.setY(pix.height());
    //初始化地图图片的实时尺寸
    CurrImgSize.setX(OriginSize.x());
    CurrImgSize.setY(OriginSize.y());
    //初始化地图图片的绘制原点
    ImgPaintOrigin.setX(-(OriginSize.x()-WINDOW_WIDTH)/2);
    ImgPaintOrigin.setY(-(OriginSize.y()-WINDOW_HEIGHT)/2);

    //初始化中心点坐标
    CentPoint_xy.setX(WINDOW_WIDTH/2);
    CentPoint_xy.setY(WINDOW_HEIGHT/2);
    //初始化鼠标全部移动坐标
    MouseAllMove_x = 0;
    MouseAllMove_y = 0;
    //初始化放大缩小次数
    AddLessCount = 0;

    //初始化预设点设置相关定时器:防止鼠标移动前的鼠标按下,错误更新预设点绘制坐标
    timer = new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(doProssTimerOut()),Qt::UniqueConnection);

    this->update();
}

//绘图事件:绘制地图图片、预设点
void ShowMap::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true); //抗锯齿
    painter.setRenderHints(QPainter::SmoothPixmapTransform); //消锯齿

    //绘制地图图片(绘制原点可以是窗口负坐标,即窗口左上角未显示区域)
    painter.drawPixmap(ImgPaintOrigin.x(),ImgPaintOrigin.y(),CurrImgSize.x(),CurrImgSize.y(),pix);

    //绘制设定点
    QPen pen;
    QPixmap pixPoint;
    pixPoint.load(":/new/prefix1/image/point.png");
    if(PreSetPoint_xy.x()!=0 && PreSetPoint_xy.y()!=0) //不允许在原点(0,0)上画点
    {
        //绘制设定点(设定点图片大小为44*44,绘制起点是图片的左上角坐标)
        painter.drawPixmap(PreSetPoint_xy.x()-pixPoint.width()/2,PreSetPoint_xy.y()-pixPoint.height(),pixPoint.width(),pixPoint.height(),pixPoint);
    }

    //绘制设置点和设置点序号
    pen.setColor(Qt::red);
    painter.setPen(pen);
    QFont font = painter.font();
    font.setPixelSize(20);
    painter.setFont(font);
    for(int i=0;i<Point_xy.count();i++)
    {
        //绘制设置点
        painter.drawPixmap(Point_xy.at(i).x()-22,Point_xy.at(i).y()-44,44,44,pixPoint);
        //绘制设置点序号
        painter.drawText(Point_xy.at(i).x()-5,Point_xy.at(i).y()-22,QString::number(i));
    }

    //绘制设置点连线
    pen.setColor(QColor(85,255,255));
    pen.setStyle(Qt::DotLine);
    pen.setWidth(3);
    painter.setPen(pen);
    if(Point_xy.count()>1)
    {
        for(int i=0;i<Point_xy.count()-1;i++)
        {
            painter.drawLine(Point_xy.at(i),Point_xy.at(i+1));
        }
    }
}

//放大背景图按钮-点击槽函数
void ShowMap::on_addBoardButton_clicked()
{
    //只允许放大3次
    if(AddLessCount<3)
    {
        //放大缩小次数加一
        AddLessCount++;

        //设置地图图片的绘制原点
        ImgPaintOrigin.setX( int(-(OriginSize.x()*pow(2,AddLessCount)-WINDOW_WIDTH)/2) );
        ImgPaintOrigin.setY( int(-(OriginSize.y()*pow(2,AddLessCount)-WINDOW_HEIGHT)/2) );
        //设置地图图片的绘制长宽
        CurrImgSize.setX(CurrImgSize.x()*2);
        CurrImgSize.setY(CurrImgSize.y()*2);

        //在放大缩小前,使中心点、预设点和设置点绘制坐标,还原成鼠标移动前的大小
        CentPoint_xy.setX(CentPoint_xy.x() - MouseAllMove_x);
        CentPoint_xy.setY(CentPoint_xy.y() - MouseAllMove_y);
        PreSetPoint_xy.setX(PreSetPoint_xy.x() - MouseAllMove_x);
        PreSetPoint_xy.setY(PreSetPoint_xy.y() - MouseAllMove_y);
        for(int i=0;i<Point_xy.count();i++)
        {
            Point_xy[i].setX(Point_xy[i].x() - MouseAllMove_x);
            Point_xy[i].setY(Point_xy[i].y() - MouseAllMove_y);
        }
        //清零鼠标全部移动坐标
        MouseAllMove_x = 0;
        MouseAllMove_y = 0;
        //使预设点坐标在放大时位置不改变(放大缩小后,预设点要求相对地图位置不变)
        PreSetPoint_xy.setX(PreSetPoint_xy.x()*2-CentPoint_xy.x());
        PreSetPoint_xy.setY(PreSetPoint_xy.y()*2-CentPoint_xy.y());
        //使设置点Point列表的各点,在放大时位置不改变
        for(int i=0;i<Point_xy.count();i++)
        {
            //使设定点坐标在放大时位置不改变
            Point_xy[i].setX(Point_xy[i].x()*2-CentPoint_xy.x());
            Point_xy[i].setY(Point_xy[i].y()*2-CentPoint_xy.y());
        }

        this->update();
    }
}

//缩小背景图按钮-点击槽函数
void ShowMap::on_lessBoardButton_clicked()
{
    //图片若小于等于MAP_WIDTH*MAP_HEIGHT,则说明是原尺存没有放大,不可以缩小,否则可以正常缩小
    if(CurrImgSize.x()>OriginSize.x()&&CurrImgSize.y()>OriginSize.y())
    {
        //放大缩小次数减一
        AddLessCount--;

        //设置图片绘制原点
        ImgPaintOrigin.setX( int(-(OriginSize.x()*pow(2,AddLessCount)-WINDOW_WIDTH)/2) );
        ImgPaintOrigin.setY( int(-(OriginSize.y()*pow(2,AddLessCount)-WINDOW_HEIGHT)/2) );
        //设置图片绘制长宽
        CurrImgSize.setX(CurrImgSize.x()/2);
        CurrImgSize.setY(CurrImgSize.y()/2);

        //在放大缩小前,使中心点、预设点和设置点绘制坐标,还原成鼠标移动前的大小
        CentPoint_xy.setX(CentPoint_xy.x() - MouseAllMove_x);
        CentPoint_xy.setY(CentPoint_xy.y() - MouseAllMove_y);
        PreSetPoint_xy.setX(PreSetPoint_xy.x() - MouseAllMove_x);
        PreSetPoint_xy.setY(PreSetPoint_xy.y() - MouseAllMove_y);
        for(int i=0;i<Point_xy.count();i++)
        {
            Point_xy[i].setX(Point_xy[i].x() - MouseAllMove_x);
            Point_xy[i].setY(Point_xy[i].y() - MouseAllMove_y);
        }
        //清零鼠标全部移动坐标
        MouseAllMove_x = 0;
        MouseAllMove_y = 0;
        //使预设点坐标在缩小时位置不改变
        PreSetPoint_xy.setX((PreSetPoint_xy.x()+CentPoint_xy.x())/2);
        PreSetPoint_xy.setY((PreSetPoint_xy.y()+CentPoint_xy.y())/2);
        //使设置点Point列表的各点,在缩小时位置不改变
        for(int i=0;i<Point_xy.count();i++)
        {
            //使设定点坐标在缩小时位置不改变
            Point_xy[i].setX((Point_xy[i].x()+CentPoint_xy.x())/2);
            Point_xy[i].setY((Point_xy[i].y()+CentPoint_xy.y())/2);
        }

        this->update();
    }
}

//复位背景图按钮-点击槽函数
void ShowMap::on_resetBoardButton_clicked()
{
    //设置地图图片的绘制原点
    ImgPaintOrigin.setX( int(-(OriginSize.x()*pow(2,0)-WINDOW_WIDTH)/2) );
    ImgPaintOrigin.setY( int(-(OriginSize.y()*pow(2,0)-WINDOW_HEIGHT)/2) );
    //设置地图图片的绘制长宽
    CurrImgSize.setX(OriginSize.x());
    CurrImgSize.setY(OriginSize.y());

    //在复位前,使预设点和设置点绘制坐标,还原成鼠标移动前的大小
    PreSetPoint_xy.setX(PreSetPoint_xy.x() - MouseAllMove_x);
    PreSetPoint_xy.setY(PreSetPoint_xy.y() - MouseAllMove_y);
    for(int i=0;i<Point_xy.count();i++)
    {
        Point_xy[i].setX(Point_xy[i].x() - MouseAllMove_x);
        Point_xy[i].setY(Point_xy[i].y() - MouseAllMove_y);
    }
    //清零鼠标全部移动坐标
    MouseAllMove_x = 0;
    MouseAllMove_y = 0;
    //复位中心点坐标
    CentPoint_xy.setX(WINDOW_WIDTH/2);
    CentPoint_xy.setY(WINDOW_HEIGHT/2);
    //逐次缩小,使预设点不变
    for(int j=0;j<AddLessCount;j++)
    {
        //使预设点坐标在复位时位置不改变(有问题,无效)
        PreSetPoint_xy.setX((PreSetPoint_xy.x()+CentPoint_xy.x())/2);
        PreSetPoint_xy.setY((PreSetPoint_xy.y()+CentPoint_xy.y())/2);
        //使设置点Point列表的各点,在复位时位置不改变
        for(int i=0;i<Point_xy.count();i++)
        {
            Point_xy[i].setX((Point_xy[i].x()+CentPoint_xy.x())/2);
            Point_xy[i].setY((Point_xy[i].y()+CentPoint_xy.y())/2);
        }
    }

    //放大缩小次数清零
    AddLessCount = 0;

    this->update();
}

//鼠标按下事件
void ShowMap::mousePressEvent(QMouseEvent *e)
{
    //获得鼠标点击坐标
    QPoint mousexy;
    mousexy = e->pos();
    //qDebug() << mousexy;

    MapRemov_Old = mousexy;

   //鼠标按下后打开定时器,500ms后超时绘制预设点
    if(timer->timerId() == -1)
        timer->start(500);

   //不能在这里直接将鼠标点击坐标,赋给预设点绘制坐标,因为后面移动图片前也会点击鼠标,
   //也要在鼠标移动事件关闭定时器,这样鼠标移动就不会修改预设点坐标了
   MousePress_xy = mousexy;
}

//鼠标移动事件
void ShowMap::mouseMoveEvent(QMouseEvent *e)
{
    //当前鼠标按下坐标
    QPoint mousexy;
    mousexy = e->pos();

    //鼠标(单次)移动坐标 = 图片移动距离 = 移动中的连续鼠标坐标减去移动前按下的仅一次鼠标坐标
    QPoint MouseMovePoint;
    MouseMovePoint.setX(mousexy.x()-MapRemov_Old.x());
    MouseMovePoint.setY(mousexy.y()-MapRemov_Old.y());

    //地图图片绘制原点坐标
    int xImgOrigin = ImgPaintOrigin.x() + MouseMovePoint.x();
    int yImgOrigin = ImgPaintOrigin.y() + MouseMovePoint.y();

    //当地图图片左上角坐标在(-(pow(2,AddLessCount)-1)*width,-(pow(2,AddLessCount)-1)*height))与(0,0)之间才可拖动图片
    if((xImgOrigin>=-(pow(2,AddLessCount)-1)*OriginSize.x() && yImgOrigin>=-(pow(2,AddLessCount)-1)*OriginSize.y()) \
            && (xImgOrigin<=0 && yImgOrigin<=0) )
    {
        //获得鼠标全部移动坐标
        MouseAllMove_x += MouseMovePoint.x();
        MouseAllMove_y += MouseMovePoint.y();

        //地图图片绘制坐标随图片移动
        ImgPaintOrigin.setX(xImgOrigin);
        ImgPaintOrigin.setY(yImgOrigin);

        //维持地图图片中心点坐标
        CentPoint_xy.setX(CentPoint_xy.x()+MouseMovePoint.x());
        CentPoint_xy.setY(CentPoint_xy.y()+MouseMovePoint.y());
        //预设点绘制坐标随图片移动
        PreSetPoint_xy.setX(PreSetPoint_xy.x()+MouseMovePoint.x());
        PreSetPoint_xy.setY(PreSetPoint_xy.y()+MouseMovePoint.y());
        for(int i=0;i<Point_xy.count();i++)
        {
            Point_xy[i].setX(Point_xy[i].x() + MouseMovePoint.x());
            Point_xy[i].setY(Point_xy[i].y() + MouseMovePoint.y());
        }

        this->update();
    }

    //移动鼠标前会点击鼠标,打开定时器,所以在这里要关闭先前打开的定时器
    timer->stop();

    MapRemov_Old = mousexy;
}

//超时函数:超时后将当前鼠标按下坐标,赋给预设点绘制坐标
void ShowMap::doProssTimerOut()
{
    //防止重复进入
    timer->stop();

    //超时后将当前鼠标按下坐标,赋给预设点绘制坐标
    PreSetPoint_xy = MousePress_xy;

    //更新绘图事件,绘制预设点
    this->update();
}

//手动按钮-点击槽函数
void ShowMap::on_manualButton_clicked()
{
    //隐藏groupBox,显示巡航点设置Group
    ui->groupBox->hide();
    ui->cruiseGroupBox->show();
}

//手动模式添加巡航点按钮-点击槽函数
void ShowMap::on_addItemButton_clicked()
{
    //当前点和设定点列表最后一个点相同则不能添加,原点也不能添加
    if( (Point_xy.count()>0 && Point_xy.last()==PreSetPoint_xy) || (PreSetPoint_xy.x()==0 && PreSetPoint_xy.y()==0) )
    {
        return;
    }

    //当listWidget没有列表项时,在设定点列表后添加项
    if(ui->listWidget->currentRow() == -1)
    {
        Point_xy.append(PreSetPoint_xy);
    }
    //当listWidget有列表项时,在设定点列表后面插入项
    else
    {
        Point_xy.insert(ui->listWidget->currentRow()+1, PreSetPoint_xy);
    }
    ui->listWidget->clear();

    //在listWidget上逐行添加项
    for(int i=0;i<Point_xy.count();i++)
    {
        ui->listWidget->addItem(QString::number(i) + "        " + QString::number(Point_xy[i].x()) + "-" + QString::number(Point_xy[i].y()));
    }

    this->update();
}

//巡航分组框的返回按钮-点击槽函数
void ShowMap::on_cruiseGroupBox_returnBtn_clicked()
{
    ui->cruiseGroupBox->hide();
    ui->groupBox->show();
}

//手动模式删除巡航点按钮-点击槽函数
void ShowMap::on_lessItemButton_clicked()
{
    //如果没有选中
    if(ui->listWidget->currentRow()==-1)
    {
        //删除最后一个点
        Point_xy.removeAt(Point_xy.count()-1);
    }
    //选中
    else
    {
        //删除当前所选点
        Point_xy.removeAt(ui->listWidget->currentRow());
    }
    ui->listWidget->clear();

    for(int i=0;i<Point_xy.count();i++)
    {
        ui->listWidget->addItem(QString::number(i)+"        "+QString::number(Point_xy[i].x())+"-"+QString::number(Point_xy[i].y()));
    }

    //清除当前点
    PreSetPoint_xy.setX(0);
    PreSetPoint_xy.setY(0);
    this->update();
}

上面是“显示地图程序”的源文件,详细说明看程序注释。

2.3 showMap.ui

上面是“显示地图程序”的ui文件。

posted @ 2019-05-28 15:53  fengMisaka  阅读(306)  评论(0编辑  收藏  举报