• Qt创建异形窗口


      

      好久没来了更新了,今天使用Qt做了个异形窗口,这里记录下,效果如下。

    老规矩,开发环境:

    System:win10x64

    Qt version:Qt 5.14.0

    编译器:MINGW_64

    Author : 点缀

      首先创建一个QWidget的项目,类名自己随便写,基类是继承QWidget,因为窗口是四方的,我们需要把窗口设置成图片的形状。代码如下:

      

    QPixmap pixmap("../res/t6.jpg",nullptr,Qt::AvoidDither|Qt::ThresholdDither|Qt::ThresholdAlphaDither);
    
    resize(pixmap.size());
    setMask(pixmap.mask());
    setWindowFlags(Qt::FramelessWindowHint
    | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint);
    首先在构造函数里,将图片加载进QPixmap,resize函数将窗口大小设置成图片大小。然后利用setMask函数设置窗口的背景掩码,最后一步很重要,就是设置下窗口的属性:无边框,无标题栏,无最大最小化按钮。

    到了这里,窗口就能变成你选择的图片的样子了,但是还没结束,我们还要做三点:一、因为我们取消了最大最小化按钮,所以窗口现在关不了了,所以我们得写下右键弹出菜单。二、让图片动起来(也可以不动,但我选择的是一个球体,动起来比较好看一点吧,哈哈)。
    三、能移动窗口

    一、添加右键菜单
    这个功能很简单,直接重写右键菜单函数:
    void contextMenuEvent(QContextMenuEvent *event) override;
    先设置私有成员变量:
        QAction* act_start = nullptr;
    
    
        QAction* act_stop = nullptr;
    
    
        QAction* act_close = nullptr;
    
    
        QMenu* menu = nullptr;

    接着在contextMenuEvent函数中添加和设置菜单:
    void Widget::contextMenuEvent(QContextMenuEvent *event)
    {
        Q_UNUSED(event)
        if(!menu)
        {
            menu = new QMenu(this);
        //为菜单设置样式,可以设置成自己喜欢的风格样式,感觉在写前端一样,哈哈。 menu
    ->setStyleSheet("QMenu{background-color:#ABABAB;border:1px solid black;margin:2px;}" "QMenu::item{padding: 2px 25px 2px 20px;margin:1px;border: 1px solid black;border-radius:5px;}" "QMenu::item:selected{background-color:#654321;border-color:darkblue;}" );

        //下面三个action就是菜单项 act_start
    = menu->addAction("开始"); act_stop = menu->addAction("停止"); act_close = menu->addAction("关闭");

        //为菜单项关联信号与槽函数 connect(act_start,
    &QAction::triggered,this,&Widget::act_start_slot); connect(act_stop,&QAction::triggered,this,&Widget::act_stop_slot); connect(act_close,&QAction::triggered,this,&Widget::close); } menu->exec(QCursor::pos()); //这一步很重要,别忘了写,让菜单显示出来。 }

    //下面是两个槽函数的实现,最后的关闭函数close是窗口自带的,不需要我们写,直接用。
    void Widget::act_start_slot()
    
    
    {
    
    
        if(0 == timerId)
    
    
        {
    
    
            timerId = startTimer(100);
    
    
            act_start->setEnabled(false);
    
    
            act_stop->setEnabled(true);
    
    
        }
    
    
    }
    
    
    
    
    
    
    void Widget::act_stop_slot()
    
    
    {
    
    
        if(timerId)
    
    
        {
    
    
            killTimer(timerId);
    
    
            timerId = 0;
    
    
            act_start->setEnabled(true);
    
    
            act_stop->setEnabled(false);
    
    
        }
    
    
    }
     

    到这里,右键菜单及其功能都实现了。

    二、让星球转起来

      上面我们在实现右键菜单的时候,注意到我们使用了startTimer函数,这个函数是QWidget窗口自带的,我们并没有去new一个QTimer,然后为timer的timeout信号函数关联一个自己写的槽函数,并用connect去关联起来。我们也可以这样做,但是我不这样做的原因: 实现转动功能本身只需要一个定时器就够了,而且QWidget自带一个定时器,能偷懒就偷懒呗,哈哈。

      现在还有一个问题,既然上面使用了startTimer(100);那么意思就是每隔100ms就会触发定时器,那么定时器触发后去哪里实现超时函数呢?答案就是需要我们重写timerEvent函数:

    1 void Widget::timerEvent(QTimerEvent *e)
    2 {
    3     Q_UNUSED(e)
    4     angle+=5;
    5     if(angle>=360)
    6         angle = 0;
    7     update();
    8 
    9 }

    angle是我设置的成员变量,代表当前转动的角度,初始值设为0。也就是说每隔100ms,让角度转动5度(就是图片转动5度),当超过360度就重新从0开始计算,最后一个是更新显示函数update,就是我们主动去更新画面。既然使用了update函数,那么该函数会去执行paintEvent函数,所以我们得去实现它。

    void Widget::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event)
        QPainter painter(this);
        painter.setRenderHint(QPainter::SmoothPixmapTransform);
        painter.translate(width()/2,height()/2);
        painter.rotate(angle);
        painter.translate(-(width()/2),-(height()/2));
        painter.drawPixmap(0,0,QPixmap("../res/test.jpg"));
    }

    上面的paintEvent函数里主要就是一个设置了防锯齿效果setRenderHint,还有就是转换坐标系,我们知道窗口的坐标系起点是左上角,然而我们要转动星球,当然是以星球的中心为原点来旋转,所以使用translate函数来重新设定坐标系原点为窗口的中心,然后使用rotate函数来旋转一定角度,最后我们需要将坐标系原点重新设置为左上角来绘制图片。搞定!!!

    三、实现窗口的移动

    功能:鼠标左键按下后移动,窗口也随着鼠标移动。

    其实这个最简单了,无非就是实现(重写)鼠标的按下,移动和释放函数,直接上代码。

    void Widget::mouseMoveEvent(QMouseEvent *event)
    {
        if(ispressed)
        {
            move(event->globalPos() - point_deff);
        }
    
    }
    
    void Widget::mousePressEvent(QMouseEvent *event)
    {
        if(event->button() & Qt::LeftButton)
        {
            ispressed = true;
            point_deff = event->globalPos() - pos();
        }
    }
    
    void Widget::mouseReleaseEvent(QMouseEvent *event)
    {
        if(event->button() & Qt::LeftButton)
            ispressed = false;
    }

    这里稍微解释下,ispressed和point_deff是两个成员变量,分别记录鼠标是否按下和坐标差。

    point_deff是记录鼠标按下时,当前鼠标的全局坐标(相对于windows桌面的左上角)和当前窗口的全局坐标之差,说白了就是算出鼠标与当前窗口左上角的距离。然后当鼠标移动时,用鼠标的全局坐标减去point_deff就是当前窗口左上角的坐标位置了,使用move函数移动窗口就行了。

      终于记录完了,虽然只是写了一个小玩意,但里面涉及的点还是蛮多的,多玩玩就熟能生巧了,当然也可以根据自己的脑洞继续添加功能,比如星球的正反转啊之类的等等。

      不说了,老板喊我搬砖去了。溜了溜了,最后送上一个静态异形窗口小花花,哈哈。

      

     
  • 相关阅读:
    设计模式系列
    设计模式系列
    【ABAP系列】SAP ABAP DYNP_VALUES_UPDATE 更新屏幕字段的函数及用法
    【HANA系列】SAP HANA 2.0简介
    【HANA系列】SAP HANA Studio使用insufficient privilege 问题
    【HANA系列】SAP HANA Studio出现"Fetching Children..."问题
    【ABAP系列】SAP ABAP smartforms设备类型CNSAPWIN不支持页格式ZXXX
    【BW系列】SAP BW实时抽取ECC数据的实现
    【ABAP系列】SAP ABAP 行列转换的方法
    【HANA系列】SAP HANA数据处理的理解与分析一
  • 原文地址:https://www.cnblogs.com/dz-study/p/14747120.html
Copyright © 2020-2023  润新知