gpt4 book ai didi

c++ - 鼠标可调整大小,可拖动的小部件

转载 作者:行者123 更新时间:2023-12-03 07:02:05 26 4
gpt4 key购买 nike

我正在尝试允许用户向一种设计区域添加新的“小部件”(图像,文本,也许还有其他自定义数据。图像目前足够好)。然后,我希望他们能够方便地调整/移动它们的大小。 Activity 部分的最佳方法似乎是使用QGraphicsView。可以用4行代码很好地完成:

auto const scene = new QGraphicsScene{this};
auto const item = scene->addPixmap(QPixmap{":/img/example.png"});
item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
ui->graphicsView->setScene(scene);
结果是这样的:
enter image description here
很好,但是不能用鼠标调整大小。我已经(在此站点上)看到了多种方法来实现这一目标,并且可以通过鼠标调整其大小,但是它们都是很棘手的。
我在Qt网站上看​​到了 this示例,该示例采用了为可移动和可调整大小的容器创建自定义容器的另一种方法。似乎可以通过一些调整使它工作,但这又不是一个很好的解决方案。该小部件实际上看起来好像不可调整大小。选中时,边界没有很好的线索,这是一个动态放置/大小的东西。
拥有像ms-paint这样的可移动小部件必须是一个常见的用例,因此我认为必须有一种很好的方法来实现这一目标。是这样吗老实说, QGraphicsScene似乎是一个不错的候选人。也许我缺少什么?

最佳答案

好的,所以我遇到了这个问题,我的解决方案是将处理程序的创建与项目的选择联系起来:
主窗口

#pragma once

#include <QMainWindow>
#include <QGraphicsItem>
#include <QPainter>

class Handler: public QGraphicsItem
{
public:
enum Mode
{
Top = 0x1,
Bottom = 0x2,
Left = 0x4,
TopLeft = Top | Left,
BottomLeft = Bottom | Left,
Right = 0x8,
TopRight = Top | Right,
BottomRight = Bottom | Right,
Rotate = 0x10
};

Handler(QGraphicsItem *parent, Mode mode);
~Handler(){}
void updatePosition();

QRectF boundingRect() const override;
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QPointF iniPos;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
private:
Mode mode;
bool isMoving = false;
};

class ObjectResizerGrip: public QGraphicsItem
{
public:
ObjectResizerGrip(QGraphicsItem *parent): QGraphicsItem(parent)
{
setFlag(QGraphicsItem::ItemHasNoContents, true);
setFlag(QGraphicsItem::ItemIsSelectable, false);
setFlag(QGraphicsItem::ItemIsFocusable, false);
}
void updateHandlerPositions();
virtual QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override{Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(widget)}

protected:
QList<Handler*> handlers;
};

class Object4SidesResizerGrip: public ObjectResizerGrip
{
public:
Object4SidesResizerGrip(QGraphicsItem *parent);
};

class Item:public QGraphicsItem
{
public:
Item(QGraphicsItem *parent=nullptr): QGraphicsItem(parent)
{
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setAcceptHoverEvents(true);
}
QRectF boundingRect() const override
{
return boundingBox;
}
void setWidth(qreal value)
{
auto width = boundingBox.width();
if(width == value) return;
width = qMax(value, 100.0);
setDimensions(width, boundingBox.height());
}

void setHeight(qreal value)
{
auto height = boundingBox.height();
if(height == value) return;
height = qMax(value, 100.0);
setDimensions(boundingBox.width(), height);
}

void setDimensions(qreal w, qreal h)
{
prepareGeometryChange();
boundingBox = QRectF(-w/2.0, -h/2.0, w, h);
if(resizerGrip) resizerGrip->updateHandlerPositions();
update();
}

private:
ObjectResizerGrip* resizerGrip = nullptr;

QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
if(change == ItemSelectedHasChanged && scene())
{
if(value.toBool())
{
if(!resizerGrip)
resizerGrip = newSelectionGrip();
}
else
{
if(resizerGrip)
{
delete resizerGrip;
resizerGrip = nullptr;
}
}
}

return QGraphicsItem::itemChange(change, value);
}
QRectF boundingBox;
virtual ObjectResizerGrip *newSelectionGrip() =0;
};

class CrossItem:public Item
{
public:
CrossItem(QGraphicsItem *parent=nullptr): Item(parent){};

private:
virtual ObjectResizerGrip *newSelectionGrip() override
{
return new Object4SidesResizerGrip(this);
}

virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
painter->drawLine(boundingRect().topLeft(), boundingRect().bottomRight());
painter->drawLine(boundingRect().topRight(), boundingRect().bottomLeft());
}
};


class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

private:
};
主窗口
#include "mainwindow.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QHBoxLayout>
// Return nearest point along the line to a given point
// http://stackoverflow.com/questions/1459368/snap-point-to-a-line
QPointF getClosestPoint(const QPointF &vertexA, const QPointF &vertexB, const QPointF &point, const bool segmentClamp)
{
QPointF AP = point - vertexA;
QPointF AB = vertexB - vertexA;
qreal ab2 = AB.x()*AB.x() + AB.y()*AB.y();
if(ab2 == 0) // Line lenth == 0
return vertexA;
qreal ap_ab = AP.x()*AB.x() + AP.y()*AB.y();
qreal t = ap_ab / ab2;
if (segmentClamp)
{
if (t < 0.0f) t = 0.0f;
else if (t > 1.0f) t = 1.0f;
}
return vertexA + AB * t;
}

Object4SidesResizerGrip::Object4SidesResizerGrip(QGraphicsItem* parent) : ObjectResizerGrip(parent)
{
handlers.append(new Handler(this, Handler::Left));
handlers.append(new Handler(this, Handler::BottomLeft));
handlers.append(new Handler(this, Handler::Bottom));
handlers.append(new Handler(this, Handler::BottomRight));
handlers.append(new Handler(this, Handler::Right));
handlers.append(new Handler(this, Handler::TopRight));
handlers.append(new Handler(this, Handler::Top));
handlers.append(new Handler(this, Handler::TopLeft));
handlers.append(new Handler(this, Handler::Rotate));
updateHandlerPositions();
}

QRectF ObjectResizerGrip::boundingRect() const
{
return QRectF();
}

void ObjectResizerGrip::updateHandlerPositions()
{
foreach (Handler* item, handlers)
item->updatePosition();
}

Handler::Handler(QGraphicsItem *parent, Mode mode): QGraphicsItem(parent), mode(mode)
{
QPen pen(Qt::white);
pen.setWidth(0);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, false);

setAcceptHoverEvents(true);
setZValue(100);
setCursor(Qt::UpArrowCursor);
updatePosition();
}

void Handler::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen pen(isMoving ? QColor(250,214,36) : QColor(100,100,100));
pen.setWidth(0);
pen.setBrush(pen.color());
painter->setPen(pen);
painter->setBrush(QColor(100,100,100,150));
if(mode & Rotate)
{
auto rect_ = ((Item*) parentItem()->parentItem())->boundingRect();
auto topPos = QPointF(rect_.left() + rect_.width() / 2 - 1, rect_.top());
painter->drawLine(mapFromParent(topPos), mapFromParent(topPos - QPointF(0, 175)));
painter->drawEllipse(boundingRect());
}
else
painter->drawRect(boundingRect());
}

QRectF Handler::boundingRect() const
{
return QRectF(-25, -25, 50, 50);
}

void Handler::updatePosition()
{
auto rect_ = ((Item*) parentItem()->parentItem())->boundingRect();
switch (mode)
{
case TopLeft:
setPos(rect_.topLeft());
break;
case Top:
setPos(rect_.left() + rect_.width() / 2 - 1,rect_.top());
break;
case TopRight:
setPos(rect_.topRight());
break;
case Right:
setPos(rect_.right(),rect_.top() + rect_.height() / 2 - 1);
break;
case BottomRight:
setPos(rect_.bottomRight());
break;
case Bottom:
setPos(rect_.left() + rect_.width() / 2 - 1,rect_.bottom());
break;
case BottomLeft:
setPos(rect_.bottomLeft());
break;
case Left:
setPos(rect_.left(), rect_.top() + rect_.height() / 2 - 1);
break;
case Rotate:
setPos(0, rect_.top() - 200);
break;
}
}

void Handler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(mode & Rotate)
{
Item* item = (Item*) parentItem()->parentItem();
auto angle = QLineF(item->mapToScene(QPoint()), event->scenePos()).angle();
if(!(QApplication::keyboardModifiers() & Qt::AltModifier)) // snap to 45deg
{
auto modAngle = fmod(angle+180, 45);
if(modAngle < 10 || modAngle > 35)
angle = round(angle/45)*45;
}
item->setRotation(0);
angle = QLineF(item->mapFromScene(QPoint()), item->mapFromScene(QLineF::fromPolar(10, angle).p2())).angle();
item->setRotation(90 - angle);
item->update();
}
else
{
Item* item = (Item*) parentItem()->parentItem();
auto diff = mapToItem(item, event->pos()) - mapToItem(item, event->lastPos());
auto bRect = item->boundingRect();
if(mode == TopLeft || mode == BottomRight)
diff = getClosestPoint(bRect.topLeft(), QPoint(0,0), diff, false);
else if(mode == TopRight || mode == BottomLeft)
diff = getClosestPoint(bRect.bottomLeft(), QPoint(0,0), diff, false);

if(mode & Left || mode & Right)
{
item->setPos(item->mapToScene(QPointF(diff.x()/2.0, 0)));
if(mode & Left)
item->setWidth(item->boundingRect().width() - diff.x());
else
item->setWidth(item->boundingRect().width() + diff.x());
}
if(mode & Top || mode & Bottom)
{
item->setPos(item->mapToScene(QPointF(0, diff.y()/2.0)));
if(mode & Top)
item->setHeight(item->boundingRect().height() - diff.y());
else
item->setHeight(item->boundingRect().height() + diff.y());
}
item->update();
}
((ObjectResizerGrip*) parentItem())->updateHandlerPositions();
}

void Handler::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
isMoving = true;
}

void Handler::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
isMoving = false;
}

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto const graphicsView = new QGraphicsView(this);
graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
auto const scene = new QGraphicsScene(this);
auto const item = new CrossItem();
item->setWidth(100);
item->setHeight(100);
scene->addItem(item);
item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
graphicsView->setScene(scene);
graphicsView-> fitInView(scene->sceneRect(), Qt::KeepAspectRatio);

setCentralWidget(graphicsView);
}

MainWindow::~MainWindow()
{
}
main.cpp
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
该解决方案远非完美,但它可以工作,并且可以作为改进的良好开端。
已知的问题:
  • 旋转夹点需要FullViewportUpdate,因为我太懒了,无法在单独的子项中实现它,并且它在边界框外绘制。
  • 可能有更好的架构,例如使用代理或信号/事件。
  • 关于c++ - 鼠标可调整大小,可拖动的小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64485490/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com