[Qt6][QML][教程]QML创建一个漂亮的音乐显示弹窗

1. 前言

以前的弹窗效果有些看腻了,这次更新下界面。

教程被应用在MediaStateT中, Tag v1.5

MediaStateT Github项目地址: https://github.com/taxue-alfred/MediaStateT

MediaStateT Gitee项目地址: https://gitee.com/MediaState/MediaStateT

2. 效果展示

以前界面:

目前界面:

窗口绘制:

  • PopWindow使用Loader控件进行了模块化,非常好用,可以随便在Loader里面添加控件

需要注意的是,添加到Loader中的控件id不能被外部所访问,所以建议在外部先加载好控件然后传递到Loader

  • 直接在source_component添加Conponent控件即可

需要注意的是Component只能有一个控件,所以建议先创建一个大的Rectangle然后再在Rectangle里面创建其他控件,最后传递Component也就传递了Rectangle及其整个界面

  • 文章最后有调用示例,可以参考

3. 代码参考

这次只是相较于上次添加了一些组件和虚化效果,还有主函数的调用逻辑都进行了更新。

整体的代码不是很难,必要的注释都写在代码里了。需要注意的是:

  1. PopWindow.qmlWindow属性的x y 可以被外部调用的时候被覆盖.
  2. 窗口的大小根据Loader里面content_loader的内容大小改变
  3. sign这个变量只是一个标志,为了防止动画被多次执行。
PopWindow.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window

Window{
    id: pop_window
    visible: true
    visibility: Window.Windowed
    color: "transparent"
    // 透明度
    opacity: 0
    // 取消边框
    flags:Qt.FramelessWindowsHint | Qt.ToolTip
    // 设置为非模态
    modality: Qt.NonModal
    //设置初始位置(外部设置会覆盖此设置)
    x: Screen.width - content_loader.width
    y: 100
    //根据Loader设置大小
    width: content_loader.width
    height: content_loader.height

    MouseArea{
        id:content_mouse
        anchors.fill: parent
        hoverEnabled: true
    }

    // 设置出现后显示时间的计时器
    Timer{
        id:show_timer
        interval: 2500
        repeat: true
        onTriggered:{
            //这个if实现了鼠标悬停保留窗口的效果
            if(!loader_mouse.containsMouse){
                hideWindow();
            }
        }
    }

    //绘制图形的Loader,供外部使用
    property alias source_component:content_loader.sourceComponent
    Loader{
        id:content_loader
        MouseArea{
            id:loader_mouse
            anchors.fill:parent
            hoverEnabled:true
        }
    }

//---------------------------动画部分开始-------------------
    //设置出现显示动画相对于设置的x值的偏移位置,这里变量外部进行了设置
    property int x_offset: 0
    property int x_origin: 0
    //设置sign标志,防止多次showWindow调用造成多次动画显示
    property int sign: 0
    ParallelAnimation{
        id: show_anim
        // 透明度动画
        PropertyAnimation{
            target:pop_window
            property: "opacity"
            easing.type: Easing.InQuart
            from:pop_window.opacity
            to: 1
            duration:350
        }
        // 位置移动动画
        PropertyAnimation{
            target:pop_window
            property: "x"
            easing.type: Easing.OutBounce
            //从当前值开始移动
            from: pop_window.x
            to: Screen.width - content_loader.width - x_offset
            duration:800
        }
        // 大小缩放动画
        PropertyAnimation{
            target:pop_window
            property: "width"
            easing.type: Easing.OutCirc
            //从当前值开始移动
            from: 0
            to: content_loader.width
            duration:200
        }
        // 大小缩放动画
        PropertyAnimation{
            target:pop_window
            property: "height"
            easing.type: Easing.OutCirc
            //从当前值开始移动
            from: 0
            to: content_loader.height
            duration:200
        }

        onStarted:{
            pop_window.show()
        }
        //出现动画结束信号
        onFinished:{
            show_timer.restart()
        }
    }
    //设置关闭显示动画
    ParallelAnimation{
        id: hide_anim
        // 透明度动画
        PropertyAnimation{
            target:pop_window
            property: "opacity"
            easing.type: Easing.OutCubic
            from:pop_window.opacity
            to: 0
            duration:800
        }
        //位置移动动画
        PropertyAnimation{
            target:pop_window
            property: "x"
            easing.type: Easing.InExpo
            //从当前值开始移动
            from: pop_window.x
            to: x_origin
            duration:800
        }
        //大小缩放动画
        PropertyAnimation{
            target:pop_window
            property: "width"
            easing.type: Easing.InQuint
            //从当前值开始移动
            from: content_loader.width + 10
            to: 0
            duration:1000
        }
        //大小缩放动画
        PropertyAnimation{
            target:pop_window
            property: "height"
            easing.type: Easing.InQuint
            //从当前值开始移动
            from: content_loader.height + 10
            to: 0
            duration:1000
        }
        //结束动画结束之后停止定时器
        onFinished:{
            show_timer.stop()
        }
    }

    //显示弹窗
    function showWindow(){
        if(sign === 0){
            hide_anim.stop()
            show_anim.start()
            sign = 1
        }
    }

    //隐藏弹窗
    function hideWindow(){
        if(sign === 1){
            show_anim.stop()
            hide_anim.start()
            sign = 0
        }
    }
//---------------------------动画部分结束-------------------
}

调用:

media_stateT.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window
import Qt5Compat.GraphicalEffects
import Qt.labs.platform

import qt.txwh.MT_info_get 1.0

Item{
    //绘制窗口,然后传到PopWindow模块的Loader里
    Component{
        id:window_draw

        Rectangle{
            id:bk_rectangle
            width:200
            height:70
            radius:10
            color: Qt.rgba(0.8,0.8,0.8,0.95)

            Image{
                id:img_background
                width:bk_rectangle.width - 40
                height:bk_rectangle.height - 16
                source:"/icon/MtIcon.jpg"
                smooth: true
                mipmap:true
                cache:false
                visible:false
                fillMode: Image.PreserveAspectCrop
                anchors.verticalCenter: parent.verticalCenter
                anchors.horizontalCenter : parent.horizontalCenter
                anchors.right: parent.right
                anchors.rightMargin: 6
            }

            Rectangle {
                id: img_bck_mask
                width: img_background.width
                height: img_background.height
                radius: 10
                color: "red"
                visible: false
            }

            OpacityMask {
                anchors.fill: img_background
                source: img_background
                maskSource: img_bck_mask
            }

            FastBlur {
                anchors.fill: img_background
                source: img_background
                radius: 32
                transparentBorder:true
            }

            Image {
                id: img
                width: 60
                height: 60
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin:6
                source: "/icon/MtIcon.jpg"
                sourceSize.width:1024
                sourceSize.height:1024
                smooth: true
                mipmap:true
                visible: false
                cache:false
            }

            Rectangle {
                id: img_mask
                width: img.width
                height: img.height
                radius: 10
                color: "red"
                visible: false
            }

            OpacityMask {
                anchors.fill: img
                source: img
                maskSource: img_mask
            }

            TextScroll{
                id:music_name_label
                width:110
                anchors.verticalCenter: parent.verticalCenter
                anchors.verticalCenterOffset: -16
                anchors.left:img.right
                anchors.leftMargin:10
                fontPixelSize:17
                text:qsTr("Music Name")
            }

            TextScroll{
                id:player_name_label
                width:110
                anchors.verticalCenter: parent.verticalCenter
                anchors.verticalCenterOffset: 16
                anchors.left:img.right
                anchors.leftMargin:10
                fontPixelSize:13
                text:qsTr("Player")
            }

            //获取信息类,这是一个C++类
            MT_info_get{
                id:mt_info_get
                //存储上一次的history时间信息,用string是为了防止数字过大造成溢出
                property string last_file_time;
                //这边从C++传来的值进行赋值的时候需要写function()
                //这里QT6和Qt5的Quick有些不同
                //这样子加个function可以很明显的标识这是由emit传来的signal的参数
                //详细链接可以参考这个: https://www.direktembedded.com/qt5-to-qt6-qml-pyside-porting/
                onNe_file_time_got:
                    function(temp_time){
                        if(temp_time != last_file_time){
                            //获取信息
                            mt_info_get.music_info_get()
                            mt_info_get.player_info_get()
                            mt_info_get.img_download()
                            //显示窗口
                            pop_window.showWindow()
                            last_file_time = temp_time
                    }
                }

                onMusic_info_got:
                    function(music_name){
                        music_name_label.text = music_name
                        //重新获取图片
                        mt_info_get.img_download()
                        //由于QtImage存在缓存机制,需要cache:false并且先置空路径再设置路径才会加载图片,不改变路径直接调用上次的缓存
                        img.source = ""
                        img.source = "file:./album.jpg"
                        img_background.source = ""
                        img_background.source = "file:./album.jpg"
                    }

                onPlayer_info_got:
                    function(player_name){
                        player_name_label.text = player_name
                    }

                onAutostart_status:
                    function(value){
                        if (value){
                            menu_right.sws.checked = true
                        }else{
                            menu_right.sws.checked = false
                        }
                    }

                onHot_key_activated:
                    function(value){
                        if(value === 1){
                            pop_window.showWindow()
                        }else if(value === 2){
                            mt_fre.showWindow()
                        }else if(value === 3){
                            mtBoardConnect.init();
                            menu_right.connectMSBoardState = true;
                        }
                    }

                onGot_mouse_cursor:
                    //Qt6用法。qt5可忽略function
                    function(x,y,scale){
                        menu_right.menu_x = x / scale
                        menu_right.menu_y = y / scale
                        menu_right.show()
                        //展现在最上层
                        menu_right.raise()
                    }

                //获取网易云音乐history的修改时间函数
                function ne_file_time(){
                    mt_info_get.NE_file_time()
                }
            }

            // 循环更新网易云歌曲等参数
            Timer{
                interval:500
                repeat:true
                running:true
                onTriggered:{
                    mt_info_get.ne_file_time();
                }
            }

            SystemTrayIcon{
                id:system_icon
                visible: true
                icon.source: "qrc:/icon/MtIcon.jpg"
                tooltip:"MediaStateT"

                onActivated:
                    //reason是SystemTrayIcon固有属性,不同数值对应不同的按键事件
                    //来源:https://gist.github.com/kviktor/
                    function(reason){
                        //双击,单机,2,3,右键1
                        if(reason === 2 || reason === 3){
                            //显示窗口
                            pop_window.showWindow()
                        }else if(reason === 1){
                            //定义过的槽函数,执行操作后,然后发射到get_mouse_cursor
                            mt_info_get.get_mouse_cursor()
                        }
                    }
            }

            PageMenu{
                id:menu_right

                onSws_click:{
                    if(menu_right.sws.checked){
                        mt_info_get.create_sws()
                        // 再次进行状态检测确保添加成功
                        mt_info_get.check_start_with_system()
                    }else{
                        mt_info_get.remove_sws()
                        mt_info_get.check_start_with_system()
                    }
                }

                Component.onCompleted: mt_info_get.check_start_with_system()

                onSw_click:{
                    pop_window.showWindow()
                }

                onSf_click:{
                    mt_fre.showWindow()
                }
            }
        }
    }

    PopWindow{
        id:pop_window
        // 设置初始位置,对PopWindow里面的x,y进行了覆盖
        x_offset: get_screen_pixel(0.005, Screen.width)
        x: get_screen_pixel(0.94, Screen.width) //设置0.94不设置1是为了防止不同缩放下屏幕边缘乱跳的问题
        y: get_screen_pixel(0.13, Screen.height)
        x_origin: get_screen_pixel(0.94, Screen.width)
        source_component:window_draw

        // 为了适应不同的屏幕,需要使用百分比表示
        function get_screen_pixel(percent, sum_pixel){
            return percent * sum_pixel
        }
    }

    PageAudioFrequency{
        id:mt_fre
    }
}

参考:

  1. QML实现桌面右下角弹窗
  2. PropertyAnimation QML Type
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你回答这个问题。 首先,在QML中,我们可以使用Canvas元素来绘制2D图形。要创建一个网格画布,我们需要使用Canvas的API来绘制网格线。 其次,我们需要通过使用MouseArea元素来监听鼠标滚轮事件,以实现画布的放大和缩小。 以下是一个简单的实现示例: ```qml import QtQuick 2.0 Item { width: 400 height: 400 property int gridSize: 20 property int scaleFactor: 100 Canvas { id: canvas anchors.fill: parent onPaint: { var ctx = getContext("2d"); var width = canvas.width; var height = canvas.height; // 清除画布 ctx.clearRect(0, 0, width, height); // 绘制网格线 ctx.strokeStyle = "black"; for (var x = 0; x <= width; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); } for (var y = 0; y <= height; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } } } MouseArea { anchors.fill: parent onWheel: { // 根据滚轮事件的delta属性,计算缩放比例 scaleFactor += wheel.delta / 120; canvas.width = width * scaleFactor / 100; canvas.height = height * scaleFactor / 100; canvas.requestPaint(); } } } ``` 在这个示例中,我们创建了一个Canvas元素,并在其onPaint信号处理函数中绘制了网格线。我们还使用了MouseArea元素来监听鼠标滚轮事件,并根据事件的delta属性计算缩放比例,以实现画布的放大和缩小。 注意,这个示例中的缩放比例是通过scaleFactor属性来控制的,它的默认值为100。你可以根据自己的需要来调整这个值。 希望这个示例对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值