您现在的位置是:网站首页> 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
}
}
}
}
}
最终的设计效果如下:
下一篇:C++中的关键字explicit