之前项目上需要用Qt来绘制可拖拽改变形状的矩形。看了Qt Graphics相关的内容,虽然对Qt怎么添加图元的有了些了解,但是具体如何实现拖拽效果,一时也没有什么好的想法。还好网上有人分享的例子,很受启发。后来又回顾了一下这部分的代码,发现了一种新的实现方式。希望这个例子也能帮助到需要的人吧。
这几篇文章对这个例子的顺利实现很有帮助,非常感谢。
通过QGraphicsItem绘制可拖拽,改变大小的矩形_qt 绘制多个可拖动矩形-CSDN博客
QGraphicsSceneBspTree::climbTree崩溃(自记)-CSDN博客
关于QGraphicsItem的scenePos一直为(0,0)的解决方案_qt scenepos-CSDN博客
运行效果:
完整代码如下:
GraphicsViewTest2.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp \
mylane.cpp \
testrect.cpp \
testview.cpp
HEADERS += \
mainwindow.h \
mylane.h \
testrect.h \
testview.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include "mylane.h"
#include "testrect.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QList<MyLane*> laneList;
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
};
#endif // MAINWINDOW_H
mylane.h
#ifndef MYLANE_H
#define MYLANE_H
#include <QGraphicsObject>
#include "testrect.h"
class MyLane : public QGraphicsObject
{
Q_OBJECT
public:
explicit MyLane(QGraphicsObject *parent = nullptr);
~MyLane();
QRectF boundingRect() const override;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
void addToScene();
void removeFromScene();
public:
TestRect *item1;
TestRect *item2;
TestRect *item3;
TestRect *item4;
public slots:
void updateLane();
};
#endif // MYLANE_H
testrect.h
#ifndef TESTRECT_H
#define TESTRECT_H
#include <QGraphicsRectItem>
class TestRect : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
TestRect(qreal x, qreal y, qreal w, qreal h, QGraphicsRectItem *parent = nullptr);
~TestRect();
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
TestRect &operator=(const TestRect& pt);
QPointF point;
public slots:
void moveMove(QPointF point);
signals:
void sendPos(QPointF point);
};
#endif // TESTRECT_H
testview.h
#ifndef TESTVIEW_H
#define TESTVIEW_H
#include <QGraphicsView>
class TestView : public QGraphicsView
{
Q_OBJECT
public:
TestView(QWidget *parent = nullptr);
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
signals:
void mouseMovePoint(QPoint point);
void mouseClicked(QPoint point);
};
#endif // TESTVIEW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsItem>
#include <QGraphicsRectItem>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QRectF rect(-200, -100, 400, 200);
scene = new QGraphicsScene(rect);
//不加下面这行发现删除矩形的时候程序会概率性的挂在QGraphicsSceneBspTree函数里,什么原因我也不清楚,有清楚的欢迎留言指正
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
QGraphicsRectItem *item = new QGraphicsRectItem(rect);
//item->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable);
QPen pen;
pen.setWidth(2);
item->setPen(pen);
scene->addItem(item);
ui->graphicsView->setScene(scene);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
if(!laneList.isEmpty()){
MyLane* lane = laneList.first();
lane->removeFromScene();
scene->removeItem(lane);
laneList.removeOne(lane);
delete lane;
lane = nullptr;
}
}
void MainWindow::on_pushButton_2_clicked()
{
MyLane* lane = new MyLane;
scene->addItem(lane);
lane->addToScene();
laneList.append(lane);
}
mylane.cpp
#include "mylane.h"
#include "testrect.h"
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QPainter>
#include <QDebug>
MyLane::MyLane(QGraphicsObject *parent) : QGraphicsObject(parent)
{
item1 = new TestRect(0, 0, 10, 10);
item1->setFlags(QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable);
item1->setPos(0, 0);
item2 = new TestRect(0, 0, 10, 10);
item2->setFlags(QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable);
item2->setPos(100, 0);
item3 = new TestRect(0, 0, 10, 10);
item3->setFlags(QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable);
item3->setPos(100, -100);
item4 = new TestRect(0, 0, 10, 10);
item4->setFlags(QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable);
item4->setPos(0, -100);
connect(item1, &TestRect::sendPos, item2, &TestRect::moveMove);
connect(item2, &TestRect::sendPos, item1, &TestRect::moveMove);
connect(item3, &TestRect::sendPos, item4, &TestRect::moveMove);
connect(item4, &TestRect::sendPos, item3, &TestRect::moveMove);
connect(item1, &TestRect::sendPos, this, &MyLane::updateLane);
connect(item2, &TestRect::sendPos, this, &MyLane::updateLane);
connect(item3, &TestRect::sendPos, this, &MyLane::updateLane);
connect(item4, &TestRect::sendPos, this, &MyLane::updateLane);
}
MyLane::~MyLane(){
qDebug() << "~MyLane";
delete item1;
delete item2;
delete item3;
delete item4;
item1 = nullptr;
item2 = nullptr;
item3 = nullptr;
item4 = nullptr;
}
QRectF MyLane::boundingRect() const{
QVector<float> vecX;
QVector<float> vecY;
vecX.append(item1->x());
vecX.append(item2->x());
vecX.append(item3->x());
vecX.append(item4->x());
vecY.append(item1->y());
vecY.append(item2->y());
vecY.append(item3->y());
vecY.append(item4->y());
float minX = *std::min_element(std::begin(vecX), std::end(vecX));
float minY = *std::min_element(std::begin(vecY), std::end(vecY));
float maxX = *std::max_element(std::begin(vecX), std::end(vecX));
float maxY = *std::max_element(std::begin(vecY), std::end(vecY));
QPointF startPoint(minX, minY);
QPointF endPoint(maxX, maxY);
QPointF pointOffset(10, 10);
return { startPoint, endPoint + pointOffset};
}
void MyLane::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
// qDebug() << "item1:" << item1->pos();
// qDebug() << "item2:" << item2->pos();
// qDebug() << "item3:" << item3->pos();
// qDebug() << "item4:" << item4->pos();
painter->save();
painter->setPen(Qt::blue);
painter->drawLine(item1->pos(), item2->pos());
painter->drawLine(item2->pos(), item3->pos());
painter->drawLine(item3->pos(), item4->pos());
painter->drawLine(item4->pos(), item1->pos());
// painter->drawRect(boundingRect());
// qDebug() << "boundingRect:" << boundingRect().x() << boundingRect().y() << boundingRect().width() << boundingRect().height();
painter->restore();
}
void MyLane::addToScene(){
if(scene()){
scene()->addItem(item1);
scene()->addItem(item2);
scene()->addItem(item3);
scene()->addItem(item4);
}
}
void MyLane::removeFromScene(){
if(scene()){
scene()->removeItem(item1);
scene()->removeItem(item2);
scene()->removeItem(item3);
scene()->removeItem(item4);
}
}
void MyLane::updateLane(){
scene()->update();
}
testrect.cpp
#include "testrect.h"
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
TestRect::TestRect(qreal x, qreal y, qreal w, qreal h, QGraphicsRectItem *parent)
: QGraphicsRectItem(x, y, w, h, parent)
{
}
void TestRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
emit sendPos(event->pos() - point);
QGraphicsRectItem::mouseMoveEvent(event);
}
void TestRect::mousePressEvent(QGraphicsSceneMouseEvent *event){
point = event->pos();
QGraphicsRectItem::mousePressEvent(event);
}
void TestRect::moveMove(QPointF point){
moveBy(point.x(), point.y());
}
TestRect &TestRect::operator=(const TestRect& pt){
setPos(pt.pos());
return *this;
}
TestRect::~TestRect(){
qDebug() << "~TestRect";
}
testview.cpp
#include "testview.h"
#include <QMouseEvent>
#include <QDebug>
TestView::TestView(QWidget *parent)
{
}
void TestView::mousePressEvent(QMouseEvent *event){
if(event->button() == Qt::LeftButton){
QPoint point = event->pos();
qDebug() << "view:" << point;
qDebug() << "scene:" << mapToScene(point);
}
QGraphicsView::mousePressEvent(event);
}
void TestView::mouseMoveEvent(QMouseEvent *event){
QGraphicsView::mouseMoveEvent(event);
}
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGraphicsView" name="graphicsView"/>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>