1.概要
创建一个可以拖拽的矩形,要求如下
鼠标接近左右边线,鼠标变成可以拖拽的形状,矩形的左右边线可以拖拽;
当鼠标点击矩形内的时候,鼠标变成手型,可以可以上下拖动;
点击+ctrl是选择矩形,选择后矩形的边线变成蓝色,再次点击取消选择;
选择的单个矩形可以对矩形进行拆分,选择多个矩形可以进行合并;
如果矩形已经被拆分成多少,当上下拖动的时候,全部矩形跟随;
2.代码
2.1 主函数
#include "mainwindow91.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow91 w; // 更改为 MainWindow91
w.resize(500,400);
w.show();
return a.exec();
}
2.2 窗口类
2.2.1 头文件
#ifndef MAINWINDOW91_H
#define MAINWINDOW91_H
#include <QWidget>
#include <QMenu>
#include <QAction>
#include <QMouseEvent>
#include <QGraphicsRectItem>
#include "CustomRectItem91.h"
class MainWindow91 : public QWidget
{
Q_OBJECT
public:
static MainWindow91* pMainWindow91;
static MainWindow91* getMy();
MainWindow91(QWidget *parent = nullptr);
~MainWindow91();
void selectId(int di);
void moveSelectId(int di);
void setPostY(qreal y);//当别的控件上下拖动的时候,全部的控件保持一致
protected:
void mousePressEvent(QMouseEvent *event) override;
private slots:
void onMergeActionTriggered();
void onSplitActionTriggered();
private:
QGraphicsScene *scene;
QMenu *contextMenu;
QAction *mergeAction;
QAction *splitAction;
QList<int> indexIds;
QList<int> selectIds;
void clearSelect();//删除选择
//void addSelect(int id);//添加选择
//void removeSelect(int id);//移除选择
QMap<int,CustomRectItem91 *> itemMaps;
//QGraphicsRectItem *originalRect;
};
#endif // MAINWINDOW91_H
2.2.2 源文件
#include "mainwindow91.h"
#include <QMouseEvent>
#include <QDebug>
#include <QGraphicsView>
#include <QGraphicsScene>
#include "CustomRectItem91.h"
#include "main9publics.h"
MainWindow91* MainWindow91::pMainWindow91 = NULL;
MainWindow91* MainWindow91::getMy(){
return pMainWindow91;
}
MainWindow91::MainWindow91(QWidget *parent)
: QWidget(parent)
{
pMainWindow91 = this;
// 创建一个QGraphicsScene和QGraphicsView
scene = new QGraphicsScene();
QGraphicsView *view = new QGraphicsView(scene, this);
view->setGeometry(10, 10, 500, 400);
// 创建原始矩形
//CustomRectItem91 *originalRect = scene->addRect(0, 0, 200, 100, QPen(Qt::black), QBrush(Qt::blue));
QRectF mergedRect(20, 20, 400, 100);
CustomRectItem91 *originalRect = new CustomRectItem91(mergedRect);
QPen pen(Qt::red); // 设置蓝色边框
originalRect->setPen(pen);
scene->addItem(originalRect);
itemMaps.insert(originalRect->Id,originalRect);
indexIds.append(originalRect->Id);
// 创建右键菜单和动作
contextMenu = new QMenu(this);
mergeAction = contextMenu->addAction("合并");
splitAction = contextMenu->addAction("拆分");
// 连接信号和槽
connect(mergeAction, &QAction::triggered, this, &MainWindow91::onMergeActionTriggered);
connect(splitAction, &QAction::triggered, this, &MainWindow91::onSplitActionTriggered);
}
MainWindow91::~MainWindow91()
{
}
void MainWindow91::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
contextMenu->exec(event->globalPos());
}
}
void MainWindow91::onMergeActionTriggered()
{
qDebug() << "合并操作被触发";
// 在这里实现合并的逻辑
if(selectIds.length()<=1){
qDebug()<<"selectIds.length():"<<selectIds.length();
}
int statId = selectIds.first();
int endId = selectIds.last();
CustomRectItem91* istart = itemMaps[statId];
CustomRectItem91* iend = itemMaps[endId];
int startIndex = indexIds.indexOf(statId);//获取选择的控件在列表中的下标
//取消选择
for (int value : selectIds){
CustomRectItem91* sItem = itemMaps[value];
sItem->moveSelect();//取消选择属性,为合并控件打好基础
scene->removeItem(sItem);//移除控件
itemMaps.remove(value);//移除对象类别
indexIds.removeOne(value);//移除对象排序列表
}
//合并
CustomRectItem91* inew = mergeRects(istart,iend);
scene->addItem(inew);//添加合并后的控件
itemMaps.insert(inew->Id,inew);//添加对象map
indexIds.insert(startIndex,inew->Id);//添加控制顺序列表
//清除创建的对象
for (int value : selectIds){
CustomRectItem91* sItem = itemMaps[value];
delete sItem;
}
selectIds.clear();//清除选择列表
}
void MainWindow91::onSplitActionTriggered()
{
qDebug() << "拆分操作被触发";
if(selectIds.length()<=0){
qDebug()<<"selectIds.length():"<<selectIds.length();
}
// 拆分矩形
int id = selectIds.first();
int startIndex = indexIds.indexOf(id);//获取选择的控件在列表中的下标
CustomRectItem91* originalRect = itemMaps[id];
originalRect->moveSelect();
auto [leftRect, rightRect] = splitRect(originalRect);
scene->removeItem(originalRect);
scene->addItem(leftRect);
scene->addItem(rightRect);
selectIds.clear();
itemMaps.remove(originalRect->Id);
itemMaps.insert(leftRect->Id,leftRect);
itemMaps.insert(rightRect->Id,rightRect);
indexIds.removeOne(originalRect->Id);
indexIds.insert(startIndex,leftRect->Id);
indexIds.insert(startIndex+1,rightRect->Id);
//indexIds.append(leftRect->Id);
//indexIds.append(rightRect->Id);
//delete originalRect;
// 在这里实现拆分的逻辑
}
void MainWindow91::selectId(int id){
int myIndex = indexIds.indexOf(id);//我的下标序号
if(selectIds.length()>0){
int indexStart = indexIds.indexOf(selectIds.first());//已经选择的序号列表的头
if(myIndex<indexStart){
if(myIndex!=indexStart-1){
//重新选择
clearSelect();
}else{
//链接
}
selectIds.insert(0,id);
}else{
int indexEnd = indexIds.indexOf(selectIds.last());
if(myIndex!=indexEnd+1){
//重新选择
clearSelect();
}else{
//链接
}
selectIds.append(id);
}
}else{
this->selectIds.append(id);
}
}
void MainWindow91::moveSelectId(int id){
this->selectIds.removeOne(id);
}
//删除选择,同时要更新ui的状态,ui也去掉选择
void MainWindow91::clearSelect(){
for (int value : selectIds){
itemMaps[value]->moveSelect();
}
selectIds.clear();
}
//当别的控件上下拖动的时候,全部的控件保持一致
void MainWindow91::setPostY(qreal y){
for (int value : indexIds){
itemMaps[value]->setPostY(y);
}
}
2.3 矩形类
2.3.1 头文件
#ifndef CUSTOMRECTITEM91_H
#define CUSTOMRECTITEM91_H
#include <QGraphicsRectItem>
#include <QColor>
#include <QRectF>
class CustomRectItem91 : public QGraphicsRectItem {
public:
static int idNum;
CustomRectItem91(QGraphicsItem *parent = nullptr);
CustomRectItem91(const QRectF &rect, QGraphicsItem *parent = nullptr);
int Id;
void moveSelect();
void setPostY(qreal y);//当别的控件上下拖动的时候,全部的控件保持一致
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
QColor originalColor;
enum Edge { NoEdge, LeftEdge, RightEdge };
Edge draggingEdge = NoEdge;
bool draggingItem = false;
QPointF lastMousePos;
bool isNearEdge(const QPointF &pos, Edge edge) const;
};
#endif // CUSTOMRECTITEM91_H
2.3.2 资源文件
#include "CustomRectItem91.h"
#include <QMouseEvent>
#include <QBrush>
#include <QPen>
#include <QGraphicsSceneMouseEvent>
#include "mainwindow91.h"
int CustomRectItem91::idNum = 0;
CustomRectItem91::CustomRectItem91(QGraphicsItem *parent)
: QGraphicsRectItem(parent) {
//setFlag(QGraphicsItem::ItemIsMovable, false);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
// 设置初始颜色并保存为原始颜色
//originalColor = Qt::black; // 或者您想要的任何初始颜色
originalColor = Qt::red; // 或者您想要的任何初始颜色
//setBrush(QBrush(originalColor));
Id = idNum++;
//MainWindow91* mw = MainWindow91::getMy();
}
CustomRectItem91::CustomRectItem91(const QRectF &rect, QGraphicsItem *parent): QGraphicsRectItem(rect,parent){
// 设置初始颜色并保存为原始颜色
originalColor = Qt::red; // 或者您想要的任何初始颜色
//setBrush(QBrush(originalColor));
Id = idNum++;
}
void CustomRectItem91::mousePressEvent(QGraphicsSceneMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
if(event->modifiers() & Qt::ControlModifier){
if (contains(event->pos())) {
QColor currentColor = pen().color();
if (currentColor == originalColor) {
// 变成红色
setPen(QPen(Qt::blue));
//setBrush(QBrush(Qt::red));
MainWindow91::getMy()->selectId(Id);
} else {
// 恢复原来的颜色
//setBrush(QBrush(originalColor));
setPen(QPen(originalColor));
MainWindow91::getMy()->moveSelectId(Id);
}
update(); // 更新图形项以反映颜色变化
}
}else{
if (isNearEdge(event->pos(), LeftEdge)) {
draggingEdge = LeftEdge;
qDebug()<<"draggingEdge left ";
setCursor(Qt::SplitHCursor);
} else if (isNearEdge(event->pos(), RightEdge)) {
draggingEdge = RightEdge;
setCursor(Qt::SplitHCursor);
qDebug()<<"draggingEdge right";
} else {
draggingItem = true;
lastMousePos = event->pos();
setCursor(Qt::OpenHandCursor);
qDebug()<<"draggingItem";
}
update();
}
}else{
QGraphicsRectItem::mousePressEvent(event); // 调用基类的mousePressEvent以处理其他可能的交互
}
//
}
void CustomRectItem91::moveSelect()
{
// 恢复原来的颜色
//setBrush(QBrush(originalColor));
setPen(QPen(originalColor));
update(); // 更新图形项以反映颜色变化
}
void CustomRectItem91::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
if (draggingEdge == LeftEdge) {
qreal newWidth = rect().width() - (event->pos().x() - rect().left());
setRect(QRectF(event->pos().x(), rect().y(), newWidth, rect().height()));
} else if (draggingEdge == RightEdge) {
qreal newWidth = event->pos().x() - rect().left();
setRect(QRectF(rect().left(), rect().y(), newWidth, rect().height()));
} else if (draggingItem) {
auto dy = event->pos().y() - lastMousePos.y();
//auto newPos = pos() + QPointF(0, dy);
//setPos(newPos);
//lastMousePos = event->pos();
MainWindow91* mw = MainWindow91::getMy();
mw->setPostY(dy);
}
}
//上下位置移动
//其他控件移动,我也跟着移动
void CustomRectItem91::setPostY(qreal movey){
auto newPos = pos() + QPointF(0, movey);
setPos(newPos);
}
void CustomRectItem91::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
if (event->button() == Qt::LeftButton) {
draggingEdge = NoEdge;
draggingItem = false;
setCursor(Qt::ArrowCursor);
update();
qDebug()<<"mouseReleaseEvent";
}
}
bool CustomRectItem91::isNearEdge(const QPointF &pos, Edge edge)const{
QRectF edgeRect;
switch (edge) {
case LeftEdge:
edgeRect = QRectF(rect().x()-10, rect().y(), 20, rect().height());
break;
case RightEdge:
edgeRect = QRectF(rect().x()+rect().width() - 10, rect().y(), 20, rect().height());
break;
default:
break;
}
return edgeRect.contains(pos);
}
2.4.矩形的拆分和合并函数
2.4.1 头文件
#ifndef MAIN9PUBLICS_H
#define MAIN9PUBLICS_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include "CustomRectItem91.h"
std::pair<CustomRectItem91*, CustomRectItem91*> splitRect(CustomRectItem91 *originalRect);
CustomRectItem91* mergeRects(CustomRectItem91 *rect1, CustomRectItem91 *rect2);
#endif // MAIN9PUBLICS_H
2.4.2 源文件
#include "main9publics.h"
// 函数用于拆分矩形
std::pair<CustomRectItem91*, CustomRectItem91*> splitRect(CustomRectItem91 *originalRect) {
QRectF originalRectF = originalRect->rect();
QPointF originalPos = originalRect->pos();
QRectF leftRect(0, 0, originalRectF.width() / 2-5, originalRectF.height());
QRectF rightRect(0, 0, originalRectF.width() / 2-5, originalRectF.height());
CustomRectItem91 *leftPart = new CustomRectItem91(leftRect);
CustomRectItem91 *rightPart = new CustomRectItem91(rightRect);
leftPart->setPos(originalPos);
rightPart->setPos(QPointF(originalPos.x() + leftRect.width() + 10, originalPos.y()));
//设置矩形的默认边框
leftPart->setPen(originalRect->pen());
rightPart->setPen(originalRect->pen());
//leftPart->setBrush(originalRect->brush());
//rightPart->setBrush(originalRect->brush());
return {leftPart, rightPart};
}
// 函数用于合并矩形,根据两个矩形的位置和大小来计算合并后的矩形
CustomRectItem91* mergeRects(CustomRectItem91 *rect1, CustomRectItem91 *rect2) {
QRectF rect1F = rect1->rect();
QPointF rect1Pos = rect1->pos();
QRectF rect2F = rect2->rect();
QPointF rect2Pos = rect2->pos();
// 计算合并后矩形的位置和大小
qreal mergedWidth = rect1F.width() + (rect2Pos.x() - rect1Pos.x() - rect1F.width()) + rect2F.width();
QRectF mergedRect(rect1Pos.x(), rect1Pos.y(), mergedWidth, rect1F.height());
CustomRectItem91 *mergedRectItem = new CustomRectItem91(mergedRect);
//mergedRectItem->setBrush(rect1->brush());
//设置矩形的默认边框
mergedRectItem->setPen(rect1->pen());
// 清理旧的矩形
//delete rect1;
//delete rect2;
return mergedRectItem;
}
3.运行结果
拖拽、缩放、鼠标形状3