当前位置: 首页 > news >正文

QT图形显示和处理8

本节代码多处为了方便简洁,应该是用了些QT creater 快速入门第三版 霍亚飞著的一些代码,在此感谢。(尽管有些项目的代码已经改到六亲不认的地步了)。以前自己玩QT4的时候也是学了这本书,之后自己做的项目里很多代码都是直接在已经写了一些代码的文件上进行修改的。学习是一边学一边封装成自己的工具,然后再使用。在此也感谢一下《C++ GUI Qt4编程》,《QT5开发及实例》等书。但是通过一些注释记起原书里面的代码应该也是有点小毛病,后面的交互则对里面的问题进行了不少修改。大家可以对照着看一下。

终于写到最后部分的内容了。我们要实现鼠标拖动图元,以及限定图元的拖动范围。下面我们重新封装两个类:为了不冲突,我们定义为 MyGraphicsItem2 和 MyGraphicsView2 。然后开始实现这些类:

#pragma once
#ifndef __MyGraphicsView2_H__
#define __MyGraphicsView2_H__

#include <QGraphicsView>

class MyGraphicsView2 : public QGraphicsView {
	Q_OBJECT

public:
	MyGraphicsView2(QGraphicsView * parent = Q_NULLPTR);
	~MyGraphicsView2();
protected:
	void keyPressEvent(QKeyEvent *event);

private:
	
};

#endif
#include "mygraphicsview2.hpp"
#include <QKeyEvent>

MyGraphicsView2::MyGraphicsView2(QGraphicsView * parent) : QGraphicsView(parent) {
	
}

MyGraphicsView2::~MyGraphicsView2() {
	
}

void MyGraphicsView2::keyPressEvent(QKeyEvent *event)
{
	switch (event->key())
	{
	case Qt::Key_Plus:
		scale(1.2, 1.2);
		break;
	case Qt::Key_Minus:
		scale(1 / 1.2, 1 / 1.2);
		break;
	case Qt::Key_Right:
		rotate(30);
		break;
	}
	QGraphicsView::keyPressEvent(event);//最后一定要调用,不然就再也无法接收信息了
}

这个类就只定义了一个键盘响应事件,接收到键盘事件,然后就根据内容进行视口放大缩小,以及视口旋转,这应该都是书上的内容(毕竟我不会闲得无聊让视口来旋转。)

主要是MyGraphicsItem2这个类。

上首先我们先定义头文件:

#pragma once
#ifndef __MyGraphicsItem2_H__
#define __MyGraphicsItem2_H__

#include <QGraphicsItem>

class MyGraphicsItem2 : public QGraphicsItem {
	//千万要注意!!! 这里不能添加Q_OBJECT
	//否则会报一大堆错 因为它并不是继承自QObject
public:
	MyGraphicsItem2(QGraphicsItem * parent = Q_NULLPTR);
	QRectF boundingRect() const;
	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
		QWidget *widget);

	void setColor(const QColor &color) { brushColor = color; }
	MyGraphicsItem2::~MyGraphicsItem2();

private:
	QColor brushColor;

protected:
	void keyPressEvent(QKeyEvent *event);
	void mousePressEvent(QGraphicsSceneMouseEvent *event);
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
	void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
	void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
	QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
	void MyGraphicsItem2::mouseMoveEvent(QGraphicsSceneMouseEvent* pEvent);
};


#endif

注意里面有很多函数,首先先注意里面实现的两个虚函数。前面讲过了。然后是setColor,用来给一个变量名叫“刷子的颜色”的东西设置颜色。

然后我们定义了键盘事件,已经鼠标按下事件,鼠标松开事件,鼠标悬停在某个位置的事件,图形项鼠标右键时显示右键菜单的事件,以及鼠标移动事件,和图形项改变事件。

下面看各个函数的实现:

首先包含头文件:

#include "mygraphicsitem2.hpp"
#include <QPainter>
#include <QCursor>
#include <QKeyEvent>
#include <QGraphicsScene>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>

然后定义构造和析构函数:

MyGraphicsItem2::MyGraphicsItem2(QGraphicsItem * parent) : QGraphicsItem(parent) {
	brushColor = Qt::red;
	setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges);
	setAcceptHoverEvents(true);

}

MyGraphicsItem2::~MyGraphicsItem2() {
	
}

首先把刷子设置为红色作为初始颜色。然后设置Flag,设置为可设置为焦点,可选择,可移动,以及可以发送几何改变的信号(这个用于限制图元的运动范围)。之前好像没说过什么是焦点,这里提一句,所谓焦点,当你选中某个图形项(以及任何控件)的时候,它就被设置为焦点了。这个时候你操作鼠标,键盘,如果它自己有相应的事件响应函数,那么它就会先捕获。如果它没有相应的事件响应函数,那么它的父类会捕获事件响应。

下面再看:

QRectF MyGraphicsItem2::boundingRect() const
{
	qreal adjust = 0.5;
	return QRectF(-10 - adjust, -10 - adjust,
		20 + adjust, 20 + adjust);
}

void MyGraphicsItem2::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
	QWidget *)
{
	if (hasFocus()) {
		painter->setPen(QPen(QColor(255, 255, 255, 200)));
	}
	else {
		painter->setPen(QPen(QColor(100, 100, 100, 100)));
	}
	painter->setBrush(brushColor);
	painter->drawRect(-10, -10, 20, 20);
}

首先看第一个函数,这样做也就是把正方形的中心设置为item原点了。

第二个函数,如果有焦点,则画的时候就把边界画亮,如果没有,就还是画成以前的灰色。

然后是:

// 鼠标按下事件处理函数,设置被点击的图形项获得焦点,并改变光标外观
void MyGraphicsItem2::mousePressEvent(QGraphicsSceneMouseEvent * pEvent)
{
	QGraphicsItem::mousePressEvent(pEvent);
	setFocus();
	setCursor(Qt::ClosedHandCursor);

}

void MyGraphicsItem2::mouseReleaseEvent(QGraphicsSceneMouseEvent * pEvent)
{
	QGraphicsItem::mouseReleaseEvent(pEvent);
	clearFocus();
	setCursor(Qt::OpenHandCursor);
	
}

也就是说点中的图形项会获得焦点(之前设置的图形项属性是可获得焦点的),以及设置手型工具变成抓握的。然后第二个函数是鼠标松开函数,会清除焦点。然后设置手型工具再把手伸开。

注意两个函数前面一定要设置

QGraphicsItem::mouseReleaseEvent(pEvent);

以及

QGraphicsItem::mousePressEvent(pEvent);

否则你会惊喜的发现图形项们会在视图里面玩“漂移大法”。大家可以试试体会一下(原书里因为只实现了QGraphicsItem::mousePressEvent(pEvent);函数,所以并没有什么影响,但是如果同时定义Press和Release函数就不行了)。

定义下面的函数:

// 键盘按下事件处理函数,判断是否是向下方向键,如果是,则向下移动图形项
void MyGraphicsItem2::keyPressEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Down)
		moveBy(0, 10);
}
void MyGraphicsItem2::mouseMoveEvent(QGraphicsSceneMouseEvent* pEvent)
{
	QGraphicsItem::mouseMoveEvent(pEvent);
}
// 悬停事件处理函数,设置光标外观和提示
void MyGraphicsItem2::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
	
	setCursor(Qt::OpenHandCursor);
	setToolTip("I am item");
}
// 右键菜单事件处理函数,为图形项添加一个右键菜单
void MyGraphicsItem2::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
	QMenu menu;
	QAction *moveAction = menu.addAction("move back");
	QAction *selectedAction = menu.exec(event->screenPos());
	if (selectedAction == moveAction) {
		setPos(0, 0);
	}
}

这里没什么好解释的。注意鼠标移动事件的自调用。然后设置鼠标悬停的事件以及右键菜单。效果大家看看就好了,这是为了显得完整才搞上来的。

然后,看最后一个函数:

QVariant MyGraphicsItem2::itemChange(GraphicsItemChange change, const QVariant &value)
{
	if (change == ItemPositionChange && scene())
	{
		QPointF newPos = value.toPointF();
		QRectF rect(40, this->pos().y(), 300, 0);
		if (!rect.contains(newPos))
		{
			newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
			newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
			return newPos;
		}
	}
	return QGraphicsItem::itemChange(change, value);
}

我们定义一个函数,当你移动图形项的时候,这个函数就会被触发,首先判断场景是否改变,以及图形项位置是否改变,如果是,首先定义一个点,作为我们要移动过去的目标位置。注意定义了一个矩阵,这个矩阵设置了可以移动的矩阵范围。因为这里的高设置为了0,所以它不能向上移动。

之后再判断移动的目标位置,如果目标位置不在这个范围里面,那么就根据矩形范围来设置你能移动到的边界点。

newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));

首先比较当前坐标点和左边谁的大,然后针对大的再跟右边边界比,选个小的作为横坐标。第二行纵坐标也类似。(原谅我表达不行,这其实很好理解的,大家画画图理解一下就好了)

我们在主函数里,或者任意构造函数(要被主函数调用哦)里写如下代码:

        //自定义图形项
	qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
	QGraphicsScene *scene2 = new QGraphicsScene;
	for (int i = 0; i < 5; ++i) {
		MyGraphicsItem2 *item2 = new MyGraphicsItem2;
		item2->setColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
		item2->setPos(i * 50 + 90, 50); //注意这个偏移是根据scene场景偏移的。
		scene2->addItem(item2);
	}
	MyGraphicsView2 * view2 = new MyGraphicsView2;
	view2->setScene(scene2);
	view2->setBackgroundBrush(QPixmap("./ShowDebug/background.png"));
	view2->resize(400, 300);
	scene2->setSceneRect(0, 0, width(), height());
	view2->show();

各位注意肯定是书上定义了一个随机数(我个人是不会这么无聊的~~),然后给图形项通过随机数赋颜色,之后再分配其在场景中的位置,之后把场景的坐标和视口(物理区域)坐标设置重合,然后显示如下:

左上角的五个小正方形只能在横坐标为40——340的区域运动。

现在我们已经万事俱备了,接下来,就开始我们的终极目标:传输函数控件的设计。

 

相关文章:

  • QT传输函数控件设计1 引子
  • QT传输函数控件设计2 体渲染和VTK
  • QT传输函数控件设计3 QVTKWidget和QT图形界面的融合
  • QT传输函数控件设计4 接着进行融合
  • QT传输函数控件设计5 显示体渲染效果
  • QT传输函数控件设计6 设计Dock窗体结构
  • QT传输函数控件设计7 边界和布局规划
  • QT传输函数控件设计8 设计小圆点
  • QT传输函数控件设计9 初步设计视口类
  • QT传输函数控件设计10 包含小圆点的图形项
  • QT传输函数控件设计11 包含小圆点的图形项2
  • QT传输函数控件设计12 自定义信号和槽
  • QT传输函数控件设计13 大结局
  • QT三维图形1
  • QT三维图形2
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • create-react-app项目添加less配置
  • CSS 专业技巧
  • Fastjson的基本使用方法大全
  • JavaScript学习总结——原型
  • Java基本数据类型之Number
  • leetcode98. Validate Binary Search Tree
  • PAT A1092
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • 大整数乘法-表格法
  • 前端之React实战:创建跨平台的项目架构
  • 前嗅ForeSpider采集配置界面介绍
  • 删除表内多余的重复数据
  • 一个项目push到多个远程Git仓库
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (Forward) Music Player: From UI Proposal to Code
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (办公)springboot配置aop处理请求.
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (一)Java算法:二分查找
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • ***利用Ms05002溢出找“肉鸡
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • *上位机的定义
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .NET HttpWebRequest、WebClient、HttpClient
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .NET委托:一个关于C#的睡前故事
  • .net中应用SQL缓存(实例使用)
  • @Not - Empty-Null-Blank
  • [ vulhub漏洞复现篇 ] Apache Flink目录遍历(CVE-2020-17519)
  • [ASP.NET 控件实作 Day7] 设定工具箱的控件图标
  • [BZOJ 3531][Sdoi2014]旅行(树链剖分+线段树)
  • [CTF]php is_numeric绕过
  • [HackMyVM]靶场 VivifyTech