实现拖动标题栏窗口恢复+窗口跟着鼠标移动

窗口跟着鼠标移动

1.重写鼠标按下事件,记录鼠标在窗口中的相对位置

2.重写鼠标移动事件,调用move方法使得窗口移动到鼠标的位置(调用globalPos方法获取鼠标的位置)

3.注意点:移动时鼠标的位置还要减去一开始的相对位置,否则,窗口直接就是左上角跟着鼠标移动了,没有保持最开始鼠标和窗口的相对位置

如下,鼠标位置没有减去相对位置,就会出现下面的情况:

 拖动标题栏窗口恢复

1.鼠标按下事件记录是否是在标题栏按下

2.鼠标移动事件中,如果是在标题栏按下,那么窗口恢复,同时记录窗口最大化时的宽度,以及用一个标记变量记录窗口从最大化恢复到了正常状态

3.如果窗口是正常状态,那么窗口跟着鼠标移动,注意此时要更新前面记录的那个相对位置的值,既然窗口从最大化变为了正常,那么我一开始在窗口按下的相对位置也要进行同比例的变化。

如果不变化就会出现下面的情况:

 完整代码如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QLabel>
#include<QPushButton>
#include<QMouseEvent>
#include<QHBoxLayout>
#include<QBoxLayout>
#include<QApplication>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr):QWidget(parent)
    {
        //去掉自带的标题栏
        setWindowFlag(Qt::FramelessWindowHint);
        setStyleSheet(R"(font-family:"Microsot YaHei";font-size:12px)");
        //整个窗口垂直布局
        QVBoxLayout* v_box=new QVBoxLayout(this);
        v_box->setContentsMargins(0,0,0,0);
        v_box->setSpacing(0);

        //创建标题栏
        title_bar=new QWidget(this);
        title_bar->setFixedHeight(60);
        title_bar->setStyleSheet(R"(.QWidget{background-color:#edf5f9})");
        //标题栏水平布局
        QHBoxLayout* h_box=new QHBoxLayout(title_bar);
        h_box->setContentsMargins(0,0,0,0);
        h_box->setSpacing(0);
        //创建窗口标题
        window_title=new QLabel(this);
        window_title->setText("自定义标题栏");
        window_title->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
        h_box->addWidget(window_title);

        //创建最大化按钮
        btn_max=new QPushButton(this);
        btn_max->setFixedSize(100,50);
        btn_max->setText("点我最大化");
        h_box->addWidget(btn_max);

        //创建窗口主体
        body=new QWidget(this);
        body->setStyleSheet("background-color:gray");

        v_box->addWidget(title_bar);
        v_box->addWidget(body);
        resize(700,500);

        //绑定槽函数,最大化窗口
        connect(btn_max,&QPushButton::clicked,this,[=](){
            //窗口为正常状态
            if(windowState().testFlag(Qt::WindowNoState))
            {
                //则点击后变为最大
                this->setWindowState(Qt::WindowMaximized);
                btn_max->setText("点我恢复");
            }
            else if(windowState().testFlag(Qt::WindowMaximized))
            {
                //窗口为最大化,点击则恢复正常
                this->setWindowState(Qt::WindowNoState);
                btn_max->setText("点我最大化");
            }

        });
    }
    ~Widget()=default;

protected:
    //实现鼠标拖动窗口移动
    //实现最大化窗口时,在标题栏按下,然后拖动,窗口恢复原来的大小
    void mousePressEvent(QMouseEvent* ev)
    {
        //记录鼠标在窗口中的相对位置
        press_pos=ev->pos();

        QPoint global_press_pos=ev->globalPos();
        if(title_bar==QApplication::widgetAt(global_press_pos)
            ||window_title==QApplication::widgetAt(global_press_pos))
        {
            //记录按下的位置是标题栏
            press_in_title=true;
        }
    }
    void mouseMoveEvent(QMouseEvent* ev)
    {
        //鼠标按下后拖动,如果此时窗口时最大状态,且是在标题栏按下
        if(windowState().testFlag(Qt::WindowMaximized)&&press_in_title)
        {
            //记录窗口最大化时的宽度
            width_of_max=width();

            //那么窗口恢复正常
            setWindowState(Qt::WindowNoState);
            btn_max->setText("点我最大化");

            //标记一下窗口从最大变为正常了
            max_to_normal=true;

            //需要注意的的是窗口恢复正常后,width()方法获取的宽度不会立即更新
        }
        
        //正常状态下,鼠标拖动窗口移动
        if(windowState().testFlag(Qt::WindowNoState))
        {
            //获取窗口当前的宽度
            int width_of_now=width();
            if(max_to_normal &&
                width_of_now!=width_of_max)
            {
                //如果窗口从最大化变为了正常,且窗口前后宽度不相等
                //此时就需要更新press_pos的值
                //来保持恢复正常之后,鼠标在窗口的位置,仍然是刚按下时,鼠标在窗口的位置
                //那么最开始按下的相对位置也要等比例缩小
                //算出比值,正常状态下的宽度比上最大化时的宽度
                float ratio=width_of_now/(float)width_of_max;
                press_pos*=ratio;

                //标记变量复原
                max_to_normal=false;
            }

            //正常状态下,鼠标移动,窗口跟着移动,则需要减去最开始的相对位置。
            //否则就会是窗口的左上角跟着鼠标移动
            this->move(ev->globalPos()-press_pos);
        }

    }
    void mouseReleaseEvent(QMouseEvent* ev)
    {
        //鼠标释放后标记变量复原
        press_in_title=false;
    }

private:
    bool press_in_title=false;//记录是否在标题栏按下

    bool max_to_normal=false;//记录窗口从最大变为正常

    int width_of_max;//记录窗口最大化下时的宽度

    QPoint press_pos;//记录鼠标按下时的在窗口中的位置

    QWidget* title_bar;//标题栏
    QLabel* window_title;//窗口标题
    QPushButton* btn_max;//最大化按钮
    QWidget* body;//窗口主体区
};
#endif // WIDGET_H

正确效果如下:

 学习链接:https://github.com/0voice

wxWidgets是一个跨平台的GUI库,可以在Windows、Linux、macOS等系统上开发应用程序。有时候我们需要在应用程序中创建一个无标题栏窗口,常见的场景是实现自定义的窗口样式或者全屏窗口等。 但是,有时候我们会发现无标题栏窗口无法拖动,这是因为窗口拖动的操作通常是在标题栏上进行的,但是该窗口并没有标题栏。 为了解决这个问题,我们可以通过鼠标移动事件来实现窗口拖动。具体来说,我们需要在窗口类中重载鼠标按下、鼠标松开和鼠标移动等事件函数。当鼠标按下时记录鼠标的位置,当鼠标移动时计算相对于鼠标按下时的位置偏移量,并根据偏移量来移动窗口的位置。当鼠标松开时结束拖动操作。 代码实现如下: ``` class MyFrame : public wxFrame { public: MyFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxFRAME_NO_TASKBAR | wxNO_BORDER | wxFRAME_FLOAT_ON_PARENT) { // 设置背景色为白色 SetBackgroundColour(wxColour(255, 255, 255)); } protected: wxPoint m_dragPos; void OnMouseDown(wxMouseEvent& event) { if (event.LeftIsDown()) { m_dragPos = event.GetPosition(); } } void OnMouseUp(wxMouseEvent& event) { if (event.LeftIsUp()) { wxPoint pos = GetPosition(); pos.x += event.GetPosition().x - m_dragPos.x; pos.y += event.GetPosition().y - m_dragPos.y; SetPosition(pos); } } void OnMouseMove(wxMouseEvent& event) { if (event.Dragging() && event.LeftIsDown()) { wxPoint pos = GetPosition(); pos.x += event.GetPosition().x - m_dragPos.x; pos.y += event.GetPosition().y - m_dragPos.y; SetPosition(pos); } } wxDECLARE_EVENT_TABLE(); }; wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_LEFT_DOWN(MyFrame::OnMouseDown) EVT_LEFT_UP(MyFrame::OnMouseUp) EVT_MOTION(MyFrame::OnMouseMove) wxEND_EVENT_TABLE() ``` 在这个例子中,我们创建了一个名为MyFrame的无标题栏窗口,通过重载OnMouseDown、OnMouseUp和OnMouseMove等事件函数来实现窗口拖动。其中,m_dragPos变量用于保存鼠标按下时的位置,OnMouseDown函数记录该位置,OnMouseUp函数计算偏移量并移动窗口的位置,OnMouseMove函数实时更新鼠标的位置并调整窗口的位置。 最后,我们还需要在类定义中通过wxDECLARE_EVENT_TABLE宏来声明该类所处理的事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值