C++ Qt 学习(八):Qt 绘图技术与图形视图

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_OBJECTpublic: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_OBJECTpublic: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;  // 起始 40int spanAngle = 120 * 16;  // 旋转 120painter.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 onceenum 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_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic: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_OBJECTpublic: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_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic: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_OBJECTpublic: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:一些基本的图形元件
      • 支持鼠标事件响应、键盘输入与按键事件
      • 支持拖放、组合操作

在这里插入图片描述

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_OBJECTpublic: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);// 画一个矩形框,大小等于 sceneQGraphicsRectItem *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_OBJECTprotected: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_OBJECTpublic: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_OBJECTpublic: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 点的中点 CQPointF ptCenter = QPointF((ptA.x() + ptB.x()) / 2, (ptA.y() + ptB.y()) / 2);// 中垂线方程式为 y=x*k + bqreal 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-180qreal 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-360qreal 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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/197895.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dart笔记:glob 文件系统遍历

Dart笔记 文件系统遍历工具&#xff1a;glob 模块 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/13442…

第7天:信息打点-资产泄漏amp;CMS识别amp;Git监控amp;SVNamp;DS_Storeamp;备份

第7天&#xff1a;信息打点-资产泄漏&CMS识别&Git监控&SVN&DS_Store&备份 知识点&#xff1a; 一、cms指纹识别获取方式 网上开源的程序&#xff0c;得到名字就可以搜索直接获取到源码。 cms在线识别&#xff1a; CMS识别&#xff1a;https://www.yun…

基于单片机C51全自动洗衣机仿真设计

**单片机设计介绍&#xff0c; 基于单片机C51全自动洗衣机仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机C51的全自动洗衣机仿真设计是一个复杂的项目&#xff0c;它涉及到硬件和软件的设计和实现。以下是对这…

SDUT OJ《算法分析与设计》搜索算法

A - 子集和问题 Description 子集和问题的一个实例为〈S,t〉。其中&#xff0c;S{ x1 &#xff0c; x2 &#xff0c;…&#xff0c;xn }是一个正整数的集合&#xff0c;c是一个正整数。子集和问题判定是否存在S的一个子集S1&#xff0c;使得&#xff1a; 。 试设计一个解子…

MATLAB与Excel的数据交互

准备阶段 clear all % 添加Excel函数 try Excel=actxGetRunningServer(Excel.Application); catch Excel=actxserver(Excel.application); end % 设置Excel可见 Excel.visible=1; 插入数据 % % 激活eSheet1 % eSheet1.Activate; % 或者 % Activate(eSheet1); % % 打开…

23.11.19日总结

经过昨天的中期答辩&#xff0c;其实可以看出来项目进度太慢了&#xff0c;现在是第十周&#xff0c;预计第十四周是终级答辩&#xff0c;在这段时间要把项目写完。 前端要加上一个未登录的拦截器&#xff0c;后端加上全局的异常处理。对于饿了么项目的商品建表&#xff0c;之前…

C语言:动态内存管理

目录 为什么存在动态内存分配 动态内存函数 malloc和free 示例 calloc 示例 realloc 示例 常见的动态内存错误 对NULL指针的解引用操作 对动态开辟的空间进行越界访问 对于非动态开辟内存使用free释放 使用free释放一块动态开辟内存的一部分 对同一块内存多次释…

fopen/fwrite/fread 对UNICODE字符写入的总结

windows对fopen函数进行了升级&#xff0c;可以支持指定文件的编码格式&#xff08;ccs参数指定&#xff09;。 例如&#xff1a; FILE *fp fopen("newfile.txt", "rt, ccsUTF-8"); 当以 ccs 模式打开文件时&#xff0c;进行读写操作的数据应为 UTF-16…

【SpringBoot3+Vue3】三【实战篇】-后端(优化)

目录 一、登录优化-redis 1、SpringBoot集成redis 1.1 pom 1.2 yml 1.3 测试程序&#xff08;非必须&#xff09; 1.4 启动redis&#xff0c;执行测试程序 2、令牌主动失效&#xff08;代码优化&#xff09; 2.1 UserController设置token到redis 2.2 登录拦截器Log…

mysql练习1

-- 1.查询出部门编号为BM01的所有员工 SELECT* FROMemp e WHEREe.deptno BM01; -- 2.所有销售人员的姓名、编号和部门编号。 SELECTe.empname,e.empno,e.deptno FROMemp e WHEREe.empstation "销售人员";-- 3.找出奖金高于工资的员工。 SELECT* FROMemp2 WHE…

FPGA设计时序约束八、others类约束之Set_Case_Analysis

目录 一、序言 二、Set Case Analysis 2.1 基本概念 2.2 设置界面 2.3 命令语法 2.4 命令示例 三、工程示例 四、参考资料 一、序言 在Vivado的时序约束窗口中&#xff0c;存在一类特殊的约束&#xff0c;划分在others目录下&#xff0c;可用于设置忽略或修改默认的时序…

综述:目标检测二十年(机翻版)(未完

原文地址 20年来的目标检测&#xff1a;一项调查 摘要关键词一 介绍二 目标检测二十年A.一个目标检测的路线图1)里程碑&#xff1a;传统探测器Viola Jones探测器HOG检测器基于可变形零件的模型&#xff08;DPM&#xff09; 2)里程碑&#xff1a;基于CNN的两阶段探测器RCNNSPPN…

Matlab绘制双坐标轴图示例函数yyaxis

一、前言 出于一些需求&#xff0c;我们需要将两个不同属性的参量绘制在同一张图上&#xff0c;但是两个参量属性不同&#xff0c;即单位不同&#xff0c;纵坐标值分布范围不同&#xff0c;此刻&#xff0c;我们只需要将一个参量的值在y轴左侧展示&#xff0c;另一个参量的值在…

centos7安装mongodb

1、下载mongodb https://www.mongodb.com/try/download/community 2、解压 3、重命名 4、创建mongodb的data、logs目录 5、启动mongodb, bin/mongod --port27017 --dbpath/data/program/mongodb/data --logpath/data/program/mongodb/logs/mongodb.log --bind_ip0.0.0.0 --f…

实用篇-ES-DSL查询文档

数据的存储不是目的&#xff0c;我们希望从海量的酒店数据中检索出需要的信息&#xff0c;这就是ES的搜索功能 官方文档: https://elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html#query-dsl。DSL是用来查询文档的 Elasticsearch提供了基于JSON的DSL来定…

算法通关村——数字中的统计、溢出、进制转换处理模板

数字与数学基础问题 1、数字统计 1.1、符号统计 LeetCode1822. 给定一个数组&#xff0c;求所有元素的乘积的符号&#xff0c;如果最终答案是负的返回-1&#xff0c;如果最终答案是正的返回1&#xff0c;如果答案是0返回0. 这题其实只用看数组中0和负数的个数就好了&#x…

力扣刷题篇之位运算

系列文章目录 目录 系列文章目录 前言 一、位运算的基本运算 二、位运算的技巧 三、布隆过滤器 总结 前言 本系列是个人力扣刷题汇总&#xff0c;本文是数与位。刷题顺序按照[力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 - 力扣&#xff08;LeetCode&#xff0…

DeepMind发布新模型Mirasol3B:更高效处理音频、视频数据

Google DeepMind日前悄然宣布了其人工智能研究的重大进展&#xff0c;推出了一款名为“Mirasol3B”的新型自回归模型&#xff0c;旨在提升对长视频输入的理解能力。该新模型展示了一种颠覆性的多模态学习方法&#xff0c;以更综合和高效的方式处理音频、视频和文本数据。 Googl…

基于STC12C5A60S2系列1T 8051单片机的SPI总线器件数模芯片TLC5615实现数模转换应用

基于STC12C5A60S2系列1T 8051单片的SPI总线器件数模芯片TLC5615实现数模转换应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍SPI总线器件数模芯片TLC5615介绍通过按…

神辅助 Cursor 编辑器,加入 GPT-4 让编码更轻松!

分类 互联网 在 ChatGPT 问世之前&#xff0c;我们的编码方式很多时候都是面向搜索引擎编码&#xff0c;需要不断地进行搜索&#xff0c;然后复制粘贴&#xff0c;俗称复制粘贴工程师。 但是&#xff0c;随着ChatGPT的出现&#xff0c;这一切将彻底改变。 ChatGPT 是一种基于…