Qt跨平台无边框窗口探索记录

news2025/1/14 1:17:15

一、前言

实现的效果为:通过黑色矩形框预操作,鼠标释放时更新窗口。效果图如下:

1.功能

1.1 已实现功能

  1. 8个方向的缩放
  2. 标题栏拖动
  3. 标题栏双击最大化/正常窗口
  4. 窗口最小尺寸
  5. 预操作框颜色与背景色互补
  6. 多屏幕
  7. 默认标题栏

1.2 待开发功能

  1. 拖动到屏幕四周进行半屏全屏。

1.3 存在的问题

1.QWebEngineView

从测试效果来看,对于普通widget效果较好,但是如果是嵌入了QWebEngineView,且其位于窗口四周,就会出现问题。

其主要原因在于,当事件进入QWebEngineView后,会被其child(QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget)"吃掉",导致后续事件不再进入主窗口,从而无法触发我们的代码。这时我们还可以遍历QWebEngineView的children,将事件安装到主窗口

        auto childs =  view->children();
        for(auto child:childs){
						child->installEventFilter(this);
        }

但是这样显得太笨重了。而且,即使如此,鼠标样式的修改,也会被QWebEngineView刷掉。因此对于QWebEngineView下是存在一些问题的。

2.多屏幕拖动时,从低缩放比拖到高缩放比释放时,会闪烁一次,目前没有进一步分析原因。

2.探索的过程

2.1 关于无边框缩放的实现方式

目前网上无非两种方式:

a)通过nativeEvent()实现。c++ - Qt/Windows, resizable frameless window - Stack Overflow

b)通过窗口的eventFilter()直接实现。

但是问题是前者基于windows接口不跨平台,而后者则会出现抖动。

本文则是基于第二种方式,并参考windows窗口的拖动方式,通过预操作框避免抖动的问题。

2.2 实现思路

  1. 首先关于无边框窗口,通过Qt标志处理:
setWindowFlags(Qt::FramelessWindowHint);
  1. 然后关于缩放和拖动,与网上大多数实现一致,通过九宫格法实现:

当鼠标位于不同区域时,触发不同的操作。

  1. 而对于预处理框,则是通过一个覆盖整个屏幕的透明窗口,并重写paintEvent在其中绘制一个rect。

2.3 实现难点

1.首先是九宫格的思路,了解了思路之后,实现就比较简单了

2.对于预处理框,首先是需要以全屏幕作为geometry,否则会导致拖不到非主屏幕,或者位置错误灯问题

3.其次是预处理框要实现window类似的与背景色互补(而非纯色),需要获取全屏幕图片作为背景绘制到预操作框,然后再通过QPainter::RasterOp_SourceAndNotDestination模式取反。

4.最后就是对于多屏幕的位置,缩放比灯。

二、代码

目录结构如下:

其中:

  • cursorTransform是用于鼠标位置和方向的计算
  • framelessData则是一些全局变量
  • framelessHelper则是对外的接口,
  • titleBar表示默认标题栏
  • transparentRect则是实现预操作框

详细代码如下:

#include "cursorTransform.h"
#include <QDebug>


AreaType CursorTransform::calculateCursorType(const QSize &size, const QPoint &pos)
{
    //TODO:minsize
    auto w = size.width();
    auto h = size.height();
    auto x = pos.x();
    auto y = pos.y();
    //left
    if(x<=FrameLessData::borderThickness){
        if(y<=FrameLessData::borderThickness)
            return AreaType::LeftTopArea;
        else if(y>=h-FrameLessData::borderThickness)
            return AreaType::LeftBottomArea;
        else
            return AreaType::LeftArea;
    }//right
    else if(x>=w-FrameLessData::borderThickness){
        if(y<=FrameLessData::borderThickness)
            return AreaType::RightTopArea;
        else if(y>=h-FrameLessData::borderThickness)
            return AreaType::RightBottomArea;
        else
            return AreaType::RightArea;
    }
    else{//center
        if(y<=FrameLessData::borderThickness)
            return AreaType::TopArea;
        else if(y<=FrameLessData::titleHeight)
                return AreaType::TitleArea;
        else if(y>=h-FrameLessData::borderThickness)
            return AreaType::BottomArea;
        else
            return AreaType::CenterArea;
    }

}

Qt::CursorShape CursorTransform::AreaType2CursorShape(AreaType type)
{
    Qt::CursorShape cursorType(Qt::ArrowCursor);
    switch (type) {
    case AreaType::LeftTopArea:
    case AreaType::RightBottomArea:
        cursorType=Qt::SizeFDiagCursor;
        break;
    case AreaType::RightTopArea:
    case AreaType::LeftBottomArea:
        cursorType=Qt::SizeBDiagCursor;
        break;
    case AreaType::TopArea:
    case AreaType::BottomArea:
        cursorType=Qt::SizeVerCursor;
        break;
    case AreaType::LeftArea:
    case AreaType::RightArea:
        cursorType=Qt::SizeHorCursor;
        break;
    default:
        break;
    }

    return cursorType;
}

void CursorTransform::calculateRect(AreaType type,QRect &rect, const QPoint &lastPos,const QPoint &newPos)
{
    int xOff=0,yOff=0,wOff=0,hOff=0;
    int posXOff=newPos.x()-lastPos.x();
    int posYOff = newPos.y()-lastPos.y();
    switch (type) {
    case AreaType::LeftTopArea:
        xOff = posXOff;
        yOff = posYOff;
        break;
    case AreaType::TopArea:
        yOff = posYOff;
        break;
    case AreaType::RightTopArea:
        yOff = posYOff;
        wOff = posXOff;
        break;
    case AreaType::LeftArea:
        xOff = posXOff;
        break;
    case AreaType::RightArea:
        wOff = posXOff;
        break;
    case AreaType::LeftBottomArea:
        xOff = posXOff;
        hOff = posYOff;
        break;
    case AreaType::BottomArea:
        hOff = posYOff;
        break;
    case AreaType::RightBottomArea:
        wOff = posXOff;
        hOff = posYOff;
        break;
    case AreaType::TitleArea:   //move
        rect.moveTopLeft(rect.topLeft()+QPoint(posXOff,posYOff));
        break;
    default:
        break;
    }
    //min judget:
    if(rect.width()-xOff<=FrameLessData::minWidth)
        xOff=0;
    else if(rect.left()<newPos.x() && xOff<0)
        xOff=0;
    if(rect.width()+wOff<=FrameLessData::minWidth)
        wOff=0;
    else if(rect.right()>newPos.x() && wOff>0)
        wOff=0;

    if(rect.height()-yOff<=FrameLessData::minHeight)
        yOff=0;
    else if(rect.top()<newPos.y() && yOff<0)
        yOff=0;
    if(rect.height()+hOff<=FrameLessData::minHeight)
        hOff=0;
    else if(rect.bottom()>newPos.y() && hOff>0)
        hOff=0;

    //resize:
    rect.setTop(rect.top()+yOff);
    rect.setLeft(rect.left()+xOff);
    rect.setWidth(rect.width()+wOff);
    rect.setHeight(rect.height()+hOff);
}

#ifndef CURSORTRANSFORM_H
#define CURSORTRANSFORM_H
#include "framelessData.h"
#include <QRect>

class CursorTransform
{
public:
    static AreaType calculateCursorType(const QSize &size, const QPoint &pos);
    static enum Qt::CursorShape AreaType2CursorShape(AreaType type);

    static void calculateRect(AreaType type,QRect &rect,const QPoint &lastPos,const QPoint &newPos);
private:
};

#endif // CURSORTRANSFORM_H
#include "framelessData.h"

int FrameLessData::borderThickness=4;
int FrameLessData::titleHeight=30;
int FrameLessData::minHeight=355;
int FrameLessData::minWidth=220;
QColor FrameLessData::borderColor=(Qt::white);
QColor FrameLessData::transparentRectBkground=(Qt::transparent);
enum Qt::PenStyle FrameLessData::borderStyle=(Qt::SolidLine);
#ifndef FRAMELESSDATA_H
#define FRAMELESSDATA_H
#include <QColor>
#include <QPoint>
#include <QSize>

//透明矩形的属性

class FrameLessData{
public:
    static int borderThickness;
    static int titleHeight;
    static int minHeight;
    static int minWidth;
    static QColor borderColor;
    static QColor transparentRectBkground;
    static enum Qt::PenStyle borderStyle;
};




enum class AreaType
{
    LeftTopArea,
    TopArea,
    RightTopArea,
    LeftArea,
    RightArea,
    LeftBottomArea,
    BottomArea,
    RightBottomArea,
    CenterArea,
    TitleArea,
    None
};

enum class WidgetOperator
{
    None,
    Resize,
    Move
};

#endif // FRAMELESSDATA_H
#include "framelessHelper.h"
#include "cursorTransform.h"
#include <QEvent>
#include <QSize>
#include <QPoint>
#include <QCursor>
#include <QWidget>
#include <QPainter>
#include <QDebug>



FrameLessHelper::FrameLessHelper(QWidget *widget)
    : m_widget(widget)
    , m_transparentRect(new TransparentRect())
    , m_widgetOperator(WidgetOperator::None)
    , m_isMove(false)
#ifdef TITLEBAR
    , m_titleBar(nullptr)
#endif
{
    m_transparentRect->setRect(m_widget->frameGeometry());
#ifdef TITLEBAR
    //if use title
    m_titleBar = new TitleBar(m_widget);

    connect(m_titleBar,&TitleBar::minimumSizeClick,this,&FrameLessHelper::onMinClick);
    connect(m_titleBar,&TitleBar::showNormalClick,this,&FrameLessHelper::onMaxClick);
    connect(m_titleBar,&TitleBar::closeClick,this,&FrameLessHelper::onCloseClick);
#endif
}

bool FrameLessHelper::eventFilter(QObject *watched, QEvent *event)
{
    int ret=false;
    switch (event->type()) {
    case QEvent::MouseButtonDblClick:   //double click
        ret = onDbClickEvent(static_cast<QMouseEvent *>(event));
        break;
    case QEvent::MouseButtonPress:
        ret = onMousePressEvent(static_cast<QMouseEvent *>(event));
        break;
    case QEvent::MouseButtonRelease:
        ret = onMouseReleaseEvent(static_cast<QMouseEvent *>(event));
        break;
    case QEvent::MouseMove:
        ret = onMoveEvent(static_cast<QMouseEvent *>(event));
        break;
    case QEvent::HoverMove:
        case QEvent::HoverEnter:
        ret = onHoverEvent(watched,static_cast<QHoverEvent *>(event));
        break;
    default:
        break;
    }
    //触发返回true
    if(ret)
        return ret;

    return QObject::eventFilter(watched,event);
}

void FrameLessHelper::setBorderThickness(int thickness)
{
    FrameLessData::borderThickness = thickness;
}

void FrameLessHelper::setTitleHeight(int height)
{
    FrameLessData::titleHeight = height;
}

void FrameLessHelper::setMinHeight(int height)
{
    FrameLessData::minHeight = height;
}

void FrameLessHelper::setMinWidth(int width)
{
    FrameLessData::minWidth = width;
}
#ifdef TITLEBAR
QWidget *FrameLessHelper::getTitleBar()
{
    return m_titleBar;
}

void FrameLessHelper::onMinClick()
{
    m_widget->showMinimized();
}

void FrameLessHelper::onMaxClick()
{
    if(m_widget->isFullScreen())
        m_widget->showNormal();
    else
        m_widget->showFullScreen();
}

void FrameLessHelper::onCloseClick()
{
    m_widget->close();
}
#endif
bool FrameLessHelper::onHoverEvent(QObject *watched,QHoverEvent *event)
{
    //操作的时候不响应hover
    if(m_widgetOperator!=WidgetOperator::None || m_widget->isFullScreen())
        return false;
    //hover move
    auto areatype = CursorTransform::calculateCursorType(m_widget->size(),event->pos());
    auto w=static_cast<QWidget *>(watched);
    if(w)
        w->setCursor(CursorTransform::AreaType2CursorShape(areatype));
    else
        m_widget->setCursor(CursorTransform::AreaType2CursorShape(areatype));
    return true;
    //hover leave -> Qt::ArrowCursor
    //not need
}

bool FrameLessHelper::onDbClickEvent(QMouseEvent *event)
{
    if(event->button()!=Qt::LeftButton){
        return false;
    }
    auto areatype = CursorTransform::calculateCursorType(m_widget->size(),event->globalPos()-m_widget->pos());
    if(areatype==AreaType::TitleArea){
        if(m_widget->isFullScreen()){
            m_widget->showNormal();
        }
        else
        {
            m_widget->showFullScreen();
        }
#ifdef TITLEBAR
        m_titleBar->onMaxClick();
#endif
    }
    return true;
}

bool FrameLessHelper::onMousePressEvent(QMouseEvent *event)
{
    if(event->button()!=Qt::LeftButton){
        return false;
    }

    auto type = CursorTransform::calculateCursorType(m_widget->size(),event->globalPos()-m_widget->pos());

    if(type<AreaType::CenterArea){
        //resize
        m_widgetOperator = WidgetOperator::Resize;

        // not start show rect
        m_transparentRect->setRect(m_widget->frameGeometry());
        m_transparentRect->onMousePressEvent(event,type);

        return true;
    }
    else if(type==AreaType::TitleArea){
        //move
        m_widgetOperator = WidgetOperator::Move;
        if(!m_widget->isFullScreen())
            m_transparentRect->setRect(m_widget->geometry());
        else{
            m_transparentRect->setRect(calMaxMoveRect(event->globalPos()));
        }

        m_transparentRect->onMousePressEvent(event,type);
        return true;
    }
    return false;
}

bool FrameLessHelper::onMoveEvent(QMouseEvent *event)
{
    if(m_widgetOperator==WidgetOperator::None ||
        (m_widgetOperator == WidgetOperator::Resize && m_widget->isFullScreen())){
        return false;
    }

#ifdef TITLEBAR
    if(m_widget->isFullScreen())
        m_titleBar->onMaxClick();
#endif

    m_isMove =true;
    m_transparentRect->show();
    m_transparentRect->onMoveEvent(event,event->globalPos()-m_widget->pos());
    return false;
}

bool FrameLessHelper::onMouseReleaseEvent(QMouseEvent *event)
{
    if(event->button()!=Qt::LeftButton || m_widgetOperator==WidgetOperator::None){
        return false;
    }
    
    m_transparentRect->onMouseReleaseEvent(event);

	m_transparentRect->hide();
    if(m_isMove){
        if(m_widget->isFullScreen())
            m_widget->showNormal();
        auto &&rect = m_transparentRect->getRect();
		m_widget->move(rect.topLeft());
        m_widget->resize(rect.size());
    }
    m_isMove = false;
    m_widgetOperator = WidgetOperator::None;
    return false;
}

QRect FrameLessHelper::calMaxMoveRect(const QPoint &evPos)
{
    //最大化时拖动标题栏的规则:宽度分为3份,鼠标位于左侧则向左上角对齐,右侧则向右上角对齐,中间部分则按中间对齐
    auto rect = m_widget->normalGeometry();
    rect.moveTop(0);
    double gPosX=evPos.x();
    double step = m_widget->geometry().width()/3.0;
    double left=0;
    if(gPosX>step*2){
        left=step*3-rect.width();
    }
    else if(gPosX>step && gPosX<=step*2){
        left=gPosX-rect.width()/2.0;
    }
    rect.moveLeft(left);
    return rect;
}

#ifndef FRAMELESSHELPER_H
#define FRAMELESSHELPER_H
#include "transparentRect.h"
#include "titleBar.h"
#include "cursorTransform.h"

#include <QObject>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QRect>
#include <QWidget>

/*
FrameLessHelper
1.管理窗口无边框功能
2.输入为QWidget(构造)和QEvent(eventFilter)
*/
class FrameLessHelper : public QObject
{
    Q_OBJECT
public:
    FrameLessHelper(QWidget *widget);
    bool eventFilter(QObject *watched, QEvent *event) override;
    void setBorderThickness(int thickness);
    void setTitleHeight(int height);
    void setMinHeight(int height);
    void setMinWidth(int width);
#ifdef TITLEBAR
    QWidget *getTitleBar();

    void onMinClick();
    void onMaxClick();
    void onCloseClick();
#endif
private:
    bool onHoverEvent(QObject *watched,QHoverEvent *event);
    bool onDbClickEvent(QMouseEvent *event);
    bool onMousePressEvent(QMouseEvent *event);
    bool onMoveEvent(QMouseEvent *event);
    bool onMouseReleaseEvent(QMouseEvent *event);

    QRect calMaxMoveRect(const QPoint &evPos);
    //代理窗口
    QWidget *m_widget;
    //预拖动窗口
    TransparentRect *m_transparentRect;
    enum WidgetOperator m_widgetOperator;
    bool m_isMove;
#ifdef TITLEBAR
    TitleBar *m_titleBar;
#endif
};
#endif // FRAMELESSHELPER_H
#include "titleBar.h"
#include <QIcon>
#include <QPalette>

int iconSize=30;


TitleBar::TitleBar(QWidget *parent,bool isMax)
    : QWidget{parent}
    , m_minBtn{new QPushButton(this)}
    , m_maxBtn{new QPushButton(this)}
    , m_closeBtn{new QPushButton(this)}
    , m_hLayout{new QHBoxLayout(this)}
    , m_spacer{new QSpacerItem(500,30,QSizePolicy::Expanding)}
    , m_isMax{isMax}
{
    m_minBtn->setIcon(QIcon(":/img/min.png"));
    QString maxPic = m_isMax?":/img/normal.png":":/img/max.png";
    m_maxBtn->setIcon(QIcon(maxPic));
    m_closeBtn->setIcon(QIcon(":/img/close.png"));

    connect(m_minBtn,&QPushButton::clicked,this,&TitleBar::minimumSizeClick);
    connect(m_maxBtn,&QPushButton::clicked,this,&TitleBar::showNormalClick);
    connect(m_maxBtn,&QPushButton::clicked,this,&TitleBar::onMaxClick);
    connect(m_closeBtn,&QPushButton::clicked,this,&TitleBar::closeClick);


    m_minBtn->resize(iconSize,iconSize);
    m_maxBtn->resize(iconSize,iconSize);
    m_maxBtn->resize(iconSize,iconSize);


    m_hLayout->addSpacerItem(m_spacer);
    m_hLayout->addWidget(m_minBtn);
    m_hLayout->addWidget(m_maxBtn);
    m_hLayout->addWidget(m_closeBtn);
    m_hLayout->setSpacing(0);
    m_hLayout->setContentsMargins(0,0,0,0);

    QPalette palette(this->palette());
    palette.setColor(QPalette::Background, QColor(100,100,100));
    this->setAutoFillBackground(true);
    this->setPalette(palette);
}

void TitleBar::onMaxClick()
{
    if(m_isMax){
        m_maxBtn->setIcon(QIcon(":/img/max.png"));
        m_isMax=false;
    }
    else{
        m_maxBtn->setIcon(QIcon(":/img/normal.png"));
        m_isMax=true;
    }
}
#ifndef TITLEBAR_H
#define TITLEBAR_H

#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include <QSpacerItem>


class TitleBar : public QWidget
{
    Q_OBJECT
public:
    explicit TitleBar(QWidget *parent = nullptr,bool isMax=false);
    void onMaxClick();
signals:
    void minimumSizeClick();
    void showNormalClick();
    void closeClick();

private:
    QPushButton *m_minBtn;
    QPushButton *m_maxBtn;
    QPushButton *m_closeBtn;

    QHBoxLayout *m_hLayout;
    QSpacerItem *m_spacer;
    bool m_isMax;
};

#endif // TITLEBAR_H
#include "transparentRect.h"
#include "framelessData.h"
#include <QMouseEvent>
#include <QDesktopWidget>
#include <QDebug>
#include <QPainter>
#include <QPen>
#include <QScreen>
#include <QGuiApplication>
#include <QList>




TransparentRect::TransparentRect(QWidget *parent)
    : QWidget(parent)
    , m_areaType(AreaType::None)
{
    setWindowFlag(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);
}

void TransparentRect::setRect(const QRect &rect)
{
    m_rect=rect;
}

QRect TransparentRect::getRect() const
{
    return m_rect;
}

void TransparentRect::onMousePressEvent(QMouseEvent *event,AreaType type)
{
    m_lastPos = event->pos();
    //
    auto screens = QGuiApplication::screens();
    QRect rect;
	//QPainter painter(&m_pixmap);
    for(auto s:screens){
		auto geom = s->geometry();
        rect |= geom;
		//auto pixmap = s->grabWindow(0, geom.x(), geom.y(), geom.width(), geom.height());
		//static int i = 0;
		//QFile file(QString("./test%1.png").arg(i++));
		//file.open(QIODevice::WriteOnly);
		//pixmap.save(&file);
		//file.close();
		//painter.drawPixmap(s->geometry(), pixmap);
    }
	//painter.end();
	m_startInch = QGuiApplication::screenAt(event->globalPos())->logicalDotsPerInch();
    setGeometry(rect);
	m_pixmap= QGuiApplication::primaryScreen()->grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height());
    m_areaType = type;

#if  0
	//
	QFile file("./test.png");
	file.open(QIODevice::WriteOnly);
	m_pixmap.save(&file);
	file.close();
#endif //  0
}

void TransparentRect::onMoveEvent(QMouseEvent *event,const QPoint &newPos)
{
	
	m_scaled = QGuiApplication::screenAt(event->globalPos())->logicalDotsPerInch()/ m_startInch;

//    auto newPos = event->pos();
    int xOff=0,yOff=0,wOff=0,hOff=0;
    int posXOff=newPos.x()-m_lastPos.x();
    int posYOff = newPos.y()-m_lastPos.y();
    switch (m_areaType) {
    case AreaType::LeftTopArea:
        xOff = posXOff;
        yOff = posYOff;
        break;
    case AreaType::TopArea:
        yOff = posYOff;
        break;
    case AreaType::RightTopArea:
        yOff = posYOff;
        wOff = posXOff;
        break;
    case AreaType::LeftArea:
        xOff = posXOff;
        break;
    case AreaType::RightArea:
        wOff = posXOff;
        break;
    case AreaType::LeftBottomArea:
        xOff = posXOff;
        hOff = posYOff;
        break;
    case AreaType::BottomArea:
        hOff = posYOff;
        break;
    case AreaType::RightBottomArea:
        wOff = posXOff;
        hOff = posYOff;
        break;
    case AreaType::TitleArea:   //move
        m_rect.moveTopLeft(m_rect.topLeft()+QPoint(posXOff,posYOff));
        break;
    default:
        break;
    }
    //min judget:
    if(m_rect.width()-xOff<=FrameLessData::minWidth)
        xOff=0;
    else if(m_rect.left()<event->globalPos().x() && xOff<0)
        xOff=0;
    if(m_rect.width()+wOff<=FrameLessData::minWidth)
        wOff=0;
    else if(m_rect.right()>event->globalPos().x() && wOff>0)
        wOff=0;

    if(m_rect.height()-yOff<=FrameLessData::minHeight)
        yOff=0;
    else if(m_rect.top()<event->globalPos().y() && yOff<0)
        yOff=0;
    if(m_rect.height()+hOff<=FrameLessData::minHeight)
        hOff=0;
    else if(m_rect.bottom()>event->globalPos().y() && hOff>0)
        hOff=0;

    //resize:
    m_rect.setTop(m_rect.top()+yOff);
    m_rect.setLeft(m_rect.left()+xOff);
    m_rect.setWidth(m_rect.width()+wOff);
    m_rect.setHeight(m_rect.height()+hOff);
    m_lastPos = newPos;
    update();
}

void TransparentRect::onMouseReleaseEvent(QMouseEvent *event)
{
    m_areaType=AreaType::None;
	//释放pixmap,
	m_pixmap = QPixmap();



	//重绘,触发paintEvent,否则会有闪动,
	//注意要在hide之前调用。
	repaint();
}

void TransparentRect::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
	painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing | QPainter::Antialiasing);
    painter.setBackground(FrameLessData::transparentRectBkground);
	painter.drawPixmap(0, 0, m_pixmap);
    QPen pen(FrameLessData::borderStyle);
    pen.setWidth(FrameLessData::borderThickness);
    pen.setColor(FrameLessData::borderColor);
    painter.setPen(pen);
	//背景色取反,要求color必须是white
    painter.setCompositionMode(QPainter::RasterOp_SourceAndNotDestination);
    //rect位置相对与this的geometry
    auto rect=m_rect;
    rect.moveTopLeft(rect.topLeft()-geometry().topLeft());

	rect.setWidth(rect.width()*m_scaled);
	rect.setHeight(rect.height()*m_scaled);

    painter.drawRect(rect);
    painter.end();
}
#ifndef TRANSPARENTRECT_H
#define TRANSPARENTRECT_H
#include <QWidget>
#include "framelessData.h"

class TransparentRect : public QWidget
{
    Q_OBJECT
public:
    TransparentRect(QWidget *parent=nullptr);

    void setRect(const QRect &rect);
    QRect getRect() const;
    void onMousePressEvent(QMouseEvent *event,AreaType type);
    void onMoveEvent(QMouseEvent *event,const QPoint &pos);
    void onMouseReleaseEvent(QMouseEvent *event);
protected:
    virtual void paintEvent(QPaintEvent *event) override;
private:

    QRect m_rect;

    QPoint m_lastPos;
    AreaType m_areaType;
    QPixmap m_pixmap;
	double m_scaled=1.0;
	double m_startInch;
};

#endif // TRANSPARENTRECT_H
cmake_minimum_required(VERSION 3.5)

project(frameLessHelper VERSION 0.1 LANGUAGES CXX)


add_definitions(-DTITLEBAR)
#add_definitions(-DRUBBER)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets WebEngineWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets)

file(GLOB  PROJECT_SOURCES  "*.cpp" "*.h" "*.ui" "framelessHelper/*.*")

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(frameLessHelper
        MANUAL_FINALIZATION
        ${PROJECT_SOURCES}
    )
# Define target properties for Android with Qt 6 as:
#    set_property(TARGET frameLessHelper APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
#                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
    if(ANDROID)
        add_library(frameLessHelper SHARED
            ${PROJECT_SOURCES}
        )
# Define properties for Android with Qt 5 after find_package() calls as:
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
    else()
        add_executable(frameLessHelper
            ${PROJECT_SOURCES}
            framelessHelper/titleBarImg.qrc
            webview.h webview.cpp



        )
    endif()
endif()

target_link_libraries(frameLessHelper PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::WebEngineWidgets)

set_target_properties(frameLessHelper PROPERTIES
    MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

install(TARGETS frameLessHelper
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(frameLessHelper)
endif()
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QGridLayout>
#include <QWebEngineView>
#include <QFrame>
#include "webview.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , m_frameHelper(new FrameLessHelper(this))
{
    ui->setupUi(this);
    setWindowFlags(Qt::FramelessWindowHint);
    installEventFilter(this);
#ifdef TITLEBAR
    //titleBar
    auto titleBar = m_frameHelper->getTitleBar();
    if(titleBar){
        QGridLayout *layout = new QGridLayout(centralWidget());
        layout->setSpacing(0);
        layout->setContentsMargins(0,0,0,0);
        layout->addWidget(titleBar,0,0,0,0,Qt::AlignTop);
        titleBar->setFixedHeight(32);

#if 0
        WebView *view = new WebView();
        view->load(QUrl("https:www.baidu.com"));
        auto childs =  view->children();
        for(auto child:childs){
//            if(child->metaObject()->className()==QString("QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget"))
                child->installEventFilter(this);
        }
        view->installEventFilter(this);
#else
        QFrame *view = new QFrame();
#endif
        layout->addWidget(view,1,0);
    }

    setMouseTracking(true);
#endif
}

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

bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    return m_frameHelper->eventFilter(watched,event);
}

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "framelessHelper/framelessHelper.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    bool eventFilter(QObject *watched, QEvent *event) override;
private:
    Ui::MainWindow *ui;
    FrameLessHelper *m_frameHelper;
};
#endif // MAINWINDOW_H

 源码:

https://download.csdn.net/download/weixin_40953784/88248380

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/927095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

张驰咨询:六西格玛培训哪家好?2024年最新推荐榜单来了!

随着2024年的到来&#xff0c;越来越多的企业和个人开始关注六西格玛&#xff08;6σ&#xff09;管理&#xff0c;以提高工作效率和质量。那么&#xff0c;在众多提供六西格玛培训的机构中&#xff0c;哪家好呢&#xff1f;张驰咨询将为您详细解析。 一、六西格玛培训背景 六…

vue项目引入svg组件全过程

文件格式 svg下方对应 .svg index.vue svg-icon 组件 <template><svg:viewBox"viewBox"xmlns"http://www.w3.org/2000/svg"xmlns:xlink"http://www.w3.org/1999/xlink"ref"svg"class"svg-icon":class"class…

Java 小白也能学会OOM内存溢出问题 排查分析

前言 最近在学习群里面&#xff0c;有聊到近几天排查生产问题上面的OOM事故。 有兄弟私聊问到怎么看。 其实非常简单&#xff0c;但是我想了下是不是有很多人没接触过&#xff1f;或者是望而生畏&#xff1f; 那么&#xff0c;就来做个简单的小教程示例吧。 正文 简单写个Us…

深层次分析字符数组和字符串的区别是什么?

前言 &#xff08;1&#xff09;休闲时刻刷B站&#xff0c;看到一个卖课的&#xff0c;发视频问&#xff0c;char arr1[]{‘H’,‘E’,‘L’,‘L’,‘O’};和char arr2[]“HELLO”;区别是什么。 &#xff08;2&#xff09;看那个卖课博主一顿分析&#xff0c;最后成功得出&…

2.linux字符设备

目录 设计字符设备 文件系统调用系统IO的内核处理过程 硬件层原理 驱动层原理 文件系统层原理 设备号的组成与哈希表 Hash Table&#xff08;哈希表、散列表&#xff0c;数组和链表的混合使用&#xff09; 设备号管理 关键的数据结构&#xff1a;char_device_struct&a…

每日一题:leetcode 1448 统计二叉树中好节点的数目

给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。 「好节点」X 定义为&#xff1a;从根到该节点 X 所经过的节点中&#xff0c;没有任何节点的值大于 X 的值。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,3,null,1,5] 输出&#xff1a;4 解释&a…

kafak消费数据,webSocket实时推送数据到前端

1.导入webSocket依赖 <!--websocket依赖包--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 2.编写webSocket类 package com.skyable.device.co…

远程连接虚拟机中ubuntu报错:Network error:Connection refused

ping检测一下虚拟机 可以ping通&#xff0c;说明主机是没问题 #检查ssh是否安装&#xff1a; ps -e |grep ssh发现ssh没有安装 #安装openssh-server sudo apt-get install openssh-server#启动ssh service ssh startps -e |grep ssh检查一下防火墙 #防火墙状态查看 sudo ufw…

工控机驱动自助检票机,打造轨道交通的智慧未来!

随着城市化进程的加速和人口的不断增长&#xff0c;城市轨道交通建设正日益成为解决交通拥堵、提高交通工作效率的重要举措。然而&#xff0c;仅仅依靠传统的交通设施已经无法满足城市发展的需求&#xff0c;轨道交通智能系统建设成为了不可忽视的发展趋势。 AFC&#xff0c;即…

数据分享|R语言PCA主成分、lasso、岭回归降维分析近年来各国土地面积变化影响...

全文链接&#xff1a;http://tecdat.cn/?p31445 机器学习在环境监测领域的应用&#xff0c;着眼于探索全球范围内的环境演化规律&#xff0c;人类与自然生态之间的关系以及环境变化对人类生存的影响&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 课题着眼于…

电脑显示“Operating System not found”该怎么办?

“Operating System not found”是一种常见的电脑错误提示&#xff0c;这类错误会导致你无法成功启动Windows。那么电脑显示“Operating System not found”该怎么办呢&#xff1f; 方法1. 检查硬盘 首先&#xff0c;您可以测试硬盘是否存在问题。为此&#xff0c;您可以采取以…

.NET敏捷开发框架-RDIFramework.NET V6.0发布

1、RDIFramework.NET 敏捷开发框架介绍 RDIFramework.NET敏捷开发框架&#xff0c;是我司重磅推出的基于最新.NET6与.NET Framework的快速信息化系统开发、整合框架&#xff0c;为企业快速构建跨平台、企业级的应用提供了强大支持。 开发人员不需要开发系统的基础功能和公共模…

CentOS7安装jq命令

1. 安装依赖 yum install gmp-devel mpfr-devel libmpc-devel -y2. 安装gcc 2.1 离线环境 wget https://ftp.gnu.org/gnu/gcc/gcc-10.3.0/gcc-10.3.0.tar.gz tar -xzf gcc-10.3.0.tar.gz编译安装 yum -y install gcc c --skip-broken./configure --disable-multilib --enab…

Rust处理JSON

基本操作 Cargo.toml: [package]name "json"version "0.1.0"edition "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]serde { version "1", features …

uniapp小程序位置信息配置

uniapp 小程序获取当前位置信息报错 报错信息&#xff1a; getLocation:fail the api need to be declared in the requiredPrivateInfos field in app.json/ext.json 需要在manifest.json配置文件中进行配置&#xff1a;

喜欢单片机?嵌入式高薪在招手!

嵌入式技术作为热门行业之一&#xff0c;近年来得到了广泛的关注和追捧。在众多嵌入式技术中&#xff0c;单片机技术因其小巧、低功耗和强大性能而备受青睐。下面我们将探讨为何喜欢单片机&#xff0c;以及嵌入式领域高薪工作的前景。 作为嵌入式系统的核心&#xff0c;单片机具…

【java】LinkedList 和 ArrayList的简介与对比

Java LinkedList和 ArrayList 在使用上&#xff0c;几乎是一样的。由于LinkedList是基于双向链表的&#xff0c;会多出list.getFirst();获取头部元素等方法 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按…

24 WEB漏洞-文件上传之WAF绕过及安全修复

目录 WAF绕过上传参数名解析:明确哪些东西能修改?常见绕过方法&#xff1a;符号变异-防匹配( " ;)数据截断-防匹配(%00 ; 换行)重复数据-防匹配(参数多次)搜索引擎搜索fuzz web字典文件上传安全修复方案 WAF绕过 safedog BT(宝塔) XXX云盾 宝塔过滤的比安全狗厉害一些&a…

无涯教程-进程 - 创建终止

到现在为止&#xff0c;我们知道无论何时执行程序&#xff0c;都会创建一个进程&#xff0c;并且该进程将在执行完成后终止&#xff0c;如果我们需要在程序中创建一个进程&#xff0c;并且可能希望为其安排其他任务&#xff0c;该怎么办。能做到吗?是的&#xff0c;显然是通过…

测试神器!RunnerGo让你的测试工作更高效!

引言&#xff1a;在软件开发领域&#xff0c;测试是非常重要的一环。然而&#xff0c;传统的测试工具往往复杂且难以使用&#xff0c;让测试工作变得异常繁琐。为了解决这一问题&#xff0c;我们迎来了RunnerGo——一款轻量级、全栈式的测试平台&#xff0c;让你的测试工作更加…