如何以交互方式调整QGraphicsObject的大小?

时间:2015-09-05 18:44:21

标签: c++ qt

我无法弄清楚如何在Qt中扩展自定义子类QGraphicsObject。 我有代码工作,当鼠标光标悬停在边缘对象的boundingRect(左,右,上,下)时,鼠标光标会发生变化。 我有重载的鼠标事件正常工作。 我有自定义方法试图更改boundingRect。 但该对象拒绝改变大小。

快速简单的测试。 我尝试使用硬编码值更改paint()方法中对象的大小。会发生什么是对象确实改变了大小。但是碰撞仍然使用旧的boundingRect大小。

似乎绊倒我的是QGraphicsObject使用'const'boundingRect值。我无法弄清楚如何处理这个问题。

ResizableObject::ResizableObject( QGraphicsItem *parent ): QGraphicsObject( parent ), mResizeLeft( false ), mResizeRight( false ), mResizeBottom( false ), mResizeTop( false )
{
    setAcceptHoverEvents( true );
    setFlags( ItemIsMovable | ItemIsSelectable );
}

QRectF ResizableObject::boundingRect() const
{
    return QRectF(0 , 0 , 100 , 100);
}

void ResizableObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED( option )
    Q_UNUSED( widget )
    painter->setRenderHint(QPainter::Antialiasing);

    //Determine whether to draw a solid, or dashed, outline border
    if (isSelected() )
    { 
        //Draw a dashed outlined rectangle
        painter->setPen( Qt::DashLine );
        painter->drawRect( boundingRect() );
    }
    else painter->drawRect(0,0,20,20); //Draw a normal solid outlined rectangle

    //No collisions are happening
    if(scene()->collidingItems(this).isEmpty())
    {
        //qDebug()<< "empty"; 
    }
    //Collisions are happening
    else
    {
        foreach(QGraphicsItem *item, collidingItems()) 
        {
            qDebug()<< item->pos();
        }
    }
}

我能找到的唯一例子是使用Qwidget或QGraphicsRectItem。但那些略有不同,构造函数不是常量。 谁能告诉我如何用鼠标改变这些QGraphicsObjects的大小?

2 个答案:

答案 0 :(得分:1)

paint方法唯一的工作就是画画。它无法做任何其他事情 - 包括尝试更改对象的大小。

不要让项目本身可调整大小,让我们看看我们是否可以采用更通用的方式来调整任何类的任何项目的大小。首先,我们可能不希望所有对象都可以调整大小。我们需要一种方法来计算它们中的哪一个。我们还需要一种方法来告诉项目调整大小 - QGraphicsItem没有提供这样做的方法。在通用编程中,用于提供此类功能的一种模式是特征类。此类实现特定于应用程序的特征,这些特征将根据我们的需要自定义通用调整大小行为。

对于这个简单的例子,我们允许任何QGraphicsEllipseItem s可调整大小。对于您的应用程序,您可以实现您想要的任何逻辑 - 无需任何子类。如果您有许多自定义的,可调整大小的项目,您当然可以为可调整大小的项目创建基类,并通过特征公开它。即便如此,traits类仍然可以支持其他类的项目。

在所有情况下,要调整大小的项目必须是可选的,我们利用选择机制挂钩到场景中。这也可以在不使用选择的情况下完成。

// https://github.com/KubaO/stackoverflown/tree/master/questions/graphics-resizable-32416527
#include <QtWidgets>

class SimpleTraits {
public:
    /// Determines whether an item is manually resizeable.
    static bool isGraphicsItemResizeable(QGraphicsItem * item) {
        return dynamic_cast<QGraphicsEllipseItem*>(item);
    }
    /// Gives the rectangle one can base the resize operations on for an item
    static QRectF rectFor(QGraphicsItem * item) {
        auto ellipse = dynamic_cast<QGraphicsEllipseItem*>(item);
        if (ellipse) return ellipse->rect();
        return QRectF();
    }
    /// Sets a new rectangle on the item
    static void setRectOn(QGraphicsItem * item, const QRectF & rect) {
        auto ellipse = dynamic_cast<QGraphicsEllipseItem*>(item);
        if (ellipse) return ellipse->setRect(rect);
    }
};

实施&#34;宽橡皮筋&#34;控制器,我们需要一个帮助器来告诉给定点与哪些矩形边相交:

/// The set of edges intersecting a rectangle of given pen width
Qt::Edges edgesAt(const QPointF & p, const QRectF & r, qreal w) {
    Qt::Edges edges;
    auto hw = w / 2.0;
    if (QRectF(r.x()-hw, r.y()-hw, w, r.height()+w).contains(p)) edges |= Qt::LeftEdge;
    if (QRectF(r.x()+r.width()-hw, r.y()-hw, w, r.height()+w).contains(p)) edges |= Qt::RightEdge;
    if (QRectF(r.x()-hw, r.y()-hw, r.width()+w, w).contains(p)) edges |= Qt::TopEdge;
    if (QRectF(r.x()-hw, r.y()+r.height()-hw, r.width()+w, w).contains(p)) edges |= Qt::BottomEdge;
    return edges;
}

然后,我们有一个&#34;橡皮带&#34;调整跟踪其场景选择的辅助项。每当选择包含一个项目,并且项目可调整大小时,帮助程序使自己成为要调整大小的项目的子项,并变为可见。它跟踪橡皮筋活动边缘上的鼠标拖动,并相应地调整父项的大小。

template <typename Tr>
class ResizeHelperItem : public QGraphicsObject {
    QRectF m_rect;
    QPen m_pen;
    Qt::Edges m_edges;
    void newGeometry() {
        prepareGeometryChange();
        auto parentRect = Tr::rectFor(parentItem());
        m_rect.setTopLeft(mapFromParent(parentRect.topLeft()));
        m_rect.setBottomRight(mapFromParent(parentRect.bottomRight()));
        m_pen.setWidthF(std::min(m_rect.width(), m_rect.height()) * 0.1);
        m_pen.setJoinStyle(Qt::MiterJoin);
    }
public:
    ResizeHelperItem() {
        setAcceptedMouseButtons(Qt::LeftButton);
        m_pen.setColor(QColor(255, 0, 0, 128));
        m_pen.setStyle(Qt::SolidLine);
    }
    QRectF boundingRect() const Q_DECL_OVERRIDE {
        auto hWidth = m_pen.widthF()/2.0;
        return m_rect.adjusted(-hWidth, -hWidth, hWidth, hWidth);
    }
    void selectionChanged() {
        if (!scene()) { setVisible(false); return; }
        auto sel = scene()->selectedItems();
        if (sel.isEmpty() || sel.size() > 1) { setVisible(false); return; }
        auto item = sel.at(0);
        if (! Tr::isGraphicsItemResizeable(item)) { setVisible(false); return; }
        setParentItem(item);
        newGeometry();
        setVisible(true);
    }
    void paint(QPainter * p, const QStyleOptionGraphicsItem *, QWidget *) Q_DECL_OVERRIDE {
        p->setPen(m_pen);
        p->drawRect(m_rect);
    }
    void mousePressEvent(QGraphicsSceneMouseEvent * ev) Q_DECL_OVERRIDE {
        m_edges = edgesAt(ev->pos(), m_rect, m_pen.widthF());
        if (!m_edges) return;
        ev->accept();
    }
    void mouseMoveEvent(QGraphicsSceneMouseEvent * ev) Q_DECL_OVERRIDE {
        auto pos = mapToItem(parentItem(), ev->pos());
        auto rect = Tr::rectFor(parentItem());
        if (m_edges & Qt::LeftEdge) rect.setLeft(pos.x());
        if (m_edges & Qt::TopEdge) rect.setTop(pos.y());
        if (m_edges & Qt::RightEdge) rect.setRight(pos.x());
        if (m_edges & Qt::BottomEdge) rect.setBottom(pos.y());
        if (!!m_edges) {
            Tr::setRectOn(parentItem(), rect);
            newGeometry();
        }
    }
};

最后,我们创建了一个简单的场景来尝试全部:

screenshot

int main(int argc, char ** argv) {
    QApplication app{argc, argv};
    QGraphicsScene scene;
    QGraphicsView view { &scene };
    typedef ResizeHelperItem<SimpleTraits> HelperItem;
    HelperItem helper;
    QObject::connect(&scene, &QGraphicsScene::selectionChanged, &helper, &HelperItem::selectionChanged);
    scene.addItem(&helper);
    auto item = scene.addEllipse(0, 0, 100, 100);
    item->setFlag(QGraphicsItem::ItemIsSelectable);
    view.setMinimumSize(400, 400);
    view.show();
    return app.exec();
}

答案 1 :(得分:0)

我认为您需要在某处声明QRectF变量并修改boundingRect()以简单地返回该变量。并覆盖鼠标事件处理程序以修改您需要的QRectF变量(如mouseReleaseEvent()可能,)

virtual void    mouseMoveEvent(QGraphicsSceneMouseEvent * event)
virtual void    mousePressEvent(QGraphicsSceneMouseEvent * event)
virtual void    mouseReleaseEvent(QGraphicsSceneMouseEvent * event)

在更改该值以使item的boundingRect()缓存无效之前,不要忘记调用prepareGeometryChange()。