Bootstrap

Qt实现可拖拽的矩形

之前项目上需要用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>

;