QML QtQuick.Controls 2 Button按钮样式自定义

测试版本:Qt5.15.2 ,参考Qt源码及文档示例

代码链接:https://github.com/gongjianbo/QmlComponentStyle.git  

自定义样式与默认样式的对比,第一个为默认样式:

扁平样式的实现代码:

import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Controls 2.15
import QtQuick.Controls.impl 2.15

// Qt5按钮样式自定义
// 龚建波 2025-03-05
// 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Button.qml
// Button继承自AbstractButton,只增加了flat和highlighted两个不常用的属性
T.Button {
    id: control

    // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
    // 定义主题颜色
    property color themeColor: "darkCyan"
    // 定义文本颜色
    property color textColor: "white"
    // 定义背景颜色
    // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
    property color backgroundColor: (control.pressed || control.checked)
                                    ? Qt.darker(themeColor)
                                    : (control.hovered || control.highlighted)
                                      ? Qt.lighter(themeColor)
                                      : themeColor
    // 定义边框宽度
    property int borderWidth: 0
    // 定义边框颜色
    property color borderColor: Qt.darker(themeColor)
    // 定义边框圆角
    property int radius: 0

    // 默认宽度,参考Qt源码的写法,实际应用可以删减
    // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
    // inset和padding都是Control基类定义的,默认为0
    // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
    //                         implicitContentWidth + leftPadding + rightPadding)
    // 默认高度
    // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
    //                          implicitContentHeight + topPadding + bottomPadding)
    // 通过内容计算宽高可能会导致一组按钮的宽高都不齐,可以用固定的默认宽高,或者固定高但是宽度自适应
    implicitWidth: implicitContentWidth + leftPadding + rightPadding
    implicitHeight: implicitContentHeight + topPadding + bottomPadding
    // 边距
    padding: 0
    // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
    leftPadding: 8
    rightPadding: 8
    // 图标和文字间距
    spacing: 6
    // checkable: true
    // 字体设置
    // 也可以给QApplication设置全局的默认字体
    font{
        family: "SimSun"
        pixelSize: 16
    }
    // 按钮图标设置
    // icon.width: 24
    // icon.height: 24
    // icon.source: "url"
    icon.color: textColor

    // Control组件点击之后,后续按空格也会触发点击,可以把空格过滤掉
    Keys.onPressed: event.accepted = (event.key === Qt.Key_Space)
    Keys.onReleased: event.accepted = (event.key === Qt.Key_Space)
    // 可以绑定一个Action到按钮,这样点击后直接触发Action的逻辑,Action一般和菜单或者快捷键相关
    // action: Action {  }
    // 在内容contentItem和背景background图层间可以设置indicator来实现一些样式效果,如点击时的水波纹
    // 三者的堆叠关系可以通过设置z值来调节谁在前谁在后
    // indicator: Rectangle { anchors.fill: parent; color: "red" }
    // 独占性,即同父时达到ButtonGroup那种单选效果
    autoExclusive: false
    // 是否可以选中
    checkable: false
    // 是否选中
    checked: false
    // 是否检测hover鼠标悬停,默认会跟随父组件的设置
    hoverEnabled: true
    // 按住后是否重复触发点击
    autoRepeat: false
    // 按住后重复触发点击的初次判断延时ms
    autoRepeatDelay: 300
    // 按住后重复触发点击的间隔时间ms
    autoRepeatInterval: 100
    // 组件默认样式下的显示方式,可以纯文本、纯图标、上下组合、左右组合
    // display: AbstractButton.TextBesideIcon
    // 文本内容
    // text: "Button"
    // 组件在视觉上是否处于按下状态,而pressed只读属性是物理意义上的按下
    // down: false
    // pressed和hovered只读属性,反应按键的按下或者鼠标悬停状态
    // pressX和pressY只读属性,反应按键按下时点击的位置

    // 按下后移出按钮范围触发取消
    // onCanceled: console.log("canceled")
    // 按钮点击信号
    // onClicked: console.log("clicked")
    // 按钮双击信号,会先触发一次clicked
    // onDoubleClicked: console.log("double clicked")
    // 按钮长按信号
    // onPressAndHold: console.log("press and hold")
    // 按钮按下信号
    // onPressed: console.log("pressed")
    // 按钮弹起信号
    // onReleased: console.log("released")

    // 按钮中显示的内容
    // 源码用的impl里的IconLabel,带图标和文字,但不能单独设置renderType: Text.NativeRendering
    // 但可以用QQuickWindow设置全局的renderType
    // contentItem: IconLabel {
    //     spacing: control.spacing
    //     mirrored: control.mirrored
    //     display: control.display
    //     icon: control.icon
    //     text: control.text
    //     font: control.font
    //     color: control.textColor
    // }
    contentItem: Item {
        implicitWidth: btn_text.implicitWidth + btn_icon.iconWidth
        implicitHeight: 30
        // Row会根据内容计算出implicitWidth
        Row {
            id: btn_row
            // 居中显示
            anchors.centerIn: parent
            // 图标和文字间隔
            spacing: control.spacing
            // 用impl模块的ColorImage,便于设置图标颜色
            ColorImage {
                id: btn_icon
                // 除了text外的内容宽度
                property int iconWidth: (btn_icon.implicitWidth > 0 ? btn_icon.implicitWidth + btn_row.spacing : 0)
                // 在Row内上下居中
                anchors.verticalCenter: parent.verticalCenter
                // 借用Button已有的icon接口设置图标url
                source: control.icon.source
                // 借用Button已有的icon接口设置图标尺寸
                sourceSize: Qt.size(control.icon.width, control.icon.height)
                // 借用Button已有的icon接口设置图标颜色
                color: control.icon.color
            }
            Text {
                id: btn_text
                // 如果按钮宽度小于默认宽度文字需要显示省略号,得先设置Text宽度
                width: (control.width - btn_icon.iconWidth - control.leftPadding - control.rightPadding)
                // 在Row内上下居中
                anchors.verticalCenter: parent.verticalCenter
                // 按钮文字内容
                text: control.text
                // 文字颜色
                color: control.textColor
                // 单独设置文本组件的渲染方式
                renderType: Text.NativeRendering
                // 文字对齐方式,设置Text宽高后设置才有意义
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
                // 换行设置,按钮一般没有换行
                // wrapMode: Text.NoWrap
                // 文字超出按钮范围显示省略号
                elide: Text.ElideMiddle
                // 字体设置
                font: control.font
            }
        }
    }

    // 背景
    background: Rectangle {
        // control设置了背景无关的宽高,这里也可以不设置默认宽高
        implicitWidth: control.implicitWidth
        implicitHeight: control.implicitHeight
        // 背景圆角
        radius: control.radius
        // 背景颜色
        color: control.backgroundColor
        // 背景边框
        border.width: control.borderWidth
        border.color: control.borderColor
    }
}
//main.qml
        Row {
            id: button_row
            spacing: 10
            Text {
                width: 90
                height: 30
                renderType: Text.NativeRendering
                text: "Button:"
            }
            Button {
                text: "Button"
                width: 120
                height: 30
            }
            BasicButton {
                width: 50
                text: "Button"
            }
            BasicButton {
                text: "Button"
                textColor: "white"
                themeColor: "green"
            }
            BasicButton {
                text: "Basic Button"
                icon.source: "qrc:/fire.png"
                textColor: "white"
                radius: 4
                backgroundColor: (pressed || checked)
                                 ? Qt.darker("purple")
                                 : (hovered || highlighted)
                                   ? Qt.lighter("darkCyan")
                                   : "red"
            }
            BasicButton {
                width: 200
                text: "Basic Button"
                icon.source: "qrc:/fire.png"
                textColor: "white"
                themeColor: "red"
                radius: 4
            }
        }

import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 //import QtQuick 2.2 //import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.14 import QtQuick.Controls.Private 1.0 Page { anchors.fill: parent font.pixelSize: 12 header: Label { text: qsTr("系统设置") font.pixelSize: Qt.application.font.pixelSize * 2 padding: 10 } ColumnLayout { anchors.fill: parent spacing: 10 // 传感器序列号输入 Label { text: qsTr("传感器序列号") } TextField { id: sensorSerialNumber placeholderText: qsTr("输入传感器序列号") } // 测试日期输入 Label { text: qsTr("测试日期") } DatePicker{ dateValue: (new Date()).toLocaleString(Qt.locale(), "yyyy-MM-dd") onDateValueChanged: {} } // 传感器类型选择 Label { text: qsTr("传感器类型") } ComboBox { id: sensorType model: [qsTr("加速度"), qsTr("速度"), qsTr("位移")] currentIndex: 0 } // 传感器耦合方式选择 Label { text: qsTr("传感器耦合方式") } ComboBox { id: couplingType model: [qsTr("电压"), qsTr("IPEPE"), qsTr("电荷"), qsTr("4-20MA(DC)"), qsTr("4-20MA(AC)")] currentIndex: 0 } // 按钮区域 RowLayout { spacing: 10 Button { id: sensitivityTestButton text: qsTr("灵敏度测试") icon.source: "/api/placeholder/32/32" // 这里使用占位符图片,你需要替换为实际图片路径 } Button { id: frequencyResponseTestButton text: qsTr("频响测试") icon.source: "/api/placeholder/32/32" // 这里使用占位符图片,你需要替换为实际图片路径 } Button { id: dataManagementButton text: qsTr("数据管理") icon.source: "/api/placeholder/32/32" // 这
03-19
<think>好的,我现在需要帮助用户解决如何在Qt Quick Controls 2QML中创建包含传感器配置的页面,具体包括序列号输入、日期选择器、组合框和按钮操作。首先,我需要回忆一下Qt Quick Controls 2中的常用组件以及QML布局相关的知识。 用户提到的组件有序列号输入,这应该是一个文本框,可能需要验证输入是否为有效的序列号格式。日期选择器可以使用QtQuick.Controls中的DatePicker或者类似的组件,但要注意Qt Quick Controls 2的版本是否支持。组合框对应的是ComboBox组件,用来让用户选择预设的选项。按钮操作则涉及Button组件和相应的点击事件处理。 接下来,布局设计。通常QML中使用ColumnLayout或GridLayout来排列组件,这样可以让界面自适应窗口大小。可能需要设置各组件之间的间距和对齐方式。此外,传感器相关的UI可能需要实时更新数据,所以需要绑定数据模型或者使用属性绑定。 然后,我需要考虑如何将这些组件组合在一起。例如,每个配置项可能需要一个标签(Label)和对应的输入控件,排列成一行或者一列。日期选择器可能需要弹出式对话框,或者使用内联的控件,这取决于用户需求。组合框需要填充选项,可能通过ListModel来动态添加。 按钮操作部分,用户点击后需要执行保存配置或者提交数据的功能。这里需要编写JavaScript函数来处理点击事件,可能需要将输入的数据收集起来,通过信号发送到C++后端处理,或者使用Qt的网络模块发送到服务器。 还需要注意样式美化,比如使用Qt Quick Controls 2的Material或Universal样式,或者自定义组件的外观。确保控件在不同平台和屏幕尺寸下表现一致。 可能遇到的问题包括日期选择器的实现,因为Qt Quick Controls 2中的标准组件可能没有现成的DatePicker,可能需要自定义或者使用Qt Labs中的Calendar组件。另外,组合框的数据填充可能需要动态加载,比如从文件或数据库中读取选项。 最后,测试布局的响应式设计,确保在窗口大小变化时,组件能够正确调整位置和大小。同时,验证用户输入的合法性,比如序列号的格式是否正确,日期是否在合理范围内,组合框是否有选中项等。 总结步骤应该是:创建布局容器,添加各个输入控件并设置属性,处理按钮点击事件,绑定数据模型,进行样式调整,测试功能。需要参考Qt官方文档确保语法正确,并考虑代码的可维护性和扩展性。</think>以下是如何使用Qt Quick Controls 2创建传感器配置页面的分步实现方案: ### 一、基础布局设计 使用`ColumnLayout`容器实现垂直布局,包含传感器序列号输入、日期选择、组合框配置和操作按钮: ```qml import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 ColumnLayout { spacing: 15 anchors.fill: parent anchors.margins: 20 Label { text: "传感器配置" font.bold: true Layout.alignment: Qt.AlignHCenter } } ``` ### 二、核心组件实现 #### 1. 序列号输入 ```qml TextField { id: serialInput Layout.fillWidth: true placeholderText: "输入传感器序列号" validator: RegExpValidator { regExp: /[A-Z0-9]{8}-[A-Z0-9]{4}/ // 验证格式如XXXX-XXXX-XXXX } } ``` #### 2. 日期选择器 使用`Popup`实现日历选择: ```qml Button { text: "校准日期: " + Qt.formatDate(datePicker.selectedDate, "yyyy-MM-dd") onClicked: datePicker.open() DatePickerPopup { id: datePicker onDateSelected: console.log("选定日期:", selectedDate) } } // 日期选择弹窗组件 Popup { id: datePicker property date selectedDate: new Date() Calendar { onClicked: { selectedDate = date datePicker.close() } } } ``` #### 3. 组合框配置 ```qml ComboBox { Layout.fillWidth: true model: ["温度传感器", "压力传感器", "光学传感器"] currentIndex: 0 onActivated: console.log("选择类型:", currentText) } ``` ### 三、按钮操作逻辑 ```qml RowLayout { spacing: 10 Layout.topMargin: 20 Button { text: "保存配置" onClicked: { if(serialInput.acceptableInput){ backend.saveConfig( serialInput.text, datePicker.selectedDate, typeCombo.currentText ) // 假设有C++后端接口[^2] } } } Button { text: "重置" flat: true onClicked: serialInput.clear() } } ``` ### 四、样式优化建议 1. 在`ApplicationWindow`中设置全局样式: ```qml ApplicationWindow { Material.theme: Material.Dark Material.accent: "#2ecc71" } ``` 2. 添加输入验证提示: ```qml Label { visible: !serialInput.acceptableInput text: "序列号格式要求:8位字母数字+分隔符+4位字母数字" color: "red" } ``` ### 五、数据绑定示例 通过Qt的属性绑定实现实时更新: ```qml property string currentConfig: JSON.stringify({ "serial": serialInput.text, "date": datePicker.selectedDate, "type": typeCombo.currentText }) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值