Qt编写可拖拽的自定义控件

这篇具有很好参考价值的文章主要介绍了Qt编写可拖拽的自定义控件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一直想做一个像卡牌游戏一样的,可以拖动卡片,实现改变位置,顺序交换的效果,今天我们一起来尝试一下。

1.先绘制一个基于QWidget的控件

类名为Card
h文件

#ifndef CARD_H
#define CARD_H

#include <QWidget>
#include <QPaintEvent>
#include <QPainter>
class Card : public QWidget
{
    Q_OBJECT
public:
    explicit Card(QWidget *parent = nullptr);
protected:
    void paintEvent(QPaintEvent *event) override;
};

#endif // CARD_H

cpp文件

#include "card.h"

Card::Card(QWidget *parent) : QWidget(parent)
{
    this->setGeometry(0,0,200,400);
}

void Card::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.drawRoundedRect(QRectF(5,5,190,390),10,10);

}

我们完成了一个很简单的200*400的圆角卡片
在主界面中展示看看
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "card.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private:
    Ui::Widget *ui;
    Card* cd[8];
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    for(int i=0;i<8;i++){
        cd[i] = new Card(this);
        connect(cd[i],&Card::sendSelf,this,&Widget::getObject);
        cd[i]->move(i%4*200,i/4*400);
    }
}

Widget::~Widget()
{
    delete ui;
}

运行后的效果:
Qt编写可拖拽的自定义控件

2.用QMouseEvent实现控件可拖动

首先要实现控件拖动,需要有2个要素,1:要拖动的控件对象,2:控件的初始位置
card[8]是以数组形式一次性加载到界面的,鼠标点击时我们并不知道当前点击的对象。我们可以在Card类中做修改,使点击时通知主界面它是谁。

card.h

#ifndef CARD_H
#define CARD_H

#include <QWidget>
#include <QPaintEvent>
#include <QPainter>
#include <QMouseEvent>
class Card : public QWidget
{
    Q_OBJECT
public:
    explicit Card(QWidget *parent = nullptr);
protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
signals:
    void sendSelf(Card *w);
};

#endif // CARD_H

我们新增了mousePressEvent和一个信号sendSelf,信号将自己本身的地址发送出去

card.cpp

#include "card.h"

Card::Card(QWidget *parent) : QWidget(parent)
{
    this->setGeometry(0,0,200,400);
}

void Card::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.drawRoundedRect(QRectF(5,5,190,390),10,10);
}

void Card::mousePressEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    emit sendSelf(this);
}

到widget.h中设置相应的槽函数

private slots:
    void getObject(Card *w);

接收到对象后,我们接下来要对其进行操作,改变位置只需要用move()函数即可,我们要实时显示出移动的过程,因此需要在mouseMoveEvent中做处理,重写mouseMoveEvent函数:

protected:
    void mouseMoveEvent(QMouseEvent *event) override;

还需要声明一个Card成员变量,来临时存储getObject中获得的对象地址,声明一个开始移动时鼠标指针位置startP,一个卡片本身的位置yuanP。

private:
    Ui::Widget *ui;
    Card *temp;
    QPoint startP;
    QPoint yuanP;

实现getObject函数和mouseMoveEvent函数
widget.cpp

void Widget::getObject(Card *w)
{
    temp = w;
    startP = cursor().pos()-this->pos();
    yuanP = temp->pos();
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    temp->move(yuanP.x()+event->x()-startP.x(),yuanP.y()+event->y()-startP.y());
}

运行后,可以成功拖动卡片了!
但是我们又发现了一个问题,当卡片重叠时,我们点击重叠部分无法确定选中的是哪张卡片,因此我们制定一个规则,最近移动的卡片总是处于最上层。我们可以在mouseReleaseEvent中做处理:

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    temp->raise();
}

到这一步,我们已经实现了可自由拖拽改变位置的控件。

3.接下来实现拖拽交换卡片位置的功能

这里我制定了一个规则,拖拽某张卡片时,若此时的鼠标指针进入了另一张卡片范围,则进行交换。

整体思路是这样的:

在widget中的mouseMoveEvent中做处理:当鼠标移动时,给Card发送信号,由Card的槽函数中做判断,指针是否进入了自身范围if(this->geometry().contains(pos)),如果进入了,就向widget发送信号,避免重复发送信号,增加一个开关量,widget收到反馈信号后就关闭。为了避免拖拽的Card本身触发进入范围的判断,在widget的getObject槽函数中先关闭了当前Card的连接。
为了能看清是否交换位置了,我们给card增加了标号显示。
完成后的代码:
card.h

#ifndef CARD_H
#define CARD_H

#include <QWidget>
#include <QPaintEvent>
#include <QPainter>
#include <QMouseEvent>
class Card : public QWidget
{
    Q_OBJECT
public:
    explicit Card(QWidget *parent = nullptr);
    QString txt;//显示卡片标号
public slots:
    void getPos(QPoint p);//接收widget发送的鼠标坐标
protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;

private:
    bool isDragging=false;//当前是否被拖拽
signals:
    void sendSelf(Card *w);
    void sendNeedChange(Card *w);//发送给widget表明自己需要被交换
};

#endif // CARD_H

card.cpp

#include "card.h"
#include <QDebug>
Card::Card(QWidget *parent) : QWidget(parent)
{
    this->setGeometry(0,0,200,400);
}

void Card::getPos(QPoint p)
{
    if(this->geometry().contains(p)){
        qDebug()<<"enter"<<txt;
        emit sendNeedChange(this);
    }
}

void Card::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.drawRoundedRect(QRectF(5,5,190,390),10,10);
    painter.drawText(50,100,100,200,Qt::AlignCenter,txt);//绘制卡片标号
}

void Card::mousePressEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    isDragging = true;
    emit sendSelf(this);
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMouseEvent>
#include "card.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void getObject(Card *w);
    void needChange(Card *w);//执行交换
protected:
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
private:
    Ui::Widget *ui;
    Card* cd[8];
    Card *temp;

    QPoint startP;
    QPoint yuanP;
    QRect yuanR;
    bool isMoving=false;
signals:
    void sendPos(QPoint p);//发送鼠标坐标
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    for(int i=0;i<8;i++){
        cd[i] = new Card(this);
        cd[i]->txt=QString::number(i+1);//绘制卡片标号
        connect(cd[i],&Card::sendSelf,this,&Widget::getObject);
        connect(cd[i],&Card::sendNeedChange,this,&Widget::needChange);
        connect(this,&Widget::sendPos,cd[i],&Card::getPos);
        cd[i]->move(i%4*200,i/4*400);
    }
}

Widget::~Widget()
{
    delete ui;
}

void Widget::getObject(Card *w)
{
    temp = w;
    disconnect(this,&Widget::sendPos,w,&Card::getPos);//暂时断开正在拖拽的card连接,避免触发与自己的交换
    startP = cursor().pos()-this->pos();
    yuanP = temp->pos();
    yuanR = temp->geometry();
    isMoving=true;
}

void Widget::needChange(Card *w)
{
    targetR = w->geometry();//记录被交换对象的位置
    w->setGeometry(yuanR);//被交换的card移动到被拖拽的card的原位置
    isMoving=false;
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    temp->move(yuanP.x()+event->x()-startP.x(),yuanP.y()+event->y()-startP.y());
    if(isMoving){
        emit sendPos(event->pos());
    }
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    connect(this,&Widget::sendPos,temp,&Card::getPos);//交换完成恢复连接
    temp->raise();
    qDebug()<<targetR;
    temp->setGeometry(targetR);//松开后被拖拽card自动调整到被交换card的原位置
}

到这一步就已经基本实现我们最初想要的功能了,接下来可以再移动过程中加点动画效果,把widget也封装起来,可以自定义设置卡片数量,卡片尺寸等

完整代码链接:Qt自定义可拖拽控件

03.01更新:增加的动画效果

利用QPropertyAnimation实现卡片的动画移动效果
card.h

#ifndef CARD_H
#define CARD_H

#include <QWidget>
#include <QPaintEvent>
#include <QPainter>
#include <QMouseEvent>
#include <QPropertyAnimation>
class Card : public QWidget
{
    Q_OBJECT
public:
    explicit Card(QWidget *parent = nullptr);
    QString txt;    
    void moveTo(QRect r);//新增移动接口,r为目标QRect
public slots:
    void getPos(QPoint p);
protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
private slots:
    void reset();//动画结束后触发
private:
    bool isDragging=true;//拖拽标志,防止重复触发动画
signals:
    void sendSelf(Card *w);
    void sendNeedChange(Card *w);
};

#endif // CARD_H

card.cpp文章来源地址https://www.toymoban.com/news/detail-511762.html

void Card::moveTo(QRect r)
{
    QPropertyAnimation *animation = new QPropertyAnimation(this,"geometry");
    connect(animation,&QPropertyAnimation::finished,this,&Card::reset);
    animation->setDuration(300);
    animation->setStartValue(this->geometry());
    animation->setEndValue(r);
    animation->start();
}

void Card::getPos(QPoint p)
{
    if(isDragging && this->geometry().contains(p)){
        isDragging=false;
        qDebug()<<"enter"<<txt;
        emit sendNeedChange(this);
    }
}
void Card::reset()
{
    isDragging = true;
}

到了这里,关于Qt编写可拖拽的自定义控件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt程序设计-无边框可移动可拖拽调整大小窗体

    本文讲解Qt-无边框可移动可拖拽调整大小窗体。 通过鼠标的按下移动进行窗体的移动,拖拽调整窗体大小。 实现过程如下: 创建QWidget窗体,添加一个按钮控制窗体的关闭。

    2024年02月19日
    浏览(31)
  • vue自定义h5video视频播放器进度条组件,可拖拽、跳转、倍速、全屏

    一个进度条组件控制多个视频的播放、进度调整。视频可点击全屏观看,唯一的进度条是某个指定视频的视频信息。 全屏 点击进度条跳转 拖动滑块 在菜鸟教程上有以下几个参数的详细解说,这张图忘记哪里看的了,如有认领可评论我贴链接 倍速 // 倍速 handleChangeSpeed(item)

    2024年02月12日
    浏览(65)
  • vue3.0 安卓和ios h5 移动端音频自定义圆环可拖拽播放(兼容微信浏览器)

    安装  npm install weixin-js-sdk 引入 template     div class=\\\"circle_box\\\"         div id=\\\"content\\\"/div          img class=\\\"img_0\\\" src=\\\"https://img.yzcdn.cn/vant/cat.jpeg\\\" alt=\\\"\\\"          img @click=\\\"changeType\\\" class=\\\"img_1\\\" v-show=\\\"playbool\\\" src=\\\"@/assets/decompression/pressure_audio_play.png\\\" alt=\\\"\\\"          img @click=\\\"changeType\\\"

    2023年04月23日
    浏览(93)
  • 在 WPF 为你的自定义控件添加属性

    首先,在你的自定义控件类 (示例: UserControl1 ) 添加以下代码: 以上代码简化了官方方法的流程,以便你不用再去额外输入不必要的参数,并提前对 sender 进行类型转换。 此后,添加一个属性只需要添加以下代码即可,相比传统方法看起来会非常直观,非常省事。 以上代码中

    2024年02月11日
    浏览(38)
  • 微信小程序实现拖拽的小球

             目录 前言  js  获取微信小程序中获取系统信息 触摸移动事件的处理函数 触摸结束事件的处理函数  用于监听页面滚动事件 全局参数  html CSS 小程序开发提供了丰富的API和事件处理函数,使得开发者可以方便地实现各种交互功能。其中,拖拽功能是一个在许多

    2024年02月12日
    浏览(30)
  • 封装React组件DragLine,鼠标拖拽的边框改变元素宽度

    原文合集地址如下,有需要的朋友可以关注 本文地址 合集地址 在项目中,设计说想做个面板,其宽度随鼠标拖拽而变化,有最大最小值。基于这个小功能封装一个可拖拽组件,在需要的地方引入即可。 这里只是实现x方向的拖拽,y轴拖拽思路差不多。 既然是鼠标操作,那肯

    2024年02月16日
    浏览(30)
  • Vue:可拖拽组件

            在实际开发中,很可能会遇到开发可拖拽组件的需求,目的是应对某些弹框组件会遮盖某些重要信息/可操作面板,通过可拖拽的形式可以将上层的弹框组件移动到其他位置,从而不影响整个系统的操作。下面,我们分两步走,开发一个可拖拽的弹框组件,最终效果如

    2024年02月12日
    浏览(28)
  • LVGL可拖拽窗口实现

    一、 LVGL拖拽功能     LVGL obj提供了使能拖拽功能的函数:     不过在多元素界面,例如下边在 bg win上创建了若干个 sub win,bg win大部分面积被覆盖的情况下,如果单纯将 bg win 设置为可拖拽,则只有点击到  没有被覆盖的区域才能成功,效果不满足要求;如果简单的把 sub

    2023年04月25日
    浏览(27)
  • 实现ElementUI tab标签可拖拽

    通过sortablejs实现 参考:https://blog.csdn.net/wangjiecsdn/article/details/121995534

    2024年01月22日
    浏览(59)
  • 前端弹窗可拖拽功能实现

           前端弹窗可拖拽功能主要实现思路就是监听鼠标移动事件,根据鼠标位置实时修改弹窗距离父级窗口(或者屏幕,根据需求设置)的left和right,但是考虑到鼠标拖拽一般都是在div的标题栏处发生,鼠标按下的位置不可能是弹窗的左上角位置,为此需要计算 鼠标按下的

    2024年02月19日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包