1. 常见 18 种 Qt 绘图技术
1.1 widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <memory>
#include <QTreeView>
#include "CPaintWidget.h"
using namespace std;
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
void treeView();
private slots:
void treeViewExpand(const QModelIndex &index);
private:
QTreeView* m_pLeftTree = nullptr;
CPaintWidget* m_pPaintWidget = nullptr;
};
#endif // WIDGET_H
1.2 widget.cpp
#pragma execution_character_set("utf-8")
#include "widget.h"
#include <QStandardItemModel>
#include <QHBoxLayout>
#include "drawtype.h"
Widget::Widget(QWidget* parent) : QWidget(parent) {
setWindowTitle(u8"draw all");
QHBoxLayout* pHLay = new QHBoxLayout(this);
// 创建左边树形视图
m_pLeftTree = new QTreeView(this);
m_pLeftTree->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置不可编辑
m_pLeftTree->setFixedWidth(300);
// 创建右边绘图窗口
m_pPaintWidget = new CPaintWidget(this);
// 添加进水平布局
pHLay->addWidget(m_pLeftTree);
pHLay->addWidget(m_pPaintWidget);
treeView();
}
Widget::~Widget() {}
void Widget::treeView() {
m_pLeftTree->setFrameShape(QFrame::NoFrame);
QStandardItemModel* model = new QStandardItemModel(m_pLeftTree);
model->setHorizontalHeaderLabels(QStringList() << "draw all");
QStandardItem* pParentItem = NULL;
QStandardItem* pChildItem = NULL;
// 点
pParentItem = new QStandardItem(QIcon(":/resources/point.png"), "draw ponit");
model->appendRow(pParentItem);
pChildItem = new QStandardItem(QIcon(":/resources/point.png"), "point");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/multipoints.png"), "multipoints");
pParentItem->appendRow(pChildItem);
// 线
pParentItem = new QStandardItem(QIcon(":/resources/line.png"), "draw line");
model->appendRow(pParentItem);
pChildItem = new QStandardItem(QIcon(":/resources/line.png"), "line");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/arc.png"), "arc");
pParentItem->appendRow(pChildItem);
// 封闭的图形
pParentItem = new QStandardItem(QIcon(":/resources/rect.png"), "draw rect");
model->appendRow(pParentItem);
pChildItem = new QStandardItem(QIcon(":/resources/rect.png"), "rect");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/roundrect.png"), "roundrect");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/chord.png"), "chord");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/ellipse.png"), "ellipse");
pParentItem->appendRow(pChildItem);
// 任意路径绘制
pParentItem = new QStandardItem(QIcon(":/resources/polygon.png"), "draw polygon");
model->appendRow(pParentItem);
pChildItem = new QStandardItem(QIcon(":/resources/polygon.png"), "polygon");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/polyline.png"), "polyline");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/ConvexPloygon.png"), "ConvexPloygon");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/lines.png"), "lines");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/path.png"), "path");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/pie.png"), "pie");
pParentItem->appendRow(pChildItem);
// 图片绘制
pParentItem = new QStandardItem(QIcon(":/resources/image.png"), "draw image");
model->appendRow(pParentItem);
pChildItem = new QStandardItem(QIcon(":/resources/image.png"), "image");
pParentItem->appendRow(pChildItem);
pChildItem = new QStandardItem(QIcon(":/resources/pixmap.png"), "pixmap");
pParentItem->appendRow(pChildItem);
// 文本绘制
pParentItem = new QStandardItem(QIcon(":/resources/text.png"), "draw text");
model->appendRow(pParentItem);
// 擦除
pParentItem = new QStandardItem(QIcon(":/resources/erase.png"), "draw erase");
model->appendRow(pParentItem);
// 路径填充
pParentItem = new QStandardItem(QIcon(":/resources/fillpath.png"), "draw fillpath");
model->appendRow(pParentItem);
// 矩形填充
pParentItem = new QStandardItem(QIcon(":/resources/fillrect.png"), "draw fillrect");
model->appendRow(pParentItem);
m_pLeftTree->setModel(model); // 模型视图
connect(m_pLeftTree, &QAbstractItemView::clicked, this, &Widget::treeViewExpand);
}
void Widget::treeViewExpand(const QModelIndex& index) {
QString text = index.data().toString();
if (text.compare("point") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::point);
// 不更新就不会立即显示
m_pPaintWidget->update();
} else if (text.compare("multipoints") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::multipoints);
m_pPaintWidget->update();
} else if (text.compare("line") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::line);
m_pPaintWidget->update();
} else if (text.compare("arc") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::arc);
m_pPaintWidget->update();
} else if (text.compare("rect") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::rect);
m_pPaintWidget->update();
} else if (text.compare("roundrect") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::roundrect);
m_pPaintWidget->update();
} else if (text.compare("chord") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::chord);
m_pPaintWidget->update();
} else if (text.compare("ellipse") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::ellipse);
m_pPaintWidget->update();
} else if (text.compare("polygon") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::polygon);
m_pPaintWidget->update();
} else if (text.compare("polyline") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::polyline);
m_pPaintWidget->update();
} else if (text.compare("ConvexPloygon") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::ConvexPloygon);
m_pPaintWidget->update();
} else if (text.compare("lines") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::lines);
m_pPaintWidget->update();
} else if (text.compare("path") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::path);
m_pPaintWidget->update();
} else if (text.compare("pie") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::pie);
m_pPaintWidget->update();
} else if (text.compare("image") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::image);
m_pPaintWidget->update();
} else if (text.compare("pixmap") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::pixmap);
m_pPaintWidget->update();
} else if (text.compare("draw text") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::draw_text);
m_pPaintWidget->update();
} else if (text.compare("draw erase") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::draw_erase);
m_pPaintWidget->update();
} else if (text.compare("draw fillpath") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::draw_fillpath);
m_pPaintWidget->update();
} else if (text.compare("draw fillrect") == 0) {
m_pPaintWidget->setDrawType(DRAW_TYPE::draw_fillrect);
m_pPaintWidget->update();
}
}
1.3 CPaintWidget.h
#pragma once
#include <QWidget>
#include "drawtype.h"
class CPaintWidget : public QWidget {
Q_OBJECT
public:
CPaintWidget(QWidget* p = nullptr);
~CPaintWidget() {}
void setDrawType(DRAW_TYPE type);
private:
void paintEvent(QPaintEvent* event) override;
private:
void draw_point();
void draw_multipoints();
void draw_line();
void draw_arc();
void draw_rect();
void draw_roundrect();
void draw_chord();
void draw_ellipse();
void draw_polygon();
void draw_polyline();
void draw_ConvexPloygon();
void draw_lines();
void draw_path();
void draw_pie();
void draw_image();
void draw_pixmap();
void draw_text();
void draw_erase();
void draw_fillpath();
void draw_fillrect();
private:
DRAW_TYPE m_drawType;
int W = 0;
int H = 0;
};
1.4 CPaintWidget.cpp
#include "CPaintWidget.h"
#include <QPainter>
#include <QPainterPath>
CPaintWidget::CPaintWidget(QWidget* p) :QWidget(p) {
this->setMinimumSize(800, 600);
m_drawType = DRAW_TYPE::polygon;
}
void CPaintWidget::paintEvent(QPaintEvent* event) {
W = this->width();
H = this->height();
switch (m_drawType) {
case DRAW_TYPE::point:
draw_point();
break;
case DRAW_TYPE::multipoints:
draw_multipoints();
break;
case DRAW_TYPE::line:
draw_line();
break;
case DRAW_TYPE::arc:
draw_arc();
break;
case DRAW_TYPE::rect:
draw_rect();
break;
case DRAW_TYPE::roundrect:
draw_roundrect();
break;
case DRAW_TYPE::chord:
draw_chord();
break;
case DRAW_TYPE::ellipse:
draw_ellipse();
break;
case DRAW_TYPE::polygon:
draw_polygon();
break;
case DRAW_TYPE::polyline:
draw_polyline();
break;
case DRAW_TYPE::ConvexPloygon:
draw_ConvexPloygon();
break;
case DRAW_TYPE::lines:
draw_lines();
break;
case DRAW_TYPE::path:
draw_path();
break;
case DRAW_TYPE::pie:
draw_pie();
break;
case DRAW_TYPE::image:
draw_image();
break;
case DRAW_TYPE::pixmap:
draw_pixmap();
break;
case DRAW_TYPE::draw_text:
draw_text();
break;
case DRAW_TYPE::draw_erase:
draw_erase();
break;
case DRAW_TYPE::draw_fillpath:
draw_fillpath();
break;
case DRAW_TYPE::draw_fillrect:
draw_fillrect();
break;
default:
break;
}
}
void CPaintWidget::draw_point() {
QPainter painter(this);
QPen pen;
pen.setWidth(10);
pen.setColor(Qt::red);
pen.setStyle(Qt::SolidLine);
painter.setPen(pen);
painter.drawPoint(QPoint(W / 2, H / 2));
}
void CPaintWidget::draw_multipoints() {
QPainter painter(this);
QPen pen;
pen.setWidth(10);
pen.setColor(Qt::blue);
pen.setStyle(Qt::SolidLine);
painter.setPen(pen);
// 画很多点
QPoint points[] = {
QPoint(5 * W / 12,H / 4),
QPoint(3 * W / 4, 5 * H / 12),
QPoint(2 * W / 4, 5 * H / 12)};
painter.drawPoints(points, 3);
}
void CPaintWidget::draw_line() {
QPainter painter(this);
//QLine Line(W / 4, H / 4, W / 2, H / 2);
QLine Line(W / 4, H / 4, W / 2, H / 4);
painter.drawLine(Line);
}
void CPaintWidget::draw_arc() {
QPainter painter(this);
QRect rect(W / 4, H / 4, W / 2, H / 2);
int startAngle = 90 * 16;
int spanAngle = 90 * 16; //旋转 90°
painter.drawArc(rect, startAngle, spanAngle);
}
void CPaintWidget::draw_rect() {
QPainter painter(this);
// 画矩形
QRect rect(W / 4, H / 4, W / 2, H / 2);
painter.drawRect(rect);
}
void CPaintWidget::draw_roundrect() {
QPainter painter(this);
// 画圆角矩形
QRect rect(W / 4, H / 4, W / 2, H / 2);
painter.drawRoundedRect(rect, 20, 20);
}
void CPaintWidget::draw_chord() {
QPainter painter(this);
QRect rect(W / 4, H / 4, W / 2, H / 2);
int startAngle = 90 * 16;
int spanAngle = 90 * 16;
painter.drawChord(rect, startAngle, spanAngle);
}
void CPaintWidget::draw_ellipse() {
QPainter painter(this);
QRect rect(W / 4, H / 4, W / 2, H / 2);
painter.drawEllipse(rect);
}
void CPaintWidget::draw_polygon() {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen; // 画笔描边
pen.setWidth(10);
pen.setColor(Qt::red);
pen.setStyle(Qt::SolidLine);
pen.setCapStyle(Qt::SquareCap);
pen.setJoinStyle(Qt::MiterJoin); // 画笔断点的样式
painter.setPen(pen);
QBrush brush; // 画刷填充
brush.setColor(Qt::yellow);
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
// 画多边形,最后一个点会和第一个点闭合
QPoint points[] = {
QPoint(5 * W / 12,H / 4),
QPoint(3 * W / 4,5 * H / 12),
QPoint(5 * W / 12,3 * H / 4),
QPoint(2 * W / 4,5 * H / 12)};
painter.drawPolygon(points, 4);
}
void CPaintWidget::draw_polyline() {
QPainter painter(this);
// 画多点连接的线,最后一个点不会和第一个点连接
QPoint points[] = {
QPoint(5 * W / 12, H / 4),
QPoint(3 * W / 4, 5 * H / 12),
QPoint(5 * W / 12, 3 * H / 4),
QPoint(2 * W / 4, 5 * H / 12)};
painter.drawPolyline(points, 4);
}
void CPaintWidget::draw_ConvexPloygon() {
QPainter painter(this);
QPoint points[4] = {
QPoint(5 * W / 12, H / 4),
QPoint(3 * W / 4, 5 * H / 12),
QPoint(5 * W / 12, 3 * H / 4),
QPoint(W / 4, 5 * H / 12)};
painter.drawConvexPolygon(points, 4);
}
void CPaintWidget::draw_lines() {
QPainter painter(this);
// 画一批直线
QRect rect(W / 4, H / 4, W / 2, H / 2);
QVector<QLine> Lines;
Lines.append(QLine(rect.topLeft(), rect.bottomRight()));
Lines.append(QLine(rect.topRight(), rect.bottomLeft()));
Lines.append(QLine(rect.topLeft(), rect.bottomLeft()));
Lines.append(QLine(rect.topRight(), rect.bottomRight()));
painter.drawLines(Lines);
}
void CPaintWidget::draw_path() {
QPainter painter(this);
// 绘制由 QPainterPath 对象定义的路线
QRect rect(W / 4, H / 4, W / 2, H / 2);
QPainterPath path;
path.addEllipse(rect);
path.addRect(rect);
painter.drawPath(path);
}
void CPaintWidget::draw_pie() {
QPainter painter(this);
// 绘制扇形
QRect rect(W / 4, H / 4, W / 2, H / 2);
int startAngle = 40 * 16; // 起始 40
int spanAngle = 120 * 16; // 旋转 120
painter.drawPie(rect, startAngle, spanAngle);
}
void CPaintWidget::draw_image() {
QPainter painter(this);
// 在指定的矩形区域内绘制图片
QRect rect(W / 4, H / 4, W / 2, H / 2);
QImage image(":/resources/tupian.png");
painter.drawImage(rect, image);
}
void CPaintWidget::draw_pixmap() {
QPainter painter(this);
// 绘制 Pixmap 图片
QRect rect(W / 4, H / 4, W / 2, H / 2);
QPixmap pixmap(":/resources/tupix.png");
painter.drawPixmap(rect, pixmap);
}
void CPaintWidget::draw_text() {
QPainter painter(this);
// 绘制文本,只能绘制单行文字,字体的大小等属性由 QPainter::font() 决定
QRect rect(W / 4, H / 4, W / 2, H / 2);
QFont font;
font.setPointSize(30);
font.setBold(true);
painter.setFont(font);
painter.drawText(rect, "Hello,Qt");
}
void CPaintWidget::draw_erase() {
QPainter painter(this);
// 擦除某个矩形区域,等效于用背景色填充该区域
QRect rect(W / 4, H / 4, W / 2, H / 2);
painter.eraseRect(rect);
}
void CPaintWidget::draw_fillpath() {
QPainter painter(this);
// 填充某个 QPainterPath 定义的绘图路径,但是轮廓线不显示
QRect rect(W / 4, H / 4, W / 2, H / 2);
QPainterPath path;
path.addEllipse(rect);
path.addRect(rect);
painter.fillPath(path, Qt::red);
}
void CPaintWidget::draw_fillrect() {
QPainter painter(this);
// 填充一个矩形,无边框线
QRect rect(W / 4, H / 4, W / 2, H / 2);
painter.fillRect(rect, Qt::green);
}
void CPaintWidget::setDrawType(DRAW_TYPE type) {
m_drawType = type;
}
1.5 drawtype.h
#pragma once
enum class DRAW_TYPE {
point,
multipoints,
line,
arc,
rect,
roundrect,
chord,
ellipse,
polygon,
polyline,
ConvexPloygon,
lines,
path,
pie,
image,
pixmap,
draw_text,
draw_erase,
draw_fillpath,
draw_fillrect
};
2. Qt 鼠标手动绘图技术
2.1 mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMenu>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
// 右键菜单需要重写的类
void contextMenuEvent(QContextMenuEvent* event) override;
private:
Ui::MainWindow *ui;
QMenu* m_pMenu;
};
#endif // MAINWINDOW_H
2.2 mainwindow.cpp
/*
使用方式:左键点击,移动鼠标开始绘制,双击左键结束绘制,或者右键点击结束绘制
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenu>
#include <QAction>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
m_pMenu = new QMenu(this);
QAction* pAc1 = new QAction(QString::fromLocal8Bit("结束绘制"), this);
pAc1->setShortcut(QKeySequence("Ctrl+E"));
QAction* pAc2 = new QAction(QString::fromLocal8Bit("清除"), this);
pAc2->setShortcut(QKeySequence("Ctrl+D"));
// 这是个假动作,为了让菜单消失,且不影响绘制路径
QAction* pAc3 = new QAction(QString::fromLocal8Bit("退出菜单"), this);
m_pMenu->addAction(pAc1);
m_pMenu->addAction(pAc2);
m_pMenu->addAction(pAc3);
m_pMenu->setStyleSheet("QMenu{font:18px;}");
connect(pAc1, &QAction::triggered, [=] {
ui->graphicsPainter->endDraw();
});
connect(pAc2, &QAction::triggered, [=] {
ui->graphicsPainter->clearPath();
});
}
MainWindow::~MainWindow() {
delete ui;
}
// 右键菜单
void MainWindow::contextMenuEvent(QContextMenuEvent* event) {
m_pMenu->move(cursor().pos()); // 移动右键菜单到鼠标当前位置
m_pMenu->show();
}
2.3 MyPainterWidget.h
#ifndef GRAPHICSPAINTER_H
#define GRAPHICSPAINTER_H
#include <QWidget>
class MyPainterWidget : public QWidget {
Q_OBJECT
public:
explicit MyPainterWidget(QWidget *parent = nullptr);
void endDraw();
void clearPath();
protected:
void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent *e) override; // 按下
void mouseMoveEvent(QMouseEvent *e) override; // 移动
void mouseReleaseEvent(QMouseEvent *e) override; // 松开
void mouseDoubleClickEvent(QMouseEvent *event) override; // 双击
bool m_bStartDraw = false; // 是否已经开始左键点击,同时标识是否开始进行绘制
bool bMove = false; // 是否处于绘制时的鼠标移动状态
QVector<QPointF> pointList;
QPointF movePoint;
};
#endif // GRAPHICSPAINTER_H
2.4 MyPainterWidget.cpp
#include "MyPainterWidget.h"
#include <QPainter>
#include <QMouseEvent>
MyPainterWidget::MyPainterWidget(QWidget *parent) : QWidget(parent) {
setMouseTracking(true);
pointList.clear();
}
void MyPainterWidget::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setPen(QColor(255,0,0));
QVector<QLineF> lines;
for(int i = 0; i < pointList.size()-1; i++) {
QLineF line(QPointF(pointList[i].x(), pointList[i].y()),
QPointF(pointList[i+1].x(), pointList[i+1].y()));
lines.push_back(line);
}
if (m_bStartDraw) {
int size = pointList.size();
if (bMove && size > 0) {
QLineF line(QPointF(pointList[pointList.size() - 1].x(), pointList[pointList.size() - 1].y()),
movePoint);
lines.push_back(line);
}
}
painter.drawLines(lines);
}
// 按下
void MyPainterWidget::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
if(!m_bStartDraw) {
pointList.clear();
m_bStartDraw = true;
}
}
}
// 移动
void MyPainterWidget::mouseMoveEvent(QMouseEvent *e) {
if(m_bStartDraw) {
movePoint = e->pos();
this->update();
// 先刷新再设为true, 防止第一点和(0,0)连在一块
bMove = true;
}
}
// 松开
void MyPainterWidget::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
if (m_bStartDraw) {
// 鼠标松开后的点需要添加到路径中
pointList.push_back(QPointF(e->x(), e->y()));
bMove = false;
this->update();
}
}
}
// 双击结束绘制
void MyPainterWidget::mouseDoubleClickEvent(QMouseEvent *event) {
endDraw();
}
void MyPainterWidget::endDraw() {
m_bStartDraw = false;
// 需要把第一个点连起来
pointList.push_back(pointList[0]);
this->update();
}
void MyPainterWidget::clearPath() {
pointList.clear();
this->update();
}
3. Qt 绘制带三角形箭头的窗口
3.1 widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
3.2 widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "triangledialog.h"
#include <QLabel>
#include "userinfowidget.h"
#include <QPropertyAnimation>
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
}
Widget::~Widget() {
delete ui;
}
void Widget::on_pushButton_clicked() {
int triangle_start_x = 60;
TriangleDialog *pDialog = new TriangleDialog(343, 320, this);
pDialog->setTriangleInfo(triangle_start_x, 20, 12);
// 设置三角窗口的弹出位置, 有Qt::Popup属性
QPoint p1 = ui->pushButton->mapToGlobal(QPoint(0, 0)); //按钮左上角相对于桌面的绝对位置
QRect rect1 = ui->pushButton->rect();
int x = p1.x() + rect1.width() / 2 - triangle_start_x - 20 / 2;
int y = p1.y() + rect1.height() + 1 - 15;
pDialog->move(x, y);
pDialog->show();
}
3.3 userinfowidget.h
#ifndef USERINFOWIDGET_H
#define USERINFOWIDGET_H
#include <QWidget>
namespace Ui {
class UserInfoWidget;
}
class UserInfoWidget : public QWidget {
Q_OBJECT
public:
explicit UserInfoWidget(QWidget *parent = nullptr);
~UserInfoWidget();
private:
Ui::UserInfoWidget *ui;
};
#endif // USERINFOWIDGET_H
3.4 userinfowidget.cpp
#include "userinfowidget.h"
#include "ui_userinfowidget.h"
UserInfoWidget::UserInfoWidget(QWidget *parent) : QWidget(parent), ui(new Ui::UserInfoWidget) {
ui->setupUi(this);
setFixedSize(this->width(), this->height());
setAttribute(Qt::WA_StyledBackground);
QString qss = "QLabel{font-family:Microsoft YaHei; font-size:18px; color:#ffffff; background-color:#363636;}";
ui->label_UserImage->setText("");
QPixmap pmp1(":/resources/user_image.png");
pmp1.scaled(ui->label_UserImage->size(), Qt::KeepAspectRatio);
ui->label_UserImage->setScaledContents(true);
ui->label_UserImage->setPixmap(pmp1);
ui->label_UserName->setText(u8"没有做完的梦最痛");
ui->label_UserName->setStyleSheet(qss);
ui->label_VipInfo->setText("");
QPixmap pmp2(":/resources/vipinfo.png");
pmp2.scaled(ui->label_VipInfo->size(), Qt::KeepAspectRatio);
ui->label_VipInfo->setScaledContents(true);
ui->label_VipInfo->setPixmap(pmp2);
}
UserInfoWidget::~UserInfoWidget() {
delete ui;
}
3.5 triangledialog.h
/*
带三角形箭头的对话框
*/
#ifndef TRIANGLEDIALOG_H
#define TRIANGLEDIALOG_H
#include <QDialog>
class TriangleDialog : public QDialog {
Q_OBJECT
public:
explicit TriangleDialog(int fixedW, int fixedH, QWidget *parent = nullptr);
// 设置小三角行的起始位置、宽、高
void setTriangleInfo(int startX, int width, int height);
protected:
void paintEvent(QPaintEvent *e);
private:
// 小三角形的起始位置
int m_startX;
// 小三角形的宽度
int m_triangleWidth;
// 小三角形的高度
int m_triangleHeight;
};
#endif // TRIANGLEDIALOG_H
3.6 triangledialog.cpp
#include "triangledialog.h"
#include <QVBoxLayout>
#include <QPainter>
#include <QGraphicsDropShadowEffect>
#include <QPainterPath>
#include "userinfowidget.h"
#define SHADOW_WIDTH 15 // 窗口阴影宽度
#define TRIANGLE_WIDTH 15 // 小三角行的宽度
#define TRIANGLE_HEIGHT 10 // 小三角行的高度
#define BORDER_RADIUS 5 // 窗口圆角
TriangleDialog::TriangleDialog(int fixedW, int fixedH, QWidget *parent) : QDialog(parent)
,m_startX(50)
,m_triangleWidth(TRIANGLE_WIDTH)
,m_triangleHeight(TRIANGLE_HEIGHT) {
setWindowFlags(Qt::FramelessWindowHint | Qt::Popup | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_TranslucentBackground); // 设置透明窗口, 为窗口阴影和圆角做准备
UserInfoWidget* pUserinfoWidget = new UserInfoWidget(this);
QVBoxLayout* pVlay = new QVBoxLayout(this);
pVlay->addWidget(pUserinfoWidget);
// 设置布局的四周边距
pVlay->setContentsMargins(SHADOW_WIDTH, SHADOW_WIDTH + TRIANGLE_HEIGHT, SHADOW_WIDTH, SHADOW_WIDTH);
// 设置阴影边框
auto shadowEffect = new QGraphicsDropShadowEffect(this);
shadowEffect->setOffset(0, 0);
shadowEffect->setColor(Qt::red);
shadowEffect->setBlurRadius(SHADOW_WIDTH);
this->setGraphicsEffect(shadowEffect);
// 设置窗口固定大小
setFixedSize(fixedW, fixedH);
}
void TriangleDialog::setTriangleInfo(int startX, int width, int height) {
m_startX = startX;
m_triangleWidth = width;
m_triangleHeight = height;
}
void TriangleDialog::paintEvent(QPaintEvent *e) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(55, 55, 55));
// 小三角区域,这里是等腰三角形
QPolygon trianglePolygon;
trianglePolygon << QPoint(m_startX, m_triangleHeight + SHADOW_WIDTH);
trianglePolygon << QPoint(m_startX + m_triangleWidth / 2, SHADOW_WIDTH);
trianglePolygon << QPoint(m_startX + m_triangleWidth, m_triangleHeight + SHADOW_WIDTH);
QPainterPath drawPath;
drawPath.addRoundedRect(QRect(SHADOW_WIDTH, SHADOW_WIDTH + m_triangleHeight,
this->width() - SHADOW_WIDTH * 2, this->height() - SHADOW_WIDTH * 2 - m_triangleHeight),
BORDER_RADIUS, BORDER_RADIUS);
// 矩形路径加上三角形
drawPath.addPolygon(trianglePolygon);
// 绘制路径
painter.drawPath(drawPath);
}
4. Qt 场景视图技术
4.1 简介
- 使用 QPainter 可以绘制一些常用的图形,如果需要对绘制的图形进行编辑,例如拉伸,拖拽,组合等,会比较复杂,用代码是可以实现的,但是会比较复杂,例如处理鼠标的形状,判定拉伸拖拽的点,然后重绘等等,如果拉伸的时候和其它图形有冲突,还需要做判定处理
- 使用 QPainter 的绘制技术不太适合对图形进行选择、编辑、拖放、修改等功能
- Qt 提供了 Graphics View 图形架构,是一种基于图形项的模型/视图模式,使用 Graphics View 架构可以绘制复杂的有几万个基本图形元件的图形,并且每个图形元件是可选择、可拖放和修改的,类似于矢量绘图软件的绘图功能
4.2 Graphics View 基本组成
- Graphics View 架构主要由 3 部分组成,即视图、场景和图形项,其构成的绘图系统结构如下图所示
- 视图 QGraphicsScene:提供绘图的视图组件,用于显示场景中的内容
- 视图比场景大时,显示场景的全部内容(默认在中间部分显示)
- 视图比场景小时,只能显示场景的部分内容,但是会自动提供卷滚条在整个场景内移动
- 场景 QGraphics View:提供绘图场景
- 场景是不可见的,是管理图形项的容器,可以向场景添加图形项,获取场景中的某个图形项等
- 图形项 QGraphicsItem:一些基本的图形元件
- 支持鼠标事件响应、键盘输入与按键事件
- 支持拖放、组合操作
- 视图 QGraphicsScene:提供绘图的视图组件,用于显示场景中的内容
4.3 Graphics View 坐标系统
4.3.1 视图坐标
- 视图坐标与设备坐标相同,是物理坐标,缺省以左上角为原点 (0, 0)
- 所有的鼠标事件、拖放事件的坐标首先是由视图坐标定义的
- 用户需要将视图坐标映射为场景坐标,以便和图形项交互
4.3.2 场景坐标
- 场景的坐标一般以场景的中心为原点
- 场景是所有图形项的基础坐标,场景坐标描述了每个顶层图形项的位置,创建场景时可定义场景坐标范围
- scene 是左上角坐标为 (-400,-300),宽度为 800,高度为 600 的矩形区域,单位是像素
scene = new QGraphicsScene(-400, -300, 800, 600);
4.3.3 图形项坐标
-
图形项使用自己的局部坐标,通常以其中心为原点 (0, 0),也是各种坐标变换的中心
- 图形项的鼠标事件的坐标是用局部坐标表示的,创建自定义图形项,绘制图形项时只需考虑其局部坐标
-
一个图形项的位置是其中心点在父坐标系统中的坐标
- 对于没有父图形项的图形项,其父对象就是场景,图形项的位置就是在场景中的坐标
-
如果一个图形项还是其他图形项的父项,父项进行坐标变换时,子项也做同样的坐标变换
4.3.4 坐标映射
- 在场景中操作图形项时,进行场景到图形项、图形项到图形项,或视图到场景之间的坐标变换是比较有用的
- 例如在 QGraphicsView 的视口上单击鼠标时,通过函数 QGraphicsView::mapToScene() 可以将视图坐标映射为场景坐标,然后用 QGraphicsScene::itemAt() 函数可以获取场景中鼠标光标处的图形项
4.4 基本图元绘制
4.4.1 主界面
- widget.h
#pragma once
#include <QWidget>
#include "ui_widget.h"
#include <QGraphicsScene>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
void initGraphicsView();
private slots:
void on_mouseMovePoint(QPoint point);
void on_mouseClicked(QPoint point);
private:
Ui::Widget *ui;
QGraphicsScene *scene;
};
- widget.cpp
#include "widget.h"
#include <QGraphicsRectItem>
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
connect(ui->graphicView, &MyGraphicsView::mouseMovePoint, this, &Widget::on_mouseMovePoint);
connect(ui->graphicView, &MyGraphicsView::mouseClicked, this, &Widget::on_mouseClicked);
initGraphicsView();
}
Widget::~Widget() {
delete ui;
}
// 视图初始化
void Widget::initGraphicsView() {
QRectF rect(-200, -100, 400, 200);
scene = new QGraphicsScene(rect);
// 给视图设置场景
ui->graphicView->setScene(scene);
// 画一个矩形框,大小等于 scene
QGraphicsRectItem *item = new QGraphicsRectItem(rect);
// 可选,可以有焦点,但是不能移动
item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
QPen pen;
pen.setWidth(20);
item->setPen(pen);
scene->addItem(item);
// 一个位于 scene 中心的椭圆
QGraphicsEllipseItem *item2 = new QGraphicsEllipseItem(-100, -50, 200, 100);
item2->setPos(-200, 0);
item2->setBrush(QBrush(Qt::blue));
item2->setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);
scene->addItem(item2);
// 一个圆,中心位于 scene 的边缘
QGraphicsEllipseItem *item3 = new QGraphicsEllipseItem(-50, -50, 100, 100);
item3->setPos(rect.right(), rect.bottom());
item3->setBrush(QBrush(Qt::red));
item3->setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);
scene->addItem(item3);
scene->clearSelection();
}
// 鼠标移动事件,point是 GraphicsView 的坐标,物理坐标
void Widget::on_mouseMovePoint(QPoint point) {
ui->label_viewpos->setText(QString::asprintf("%d, %d", point.x(), point.y()));
QPointF pointScene = ui->graphicView->mapToScene(point); // 视图转换到场景坐标
ui->label_scenepos->setText(QString::asprintf("%.0f,%.0f", pointScene.x(), pointScene.y()));
}
// 鼠标单击事件
void Widget::on_mouseClicked(QPoint point) {
QPointF pointScene = ui->graphicView->mapToScene(point); // 视图转换到场景坐标
QGraphicsItem *item = NULL;
item = scene->itemAt(pointScene,ui->graphicView->transform()); // 获取光标下的绘图项
if (item != NULL) { // 有图形项
QPointF pointItem = item->mapFromScene(pointScene); // 场景坐标转换为图形项的局部坐标
ui->label_itempos->setText(QString::asprintf("%.0f, %.0f", pointItem.x(), pointItem.y()));
}
}
4.4.2 场景视图界面
- MyGraphicsView.h
#pragma once
#include <QObject>
#include <QGraphicsView>
class MyGraphicsView : public QGraphicsView {
Q_OBJECT
protected:
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
public:
MyGraphicsView(QWidget *parent = 0);
signals:
void mouseMovePoint(QPoint point);
void mouseClicked(QPoint point);
};
- MyGraphicsView.cpp
#include "MyGraphicsView.h"
#include <QMouseEvent>
#include <QPoint>
MyGraphicsView::MyGraphicsView(QWidget* parent) :QGraphicsView(parent) {
// 设置视图的鼠标指针形状为十字光标
this->setCursor(Qt::CrossCursor);
// 启用鼠标追踪功能,即使没有按下鼠标按钮,也可以接收到鼠标移动事件
this->setMouseTracking(true);
// 设置视图的拖动模式为拉框拖动模式
// 通过点击并按住鼠标左键来创建一个矩形框选择多个图形项
this->setDragMode(QGraphicsView::RubberBandDrag);
}
// 鼠标移动事件
void MyGraphicsView::mouseMoveEvent(QMouseEvent *event) {
QPoint point = event->pos(); // QGraphicsView 视图坐标
emit mouseMovePoint(point); // 释放信号
QGraphicsView::mouseMoveEvent(event);
}
// 鼠标左键按下事件
void MyGraphicsView::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
QPoint point = event->pos(); // 返回当前鼠标光标的位置(视图坐标系下)
emit mouseClicked(point); // 发射鼠标点击信号
}
QGraphicsView::mousePressEvent(event); // 响应自身的鼠标移动事件
}
4.5 自定义图元实现拖拽、拉伸、旋转
4.5.1 主界面
- ch88_CustomRectItem.h
#pragma once
#include <QtWidgets/QWidget>
#include "ui_ch88_CustomRectItem.h"
class ch88_CustomRectItem : public QWidget {
Q_OBJECT
public:
ch88_CustomRectItem(QWidget *parent = nullptr);
~ch88_CustomRectItem();
private:
Ui::ch88_CustomRectItemClass ui;
};
- ch88_CustomRectItem.cpp
#include "ch88_CustomRectItem.h"
#include "MyRectItem.h"
ch88_CustomRectItem::ch88_CustomRectItem(QWidget *parent) : QWidget(parent) {
ui.setupUi(this);
QRect rect = ui.graphicsView->rect();
// 创建场景
QGraphicsScene* pScene = new QGraphicsScene(rect);
// 视图里设置场景
ui.graphicsView->setScene(pScene);
// 创建自定义图元
MyRectItem* pRectItem = new MyRectItem();
// 场景添加图元
pScene->addItem(pRectItem);
}
ch88_CustomRectItem::~ch88_CustomRectItem() {}
4.5.2 自定义矩形图元
- MyRectItem.h
/*
自定义矩形图元
*/
#pragma once
#include <QGraphicsItem>
enum STATE_FLAG {
DEFAULT_FLAG = 0,
MOV_LEFT_LINE, // 矩形的左边界区域
MOV_TOP_LINE, // 矩形的上边界区域
MOV_RIGHT_LINE, // 矩形的右边界区域
MOV_BOTTOM_LINE, // 矩形的下边界区域
MOV_RIGHTBOTTOM_RECT, // 矩形的右下角
MOV_RECT, // 移动状态
ROTATE // 旋转状态
};
class MyRectItem : public QObject, public QGraphicsItem {
Q_OBJECT
public:
MyRectItem(QGraphicsItem* parent = nullptr);
~MyRectItem();
// boundingRect() 是纯虚函数,必须由派生类重新实现
QRectF boundingRect() const override;
void setRectSize(QRectF mrect, bool bResetRotateCenter = true);
void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget) override;
void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;
void SetRotate(qreal RotateAngle, QPointF ptCenter = QPointF(-999, -999));
// 获取旋转后的点
QPointF getRotatePoint(QPointF ptCenter, QPointF ptIn, qreal angle);
// 获取多个旋转后的点
QList<QPointF> getRotatePoints(QPointF ptCenter, QList<QPointF> ptIns, qreal angle);
// 将矩形旋转之后返回多边形
QPolygonF getRotatePolygonFromRect(QPointF ptCenter, QRectF rectIn, qreal angle);
QRectF getCrtPosRectToSceen();
// 获取旋转时候矩形正上方的旋转标记矩形
QPointF getSmallRotateRectCenter(QPointF ptA, QPointF ptB);
QRectF getSmallRotateRect(QPointF ptA, QPointF ptB);
private:
QRectF m_oldRect;
QPolygonF m_oldRectPolygon;
QRectF m_RotateAreaRect;
bool m_bResize;
QPolygonF m_insicedPolygon;
QRectF m_insicedRectf;
QPolygonF m_leftPolygon;
QRectF m_leftRectf;
QPolygonF m_topPolygon;
QRectF m_topRectf;
QPolygonF m_rightPolygon;
QRectF m_rightRectf;
QPolygonF m_bottomPolygon;
QRectF m_bottomRectf;
QPointF m_startPos;
STATE_FLAG m_StateFlag;
QPointF* m_pPointFofSmallRotateRect;
QRectF m_SmallRotateRect; // 矩形顶部用来表示旋转的标记的矩形
QPolygonF m_SmallRotatePolygon; // 矩形顶部用来表示旋转的标记的矩形旋转后形成的多边形
bool m_bRotate;
qreal m_RotateAngle;
QPointF m_RotateCenter;
};
- MyRectItem.cpp
#include "MyRectItem.h"
#include <QtMath>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
MyRectItem::MyRectItem(QGraphicsItem* parent) :
m_bResize(false),
m_oldRect(0, 0, 200, 200),
m_bRotate(false),
m_RotateAngle(0),
m_StateFlag(DEFAULT_FLAG) {
setRectSize(m_oldRect);
// 设置光标形状,手的形状
setCursor(Qt::ArrowCursor);
// 设置图元是可移动的
setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);
m_pPointFofSmallRotateRect = new QPointF[4];
SetRotate(0);
}
MyRectItem::~MyRectItem() {
delete[] m_pPointFofSmallRotateRect;
m_pPointFofSmallRotateRect = nullptr;
}
QRectF MyRectItem::boundingRect() const {
QRectF boundingRectF = m_oldRectPolygon.boundingRect();
return QRectF(boundingRectF.x() - 40, boundingRectF.y() - 40,
boundingRectF.width() + 80, boundingRectF.height() + 80);
}
void MyRectItem::setRectSize(QRectF mrect, bool bResetRotateCenter) {
m_oldRect = mrect;
if (bResetRotateCenter) {
m_RotateCenter.setX(m_oldRect.x() + m_oldRect.width() / 2);
m_RotateCenter.setY(m_oldRect.y() + m_oldRect.height() / 2);
}
m_oldRectPolygon = getRotatePolygonFromRect(m_RotateCenter, m_oldRect, m_RotateAngle);
m_insicedRectf = QRectF(m_oldRect.x() + 8, m_oldRect.y() + 8, m_oldRect.width() - 16, m_oldRect.height() - 16);
m_insicedPolygon = getRotatePolygonFromRect(m_RotateCenter, m_insicedRectf, m_RotateAngle);
m_leftRectf = QRectF(m_oldRect.x(), m_oldRect.y(), 8, m_oldRect.height() - 8);
m_leftPolygon = getRotatePolygonFromRect(m_RotateCenter, m_leftRectf, m_RotateAngle);
m_topRectf = QRectF(m_oldRect.x() + 8, m_oldRect.y(), m_oldRect.width() - 8, 8);
m_topPolygon = getRotatePolygonFromRect(m_RotateCenter, m_topRectf, m_RotateAngle);
m_rightRectf = QRectF(m_oldRect.right() - 8, m_oldRect.y() + 8, 8, m_oldRect.height() - 16);
m_rightPolygon = getRotatePolygonFromRect(m_RotateCenter, m_rightRectf, m_RotateAngle);
m_bottomRectf = QRectF(m_oldRect.x(), m_oldRect.bottom() - 8, m_oldRect.width() - 8, 8);
m_bottomPolygon = getRotatePolygonFromRect(m_RotateCenter, m_bottomRectf, m_RotateAngle);
m_SmallRotateRect = getSmallRotateRect(mrect.topLeft(), mrect.topRight());//矩形正上方的旋转标记矩形
m_SmallRotatePolygon = getRotatePolygonFromRect(m_RotateCenter, m_SmallRotateRect, m_RotateAngle);
}
void MyRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
// 反锯齿
painter->setRenderHint(QPainter::Antialiasing, true);
QPen mPen = QPen(Qt::black);
mPen.setWidth(5);
painter->setPen(mPen);
// 绘制旋转后的矩形
painter->drawPolygon(m_oldRectPolygon);
// 绘制旋转圆形
mPen.setWidth(2);
mPen.setColor(Qt::green);
painter->setPen(mPen);
QPointF pf = getSmallRotateRectCenter(m_oldRectPolygon[0], m_oldRectPolygon[1]);
QRectF rect = QRectF(pf.x() - 10, pf.y() - 10, 20, 20);
// 绘制圆形
painter->drawEllipse(rect);
// 绘制点
painter->drawPoint(pf);
if (option->state & QStyle::State_Selected) {
// 绘制选择后的虚线框
const qreal penWidth = 1;
// 边框区域颜色
QColor color = QColor(Qt::white);
painter->setPen(QPen(color, penWidth, Qt::DashLine));
painter->setBrush(Qt::NoBrush);
painter->drawPolygon(m_oldRectPolygon);
}
}
void MyRectItem::mousePressEvent(QGraphicsSceneMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
m_startPos = event->pos();
if (m_SmallRotatePolygon.containsPoint(m_startPos, Qt::WindingFill)) {
// 鼠标在旋转圆形内内
setCursor(Qt::PointingHandCursor);
m_StateFlag = ROTATE;
} else if (m_insicedPolygon.containsPoint(m_startPos, Qt::WindingFill)) {
// 在矩形内框区域时
setCursor(Qt::ClosedHandCursor); // 改变光标形状,手的形状
m_StateFlag = MOV_RECT;
} else if (m_leftPolygon.containsPoint(m_startPos, Qt::WindingFill)) {
// 矩形的左边界区域
setCursor(Qt::SizeHorCursor);
m_StateFlag = MOV_LEFT_LINE;
} else if (m_rightPolygon.containsPoint(m_startPos, Qt::WindingFill)) {
// 矩形的右边界区域
setCursor(Qt::SizeHorCursor);
m_StateFlag = MOV_RIGHT_LINE;
} else if (m_topPolygon.containsPoint(m_startPos, Qt::WindingFill)) {
// 矩形的上边界区域
setCursor(Qt::SizeVerCursor);
m_StateFlag = MOV_TOP_LINE;
} else if (m_bottomPolygon.containsPoint(m_startPos, Qt::WindingFill)) {
// 矩形的下边界区域
setCursor(Qt::SizeVerCursor);
m_StateFlag = MOV_BOTTOM_LINE;
} else {
m_StateFlag = DEFAULT_FLAG;
}
} else {
QGraphicsItem::mousePressEvent(event);
}
}
void MyRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
if (m_StateFlag == ROTATE) {
// atan2 计算出来的是弧度值,需要转化为角度值
int nRotateAngle = atan2((event->pos().x() - m_RotateCenter.x()),
(event->pos().y() - m_RotateCenter.y())) * 180 / M_PI;
SetRotate(180 - nRotateAngle);
setRectSize(m_oldRect);
} else if (m_StateFlag == MOV_RECT) {
QPointF point = (event->pos() - m_startPos);
moveBy(point.x(), point.y());
setRectSize(m_oldRect);
scene()->update();
} else if (m_StateFlag == MOV_LEFT_LINE) {
QPointF pf = QPointF((m_oldRectPolygon.at(1).x() + m_oldRectPolygon.at(2).x()) / 2, ((m_oldRectPolygon.at(1).y() + m_oldRectPolygon.at(2).y()) / 2));
// 计算到右侧边中点的距离
qreal dis = sqrt((event->pos().x() - pf.x()) * (event->pos().x() - pf.x()) + (event->pos().y() - pf.y()) * (event->pos().y() - pf.y()));
qreal dis2LT = sqrt((event->pos().x() - m_oldRectPolygon.at(0).x()) * (event->pos().x() - m_oldRectPolygon.at(0).x()) + (event->pos().y() - m_oldRectPolygon.at(0).y()) * (event->pos().y() - m_oldRectPolygon.at(0).y()));
qreal dis2RT = sqrt((event->pos().x() - m_oldRectPolygon.at(1).x()) * (event->pos().x() - m_oldRectPolygon.at(1).x()) + (event->pos().y() - m_oldRectPolygon.at(1).y()) * (event->pos().y() - m_oldRectPolygon.at(1).y()));
if (dis < 16 || dis2LT > dis2RT) {
return;
} else {
QRectF newRect(m_oldRect);
newRect.setLeft(m_oldRect.right() - dis);
newRect.setRight(m_oldRect.right());
setRectSize(newRect, false);
m_RotateCenter = QPointF((m_oldRectPolygon.at(0).x() + m_oldRectPolygon.at(2).x()) / 2, (m_oldRectPolygon.at(0).y() + m_oldRectPolygon.at(2).y()) / 2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
// 必须要用scene()->update(),不能用update();否则会出现重影
scene()->update();
}
} else if (m_StateFlag == MOV_TOP_LINE) {
// 底边中点
QPointF pf = QPointF((m_oldRectPolygon.at(2).x() + m_oldRectPolygon.at(3).x()) / 2, ((m_oldRectPolygon.at(2).y() + m_oldRectPolygon.at(3).y()) / 2));
//计算到底边中点的距离
qreal dis = sqrt((event->pos().x() - pf.x()) * (event->pos().x() - pf.x()) + (event->pos().y() - pf.y()) * (event->pos().y() - pf.y()));
qreal dis2LT = sqrt((event->pos().x() - m_oldRectPolygon.at(0).x()) * (event->pos().x() - m_oldRectPolygon.at(0).x()) + (event->pos().y() - m_oldRectPolygon.at(0).y()) * (event->pos().y() - m_oldRectPolygon.at(0).y()));
qreal dis2LB = sqrt((event->pos().x() - m_oldRectPolygon.at(3).x()) * (event->pos().x() - m_oldRectPolygon.at(3).x()) + (event->pos().y() - m_oldRectPolygon.at(3).y()) * (event->pos().y() - m_oldRectPolygon.at(3).y()));
if (dis < 16 || dis2LT > dis2LB) {
return;
} else {
QRectF newRect(m_oldRect);
newRect.setTop(m_oldRect.bottom() - dis);
newRect.setBottom(m_oldRect.bottom());
setRectSize(newRect, false);
m_RotateCenter = QPointF((m_oldRectPolygon.at(0).x() + m_oldRectPolygon.at(2).x()) / 2, (m_oldRectPolygon.at(0).y() + m_oldRectPolygon.at(2).y()) / 2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
scene()->update(); // 必须要用scene()->update(),不能用 update(),否则会出现重影
}
} else if (m_StateFlag == MOV_RIGHT_LINE) {
QPointF pf = QPointF((m_oldRectPolygon.at(0).x() + m_oldRectPolygon.at(3).x()) / 2, ((m_oldRectPolygon.at(0).y() + m_oldRectPolygon.at(3).y()) / 2));
// 计算到左侧边中点的距离
qreal dis = sqrt((event->pos().x() - pf.x()) * (event->pos().x() - pf.x()) + (event->pos().y() - pf.y()) * (event->pos().y() - pf.y()));
qreal dis2LT = sqrt((event->pos().x() - m_oldRectPolygon.at(0).x()) * (event->pos().x() - m_oldRectPolygon.at(0).x()) + (event->pos().y() - m_oldRectPolygon.at(0).y()) * (event->pos().y() - m_oldRectPolygon.at(0).y()));
qreal dis2RT = sqrt((event->pos().x() - m_oldRectPolygon.at(1).x()) * (event->pos().x() - m_oldRectPolygon.at(1).x()) + (event->pos().y() - m_oldRectPolygon.at(1).y()) * (event->pos().y() - m_oldRectPolygon.at(1).y()));
if (dis < 16 || dis2LT < dis2RT) {
return;
} else {
QRectF newRect(m_oldRect);
newRect.setLeft(m_oldRect.left());
newRect.setRight(m_oldRect.left() + dis);
setRectSize(newRect, false);
m_RotateCenter = QPointF((m_oldRectPolygon.at(0).x() + m_oldRectPolygon.at(2).x()) / 2, (m_oldRectPolygon.at(0).y() + m_oldRectPolygon.at(2).y()) / 2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
// 必须要用 scene()->update(),不能用 update(),否则会出现重影
scene()->update();
}
} else if (m_StateFlag == MOV_BOTTOM_LINE) {
// 顶边中点
QPointF pf = QPointF((m_oldRectPolygon.at(0).x() + m_oldRectPolygon.at(1).x()) / 2, ((m_oldRectPolygon.at(0).y() + m_oldRectPolygon.at(1).y()) / 2));
// 计算到底边中点的距离
qreal dis = sqrt((event->pos().x() - pf.x()) * (event->pos().x() - pf.x()) + (event->pos().y() - pf.y()) * (event->pos().y() - pf.y()));
qreal dis2LT = sqrt((event->pos().x() - m_oldRectPolygon.at(0).x()) * (event->pos().x() - m_oldRectPolygon.at(0).x()) + (event->pos().y() - m_oldRectPolygon.at(0).y()) * (event->pos().y() - m_oldRectPolygon.at(0).y()));
qreal dis2LB = sqrt((event->pos().x() - m_oldRectPolygon.at(3).x()) * (event->pos().x() - m_oldRectPolygon.at(3).x()) + (event->pos().y() - m_oldRectPolygon.at(3).y()) * (event->pos().y() - m_oldRectPolygon.at(3).y()));
if (dis < 16 || dis2LT < dis2LB) {
return;
} else {
QRectF newRect(m_oldRect);
newRect.setTop(m_oldRect.top());
newRect.setBottom(m_oldRect.top() + dis);
setRectSize(newRect, false);
m_RotateCenter = QPointF((m_oldRectPolygon.at(0).x() + m_oldRectPolygon.at(2).x()) / 2, (m_oldRectPolygon.at(0).y() + m_oldRectPolygon.at(2).y()) / 2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
// 必须要用 scene()->update(),不能用 update(),否则会出现重影
scene()->update();
}
}
}
void MyRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
setCursor(Qt::ArrowCursor);
if (m_StateFlag == MOV_RECT) {
m_StateFlag = DEFAULT_FLAG;
} else {
QGraphicsItem::mouseReleaseEvent(event);
}
}
void MyRectItem::SetRotate(qreal RotateAngle, QPointF ptCenter) {
m_bRotate = true;
if (ptCenter.x() == -999 && ptCenter.y() == -999) {
m_RotateCenter = QPointF(m_oldRect.x() + m_oldRect.width() / 2, m_oldRect.y() + m_oldRect.height() / 2);
} else {
m_RotateCenter = ptCenter;
}
m_RotateAngle = RotateAngle;
this->update();
}
QPointF MyRectItem::getRotatePoint(QPointF ptCenter, QPointF ptIn, qreal angle) {
double dx = ptCenter.x();
double dy = ptCenter.y();
double x = ptIn.x();
double y = ptIn.y();
double xx, yy;
xx = (x - dx) * cos(angle * M_PI / 180) - (y - dy) * sin(angle * M_PI / 180) + dx;
yy = (x - dx) * sin(angle * M_PI / 180) + (y - dy) * cos(angle * M_PI / 180) + dy;
return QPointF(xx, yy);
}
QList<QPointF> MyRectItem::getRotatePoints(QPointF ptCenter, QList<QPointF> ptIns, qreal angle) {
QList<QPointF> lstPt;
for (int i = 0; i < ptIns.count(); i++) {
lstPt.append(getRotatePoint(ptCenter, ptIns.at(i), angle));
}
return lstPt;
}
QPolygonF MyRectItem::getRotatePolygonFromRect(QPointF ptCenter, QRectF rectIn, qreal angle) {
QVector<QPointF> vpt;
QPointF pf = getRotatePoint(ptCenter, rectIn.topLeft(), angle);
vpt.append(pf);
pf = getRotatePoint(ptCenter, rectIn.topRight(), angle);
vpt.append(pf);
pf = getRotatePoint(ptCenter, rectIn.bottomRight(), angle);
vpt.append(pf);
pf = getRotatePoint(ptCenter, rectIn.bottomLeft(), angle);
vpt.append(pf);
pf = getRotatePoint(ptCenter, rectIn.topLeft(), angle);
vpt.append(pf);
return QPolygonF(vpt);
}
QRectF MyRectItem::getCrtPosRectToSceen() {
QRectF retRect = QRectF(m_oldRect.x() + pos().x(), m_oldRect.y() + pos().y(), m_oldRect.width(), m_oldRect.height());
return retRect;
}
QRectF MyRectItem::getSmallRotateRect(QPointF ptA, QPointF ptB) {
QPointF pt = getSmallRotateRectCenter(ptA, ptB);
return QRectF(pt.x() - 10, pt.y() - 10, 20, 20);
}
QPointF MyRectItem::getSmallRotateRectCenter(QPointF ptA, QPointF ptB) {
// A,B 点的中点 C
QPointF ptCenter = QPointF((ptA.x() + ptB.x()) / 2, (ptA.y() + ptB.y()) / 2);
// 中垂线方程式为 y=x*k + b
qreal x, y;//旋转图标矩形的中心
if (abs(ptB.y() - ptA.y()) < 0.1) {
if (ptA.x() < ptB.x()) { // 矩形左上角在上方
x = ptCenter.x();
y = ptCenter.y() - 20;
} else {
// 矩形左上角在下方
x = ptCenter.x();
y = ptCenter.y() + 20;
}
} else if (ptB.y() > ptA.y()) { // 顺时针旋转 0-180
qreal k = (ptA.x() - ptB.x()) / (ptB.y() - ptA.y()); // 中垂线斜率
qreal b = (ptA.y() + ptB.y()) / 2 - k * (ptA.x() + ptB.x()) / 2;
// 求 AB 线中垂线上离 AB 中点 20 个像素的点 C 的坐标
x = 20 * cos(atan(k)) + ptCenter.x();
y = k * x + b;
} else if (ptB.y() < ptA.y()) { // 顺时针旋转 180-360
qreal k = (ptA.x() - ptB.x()) / (ptB.y() - ptA.y()); // 中垂线斜率
qreal b = (ptA.y() + ptB.y()) / 2 - k * (ptA.x() + ptB.x()) / 2;
// 求 AB 线中垂线上离 AB 中点 20 个像素的点 C 的坐标
x = -20 * cos(atan(k)) + ptCenter.x();
y = k * x + b;
}
return QPointF(x, y);
}