Qt画矩形与椭圆的问题

Qt的QPainter类中提供了drawEllipse和drawRect函数分别画椭圆与矩形,但是这两个函数有一个共同的问题:所画椭圆的长短轴与x、y轴平行,矩形的长短边也与x、y轴平行,如果想出现与x、y轴相交的效果(斜的),QPainter提供了Rotate()和translate()函数分别对坐标系进行旋转和平移,但是对坐标系变换之后,再次画图便会以新的坐标系为标准进行绘制,如出现椭圆是斜的效果。
我的功能需求是:在二维图形上画椭圆,旋转图像时,椭圆随之在二维图像上正确旋转。
解决方案:1、记下椭圆左上角与右下角的坐标。
        2、使用 QPainterPath的addEllipse()函数生成椭圆,使用toFillPolygon()转换成
          polygon。并保存此polygon的点。
         切记要在初始的时候保存点,因为旋转之后,左上、右下点组成的矩形是平行坐标轴的,所以矩形不是
         想要的,故生成的椭圆也是不正确的,所以要在初始时记下椭圆上的点,旋转时不断更新这些点的
          display坐标。
        3、图像变换时(旋转、缩放等)利用每个点的世界坐标不变原则更新Polygon里的每个点,得到
        display坐标,用drawPolygon()即可得到想要的椭圆。
    QPainterPath ellipsePath;
    QRectF rectPath(LeftUpDisplayPoint, stRightBottomDisplayPoint);
    ellipsePath.addEllipse(rectPath);
    QPolygonF polygon = ellipsePath.toFillPolygon();//Converts the path into a polygon

    m_vecPolygonPath.clear();
    for(int nPolygonPointIndex = 0; nPolygonPointIndex < polygon.count(); nPolygonPointIndex++)
    {
        double dPointX = polygon.at(nPolygonPointIndex).x();
        double dPointY = polygon.at(nPolygonPointIndex).y();
        ControlPoint CurControl;
        CurControl.SetDisplayPosition(QPoint((int)dPointX, (int)dPointY));
        DisplayToWorld(CurControl);
        m_vecPolygonPath.push_back(CurControl);//存储起来
    }

下面是QPainterPath类的简介,摘抄于http://blog.csdn.net/liang19890820/article/details/51393152#椭圆,对作者表示感谢

QPainterPath 类(绘图路径)提供了一个容器,用于绘图操作,可以创建和重用图形形状。

绘图路径是由许多图形化的构建块组成的对象,例如:矩形、椭圆、直线和曲线。构建块可以加入在封闭的子路径中,例如:矩形或椭圆。封闭的路径的起点和终点是一致的,或者他们可以作为未封闭的子路径独立存在,如:直线和曲线。

QPainterPath 可以被填充、描绘轮廓、裁剪。要为一个指定的绘图路径生成可填充的轮廓,可以使用 QPainterPathStroker 类。与正常绘图相比,QPainterPath 的主要优点在于:复杂的图形只需创建一次,然后只需调用 QPainter::drawPath() 函数即可绘制多次。

QPainterPath 提供了一组函数,可用于获取绘图路径及其元素的信息。除了可以使用 toReversed() 函数来改变元素的顺序外,还有几个函数将 QPainterPath 对象转换成一个多边形表示。

创建 QPainterPath

QPainterPath 对象可以用指定的起点,或者另一个 QPainterPath 对象的副本来构造一个空路径。

一旦创建,可以使用 lineTo()、arcTo()、cubicTo() 和 quadTo() 函数将直线和曲线添加到路径中,直线和曲线从 currentPosition() 处伸展到其传递的参数的所在点的位置。

QPainterPath 对象的 currentPosition() 始终是最后一个添加的子路径的最终位置(或初始起点),使用 moveTo() 函数可以在不增加组件的情况下移动 currentPositon(),moveTo() 函数会隐式地启动一个新的子路径,并关闭前一个。启动新的子路径的另一种方式是调用 closeSubpath() 函数,该函数通过添加一条直线(从 currentPosition() 到起始位置)来关闭当前路径。注意:新路径将 (0, 0) 作为其初始 currentPosition()。

QPainterPath 也提供了一些便利的函数来添加一个封闭的子路径 - addEllipse()、addPath()、 addRect()、addRegion() 和 addText()。addPolygon() 函数添加一个未封闭的子路径。事实上,这些函数都是 moveTo()、lineTo()、cubicTo() 操作的集合。

此外,使用 connectPath() 函数将路径添加至当前路径。但需要注意,该函数将通过添加一条直线,将当前路径的最后一个元素连接到给定的第一个元素。

QPainterPath 信息

QPainterPath 类提供了一组函数,用于返回有关该路径及其元素的信息。

currentPosition() 函数返回被添加的最后一个子路径的终点(或初始起始点)。elementAt() 函数可用于检索各种子路径元素,可以使用 elementCount() 函数检索元素的数量,isEmpty() 函数可以告诉该 QPainterPath 对象是否包含任何元素。

controlPointRect() 函数返回包含此路径中所有点和控制点的矩形。与使用浮点精度返回此画家路径的边界矩形的精确的 boundingRect() 相比,此函数的计算速度要快得多。

最后,QPainterPath 提供了 contains() 函数,用于确定给定点或矩形是否在路径内。以及 intersects() 函数,用于确定给定矩形内的任何点是否也在该路径内。

QPainterPath 转换

出于兼容性原因,可能需要简化绘图路径的表示形式:QPainterPath 提供的 toFillPolygon()、toFillPolygons()和 toSubpathPolygons() 函数,用于将绘图路径转换为多边形。toFillPolygon() 将绘图路径作为单个多边形返回,而后两个函数返回一个多边形列表。

提供了 toFillPolygons() 和 toSubpathPolygons() 函数,因为绘制几个小多边形通常比绘制一个大的多边形更快,即使绘制的总点数相同。两者之间的差异是它们返回的多边形数:toSubpathPolygons() 为每个子路径创建一个多边形,而不管相交的子路径(即重叠的边界矩形),而 toFillPolygons() 函数仅为重叠的子路径创建一个多边形。

toFillPolygon() 和 toFillPolygons() 函数首先将所有子路径转换为多边形,然后使用重卷技术确保可以使用正确的填充规则来填充重叠的子路径。注意:重卷会在多边形中插入额外的线,因此填充多边形的轮廓与路径的轮廓不匹配。

椭圆
void QPainterPath::addEllipse(const QRectF & boundingRectangle)
在指定的 boundingRectangle 内创建一个椭圆,并将其作为一个封闭的子路径添加至绘图路径中。
椭圆由顺时针曲线组成,起始点和结束点在 0°(3 点钟的位置)。
这里写图片描述

QLinearGradient myGradient;
QPen myPen;
QRectF boundingRectangle;

QPainterPath myPath;
myPath.addEllipse(boundingRectangle);

QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);

多边形
void QPainterPath::addPolygon(const QPolygonF & polygon)
将指定的 polygon 作为子路径(未封闭)添加至绘图路径中。

注意:添加了 polygon 后,当前位置是 polygon 的最后一个点。要画一条线回到起始点,使用 closeSubpath() 函数。
这里写图片描述

QLinearGradient myGradient;
QPen myPen;
QPolygonF myPolygon;

QPainterPath myPath;
myPath.addPolygon(myPolygon);

QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);

矩形

void QPainterPath::addRect(const QRectF & rectangle)
将指定的 rectangle 作为子路径(封闭)添加至绘图路径中。
rectangle 作为顺时针的一组线被添加。添加 rectangle 后,绘图路径的当前位置是 rectangle 的左上角。
这里写图片描述

QLinearGradient myGradient;
QPen myPen;
QRectF myRectangle;

QPainterPath myPath;
myPath.addRect(myRectangle);

QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);

文本

void QPainterPath::addText(const QPointF & point, const QFont & font, const QString & text)
将指定的 text 添加至此路径中,作为由 font 创建的一组封闭子路径。定位子路径,使 text 基线的左端位于指定的 point。
这里写图片描述

QLinearGradient myGradient;
QPen myPen;
QFont myFont;
QPointF baseline(x, y);

QPainterPath myPath;
myPath.addText(baseline, myFont, tr("Qt"));

QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);

弧形

void QPainterPath::arcTo(const QRectF & rectangle, qreal startAngle, qreal sweepLength)
创建一个弧形,占据了指定的 rectangle,以指定 startAngle 开始并逆时针扩展 sweepLength 度。

角度都以度为单位,可以用负角度来指定顺时针弧形。

注意:如果它们尚未连接,此函数将连接弧的起点到当前位置。弧形被加入后,当前位置是弧的最后一点。要画一条线回到起始点,使用 closeSubpath() 函数。
这里写图片描述

QLinearGradient myGradient;
QPen myPen;

QPointF center, startPoint;

QPainterPath myPath;
myPath.moveTo(center);
myPath.arcTo(boundingRect, startAngle,
             sweepLength);

QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);

贝塞尔曲线

void QPainterPath::cubicTo(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
使用指定的控制点 c1、c2,在当前位置和指定的 endPoint 之间添加一条贝塞尔曲线。
曲线被添加后,当前位置会被更新为曲线的终点。
这里写图片描述

QLinearGradient myGradient;
QPen myPen;

QPainterPath myPath;
myPath.cubicTo(c1, c2, endPoint);

QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);

填充规则
Qt 提供了两种填充路径的规则:
Qt::OddEvenFill (默认)
这里写图片描述
Qt::WindingFill
这里写图片描述

### 回答1: Qt是一个跨平台的C++框架,具有丰富的绘图功能。实现矩形椭圆ROI移动、大小改变可以通过继承QWidget类创建自定义控件,重载其paintEvent()函数实现绘图,以及鼠标事件处理函数实现ROI的交互操作。 以下是一个实现矩形椭圆ROI的示例代码: ```c++ #include <QWidget> #include <QPainter> class ROIWidget : public QWidget { public: enum ROIType {RectROI, EllipseROI}; ROIWidget(QWidget* parent = nullptr) : QWidget(parent), m_roiType(RectROI) { } void setROIType(ROIType type) { m_roiType = type; update(); } protected: void paintEvent(QPaintEvent* event) override { QPainter painter(this); painter.setPen(QPen(Qt::blue, 2)); painter.setBrush(QBrush(Qt::transparent)); if (m_roiType == RectROI) painter.drawRect(m_roiRect); else if (m_roiType == EllipseROI) painter.drawEllipse(m_roiRect); } void mousePressEvent(QMouseEvent* event) override { m_lastPos = event->pos(); } void mouseMoveEvent(QMouseEvent* event) override { QPoint delta = event->pos() - m_lastPos; m_roiRect.translate(delta); m_lastPos = event->pos(); update(); } void mouseReleaseEvent(QMouseEvent* event) override { // do nothing } QRect m_roiRect; ROIType m_roiType; QPoint m_lastPos; }; ``` 该示例定义了一个ROIWidget控件类,继承自QWidget类。其中,paintEvent()函数根据ROI类型绘制矩形椭圆ROI;mousePressEvent()函数记录鼠标按下时的位置,mouseMoveEvent()函数计算鼠标移动的距离,更新ROI的位置,mouseReleaseEvent()函数暂时不做处理。 实现ROI大小改变可以在该示例的基础上进行修改。在mousePressEvent()函数中,判断鼠标的位置是否在ROI的边框上,如果是,则记录鼠标按下时的位置,并将状态设置为正在改变ROI大小;在mouseMoveEvent()函数中,如果状态为正在改变ROI大小,则计算鼠标移动的距离,更新ROI的大小;在mouseReleaseEvent()函数中,将状态设置为ROI未在改变大小。 完整代码请参考以下示例链接: https://github.com/vickymaze/QtROIWidget ### 回答2: 要实现矩形椭圆ROI的移动和大小改变,我们可以使用Qt中的QGraphicsView和QGraphicsScene来实现。以下是一个示例代码,让您可以了解这个过程。 在我们的示例中,我们将创建一个简单的图形场景,其中包含一个矩形和一个椭圆。然后,我们将允许用户使用鼠标移动和调整这些形状。 #include <QApplication> #include <QGraphicsEllipseItem> #include <QGraphicsRectItem> #include <QGraphicsScene> #include <QGraphicsView> #include <QMouseEvent> class Rectangle : public QGraphicsRectItem{ public: Rectangle(){ setRect(0, 0, 100, 100); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); } }; class Ellipse : public QGraphicsEllipseItem{ public: Ellipse(){ setRect(0, 0, 100, 100); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); } }; class Scene : public QGraphicsScene{ public: Scene(QObject* parent = NULL) : QGraphicsScene(parent){} void mousePressEvent(QGraphicsSceneMouseEvent* event){ QGraphicsScene::mousePressEvent(event); } void mouseReleaseEvent(QGraphicsSceneMouseEvent* event){ QGraphicsScene::mouseReleaseEvent(event); } }; int main(int argc, char *argv[]){ QApplication a(argc, argv); QGraphicsScene* scene = new Scene(); QGraphicsView view(scene); Rectangle* rect = new Rectangle(); scene->addItem(rect); Ellipse* ellipse = new Ellipse(); ellipse->setPos(150, 0); scene->addItem(ellipse); view.show(); return a.exec(); } 在上面的代码中,我们创建了一个Scene类来扩展QGraphicsScene。然后,我们在其中重写了鼠标事件,使其在被选择的情况下可以移动和改变大小。 我们还创建了Rectangle和Ellipse类,分别扩展了QGraphicsRectItem和QGraphicsEllipseItem。在这些类中,我们设置了标志,使它们可以被拖动和选择。 要在代码中添加其他功能,您可以将其添加到上述类中。使用QGraphicsView和QGraphicsScene,您可以轻松地创建交互式图形应用程序。 ### 回答3: Qt是一个基于C++语言的跨平台GUI应用开发框架,它提供了一种方便快捷的方式来实现图形用户界面的设计和实现,同时它还提供了许多方便的工具和类库来实现不同的功能。 通过QT,可以实现矩形椭圆roi移动、大小改变的功能。下面是一个实例源码,通过实现该代码,不仅可以实现这些功能,也可以帮助您更好地理解QT的使用。 ``` #include <QtWidgets/QApplication> #include <QtWidgets/QMainWindow> #include <QtGui/QMouseEvent> #include <QtGui/QPainter> class RectEllipse : public QMainWindow { public: RectEllipse(QWidget *parent = nullptr) : QMainWindow(parent) { this->setWindowTitle("RectEllipse"); this->setFixedSize(400, 400); m_rect = QRect(50, 50, 100, 50); m_ellipse = QRect(150, 150, 50, 100); m_location = NONE; } protected: void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE { QPainter painter(this); painter.setPen(Qt::red); painter.drawRect(m_rect); painter.setPen(Qt::green); painter.drawEllipse(m_ellipse); } void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE { if (event->button() == Qt::LeftButton) { if (m_rect.contains(event->pos())) m_location = RECT; else if (m_ellipse.contains(event->pos())) m_location = ELLIPSE; } m_startPos = event->pos(); } void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE { if (m_location == RECT) { m_rect.moveTo(m_rect.topLeft() + event->pos() - m_startPos); update(); } else if (m_location == ELLIPSE) { QPointF center = m_ellipse.center(); center += event->pos() - m_startPos; m_ellipse.moveCenter(center.toPoint()); update(); } m_startPos = event->pos(); } void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE { Q_UNUSED(event); m_location = NONE; } void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE { Q_UNUSED(event); m_ellipse.moveTo(m_ellipse.x(), m_ellipse.y() + height() - m_oldHeight); m_oldHeight = height(); } private: enum Location { NONE, RECT, ELLIPSE }; QRect m_rect, m_ellipse; Location m_location; QPoint m_startPos; int m_oldHeight; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); RectEllipse w; w.show(); return a.exec(); } ``` 这段代码实现了一个继承自QMainWindow的主窗口,并在窗口中绘制了一个矩形和一个椭圆。当用户在矩形椭圆上单击左键时,可以通过鼠标移动来移动和调整矩形椭圆的大小。 在这个例子中,我们重载了QMainWindow的三个事件方法—paintEvent, mousePressEvent和mouseMoveEvent,以响应用户的操作。在paintEvent方法中,我们使用QPainter绘制了一个红色的矩形和一个绿色的椭圆。在mousePressEvent中,我们通过判断鼠标单击的位置是否在矩形椭圆内来确定用户想要操作的对象。在mouseMoveEvent中,我们匹配所选定的对象来移动或调整矩形椭圆的位置和大小。在resizeEvent中,我们调整了椭圆的位置,以使其始终保持在窗口的底部。 该实例是一个很好的例子,演示了如何使用Qt来实现矩形椭圆roi移动、大小改变的功能。您可以通过运行该代码,并探索其中不同部分的具体实现来更好地理解Qt的使用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

COSummer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值