QML Shape绘制tab页

一.QML中的Shape详细介绍

  1. 功能概述
  • Qt Quick Shapes模块提供了在QML中创建自定义形状的功能,这些形状可以通过QML的属性绑定系统动态变化。
  • 从Qt 5.10版本开始,QML引入了Quick.Shapes功能,这是官方提供的在性能和易用性之间取得平衡的自绘途径。
  1. 技术优势
  • Shapes底层基于GPU渲染(SceneGraph),这意味着QPainter能绘制的基础图元都可以通过Shapes实现,且性能得到优化。
  • 与通过QQuickPaintedItem或2D Canvas渲染形状不同,Shape通过QPainterPath生成几何图形,路径不会在软件中光栅化,适合创建分布在屏幕更大区域的形状,避免了纹理上传或帧缓冲区blit的性能损失。
  1. 使用方式
  • Shape可以作为可视化元素的一部分,与其他元素如Rectangle(矩形)、Text(文本)、Image(图像)等一起使用,以创建丰富的用户界面。
  • 它可以通过锚点定位在窗口或父元素上,并且可以设置描边宽度和颜色来描绘形状的边缘。除了描边外,还可以选择填充形状的内部,以创建不同的填充区域。

 二.QML Shape绘制tab页使用示例

RibbonTabButton.qml

import QtQuick 2.4
import QtQuick.Controls 2.4

RoundButton
{
    id: control
    font.pixelSize: checked? 16:14
    checkable: true
    autoExclusive: true
    //font.family: checked?Style.font_notosanscjksc_bold:Style.font_notosanscjksc_medium

    property color textColor: "black"
    property bool horizontal: false
    property int index: 0

    contentItem: Text {
        text: control.text
        font: control.font
        color: control.textColor
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
    }

    background: Rectangle {
        radius: control.radius
        color: (control.pressed && !control.checked) ? "#41000000" : "transparent";
    }
}

 RibbonTab.qml

import QtQuick 2.11
import QtQuick.Shapes 1.0
import QtGraphicalEffects 1.0
import QtQuick.Controls 2.4

Item
{
    id: root
    //property real firstBtnWidth: 90 //在第一个按钮处是一个固定的菜单按钮
    property real btnWidth: 80
    property real radius: 9
    property real tabWidth: 42
    property real tabPageWidth:width
    property int currentIndex: 0
    property alias tabBtnsCurrentIndex: tabBtns.currentIndex
    property var tabData: [{text: qsTr("Tab1"), page: null},
                           {text: qsTr("Tab2"), page: null}]
    //tab页形状
    Item
    {
        id: tabBlock
        anchors.fill: parent
        //所有路径段(路径从左上角tab页面顶点开始)
        //向左
        PathLine { id: path0; x: currentIndex*btnWidth /*+ firstBtnWidth*/ - root.radius; y: tabBtns.height}

        //向上
        PathArc  { id: path1; x: currentIndex*btnWidth /*+ firstBtnWidth*/; y: tabBtns.height - root.radius; radiusX: root.radius; radiusY: radiusX; direction: PathArc.Counterclockwise }
        PathLine { id: path2; x: currentIndex*btnWidth /*+ firstBtnWidth*/; y: root.radius}

        //向右
        PathArc  { id: path3; x: currentIndex*btnWidth /*+ firstBtnWidth*/ + root.radius; y: 0; radiusX: root.radius; radiusY: radiusX; direction: PathArc.Clockwise }
        PathLine { id: path4; x: currentIndex*btnWidth /*+ firstBtnWidth*/ + btnWidth - root.radius; y: 0}

        //向下
        PathArc  { id: path5; x: currentIndex*btnWidth /*+ firstBtnWidth*/ + btnWidth; y: root.radius; radiusX: root.radius; radiusY: radiusX; direction: PathArc.Clockwise }
        PathLine { id: path6; x: currentIndex*btnWidth /*+ firstBtnWidth*/ + btnWidth; y: tabBtns.height-root.radius}

        //向右
        PathArc  { id: path7; x: currentIndex*btnWidth /*+ firstBtnWidth*/ + btnWidth + root.radius; y: tabBtns.height;radiusX: root.radius; radiusY: radiusX; direction: PathArc.Counterclockwise}
        PathLine { id: path8; x: root.width; y: tabBtns.height }

        //向下
        PathLine { id: path9; x: root.width; y: root.height}

        //向左
        PathLine { id: path10; x: 0; y: root.height }

        //向上
        PathLine { id: path11; x: 0; y: tabBtns.height}

        //获取shape路径
        function getTabShapePath(){
            var paths = new Array;
            paths.push(path0);
            paths.push(path1);
            paths.push(path2);
            paths.push(path3);
            paths.push(path4);
            paths.push(path5);
            paths.push(path6);
            paths.push(path7);
            paths.push(path8);
            paths.push(path9);
            paths.push(path10);
            paths.push(path11);
            return paths;
        }

        //刷新形状
        function refreshShape(){
            tabShapPath.pathElements = getTabShapePath();
        }

        //tab页shape
        Shape {
            id: tabBlockShape
            anchors.fill: parent
            ShapePath{
                id: tabShapPath
                startX: 0; startY: tabBtns.height;
                fillColor: 'black';
                strokeWidth: -1;
                pathElements: tabBlock.getTabShapePath()
            }
            visible: false
        }

        //tab页背景
        Rectangle{
            id: tabBlockBackground
            anchors.fill: parent
            color: "lightgray"
            visible: false
        }

        //裁剪
        OpacityMask{
            id: tabBlockCliped
            anchors.fill: parent
            source: tabBlockBackground
            maskSource: tabBlockShape

            //反走样支持
            layer.enabled: true
            layer.smooth: true
            layer.samples: 4
        }

        //stackview区域
        StackView {
            id: tabBlockStack
            x: 0
            y: tabBtns.height
            width: root.tabPageWidth
            height: root.height - tabBtns.height
            initialItem: tabData[root.currentIndex].page
        }
    }

    property var btnObjects: new Array
    //tab按钮栏
    Item {
        id: tabBtns
        width: root.width
        height: 35
        property int tabCount: tabData.length
        property int currentIndex: root.currentIndex
        property real currentWidth: width/tabCount*currentIndex
        property real btnWidth: root.btnWidth
        property real btnHeight: 35

        //tab按钮控件
        Component{
            id: tabButton
            RibbonTabButton{
                x: index*width /*+ root.firstBtnWidth*/
                radius: root.radius
                text: tabData[index].text

                onClicked: {
                    tabBtns.currentIndex = index;
                    root.currentIndex = index;
                }
            }
        }

        property var btnYPos: new Array

        Component.onCompleted: {
            var i = 0;
            //动态创建tab按钮和tab分割线
            for(i = 0; i < tabCount; i++) {
                var btnObject = tabButton.createObject(tabBtns, {index: i,
                                           width: Qt.binding(function() {return btnWidth}),
                                           height: Qt.binding(function() {return btnHeight}),
                                           checked: i === 0 ? true : false});
                btnObjects.push(btnObject);
            }
        }

        onCurrentIndexChanged:{
            tabBlock.refreshShape();
            tabBlockStack.clear();
            if(tabData[currentIndex].page !== undefined && tabData[currentIndex].page !== null)
                tabBlockStack.push(tabData[currentIndex].page);
        }
    }
}

 main.qml

import QtQuick 2.12
import QtQuick.Window 2.12

Window
{
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    color: "gray"

    RibbonTab
    {
        id:_mainpanel
        tabWidth: 60
        tabPageWidth: parent.width
        width: parent.width
        height: parent.height - 35

        tabData: [{text: qsTr("基本"), page: tabBasic},
            {text: qsTr("绘图"), page: tabDraw}]


        Loader{
            id: tabBasic
            sourceComponent:Rectangle
            {
                width:200
                height:200
                color:"lightgray"
                Text {
                    anchors.centerIn: parent
                    text: qsTr("基本页面")
                }
            }
            visible: false
        }

        //绘图
        Loader{
            id: tabDraw
            sourceComponent: Rectangle
            {
                width:200
                height:200
                color:"lightgray"
                Text {
                    anchors.centerIn: parent
                    text: qsTr("绘图页面")
                }
            }
            visible: false
        }
    }
}

运行结果图:

 

 

 

 

 

 

相关推荐

  1. tab高亮切换及tab交互

    2024-03-24 11:06:01       58 阅读
  2. 【爬虫】Selenium打开新tab

    2024-03-24 11:06:01       47 阅读
  3. quasar框架切换Tab使用<keep-alive>缓存

    2024-03-24 11:06:01       35 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-24 11:06:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-24 11:06:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-24 11:06:01       82 阅读
  4. Python语言-面向对象

    2024-03-24 11:06:01       91 阅读

热门阅读

  1. C++基础面试题(二)

    2024-03-24 11:06:01       42 阅读
  2. Android 最新权限请求利器----PermissonX

    2024-03-24 11:06:01       36 阅读
  3. 从“不破不立”到变革

    2024-03-24 11:06:01       43 阅读
  4. HashMap的简单原理

    2024-03-24 11:06:01       41 阅读
  5. docker 容器与本地主机间文件/文件夹的传输

    2024-03-24 11:06:01       37 阅读
  6. 数据库的介绍、分类、作用和特点

    2024-03-24 11:06:01       42 阅读
  7. Pytorch:nn.Upsample() 和nn.ConvTranspose2d()

    2024-03-24 11:06:01       46 阅读
  8. Docker 容器中使用 RAM 角色实现云监控事件监控

    2024-03-24 11:06:01       44 阅读