自定义可拖动多边形控件,原创做者是赵彦博(QQ:408815041 zyb920@hotmail.com),创做之初主要是为了可以在视频区域内用户自定义可拖动的多个区域,便可用来做为警惕区域,也可用来其余的处理,拿到对应的多边形坐标集合,本控件的主要难点是如何计算一个点在一个多边形区域内,什么时候完成一个多边形区域,支持多个多边形。c++
#ifndef CUSTOMGRAPHICS_H #define CUSTOMGRAPHICS_H /** * 自定义多边形控件 做者:赵彦博(QQ:408815041 zyb920@hotmail.com) 2019-3-28 * 1:自定义随意绘制多边形 * 2:产生闭合形状后可单击选中移动整个多边形 * 3:可拉动某个点 * 4:支持多个多边形 * 5:鼠标右键退出绘制 * 6:可设置各类颜色 */ #include <QWidget> #ifdef quc #if (QT_VERSION < QT_VERSION_CHECK(5,7,0)) #include <QtDesigner/QDesignerExportWidget> #else #include <QtUiPlugin/QDesignerExportWidget> #endif class QDESIGNER_WIDGET_EXPORT CustomGraphics : public QWidget #else class CustomGraphics : public QWidget #endif { Q_OBJECT Q_PROPERTY(bool selectDotVisible READ getSelectDotVisible WRITE setSelectDotVisible) Q_PROPERTY(int dotRadius READ getDotRadius WRITE setDotRadius) Q_PROPERTY(int lineWidth READ getLineWidth WRITE setLineWidth) Q_PROPERTY(QColor dotColor READ getDotColor WRITE setDotColor) Q_PROPERTY(QColor lineColor READ getLineColor WRITE setLineColor) Q_PROPERTY(QColor polygonColor READ getPolygonColor WRITE setPolygonColor) Q_PROPERTY(QColor selectColor READ getSelectColor WRITE setSelectColor) public: typedef struct { QVector<QPoint> pos; bool selected; } Polygon; explicit CustomGraphics(QWidget *parent = 0); protected: void mousePressEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); void paintEvent(QPaintEvent *); void drawPolygon(QPainter *p, const Polygon &v); void drawLines(QPainter *p, const QList<QPoint> &list, bool isFirst = true); private: bool selectDotVisible; //选中点可见 int dotRadius; //点的半径 int lineWidth; //线条宽度 QColor dotColor; //点的颜色 QColor lineColor; //线条颜色 QColor polygonColor; //多边形颜色 QColor selectColor; //选中颜色 QPoint tempPoint; //临时点 QList<QPoint> tempPoints; //点集合 QList<Polygon> tempPolygons;//多边形集合 bool pressed; //鼠标是否按下 QPoint lastPoint; //鼠标按下处的坐标 QPoint ellipsePos; //保存按下点的坐标 int selectedEllipseIndex; //选中点的index Polygon pressedPolygon; //保存按下时多边形的原始坐标 int selectedIndex; //选中多边形的index private: //计算两点间的距离 double length(const QPoint &p1, const QPoint &p2); //检测是否选中多边形 bool checkPoint(const QVector<QPoint> &points, int x, int y); public: bool getSelectDotVisible() const; int getDotRadius() const; int getLineWidth() const; QColor getDotColor() const; QColor getLineColor() const; QColor getPolygonColor() const; QColor getSelectColor() const; QSize sizeHint() const; QSize minimumSizeHint() const; public Q_SLOTS: void setSelectDotVisible(bool selectDotVisible); void setDotRadius(int dotRadius); void setLineWidth(int lineWidth); void setDotColor(const QColor &dotColor); void setLineColor(const QColor &lineColor); void setPolygonColor(const QColor &polygonColor); void setSelectColor(const QColor &selectColor); //清除临时绘制的 void clearTemp(); //清除全部 void clearAll(); }; #endif // CUSTOMGRAPHICS_H
void CustomGraphics::mousePressEvent(QMouseEvent *e) { QPoint p = e->pos(); pressed = true; lastPoint = this->mapToGlobal(p); //连线模式下不选中 if(tempPoints.isEmpty()) { //若是选中了,检测是否点到点上 bool selectedPot = false; selectedEllipseIndex = -1; if (selectedIndex != -1) { for(int i = tempPolygons.at(selectedIndex).pos.size() - 1; i >= 0; --i) { if(length(p, tempPolygons.at(selectedIndex).pos[i]) <= 36) { selectedPot = true; selectedEllipseIndex = i; ellipsePos = tempPolygons.at(selectedIndex).pos[i]; break; } } } //当前选中了点则不用重绘 if(selectedPot) { return; } //判断是否选中一个 selectedIndex = -1; for(int i = tempPolygons.size() - 1; i >= 0; --i) { tempPolygons[i].selected = checkPoint(tempPolygons.at(i).pos, p.x(), p.y()); if(tempPolygons.at(i).selected) { //防止重叠部分 if(selectedIndex == -1) { selectedIndex = i; pressedPolygon = tempPolygons.at(i); } else { tempPolygons[i].selected = false; } } } this->update(); } } void CustomGraphics::mouseMoveEvent(QMouseEvent *e) { tempPoint = e->pos(); if(pressed && selectedIndex != -1) { //总体偏移坐标 QPoint delta = this->mapToGlobal(tempPoint) - lastPoint; int len = tempPolygons.at(selectedIndex).pos.size(); if(selectedEllipseIndex != -1) { //移动点 tempPolygons[selectedIndex].pos[selectedEllipseIndex] = ellipsePos + delta; } else if(selectedIndex != -1) { //移动面 for(int i = 0; i < len; ++i) { tempPolygons[selectedIndex].pos[i] = pressedPolygon.pos.at(i) + delta; } } } this->update(); } void CustomGraphics::mouseReleaseEvent(QMouseEvent *e) { //鼠标右键清空临时的 if (e->button() == Qt::RightButton) { clearTemp(); return; } //检测再次点击与最后个点 - 还没写 pressed = false; if(selectedIndex != -1) { return; } QPoint point = e->pos(); if(tempPoints.count() > 0) { qreal len = (qPow(tempPoints.first().x() - point.x() , 2.0) + qPow(tempPoints.first().y() - point.y() , 2.0) ); if(len < 100) { //完成一个多边形 if(tempPoints.size() >= 3) { Polygon pol; pol.pos = tempPoints.toVector(); pol.selected = false; tempPolygons.append(pol); } tempPoints.clear(); this->update(); return; } } tempPoints.append(point); this->update(); } void CustomGraphics::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing, true); //绘制多边形 foreach(const Polygon &p, tempPolygons) { drawPolygon(&painter, p); } //绘制点集合 drawLines(&painter, tempPoints, false); } void CustomGraphics::drawPolygon(QPainter *p, const Polygon &v) { p->save(); //绘制多边形 p->setPen(QPen(lineColor, lineWidth)); v.selected ? p->setBrush(selectColor) : p->setBrush(polygonColor); p->drawPolygon(v.pos.data(), v.pos.size()); //绘制圆点 if(selectDotVisible && v.selected) { p->setPen(Qt::NoPen); p->setBrush(dotColor); foreach(const QPoint &point, v.pos) { p->drawEllipse(point, dotRadius, dotRadius); } } p->restore(); } void CustomGraphics::drawLines(QPainter *p, const QList<QPoint> &list, bool isFirst) { p->save(); int count = list.count(); if (count > 0) { //绘制点集合 p->setPen(Qt::NoPen); p->setBrush(dotColor); for(int i = 0; i < count; ++i) { p->drawEllipse(list.at(i), dotRadius, dotRadius); } //绘制线条集合 p->setPen(QPen(lineColor, lineWidth)); p->setBrush(Qt::NoBrush); for(int i = 0; i < count - 1; ++i) { p->drawLine(list.at(i), list.at(i + 1)); } //绘制最后一条线条 p->drawLine(list.last(), isFirst ? list.first() : tempPoint); } p->restore(); } double CustomGraphics::length(const QPoint &p1, const QPoint &p2) { //平方和 return qPow(p1.x() - p2.x(), 2.0) + qPow(p1.y() - p2.y(), 2.0); } bool CustomGraphics::checkPoint(const QVector<QPoint> &points, int testx, int testy) { //最少保证3个点 const int count = points.size(); if(count < 3) { return false; } QList<int> vertx, verty; for(int i = 0; i < count; ++i) { vertx << points.at(i).x(); verty << points.at(i).y(); } //核心算法,计算坐标是否在多边形内部 int i = 0, j, c = 0; for (i = 0, j = count - 1; i < count; j = i++) { bool b1 = (verty.at(i) > testy) != (verty.at(j) > testy); bool b2 = (testx < (vertx.at(j) - vertx.at(i)) * (testy - verty.at(i)) / (verty.at(j) - verty.at(i)) + vertx.at(i)); if (b1 && b2) { c = !c; } } return c; }