QT笔记(5)——Qt图形视图框架实例

时间:2023-02-01 17:56:28

Qt的图形视图框架很强大,同时也很复杂,这里做了一个简单的练习,对图片加载,并移动放缩,的一个图片查看器;需要继承图元类;

下面直接贴源码了:

新建一个widget应用,然后添加如下:

pixitem.h:

#ifndef PIXITEM_H
#define PIXITEM_H
#include <QGraphicsItem>
#include <QPixmap>
#include <QPainter>
#include <QRectF>
#include <QMouseEvent>
#include <QPointF>
#include <QDragEnterEvent>
#include <QGraphicsSceneWheelEvent>
#include <QGraphicsObject>

//枚举方便传递
enum Enum_ZoomState{
NO_STATE,
RESET,
ZOOM_IN,
ZOOM_OUT
};

//放大缩小值范围
enum Enum_ZoomTimes{
ZOOM_IN_TIMES = 50,
ZOOM_OUT_TIMES = -50,
};

//直接继承QGraphicsObject,这样有信号和槽QGraphicsItem没有信号和槽功能
class PixItem :public QGraphicsObject//public QGraphicsItem
//继承自图元类,实现自定义的图元,qt预置的有直线,椭圆,文本图元,矩形图元等
{
public:
PixItem(QPixmap *pixmap); //构造函数初始化了变量pix
QRectF boundingRect() const;
//实现自己的boundingRect 图元边界方法,继承QGraphicsItem必须要初始化话的虚函数
//完成以图元坐标系为基础增加两个像素点的冗余的工作

void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget); //重画图形函数

void wheelEvent(QGraphicsSceneWheelEvent *event);//滚轮放大缩小
void setZoomState(const int &zoomState);//设置图像恢复原始
void mousePressEvent(QGraphicsSceneMouseEvent *event);//用于移动图像
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);//用于移动图像
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);//用于移动图像
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);//双击恢复图像
int getScaleValue() const;//获取当前图元放缩值
void setScaleValue(const int &);//返回放放缩值
void setValue(const QPointF &);//返回窗口大小值
int widx;
int widy;

signals:


private:
qreal m_scaleValue; //缩放值
QPixmap pix; //作为图元显示的图片
int m_zoomState;
bool m_isMove;
QPointF m_startPos;
};

#endif // PIXITEM_H

pixitem.cpp:

#include "pixitem.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QPointF>
#include <QGraphicsSceneDragDropEvent>
#include <QDrag>
#include <math.h>
#include <QMessageBox>
#include <QGraphicsObject>
#include "widget.h"

//构造函数初始化了变量pix
PixItem::PixItem(QPixmap *pixmap)
{
pix = *pixmap;
setAcceptDrops(true); //设置可拖拽
m_scaleValue = 0;//放缩值初始化
m_isMove = false;//不可移动
widx=0;
widy=0;
}

//实现自己的图元边界函数
QRectF PixItem::boundingRect() const
{
return QRectF(-pix.width()/2, -pix.height()/2,
pix.width(), pix.height());//需要对应图元,不然出现残影
}

//只需QPainter的drawPixmap()函数将图元图片绘出即可
void PixItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
QWidget *)
{
painter->drawPixmap(-pix.width()/2, -pix.height()/2, pix);//需要对应边界函数
}

//鼠标点击事件
void PixItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
m_startPos = event->pos();
m_isMove = true;//图元是否可以移动
//////下面测试代码
// QPointF m_center;
// m_center=mapToScene(0,0);//坐标映射,图元坐标映射到窗口
// double a =m_center.x();
// QString str=QString::number(a,10,0); // 这是你的变量
// QMessageBox mesg;
// mesg.about(NULL,QString::fromLocal8Bit("图片大小") , str);
}

void PixItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{

// if(sceneBoundingRect().width()<widx&&sceneBoundingRect().height()<widy)
// {
// m_isMove = false;//小于窗口时候不可以移动
// }
// if(sceneBoundingRect().width()>widx-20||sceneBoundingRect().height()>widy-20)
// {
// QPointF m_center;
// m_center=mapToScene(0,0);//图元中心坐标
// QPointF points = event->pos()- m_startPos;
// int wx,wy;
// wx=sceneBoundingRect().width()-widx;
// wy=sceneBoundingRect().height()-widy;
// if(pow(1.1,wx/2)>pow(1.1,m_center.x())||pow(1.1,wy/2)>pow(1.1,m_center.y()))
// {
// moveBy(points.x(),points.y());
// }
// m_isMove = false;
// }


if(m_isMove)
{
QPointF point =mapToScene(event->pos())-mapToScene(m_startPos);
moveBy(point.x(),point.y());
// 鼠标点击后并移动则图元相应移动,进行了图元坐标映射,映射到窗口中
}
}

void PixItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *)
{
m_isMove = false;
}


//双击复位
void PixItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{

// int ag=sceneBoundingRect().width();
// int bg=sceneBoundingRect().height();
// QString strag=QString::number(ag,10,0); // 这是你的变量
// QString strbg=QString::number(bg,10,0); // 这是你的变量
// QString strg = QString::fromLocal8Bit("图片宽度:%1,图片高度:%2").arg(strag).arg(strbg);
// QMessageBox mesgg;
// mesgg.about(NULL,QString::fromLocal8Bit("图片信息") ,strg);

setPos(0,0);
m_scaleValue = 0;
setScale(1);
setTransformOriginPoint(0, 0);

//做了坐标映射,将图片回复到窗口中心
QPointF m_center;
m_center=mapToScene(0,0);
if(m_center.x()!=0||m_center.y()!=0)
{
if(m_center.x()>0&&m_center.y()>0) moveBy(-m_center.x(), -m_center.y());
if(m_center.x()<0&&m_center.y()<0) moveBy(m_center.x(), m_center.y());
if(m_center.x()>0&&m_center.y()<0) moveBy(-m_center.x(), m_center.y());
if(m_center.x()<0&&m_center.y()>0) moveBy(m_center.x(), -m_center.y());
}

}

//使用滚轮整体缩放
void PixItem::wheelEvent(QGraphicsSceneWheelEvent *event)
{
setZoomState(NO_STATE);
int scaleValue = m_scaleValue;
if(event->delta() > 0) //delta()为正,滚轮向上滚
{
scaleValue++;
}
else
{
scaleValue--;
}

/////测试代码以下
// if(scaleValue<=0)//小于窗口时候自动回*
// {
// setPos(0,0);
// QPointF m_center;
// m_center=mapToScene(0,0);
// if(m_center.x()!=0||m_center.y()!=0)
// {
// if(m_center.x()>0&&m_center.y()>0) moveBy(-m_center.x(), -m_center.y());
// if(m_center.x()<0&&m_center.y()<0) moveBy(m_center.x(), m_center.y());
// if(m_center.x()>0&&m_center.y()<0) moveBy(-m_center.x(), m_center.y());
// if(m_center.x()<0&&m_center.y()>0) moveBy(m_center.x(), -m_center.y());
// }
// }
//////////////*************以上

if (scaleValue > ZOOM_IN_TIMES || scaleValue < ZOOM_OUT_TIMES)
return;

m_scaleValue = scaleValue;
qreal s;
if(m_scaleValue > 0)
{
s = pow(1.1, m_scaleValue); //放大 计算x的y方次 参数都是double类型
}
else
{
s = pow(1 / 1.1, -m_scaleValue); //缩小
}
setScale(s);//setScale设置比例放缩,内置的处理图像放缩的方法
if(sceneBoundingRect().width()>=widx||sceneBoundingRect().height()>=widy)
{
// m_isMove = false;
setTransformOriginPoint(event->pos());//基于图元坐标内鼠标指针变换中心
}

// QPointF m_center;
// m_center=mapToScene(event->pos().x(),event->pos().y());//转换至窗口后指针变换中心
// setTransformOriginPoint(m_center);
}

//从widget获取的缩放值,用于同步滚轮和按键
void PixItem::setScaleValue(const int &scaleValue)
{
if (scaleValue > ZOOM_IN_TIMES || scaleValue < ZOOM_OUT_TIMES)
return;

m_scaleValue = scaleValue;

////////*******测试代码以下
// if(scaleValue<=0)//小于窗口时候自动回*
// {
// setPos(0,0);
// QPointF m_center;
// m_center=mapToScene(0,0);
// if(m_center.x()!=0||m_center.y()!=0)
// {
// if(m_center.x()>0&&m_center.y()>0) moveBy(-m_center.x(), -m_center.y());
// if(m_center.x()<0&&m_center.y()<0) moveBy(m_center.x(), m_center.y());
// if(m_center.x()>0&&m_center.y()<0) moveBy(-m_center.x(), m_center.y());
// if(m_center.x()<0&&m_center.y()>0) moveBy(m_center.x(), -m_center.y());
// }
// }

qreal s;
if(m_scaleValue > 0)
{
s = pow(1.1, m_scaleValue); //放大 计算x的y方次 参数都是double类型,去除正负数
}
else
{
s = pow(1 / 1.1, -m_scaleValue); //缩小
}
setScale(s);
}

//复原图像
void PixItem::setZoomState(const int &zoomState)
{
m_zoomState = zoomState;
if (m_zoomState == RESET)
{
m_scaleValue = 0;
setScale(1);
setTransformOriginPoint(0, 0);
}
}

//获取放缩值
int PixItem::getScaleValue() const
{
return m_scaleValue;
}

//返回窗口值
void PixItem::setValue(const QPointF &pointxy)
{
QPointF p;
p=pointxy;
widx=p.x();
widy=p.y();
}

在widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include"pixitem.h"
#include <QGraphicsView> //视图类
#include <QGraphicsScene> //场景类
#include <QGraphicsItem> //图元类
#include <math.h>
#include <QWheelEvent>

namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void mouseDoubleClickEvent(QMouseEvent *e);
void wheelEvent(QWheelEvent *event);
private slots:
void my_change();//用于接收放缩值变化
void on_spinBox_valueChanged(int arg1);//系统自动生成槽函数,检测对应控件值
void resetzoom();//恢复图像
void on_B_OpenFile_clicked();//打开文件按钮
void on_B_Boost_clicked();//放大按钮
void on_B_Reduce_clicked();//缩小按钮
private:
Ui::Widget *ui;
QPoint last; //点
PixItem *pixItem; //自定义的图元类
QGraphicsScene *m_graphicsScene; //场景
QGraphicsView *m_graphicsView;
int wt;
int ht;
};
#endif // WIDGET_H

在widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QFileDialog>
#include <QPolygon>
#include <QMouseEvent>
#include <QMessageBox>
#include <QGraphicsObject>
#include "pixitem.h"

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

// this->setWindowFlags(Qt::FramelessWindowHint |
// Qt::WindowSystemMenuHint |
// Qt::WindowMinimizeButtonHint);//隐藏窗口标题栏和外框
//this->setAttribute(Qt::WA_TranslucentBackground);//设置背景透明

pixItem = new PixItem(new QPixmap("D:/1.jpg"));
//将该图元对象添加到场景中,并设置此图元在场景中的位置为中心(0,0)

m_graphicsScene = new QGraphicsScene; //new 一个新的场景对象
ui->graphicsView->setSceneRect(-10/2,-10/2,10,10);
//设置开启滚动条范围,不然即使隐藏了滚动条后还是有滚动效果
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//隐藏横向滚动条
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//隐藏纵向滚动条
ui->graphicsView->setScene(m_graphicsScene);//添加场景
m_graphicsScene->addItem(pixItem);//将图元添加到场景中
pixItem->setPos(0,0);//中心位置

QObject::connect(pixItem,SIGNAL(scaleChanged()),
this,SLOT(my_change()));//自定义信号和槽获取scale值反馈出来

QObject::connect(ui->spinBox,SIGNAL(valueChanged(int)),
ui->horizontalSlider,SLOT(setValue(int)));//spinBox值与滑条对应

QObject::connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),
ui->spinBox,SLOT(setValue(int)));//滑条与spinBox值对应

ui->horizontalSlider->setValue(0);//滑条初始值,范围值在窗口设计器里设置了

QObject::connect(ui->B_ReZoom,SIGNAL(clicked(bool)),
this,SLOT(resetzoom()));//复位按钮

}

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

//下面代码可以不添加//
////////////////******接下*******/////////////
void Widget::mousePressEvent(QMouseEvent *e)
{
last = e->globalPos(); //鼠标点击点位
wt=width();//获取窗口大小
ht=height();//同上
//下面注释的代码为了检测值用
// int a=wt;
// int b=ht;
// QString stra=QString::number(a,10,0); // 这是你的变量
// QString strb=QString::number(b,10,0); // 这是你的变量
// QString str = QString::fromLocal8Bit("窗口宽度:%1,窗口高度:%2").arg(stra).arg(strb);
// QMessageBox mesg;
// mesg.about(NULL,QString::fromLocal8Bit("窗口信息") ,str);
QPointF p(wt,ht);//定义为点
pixItem->setValue(p);//传到pixitem类
}

void Widget::mouseMoveEvent(QMouseEvent *e)
{
//下面代码是移动窗口用,在无标题和窗口外框情况下用
int dx = e->globalX() - last.x();
int dy = e->globalY() - last.y();
last = e->globalPos();
move(x()+dx, y()+dy);
}

void Widget::mouseReleaseEvent(QMouseEvent *e)
{
//下面代码是移动窗口用,在无标题和窗口外框情况下用
int dx = e->globalX() - last.x();
int dy = e->globalY() - last.y();
move(x()+dx, y()+dy);
}

///////////*********接上*************///////

//双击恢复图像
void Widget::mouseDoubleClickEvent(QMouseEvent *e)
{


////////***********图片复原代码**************/////
pixItem->setPos(0,0);
pixItem->setScaleValue(1);
pixItem->setZoomState(NO_STATE);
ui->horizontalSlider->setValue(0);

//////////////**********下面注释为测试用***********////////////
// int ag=pixItem->sceneBoundingRect().width();//图元边缘
// int bg=pixItem->sceneBoundingRect().height();
// QString strag=QString::number(ag,10,0); // 这是你的变量
// QString strbg=QString::number(bg,10,0); // 这是你的变量
// QString strg = QString::fromLocal8Bit("图片宽度:%1,图片高度:%2").arg(strag).arg(strbg);
// QMessageBox mesgg;
// mesgg.about(NULL,QString::fromLocal8Bit("图片信息") ,strg);

// int ap=pixItem->boundingRect().width();//图片边缘
// int bp=pixItem->boundingRect().height();
// QString strap=QString::number(ap,10,0); // 这是你的变量
// QString strbp=QString::number(bp,10,0); // 这是你的变量
// QString strp = QString::fromLocal8Bit("图片宽度:%1,图片高度:%2").arg(strap).arg(strbp);
// QMessageBox mesgp;
// mesgp.about(NULL,QString::fromLocal8Bit("图片信息") ,strp);


// wt=width();//窗口大小
// ht=height();
// int a=wt;
// int b=ht;
// QString stra=QString::number(a,10,0); // 这是你的变量
// QString strb=QString::number(b,10,0); // 这是你的变量
// QString str = QString::fromLocal8Bit("窗口宽度:%1,窗口高度:%2").arg(stra).arg(strb);
// QMessageBox mesg;
// mesg.about(NULL,QString::fromLocal8Bit("窗口信息") ,str);
}

//滚轮滚动图片缩小放大
void Widget::wheelEvent(QWheelEvent *event)
{
pixItem->setZoomState(NO_STATE);//枚举,对应pixitem类中
int scaleValue=pixItem->getScaleValue();//获取ScaleValue值(放大缩小值)
if(event->delta() > 0) //delta()为正,滚轮向上滚
{
scaleValue++;
ui->horizontalSlider->setValue(scaleValue);//同步滑条
}
else
{
scaleValue--;
ui->horizontalSlider->setValue(scaleValue);//同步滑条
}
pixItem->setScaleValue(scaleValue);//重新设置ScaleValue值(放大缩小值)
}

//自定义槽函数接受pixitem类放大缩小值
void Widget::my_change()
{
ui->horizontalSlider->setValue(pixItem->getScaleValue());//获取图元的放大缩小值
}


//系统内置spinBox槽函数,在设计窗口选中控件,右键添加valueChanged,当值改变时候自动启动
void Widget::on_spinBox_valueChanged(int arg1)
{
pixItem->setZoomState(NO_STATE);//对应图元类中放大缩小原始枚举项
pixItem->setScaleValue(arg1); //获取控件改变的值
wt=width();
ht=height();
QPointF p(wt,ht);
pixItem->setValue(p);//将窗口值传给pixitem类
//////测试代码////
// int a=wt;
// int b=ht;
// QString stra=QString::number(a,10,0); // 这是你的变量
// QString strb=QString::number(b,10,0); // 这是你的变量
// QString str = QString::fromLocal8Bit("窗口宽度:%1,窗口高度:%2").arg(stra).arg(strb);
// QMessageBox mesg;
// mesg.about(NULL,QString::fromLocal8Bit("窗口信息") ,str);
}

//复原函数
void Widget::resetzoom()
{
pixItem->setPos(0,0);//复原位置
pixItem->setScaleValue(0);//复原比例
pixItem->setZoomState(NO_STATE);
ui->horizontalSlider->setValue(0);//滑条值
}


//打开文件按钮
void Widget::on_B_OpenFile_clicked()
{
QString fileName = QFileDialog::getOpenFileName(
this, tr("open image file"),
"./", tr("Image files(*.bmp *.jpg *.pbm *.pgm *.png *.ppm *.xbm *.xpm);;All files (*.*)"));
if(fileName.isEmpty())
{
// QMessageBox::warning(this, tr("警告"), tr("打开图片失败,请重新选择图片!"),NULL, QMessageBox::Yes);
return;
}
m_graphicsScene->removeItem(pixItem); //将上一个图元从场景中移除,重新添加新的图元
pixItem = new PixItem(new QPixmap(fileName));
ui->graphicsView->setScene(m_graphicsScene);//添加到场景中
m_graphicsScene->addItem(pixItem);
pixItem->setPos(0,0);
QObject::connect(pixItem,SIGNAL(scaleChanged()),this,SLOT(my_change()));//刷新一下链接
}

//放大按钮
void Widget::on_B_Boost_clicked()
{
int a=ui->spinBox->value()+1;
ui->horizontalSlider->setValue(a);
}

//缩小按钮
void Widget::on_B_Reduce_clicked()
{
int a=ui->spinBox->value()-1;
ui->horizontalSlider->setValue(a);
}
运行效果如下:

QT笔记(5)——Qt图形视图框架实例