您现在的位置是:网站首页> C/C++

纯QML实现画图工具

  • C/C++
  • 2022-03-26
  • 1149人已阅读
摘要

首先,介绍整体布局为。最上侧是菜单,下面是工具条,中间是Canvas(画布),最底侧是状态栏。


    1.菜单栏设计


     为实现分页显示不同的工具,采用TabView进行布局。代码如下:


import QtQuick 2.0

import QtQuick.Controls 1.2

import QtQuick.Controls.Styles 1.2

import QtQuick.Window 2.0

import QtQuick.Dialogs 1.2

//菜单选项

Rectangle{

    id:root

    height: 120

    width: appWindow.width

    property color paintColor //绘制颜色

    property string picPath:""//图片路径

    TabView{

        width: appWindow.width

        id:tabView

        Tab{

            title: "文件"

            Rectangle {

                id: see

                width: appWindow.width

                anchors {

                    horizontalCenter: parent.horizontalCenter

                    top: parent.top

                    topMargin: 8

                }

                color: "transparent"

                Row{

                    spacing: 5

                    Column{

                        Row{

                            spacing: 5

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/new.png"

                                rectColor: "transparent"

                                describle: "新建"

                                onClicked: {

                                    messageDialog.text="是否保存?"

                                    messageDialog.standardButtons=StandardButton.Yes | StandardButton.No

                                    messageDialog.icon=StandardIcon.Question

                                    messageDialog.open()

                                }

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/open.png"

                                rectColor: "transparent"

                                describle: "打开"

                                onClicked: {

                                    fileDialog.open()

                                    fileDialog.selectExisting=true

                                }

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/save.png"

                                rectColor: "transparent"

                                describle: "保存"

                                onClicked:{

                                    fileDialog.selectExisting=false

                                    fileDialog.open()

                                }

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/saveOther.png"

                                describle: "另存"

                                onClicked:{

                                    fileDialog.selectExisting=false

                                    fileDialog.open()

                                }

                            }

                        }

                        Rectangle{

                            width: parent.width

                            height: 25

                            color:"#F5F6F7"

                            anchors.horizontalCenter: parent.horizontalCenter

                            Text{

                                text:"文件操作"

                                color:"#929292"

                                anchors.centerIn: parent

                            }

                        }

                    }

                    //分隔符

                    Rectangle{height: 64;width: 1;color:"#E2E3E4";}

                    Column{

                        Row{

                            spacing: 5

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/print.png"

                                rectColor: "transparent"

                                describle: "打印"

                                onClicked: appWindow.showFullScreen()

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/exit.png"

                                rectColor: "transparent"

                                describle: "退出"

                                onClicked:  Qt.quit()

                            }

                        }

                        Rectangle{

                            width: parent.width

                            height: 25

                            color:"#F5F6F7"

                            anchors.horizontalCenter: parent.horizontalCenter

                            Text{

                                text:"操作"

                                color:"#929292"

                                anchors.centerIn: parent

                            }

                        }

                    }

                }

                FileDialog {

                    id: fileDialog;

                    title: qsTr("Please choose an image file");

                    nameFilters:["PNG(*.png)","JPEG(*.jpg *.jpeg *.jpe)","GIF(*.gif)","位图(*.bmp)"]

                    property int isSave: 1 //判断是打开还是保存操作

                    onAccepted: {

                        if(!fileDialog.selectExisting)

                            if(canvas.saveImage(picPath))

                            {

                                messageDialog.title="提示"

                                messageDialog.text="保存成功!"

                                messageDialog.icon=StandardIcon.Information

                                messageDialog.standardButtons=StandardButton.Ok

                                messageDialog.open()

                            }

                        var filepath = new String(fileUrl);

                        if(Qt.platform.os == "windows")

                            root.picPath= filepath.slice(8);

                        else

                            root.picPath = filepath.slice(7);

                    }

                }

                //弹窗提示

                MessageDialog {

                    id: messageDialog

                    onAccepted: {

                        console.log("do samething")

                    }

                    onYes: {fileDialog.selectExisting=false;fileDialog.open();}

                    onNo: canvas.newImage()

                }

            }

        }

        Tab{

            title: "设置"

            Rectangle {

                id: colorTools

                width: appWindow.width

                anchors {

                    horizontalCenter: parent.horizontalCenter

                    top: parent.top

                    topMargin: 8

                }

                color: "transparent"

                Row{

                    spacing: 5

                    anchors.left: parent.left

                    anchors.leftMargin: 15

                    Column{

                        Row{

                            spacing: 5

                            //设置线宽

                            PicButton{

                                id:selectWeight

                                width:50

                                height: grid.height

                                imagePath: "res/weight.png"

                                describle: "粗细"

                                isImageVisible:true

                                onClicked: {

                                    listWeight.visible=listWeight.visible==true?false:true;

                                    listWeight.z=3

                                }

                            }

                            //分隔符

                            Rectangle{height: grid.height;width: 1;color:"#E2E3E4";}

                            //选中的颜色

                            PicButton{

                                id:selectRect

                                width:50

                                height: grid.height

                                describle: "颜色"

                            }

                            //分隔符

                            Rectangle{height: grid.height;width: 1;color:"#E2E3E4";}

                            //色彩网格

                            Grid{

                                id:grid

                                columns: 7

                                rows:2

                                spacing: 2

                                Repeater {

                                    model: ["#33B5E5", "#99CC00", "#FFBB33", "#FF4444","#DD6644","#AA4444","#FFBB33", "#FF4444","#DD6644","#AA4444","#FFBB33", "#FF4444","#DD6644","#AA4444"]

                                    ColorGrid {

                                        id: red

                                        color: modelData

                                        onClicked:{

                                            selectRect.rectColor=color

                                            root.paintColor = color

                                        }

                                    }

                                }

                            }

                            //分隔符

                            Rectangle{height: grid.height;width: 1;color:"#E2E3E4";}

                            //颜色对话框

                            PicButton{

                                id:editRect

                                width:50

                                height: grid.height

                                isImageVisible: true

                                imagePath: "res/pic.png"

                                rectColor: "transparent"

                                describle: "编辑"

                                onClicked: colorD.open()

                                ColorDialog{

                                    id:colorD

                                    onColorChanged: {selectRect.rectColor=color;root.paintColor=color;}

                                }

                            }

                            //分隔符

                            Rectangle{height: grid.height;width: 1;color:"#E2E3E4";}

                        }

                        Rectangle{

                            width: grid.width

                            height: 30

                            color:"#F5F6F7"

                            anchors.horizontalCenter: parent.horizontalCenter

                            Text{

                                text:"颜色"

                                color:"#929292"

                                anchors.centerIn: parent

                            }

                        }

                    }

                    //形状设置

                    Column{

                        Grid{

                            id:xz

                            width: 200

                            height: 60

                            Repeater{

                                model:[]

                            }

                        }

                        Rectangle{

                            width: xz.width

                            height: 30

                            color:"#F5F6F7"

                            anchors.horizontalCenter: parent.horizontalCenter

                            Text{

                                text:"形状"

                                color:"#929292"

                                anchors.centerIn: parent

                            }

                        }

                    }

                }

            }

        }

        Tab{

            title: "查看"

            Rectangle {

                id: show

                width: appWindow.width

                anchors {

                    horizontalCenter: parent.horizontalCenter

                    top: parent.top

                    topMargin: 8

                }

                color: "transparent"

                Row{

                    spacing: 5

                    Column{

                        Row{

                            spacing: 5

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/increase.png"

                                rectColor: "transparent"

                                describle: "放大"

                                onClicked: {

                                    if( statusbar.sliderValue+0.25>1)

                                        statusbar.sliderValue=1;

                                    else

                                        statusbar.sliderValue= statusbar.sliderValue+0.25

                                }

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/decrease.png"

                                rectColor: "transparent"

                                describle: "缩小"

                                onClicked: {

                                    if( statusbar.sliderValue-0.25<-1)

                                        statusbar.sliderValue=-1;

                                    else

                                        statusbar.sliderValue= statusbar.sliderValue-0.25

                                }

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/big.png"

                                describle: "100%"

                                onClicked: statusbar.sliderValue=0;

                            }

                        }

                        Rectangle{

                            width: parent.width

                            height: 25

                            color:"#F5F6F7"

                            anchors.horizontalCenter: parent.horizontalCenter

                            Text{

                                text:"缩放"

                                color:"#929292"

                                anchors.centerIn: parent

                            }

                        }

                    }

                    //分隔符

                    Rectangle{height: 64;width: 1;color:"#E2E3E4";}

                    Column{

                        Row{

                            spacing: 5

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/fullScreen.png"

                                rectColor: "transparent"

                                describle: "全屏"

                                onClicked: appWindow.showFullScreen()

                            }

                            PicButton{

                                width: 50

                                height: 64

                                isImageVisible: true

                                imagePath: "res/exitFull.png"

                                rectColor: "transparent"

                                describle: "退出"

                                onClicked: appWindow.showNormal()

                            }

                        }

                        Rectangle{

                            width: parent.width

                            height: 25

                            color:"#F5F6F7"

                            anchors.horizontalCenter: parent.horizontalCenter

                            Text{

                                text:"显示"

                                color:"#929292"

                                anchors.centerIn: parent

                            }

                        }

                    }

                }

            }

        }

        style:TabViewStyle{

            frameOverlap: 1

            tab: Rectangle {

                color: styleData.selected ? "#F5F6F7" :"#FFFFFF"

                implicitWidth: Math.max(text.width + 4, 100)

                implicitHeight: 30

                radius: 1

                Text {

                    id: text

                    anchors.centerIn: parent

                    text: styleData.title

                    color: "black"

                    font.pixelSize: 12

                }

            }

            frame: Rectangle {

                width: appWindow.width

                color: "#F5F6F7"

            }

        }

    }

}

    2.工具栏,采用ToolBar设计。代码如下:

     

ToolBar{

        id:toolBar

        anchors.top: menuBar.bottom

        Row{

            anchors.verticalCenter: parent.verticalCenter

            spacing: 5

            ToolButton{

                width:25

                height: 25

                tooltip: "保存"

                iconSource: "./res/save.png"

            }

            ToolButton{

                width:25

                height: 25

                tooltip: "撤销"

                iconSource: "./res/revoke.png"

            }

            ToolButton{

                width:25

                height: 25

                tooltip: "恢复"

                iconSource: "./res/undo.png"

            }

            Rectangle{

                width:25

                height: 25

                color: menuBar.paintColor

                border.color: "gray"

                border.width: 1

            }

        }

    }    

3. 中间画布使用QML自己的Canvas 实现代码如下:

import QtQuick 2.3

import QtQml 2.0

import QtQuick.Controls 1.2

import QtQuick.Controls.Styles 1.2

import QtQuick.Window 2.0

import QtGraphicalEffects 1.0

Rectangle{

    id:root

    width:500*statusbar.rate

    height: 300*statusbar.rate

    property alias mouseX: area.mouseX

    property alias mouseY: area.mouseY

    property string imgPath: menuBar.picPath

    signal save(string str)

    property double lineWidth: 1 //线宽

    property color paintColor: menuBar.paintColor //画笔颜色

    property var ctx: canvas.getContext('2d')

    function saveImage(imgName)

    {

        return canvas.save(imgName)

    }

    //新建画布,清除画布

    function newImage()

    {

         ctx.clearRect(0,0,canvas.width,canvas.height)

         canvas.requestPaint()

    }

    function rePaint()

    {

    }

    Image{

        id:imgP

        source:{

            if(imgPath!="")

                "file:///"+imgPath;

            else

                "";

        }

        visible: false

        width:canvas.width

        height: canvas.height

    }

    //

    Timer{

        interval: 100

        running: true

        triggeredOnStart: true

        repeat: true

        onTriggered: canvas.requestPaint()

    }

//    Image{

//        id:picImg

//        anchors.fill: parent

//    }

    //画板

    Canvas {

        id: canvas

        anchors.fill: parent

        antialiasing: true

        property real lastX //画笔的终止位置

        property real lastY

        //opacity: 0

        onImageLoaded: {

            if(canvas.isImageLoaded(imgP.source))

            {

                console.log("imps")

            }

            if(canvas.isImageError(imgP.source))

            {

                console.log("impE")

            }

        }

        onPaint: {

            if(imgP.source!="")

                ctx.drawImage(imgP,0,0)

            ctx.lineWidth = lineWidth

            ctx.strokeStyle = paintColor

            ctx.beginPath()

            ctx.moveTo(lastX, lastY)

            lastX = area.mouseX

            lastY = area.mouseY

            ctx.lineTo(lastX, lastY)

            ctx.stroke()

        }

        MouseArea {

            id: area

            anchors.fill: parent

            acceptedButtons: Qt.AllButtons

            onPressed: {

                canvas.lastX = mouseX

                canvas.lastY = mouseY

            }

            onPositionChanged: {

                canvas.requestPaint()

            }

            onClicked: {

                if(mouse.button==Qt.RightButton)

                    contentMenu.popup();

//                var url=canvas.toDataURL('image/png');

//                picImg.source=url;

            }

            //鼠标形状改变

            cursorShape: (containsMouse? (pressed? Qt.CrossCursor: Qt.ArrowCursor): Qt.ArrowCursor);

            Menu { // 右键菜单

                //title: "Edit"

                id: contentMenu

                MenuItem {

                    text: "新建"

                    shortcut: "Ctrl+N"

                    onTriggered: {}

                }

                MenuItem {

                    text: "保存"

                    shortcut: "Ctrl+S"

                    onTriggered: {}

                }

                MenuItem {

                    text: "粘贴"

                    shortcut: "Ctrl+V"

                    onTriggered: {}

                }

                MenuSeparator { }

                Menu {

                    title: "More Stuff"

                    MenuItem {

                        text: "Do Nothing"

                    }

                }

            }

        }

    }

    //左侧

    Rectangle{

        width: 5

        height: 5

        x:parent.width

        y:parent.height/2

        border.color: "black"

        border.width: 1

        MouseArea{

            id:xRate

            anchors.fill: parent

            cursorShape: (containsMouse? (pressed? Qt.SizeHorCursor: Qt.ArrowCursor): Qt.ArrowCursor);

            drag.target: parent

            onPositionChanged: {

                root.width=parent.x

            }

        }

    }

    //下侧

    Rectangle{

        width: 5

        height: 5

        border.color: "black"

        border.width: 1

        x:parent.width/2

        y:parent.height

        Drag.active: yRate.drag.active

        Drag.hotSpot.x: 10

        Drag.hotSpot.y: 10

        MouseArea{

            id:yRate

            anchors.fill: parent

            cursorShape: (containsMouse? (pressed? Qt.SizeVerCursor: Qt.ArrowCursor): Qt.ArrowCursor);

            drag.target: parent

            onPositionChanged: {

                root.height=parent.y

            }

        }

    }

    //对角

    Rectangle{

        width: 5

        height: 5

        x:parent.width

        y:parent.height

        border.color: "black"

        border.width: 1

        Drag.active: xyRate.drag.active

        Drag.hotSpot.x: 10

        Drag.hotSpot.y: 10

        MouseArea{

            id:xyRate

            anchors.fill: parent

            cursorShape: (containsMouse? (pressed? Qt.SizeFDiagCursor: Qt.ArrowCursor): Qt.ArrowCursor);

            drag.target: parent

            onPositionChanged: {

                root.width=parent.x

                root.height=parent.y

            }

        }

    }

}

3.底部状态栏设计,左侧为鼠标坐标,中间为图像大小,右侧为缩放比例调整。代码如下:

import QtQuick 2.0

import QtQuick.Controls 1.2

import QtQuick.Controls.Styles 1.2

Rectangle {

    color: "#F0F0F0";

    implicitHeight: 30;

    width: parent.width;

    property string position: "" //位置坐标

    property int pWidth: 0  //图像宽度

    property int pHeight: 0 //图像高度

    property alias sliderValue:pslider.value //放大缩小值

    //放大倍数

    property double  rate: {

        if(pslider.value==0)1;

        else

            (1+pslider.value).toFixed(2)

    }

    Row{

        anchors.left: parent.left

        anchors.leftMargin: 10

        anchors.verticalCenter: parent.verticalCenter

        Image{

            width: 20

            height: 20

            source: "./res/pic.png"

            anchors.verticalCenter: parent.verticalCenter

        }

        Text{

            id:pos

            anchors.verticalCenter: parent.verticalCenter

            text:" "+position

        }

    }

    Row{

        anchors.verticalCenter: parent.verticalCenter

        anchors.horizontalCenter: parent.horizontalCenter

        Image{

            width: 20

            height: 20

            source: "./res/pic.png"

            anchors.verticalCenter: parent.verticalCenter

        }

        Text{

            id:pix

            text:" "+pWidth+" x "+pHeight+"像素"

            anchors.verticalCenter: parent.verticalCenter

        }

    }

    Row{

        anchors.right: parent.right

        anchors.rightMargin: 10

        anchors.verticalCenter: parent.verticalCenter

        spacing: 5

        Text{

            id:pre //百分比

            color: "black"

            anchors.verticalCenter: parent.verticalCenter

            text:{

                if(pslider.value==0)

                    "100%"

                else

                    (1+pslider.value).toFixed(2)*100+"%"

            }

        }

        Image{

            width: 20

            height: 20

            source: "./res/decrease.png"

            MouseArea{

                anchors.fill: parent

                onClicked: {

                    if( pslider.value-0.25<-1)

                        pslider.value=-1;

                    else

                        pslider.value=pslider.value-0.25

                }

            }

        }

        Slider {

            id:pslider

            minimumValue: -1

            maximumValue: 1

            value:0

            style: SliderStyle {

                groove: Rectangle {

                    implicitWidth: 150

                    implicitHeight: 5

                    color: "#F0F0F0"

                    border.color: "lightgray"

                    border.width: 1

                }

                handle: Rectangle {

                    anchors.centerIn: parent

                    color: control.pressed ? "blue" : "lightgray"

                    implicitWidth: 12

                    implicitHeight: 20

                }

            }

            onValueChanged: {

                var img=canvas.ctx.getImageData(0, 0, canvas.width, canvas.height)

                  canvas.ctx.putImageData(img, 0, 0)

            }

        }

        Image{

            width: 20

            height: 20

            source: "./res/increase.png"

            MouseArea{

                anchors.fill: parent

                onClicked: {

                    if( pslider.value+0.25>1)

                        pslider.value=1;

                    else

                       pslider.value= pslider.value+0.25

                }

            }

        }

    }

}

最终的设计效果如下:


Top