四小时学习opencv+qt系列(第六天)
一、图形视图框架
三大类:
场景(QGraphicsScene类)
视图(QGraphicsView类)
图形对象元素(QGraphicsItem及其子类)
1.场景-视图-对象元素架构
场景是用于管理对象元素的实例,包含他们的同时还将事件传递给他们。
视图是用于可视化场景的内容,还负责将事件传递给场景。
对象元素是场景包含的对象元素,可以是线、矩形、图像等。
实例:
(1)创建名为Graphics_Viewer的应用程序,基类选择QMainWindow,在ui文件中将工具栏和状态栏删除后只添加一个Display widget里面的Graphics View,属性名字为graphicsView,再对整个窗口进行一个网格布局。
(2)添加一个拖拽功能,还记的推拽功能具有三个环节包括:拖拽处理函数dragEnterEvent(QDragEnterEvent *event),推拽释放函数dropEvent(QDropEvent *event),尺寸调整的函数resizeEvent(QResizeEvent *event),我们这里只用到前两个,推拽处理函数和上一次的相同,但是上一次的推拽释放函数用到的是QLabel这次没有。还是一个重点使用拖拽事件必须在构造函数中添加:
this->setAcceptDrops(true);
否则任何推拽事件都没用。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QPixmap>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QFileInfo>
#include <QMessageBox>
#include <QDebug>
#include "qcustomgraphicseffect.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
private:
Ui::MainWindow *ui;
QGraphicsScene scene;
};
#endif // MAINWINDOW_H
代码分析:首先引入的头文件可以看出用到了场景、对象元素、pixmap、拖拽处理事件、推拽释放处理事件、数据记录、文件信息的获取、拟态对话框和打印的功能,定义了被保护的两个事件,还创建了私有的场景scene。
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setAcceptDrops(true);
ui->graphicsView->setAcceptDrops(false);
ui->graphicsView->setScene(&scene);
ui->graphicsView->setInteractive(true);
ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
ui->graphicsView->setRubberBandSelectionMode(Qt::ContainsItemShape);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
QStringList acceptedFileTypes;
acceptedFileTypes.append("jpg");
acceptedFileTypes.append("png");
acceptedFileTypes.append("bmp");
if (event->mimeData()->hasUrls() &&
event->mimeData()->urls().count() == 1)
{
QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
if(acceptedFileTypes.contains(file.suffix().toLower()))
{
event->acceptProposedAction();
}
}
}
void MainWindow::dropEvent(QDropEvent *event)
{
QPoint viewPos = ui->graphicsView->mapFromParent(event->pos());
QPointF sceneDropPos = ui->graphicsView->mapToScene(viewPos);
QFileInfo file(event
->mimeData()
->urls()
.at(0)
.toLocalFile());
QPixmap pixmap;
if(pixmap.load(file
.absoluteFilePath()))
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
item->setPos(sceneDropPos);
item->setFlag(QGraphicsItem::ItemIsSelectable);
item->setAcceptedMouseButtons(Qt::LeftButton);
scene.addItem(item);
qDebug()<<scene.items().count();
}
else
{
QMessageBox::critical(this,
tr("Error"),
tr("The image file cannot be read!"));
}
}
代码分析:在构造函数中确保ui界面中的视图也就是graphicsView控件能够访问场景,而且禁用了setAcceptDrops确保能够抓放在窗口上的任何一个地方的图,所以:
this->setAcceptDrops(true);
ui->graphicsView->setAcceptDrops(false);
ui->graphicsView->setScene(&scene);
接下来在推拽释放函数中要确保对象的创建,还要将其添加到场景中
QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
QPixmap pixmap;
if(pixmap.load(file.absoluteFilePath()))
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
item->setPos(sceneDropPos);
item->setFlag(QGraphicsItem::ItemIsSelectable);
item->setAcceptedMouseButtons(Qt::LeftButton);
scene.addItem(item);
qDebug()<<scene.items().count();
}
else
{
QMessageBox::critical(this,
tr("Error"),
tr("The image file cannot be read!"));
}
现在的程序可以支持不断往里面拖拽图片,但是图片会不断地累加而不是只显示一张图片, qDebug()<<scene.items().count();可以显示图片的数目。效果图:
如果想删除之前的,则在scene.addItem(item);之前加入:
scene.clear();
效果图:
2.场景QGraphicsScene
首先就是向场景中添加对象元素比如线、矩形、三角形、椭圆等,直接在刚刚我们建的那个工程中的mainwindow的构造函数中依次添加代码:
画椭圆:
scene.addEllipse(-100.0,100.0,200.0,100.0,QPen(QBrush(Qt::SolidPattern),2.0),QBrush(Qt::Dense2Pattern));
画线:
scene.addLine(-200.0,200,+200,200,QPen(QBrush(Qt::SolidPattern),5.0));
画矩形:
scene.addRect(-150,150,300,140);
画三角:
QVector<QPoint> points;
points.append(QPoint(150,250));
points.append(QPoint(250,250));
points.append(QPoint(165,280));
points.append(QPoint(150,250));
scene.addPolygon(QPolygon(points));
依次添加的效果图:
3.对象元素QGraphicsItem
在之前新建的Graphics_Viewer工程里,新建一个类QCustomGraphicsEffect,基类QObject,勾选Includer QObject
qcustomgraphicseffect.h
#ifndef QCUSTOMGRAPHICSEFFECT_H
#define QCUSTOMGRAPHICSEFFECT_H
#include <QObject>
#include <QGraphicsEffect>
#include <QPainter>
class QCustomGraphicsEffect : public QGraphicsEffect
{
Q_OBJECT
public:
explicit QCustomGraphicsEffect(QObject *parent = nullptr);
protected:
void draw(QPainter *painter);
signals:
public slots:
};
#endif // QCUSTOMGRAPHICSEFFECT_H
代码分析:这里确保class QCustomGraphicsEffect : public QGraphicsEffect和.cpp中的QCustomGraphicsEffect::QCustomGraphicsEffect(QObject *parent): QGraphicsEffect(parent),定义被保护函数 void draw(QPainter *painter);
qcustomgraphicseffect.cpp
#include "qcustomgraphicseffect.h"
QCustomGraphicsEffect::QCustomGraphicsEffect(QObject *parent)
: QGraphicsEffect(parent)
{
}
void QCustomGraphicsEffect::draw(QPainter *painter)
{
QImage image;
image = sourcePixmap().toImage();
image = image.convertToFormat(
QImage::Format_Grayscale8);
for(int i=0; i<image.byteCount(); i++)
image.bits()[i] =
image.bits()[i] < 100 ?
0
:
255;
painter->drawPixmap(0,0,QPixmap::fromImage(image));
}
代码分析:应用draw函数进行一个阈值的滤波,先转灰度再进行二值化
在mainwindow.cpp中引入头文件,修改拖拽释放函数如下:
#include "qcustomgraphicseffect.h"
void MainWindow::dropEvent(QDropEvent *event)
{
QPoint viewPos = ui->graphicsView->mapFromParent(event->pos());
QPointF sceneDropPos = ui->graphicsView->mapToScene(viewPos);
QFileInfo file(event
->mimeData()
->urls()
.at(0)
.toLocalFile());
QPixmap pixmap;
if(pixmap.load(file
.absoluteFilePath()))
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
item->setGraphicsEffect(new QCustomGraphicsEffect(this));
scene.addItem(item);
qDebug()<<scene.items().count();
}
else
{
QMessageBox::critical(this,
tr("Error"),
tr("The image file cannot be read!"));
}
}
代码分析:修改部分就是将图片在添加前使用我们新建的类进行一个二值化的处理。
效果图:
4.视图QGraphicsView
在之前新建的Graphics_Viewer工程里,新建一个类QEnhancedGraphicsView,基类QObject,勾选Includer QObject
qenhancedgraphicsview.h
#ifndef QENHANCEDGRAPHICSVIEW_H
#define QENHANCEDGRAPHICSVIEW_H
#include <QWidget>
#include <QGraphicsView>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QtMath>
#include <QContextMenuEvent>
#include <QMenu>
#include <QGraphicsItem>
#include <QDebug>
#include <QGraphicsEffect>
#include "qcustomgraphicseffect.h"
class QEnhancedGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit QEnhancedGraphicsView(QWidget *parent = nullptr);
protected:
void wheelEvent(QWheelEvent *event);
void mousePressEvent(QMouseEvent *event);
signals:
public slots:
private slots:
void clearAll(bool);
void clearSelected(bool);
void noEffect(bool);
void blurEffect(bool);
void dropShadowEffect(bool);
void colorizeEffect(bool);
void customEffect(bool);
private:
QPointF sceneMousePos;
};
#endif // QENHANCEDGRAPHICSVIEW_H
代码分析:首先新建的这个类继承于QGraphicsView,其中写到了两个被保护事件,wheelEvent是负责检查鼠标的滚动事件,根据鼠标的滚轮运动次数对X和Y轴进行缩放。mousePressEvent是右击鼠标会触发的事件,接下来对一些槽函数的定义便于鼠标右键后生成一个上下文菜单可以选择触发不同的槽函数。
qenhancedgraphicsview.cpp
#include "qenhancedgraphicsview.h"
QEnhancedGraphicsView::QEnhancedGraphicsView(QWidget *parent)
: QGraphicsView(parent)
{
}
void QEnhancedGraphicsView::wheelEvent(QWheelEvent *event)
{
if (event->orientation() == Qt::Vertical)
{
double angleDeltaY = event->angleDelta().y();
double zoomFactor = qPow(1.0015, angleDeltaY);
scale(zoomFactor, zoomFactor);
if(angleDeltaY > 0)
{
this->centerOn(sceneMousePos);
sceneMousePos = this->mapToScene(event->pos());
}
this->viewport()->update();
event->accept();
}
else
{
event->ignore();
}
}
void QEnhancedGraphicsView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton)
{
QMenu menu;
QAction *clearAllAction = menu.addAction("Clear All");
connect(clearAllAction,
SIGNAL(triggered(bool)),
this,
SLOT(clearAll(bool)));
QAction *clearSelectedAction = menu.addAction("Clear Selected");
connect(clearSelectedAction,
SIGNAL(triggered(bool)),
this,
SLOT(clearSelected(bool)));
QAction *noEffectAction = menu.addAction("No Effect");
connect(noEffectAction,
SIGNAL(triggered(bool)),
this,
SLOT(noEffect(bool)));
QAction *blurEffectAction = menu.addAction("Blur Effect");
connect(blurEffectAction,
SIGNAL(triggered(bool)),
this,
SLOT(blurEffect(bool)));
QAction *dropShadEffectAction = menu.addAction("Drop Shadow Effect");
connect(dropShadEffectAction,
SIGNAL(triggered(bool)),
this,
SLOT(dropShadowEffect(bool)));
QAction *colorizeEffectAction = menu.addAction("Colorize Effect");
connect(colorizeEffectAction,
SIGNAL(triggered(bool)),
this,
SLOT(colorizeEffect(bool)));
QAction *customEffectAction = menu.addAction("Custom Effect");
connect(customEffectAction,
SIGNAL(triggered(bool)),
this,
SLOT(customEffect(bool)));
menu.exec(event->globalPos());
event->accept();
}
else
{
QGraphicsView::mousePressEvent(event);
}
}
void QEnhancedGraphicsView::clearAll(bool)
{
scene()->clear();
}
void QEnhancedGraphicsView::clearSelected(bool)
{
while(scene()->selectedItems().count() > 0)
{
delete scene()->selectedItems().at(0);
scene()->selectedItems().removeAt(0);
}
}
void QEnhancedGraphicsView::noEffect(bool)
{
foreach(QGraphicsItem *item, scene()->selectedItems())
{
item->setGraphicsEffect(Q_NULLPTR);
}
}
void QEnhancedGraphicsView::blurEffect(bool)
{
foreach(QGraphicsItem *item, scene()->selectedItems())
{
item->setGraphicsEffect(new QGraphicsBlurEffect(this));
}
}
void QEnhancedGraphicsView::dropShadowEffect(bool)
{
foreach(QGraphicsItem *item, scene()->selectedItems())
{
item->setGraphicsEffect(new QGraphicsDropShadowEffect(this));
}
}
void QEnhancedGraphicsView::colorizeEffect(bool)
{
foreach(QGraphicsItem *item, scene()->selectedItems())
{
item->setGraphicsEffect(new QGraphicsColorizeEffect(this));
}
}
void QEnhancedGraphicsView::customEffect(bool)
{
foreach(QGraphicsItem *item, scene()->selectedItems())
{
item->setGraphicsEffect(new QCustomGraphicsEffect(this));
}
}
最后将graphicsView提升为类QEnhancedGraphicsView,然后修改
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QPixmap>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QFileInfo>
#include <QMessageBox>
#include <QDebug>
#include "qcustomgraphicseffect.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
private:
Ui::MainWindow *ui;
QGraphicsScene scene;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QVector>
#include "qcustomgraphicseffect.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setAcceptDrops(true);
ui->graphicsView->setAcceptDrops(false);
ui->graphicsView->setScene(&scene);
ui->graphicsView->setInteractive(true);
ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
ui->graphicsView->setRubberBandSelectionMode(Qt::ContainsItemShape);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
QStringList acceptedFileTypes;
acceptedFileTypes.append("jpg");
acceptedFileTypes.append("png");
acceptedFileTypes.append("bmp");
if (event->mimeData()->hasUrls() &&
event->mimeData()->urls().count() == 1)
{
QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
if(acceptedFileTypes.contains(file.suffix().toLower()))
{
event->acceptProposedAction();
}
}
}
void MainWindow::dropEvent(QDropEvent *event)
{
QPoint viewPos = ui->graphicsView->mapFromParent(event->pos());
QPointF sceneDropPos = ui->graphicsView->mapToScene(viewPos);
QFileInfo file(event
->mimeData()
->urls()
.at(0)
.toLocalFile());
QPixmap pixmap;
if(pixmap.load(file
.absoluteFilePath()))
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
item->setPos(sceneDropPos);
item->setFlag(QGraphicsItem::ItemIsSelectable);
item->setAcceptedMouseButtons(Qt::LeftButton);
scene.clear();
scene.addItem(item);
qDebug()<<scene.items().count();
}
else
{
QMessageBox::critical(this,
tr("Error"),
tr("The image file cannot be read!"));
}
}
运行程序,拖拽图片进来,鼠标右键选择处理方式。
效果图:
今天学习的东西很多,明天继续学习新的基于Opencv的图像处理,,,,,,,如果博客对你有帮助的话,点个赞吧,欢迎交流,欢迎关注,明天继续学习。