QML grabToImage() 对组件截图

1.文档学习

QML 的 Item 具有一个 grabToImage() 方法,可以抓取 Item 的内存图像。注意,这不是桌面截屏,是对 Item 截图,要截屏的话可以自己扩展 grabWindow() ,只可惜它不能直接在 QML 中使用。

grabToImage() 方法可接受两个参数,第一个为回调函数,第二个为size,不过一般size可以不传,保持原大小就行了。如果保存失败,返回false。文档里给了示例:

Rectangle {
    id: source
    width: 100
    height: 100
    gradient: Gradient {
        GradientStop { position: 0; color: "steelblue" }
        GradientStop { position: 1; color: "black" }
    }
}
// 调用 Item 的 grabToImage 进行截图
// grabToImage 或者 saveToFile 失败都会返回 false
    source.grabToImage(function(result) {
                           result.saveToFile("something.png");
                       });

文档还给了另一个示例,获取 Item 图像并在另一个 Image 元素中使用:

Image {
    id: image
}
// ...
source.grabToImage(function(result) { 
    image.source = result.url;},Qt.size(50, 50));

不过第二种用法效率低, 会将 Item 渲染到屏幕外表面,并将该表面从GPU内存复制到CPU内存(谷歌翻译的),这可能会非常昂贵。对于实时预览,文档推荐用 layers 或者 ShaderEffectSource 。

参考文档:https://doc.qt.io/qt-5.12/qml-qtquick-item.html#grabToImage-method

2.扩展,制作一个带遮罩的截图组件

设计思路,使用 ShaderEffectSource 获取目标 Item 被选定的矩形范围的图像,使用 grabToImage() 保存截图,再增加一个遮罩(遮罩由 Rectangle 组合)。对于保存路径,通过双击后弹出对话框来设置。效果如下,左侧为组件效果,右侧为保存的图片:

问题与想法:

  1. 遮罩写的不好,待改进;
  2. 截图组件要放在待截图 Item{ } 的外面,不然自己的遮罩影像也被捕获了;
  3. QtQuick.Dialogs 中的 FileDialog 需要设置路径,不然他会提示你给App设置("organizationName", "organizationDomain");
  4. 有些组件如果把 MouseArea 放在里面 或 上面的话,一些拖拽之类的会被偷取,如ScrollView、Map之类的,可以把 MouseArea 的 preventStealing 设置为 true 。

完整代码如下:

( github 链接:https://github.com/gongjianbo/QmlComponentStyle )

//QuickItemShot.qml
import QtQuick 2.12
import QtQuick.Dialogs 1.2

//框选截图工具
//使用时需要anchors.fill目标,并把目标赋值给shotTarget
//pop()初始化显示,close()隐藏
//注意要放在target Item的外部,不然截图组件的影像也被捕获了

//2019-12-21
//设置preventStealing: true防止鼠标事件被偷走
//如在ScrollView或Map中,MouseArea不能正常处理拖动效果
Item {
    id: control
    visible: false

    property int areaMinSize: 35
    property int indicatorWidth: 14
    property color indicatorColor: "red"
    property alias areaBorder: shot_area.border
    property alias areaColo: shot_area.color
    property alias centerText: center_text.text
    property alias textColor: center_text.color
    property alias textFont: center_text.font
    // 要截图的Item,通过ShaderEffectSource来获取目标区域
    property alias shotTarget: shot_shader.sourceItem

    // 尺寸改变时,避免超出范围
    onWidthChanged: {
        if(shot_area.x+shot_area.width>control.width){
            shot_area.width=control.width-shot_area.x;
        }
    }
    onHeightChanged: {
        if(shot_area.y+shot_area.height>control.height){
            shot_area.height=control.height-shot_area.y;
        }
    }

    function pop(){
        shot_area.x=0;
        shot_area.y=0;
        shot_area.width=control.width;
        shot_area.height=control.height;
        control.visible=true;
    }

    function close(){
        control.visible=false;
    }

    function requestshot(){
        shot_dailog.open();
    }

    function quickitemshot(filepath){
        shot_shader.sourceRect=Qt.rect(shot_area.x
                                       ,shot_area.y
                                       ,shot_area.width
                                       ,shot_area.height);
        if(shotTarget&&filepath){
            shot_shader.grabToImage(function(result){
                var saveresult=result.saveToFile(filepath);
                console.log("quickitemshot",saveresult,filepath);
            });
        }
    }

    FileDialog{
        id: shot_dailog
        visible: false
        title: "保存截图"

        //folder: shortcuts.home
        nameFilters: ["Image(*.png)"]
        //另存文件名
        selectExisting: false
        //确认
        onAccepted: {
            var selectpath=shot_dailog.fileUrl.toString();
            if(selectpath){
                //去掉url中的"file:///"
                control.quickitemshot(selectpath.replace(/^(file:\/{3})/,""));
            }
        }
        //取消
        //onRejected:
        //如果不设置文件路径,那么就要App设置("organizationName", "organizationDomain")
        settings.fileName: "dialog.ini"
    }

    //用于截图
    ShaderEffectSource{
        id: shot_shader
        visible: false
        anchors.fill: shot_area
        //数据源
        //sourceItem:
        //截图区域
        //sourceRect: shot_area.layer.sourceRect
    }

    //截屏区域
    //这里没有处理和窗口等比缩放
    Rectangle{
        id: shot_area
        implicitWidth: control.width
        implicitHeight: control.height

        color: Qt.rgba(0,1,0,0.3)
        border{
            width: 1
            color: "red"
        }

        Text{
            id: center_text
            anchors.centerIn: parent
            text: "双击保存截图"
            color: "red"
            font{
                pixelSize: 16
                weight: Font.Bold
            }
        }

        MouseArea{
            id: click_area
            anchors.fill: parent
            property int pressPointX: 0
            property int pressPointY: 0
            preventStealing: true
            onPressed: {
                pressPointX=mouse.x;
                pressPointY=mouse.y;
            }

            onDoubleClicked: control.requestshot();
            onPositionChanged: {
                //判断范围,贴边移动的情况需要细化处理
                if((shot_area.x+mouse.x-pressPointX>0)
                        &&(shot_area.x+shot_area.width+mouse.x-pressPointX<=control.width)){
                    shot_area.x+=mouse.x-pressPointX;
                }
                if((shot_area.y+mouse.y-pressPointY>0)
                        &&(shot_area.y+shot_area.height+mouse.y-pressPointY<=control.height)){
                    shot_area.y+=mouse.y-pressPointY;
                }
            }
        }

        //四角的四个拖动按钮
        Rectangle{
            id: btn_top_left
            width: control.indicatorWidth
            height: control.indicatorWidth
            radius: control.indicatorWidth/2
            color: control.indicatorColor
            anchors{
                left: parent.left
                top: parent.top
                margins: -radius
            }
            MouseArea{
                property int pressPosX: 0
                property int pressPosY: 0
                anchors.fill: parent
                preventStealing: true
                onClicked: {
                    pressPosX=mouse.x;
                    pressPosY=mouse.y;
                }
                onPositionChanged: {
                    var new_width=shot_area.width-mouse.x-pressPosX;
                    var new_height=shot_area.height-mouse.y-pressPosY;
                    if(new_width>=control.areaMinSize){
                        shot_area.width=new_width;
                        shot_area.x+=mouse.x-pressPosX;
                        if(shot_area.x<0){
                            shot_area.width+=shot_area.x;
                            shot_area.x=0;
                        }
                    }
                    if(new_height>=control.areaMinSize){
                        shot_area.height=new_height;
                        shot_area.y+=mouse.y-pressPosY;
                        if(shot_area.y<0){
                            shot_area.height+=shot_area.y;
                            shot_area.y=0;
                        }
                    }
                }
            }
        }
        Rectangle{
            id: btn_top_right
            width: control.indicatorWidth
            height: control.indicatorWidth
            radius: control.indicatorWidth/2
            color: control.indicatorColor
            anchors{
                right: parent.right
                top: parent.top
                margins: -radius
            }
            MouseArea{
                property int pressPosX: 0
                property int pressPosY: 0
                anchors.fill: parent
                preventStealing: true
                onClicked: {
                    pressPosX=mouse.x;
                    pressPosY=mouse.y;
                }
                onPositionChanged: {
                    var new_width=shot_area.width+mouse.x-pressPosX;
                    var new_height=shot_area.height-mouse.y-pressPosY;
                    if(new_width>=control.areaMinSize){
                        shot_area.width=new_width;
                        if(shot_area.x+shot_area.width>control.width){
                            shot_area.width=control.width-shot_area.x;
                        }
                    }
                    if(new_height>=control.areaMinSize){
                        shot_area.height=new_height;
                        shot_area.y+=mouse.y-pressPosY;
                        if(shot_area.y<0){
                            shot_area.height+=shot_area.y;
                            shot_area.y=0;
                        }
                    }
                }
            }
        }
        Rectangle{
            id: btn_bottom_left
            width: control.indicatorWidth
            height: control.indicatorWidth
            radius: control.indicatorWidth/2
            color: control.indicatorColor
            anchors{
                left: parent.left
                bottom: parent.bottom
                margins: -radius
            }
            MouseArea{
                property int pressPosX: 0
                property int pressPosY: 0
                anchors.fill: parent
                preventStealing: true
                onClicked: {
                    pressPosX=mouse.x;
                    pressPosY=mouse.y;
                }
                onPositionChanged: {
                    var new_width=shot_area.width-mouse.x-pressPosX;
                    var new_height=shot_area.height+mouse.y-pressPosY;
                    if(new_width>=control.areaMinSize){
                        shot_area.width=new_width;
                        shot_area.x+=mouse.x-pressPosX;
                        if(shot_area.x<0){
                            shot_area.width+=shot_area.x;
                            shot_area.x=0;
                        }
                    }
                    if(new_height>=control.areaMinSize){
                        shot_area.height=new_height;
                        if(shot_area.y+shot_area.height>control.height){
                            shot_area.height=control.height-shot_area.y;
                        }
                    }
                }
            }
        }
        Rectangle{
            width: control.indicatorWidth
            height: control.indicatorWidth
            radius: control.indicatorWidth/2
            color: control.indicatorColor
            anchors{
                right: parent.right
                bottom: parent.bottom
                margins: -radius
            }
            MouseArea{
                property int pressPosX: 0
                property int pressPosY: 0
                anchors.fill: parent
                preventStealing: true
                onClicked: {
                    pressPosX=mouse.x;
                    pressPosY=mouse.y;
                }
                onPositionChanged: {
                    var new_width=shot_area.width+mouse.x-pressPosX;
                    var new_height=shot_area.height+mouse.y-pressPosY;
                    if(new_width>=control.areaMinSize){
                        shot_area.width=new_width;
                        if(shot_area.x+shot_area.width>control.width){
                            shot_area.width=control.width-shot_area.x;
                        }
                    }
                    if(new_height>=control.areaMinSize){
                        shot_area.height=new_height;
                        if(shot_area.y+shot_area.height>control.height){
                            shot_area.height=control.height-shot_area.y;
                        }
                    }
                }
            }
        }
    }
}

使用方式;

//使用
    Button{
        width: 90
        height: 30
        text: "item shot"
        onClicked: {
            if(shot_area.visible){
                shot_area.close();
            }else{
                shot_area.pop();
            }
        }
    }

    QuickItemShot{
        id: shot_area
        shotTarget: item_map //要抓取的Item
        anchors.fill: item_map //范围
        anchors.margins: 1
        visible: false
    }
发布了95 篇原创文章 · 获赞 26 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/gongjianbo1992/article/details/103639353
QML
今日推荐