QML QtQuick.Controls 1 Calendar日历样式自定义

0.前言

QML中QtQuick.Controls 1里有Calendar这个控件,官方的文档以及示例都很简单,网上很多人也是自己重新实现的,我比较懒,照着QtQuick.Controls.Styles里的源码改了下。

(2021-1-7补充)最近又在用这个,发现之前从项目里copy代码出来的时候有些地方没弄好,所以文末补了个新的,色调和Qt默认效果差不多。

1.展示

效果如图:

我是用的Qt 5.9,不过这个控件在低版本里也可以用。还有就是他用的js的Date类,月份是从0开始的,用的时候要+1。代码如下:

//main.qml

import QtQuick 2.9
import QtQuick.Window 2.2

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

    CalendarBox{
        width: 300
        height: 300
        anchors.centerIn: parent

        calendar.onSelectedDateChanged:{
        }
        calendar.onDoubleClicked: {
        }

    }
}

//CalendarBox.qml

import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4


Rectangle{
    width: 300
    height: 300
    border.width: 1
    border.color: Qt.rgba(33/255,104/255,101/255,1)
    color: "black"
    property alias calendar: the_calendar
    //这里没有把字体颜色之类的引出来,作为控件的时候需要定义这些

    Calendar{
        id:the_calendar
        anchors.fill: parent
        anchors.margins: 2

        style: CalendarStyle {
            gridColor: Qt.rgba(18/255,62/255,68/255,1)
            //gridVisible: false

            background: Rectangle{
                id:background
                anchors.fill: parent
                color:Qt.rgba(18/255,62/255,68/255,1)
            }

            //标题年月
            navigationBar:Item{
                //color: "transparent"
                height: the_calendar.height/8
                Canvas {
                    id: prevYear
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
                    anchors.leftMargin: 10
                    width: parent.height/2
                    height: width
                    //color: bgColor
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.lineWidth = 1
                        ctx.strokeStyle = Qt.rgba(100/255,1,1,1)
                        ctx.beginPath()
                        ctx.moveTo(0,height*3/4)
                        ctx.lineTo(width/2,height/4)
                        ctx.lineTo(width,height*3/4)
                        //ctx.closePath()
                        ctx.stroke()
                    }

                    MouseArea{
                        anchors.fill: parent
                        onClicked: {
                            control.showPreviousYear()
                        }
                    }
                }
                Canvas {
                    id: nextYear
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: prevYear.right
                    anchors.leftMargin: 10
                    width: parent.height/2
                    height: width
                    //color: bgColor
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.lineWidth = 1
                        ctx.strokeStyle = Qt.rgba(100/255,1,1,1)
                        ctx.beginPath()
                        ctx.moveTo(0,height/4)
                        ctx.lineTo(width/2,height*3/4)
                        ctx.lineTo(width,height/4)
                        //ctx.closePath()
                        ctx.stroke()
                    }
                    MouseArea{
                        anchors.fill: parent
                        onClicked: {
                            control.showNextYear()
                        }
                    }
                }
                Label {
                    id: labelYear
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: nextYear.right
                    anchors.leftMargin: 15
                    //text: the_calendar.selectedDate.getFullYear()+qsTr('年')
                    text:control.visibleYear+qsTr('年')
                    //elide: Text.ElideRight
                    horizontalAlignment: Text.AlignLeft
                    font.pixelSize: 14
                    font.family: "SimSun"
                    color: Qt.rgba(101/255,1,1,1)
                }

                Canvas {
                    id: nextMonth
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.right: parent.right
                    anchors.rightMargin: 10
                    width: parent.height/2
                    height: width
                    //color: bgColor
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.lineWidth = 1
                        ctx.strokeStyle = Qt.rgba(100/255,1,1,1)
                        ctx.beginPath()
                        ctx.moveTo(0,height/4)
                        ctx.lineTo(width/2,height*3/4)
                        ctx.lineTo(width,height/4)
                        //ctx.closePath()
                        ctx.stroke()
                    }
                    MouseArea{
                        anchors.fill: parent
                        onClicked: {
                            control.showNextMonth()
                        }
                    }
                }
                Canvas {
                    id: prevMonth
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.right: nextMonth.left
                    anchors.rightMargin: 10
                    width: parent.height/2
                    height: width
                    //color: bgColor
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.lineWidth = 1
                        ctx.strokeStyle = Qt.rgba(100/255,1,1,1)
                        ctx.beginPath()
                        ctx.moveTo(0,height*3/4)
                        ctx.lineTo(width/2,height/4)
                        ctx.lineTo(width,height*3/4)
                        //ctx.closePath()
                        ctx.stroke()
                    }
                    MouseArea{
                        anchors.fill: parent
                        onClicked: {
                            control.showPreviousMonth()
                        }
                    }
                }
                Label {
                    id: labelMonth
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.right: prevMonth.left
                    anchors.rightMargin: 15
                    //注意Date原本的月份是0开始
                    text: (control.visibleMonth+1)+qsTr('月')
                    //elide: Text.ElideRight
                    horizontalAlignment: Text.AlignRight
                    font.pixelSize: 14
                    font.family: "SimSun"
                    color: Qt.rgba(101/255,1,1,1)
                }

                Rectangle{
                    width: parent.width
                    height: 1
                    anchors.bottom: parent.bottom
                    color: "black"
                }
            }
            //星期
            dayOfWeekDelegate: Item{
                //color: "transparent"
                height: the_calendar.height/8
                Label {
                    text: control.__locale.dayName(styleData.dayOfWeek, control.dayOfWeekFormat)
                    anchors.centerIn: parent
                    color: Qt.rgba(101/255,1,1,1)
                    font.pixelSize: 12
                    font.family: "SimSun"
                }
            }
            dayDelegate: Rectangle {
                //选中-当月未选中-其他
                color: styleData.selected
                       ?Qt.rgba(52/255,142/255,145/255,1)
                       : (styleData.visibleMonth && styleData.valid
                          ?Qt.rgba(6/255,45/255,51/255,1)
                          : Qt.rgba(3/255,28/255,35/255,1));

                Label {
                    text: styleData.date.getDate()
                    anchors.centerIn: parent
                    font.pixelSize: 12
                    font.family: "SimSun"
                    color: styleData.valid
                           ?Qt.rgba(197/255,1,1,1)
                           : Qt.rgba(16/255,100/255,100/255,1)
                }
            }

        }
    }
}

2.更新

使用时可以放到 Dialog 或者 Popup 里

import QtQuick 2.12
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4

//日历样式自定义
//龚建波 2021-1-7
Calendar{
    id:control

    implicitHeight: 280
    implicitWidth: 280

    //普通色块背景
    property color normalBgColor: "#FFFFFF"
    //选中项背景
    property color selectBgColor: "#305FDE"
    //超出月份背景
    property color outBgColor: normalBgColor
    //不可选背景(最大最小范围外)
    property color disableBgColor: "#F0F0F0"
    //网格颜色
    property color gridColor: "#E5E5E5"
    //标题文本颜色
    property color darkTextColor: "#242526"
    //日期文本颜色
    property color lightTextColor: "#555658"
    //超出月份文本颜色
    property color outTextColor: "#999999"
    //不可选文本(最大最小范围外)
    property color disableTextColor: "#BBBBBB"

    style: CalendarStyle {
        gridColor: control.gridColor
        //gridVisible: false

        background: Rectangle{
            id:background
            anchors.fill: parent
            color: control.normalBgColor
        }

        //标题年月
        navigationBar:Item{
            //color: "transparent"
            height: control.height/8
            Canvas {
                id: prevYear
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10
                width: parent.height/2
                height: width
                //color: bgColor
                onPaint: {
                    var ctx = getContext("2d")
                    ctx.lineWidth = 1
                    ctx.strokeStyle = control.darkTextColor
                    ctx.beginPath()
                    ctx.moveTo(0,height*3/4)
                    ctx.lineTo(width/2,height/4)
                    ctx.lineTo(width,height*3/4)
                    //ctx.closePath()
                    ctx.stroke()
                }

                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        control.showPreviousYear()
                    }
                }
            }
            Canvas {
                id: nextYear
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: prevYear.right
                anchors.leftMargin: 10
                width: parent.height/2
                height: width
                //color: bgColor
                onPaint: {
                    var ctx = getContext("2d")
                    ctx.lineWidth = 1
                    ctx.strokeStyle = control.darkTextColor
                    ctx.beginPath()
                    ctx.moveTo(0,height/4)
                    ctx.lineTo(width/2,height*3/4)
                    ctx.lineTo(width,height/4)
                    //ctx.closePath()
                    ctx.stroke()
                }
                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        control.showNextYear()
                    }
                }
            }
            Label {
                id: labelYear
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: nextYear.right
                anchors.leftMargin: 15
                //text: control.selectedDate.getFullYear()+qsTr('年')
                text: control.visibleYear+qsTr('年')
                //elide: Text.ElideRight
                horizontalAlignment: Text.AlignLeft
                font.pixelSize: 14
                font.family: "Microsoft YaHei"
                color: control.darkTextColor
            }

            Canvas {
                id: nextMonth
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                anchors.rightMargin: 10
                width: parent.height/2
                height: width
                //color: bgColor
                onPaint: {
                    var ctx = getContext("2d")
                    ctx.lineWidth = 1
                    ctx.strokeStyle = control.darkTextColor
                    ctx.beginPath()
                    ctx.moveTo(0,height/4)
                    ctx.lineTo(width/2,height*3/4)
                    ctx.lineTo(width,height/4)
                    //ctx.closePath()
                    ctx.stroke()
                }
                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        control.showNextMonth()
                    }
                }
            }
            Canvas {
                id: prevMonth
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: nextMonth.left
                anchors.rightMargin: 10
                width: parent.height/2
                height: width
                //color: bgColor
                onPaint: {
                    var ctx = getContext("2d")
                    ctx.lineWidth = 1
                    ctx.strokeStyle = control.darkTextColor
                    ctx.beginPath()
                    ctx.moveTo(0,height*3/4)
                    ctx.lineTo(width/2,height/4)
                    ctx.lineTo(width,height*3/4)
                    //ctx.closePath()
                    ctx.stroke()
                }
                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        control.showPreviousMonth()
                    }
                }
            }
            Label {
                id: labelMonth
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: prevMonth.left
                anchors.rightMargin: 15
                //注意Date原本的月份是0开始
                text: (control.visibleMonth+1)+qsTr('月')
                //elide: Text.ElideRight
                horizontalAlignment: Text.AlignRight
                font.pixelSize: 14
                font.family: "Microsoft YaHei"
                color: control.darkTextColor
            }

            Rectangle{
                width: parent.width
                height: 1
                anchors.bottom: parent.bottom
                color: control.gridColor
            }
        }
        //星期
        dayOfWeekDelegate: Item{
            //color: "transparent"
            height: control.height/8
            Label {
                text: control.__locale.dayName(styleData.dayOfWeek, control.dayOfWeekFormat)
                anchors.centerIn: parent
                color: control.darkTextColor
                font.pixelSize: 14
                font.family: "Microsoft YaHei"
            }
        }
        dayDelegate: Rectangle {
            //选中-当月未选中-其他
            color: (styleData.selected
                    ? control.selectBgColor
                    : styleData.valid
                      ? styleData.visibleMonth
                        ? control.normalBgColor
                        : control.outBgColor
                    : control.disableBgColor)

            Label {
                text: styleData.date.getDate()
                anchors.centerIn: parent
                font.pixelSize: 12
                font.family: "Microsoft YaHei"
                color: (styleData.selected
                        ? control.normalBgColor
                        : styleData.valid
                          ? styleData.visibleMonth
                            ? control.lightTextColor
                            : control.outTextColor
                        : control.disableTextColor)
            }
        }
    }
}

3.参考文档

 Qt文档:5.7\Src\qtquickcontrols\src\controls\Styles\Base\CalendarStyle.qml

Qt文档:5.7\Src\qtquickcontrols\src\controls\Calendar.qml

 

 

//自定义日历可看上篇文章 QCalendarTimeEdit::QCalendarTimeEdit(QWidget *parent) : QLineEdit(parent) , m_calendarWidget(nullptr) , m_widget(nullptr) { m_minDateTime = QDateTime::currentDateTime(); { QTime maxtime; maxtime.setHMS(23, 59, 59); m_maxDateTime.setTime(maxtime); m_maxDateTime.setDate(QDate::currentDate().addDays(365)); } setContextMenuPolicy(Qt::NoContextMenu); } QCalendarTimeEdit::~QCalendarTimeEdit() { if (m_widget) { delete m_widget; m_widget = nullptr; } } void QCalendarTimeEdit::initControlcalendar() { m_widget = new BackPaintWidget(nullptr); m_widget->setObjectName("calendarFramWidget"); loadStyleSheet(m_widget, "CalendarWidget"); m_widget->setAttribute(Qt::WA_TranslucentBackground); m_widget->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::SubWindow); QHBoxLayout* layout = new QHBoxLayout; layout->setMargin(8); layout->setSpacing(0); m_calendarWidget = new QCustomCalendarTimeWidget(m_widget); m_widget->setCalendarTimeWidget(m_calendarWidget); m_calendarWidget->setCalendarMinDate(m_minDateTime); m_calendarWidget->setCalendarMaxDate(m_maxDateTime); //m_calendarWidget->disconnect(SIGNAL(selectionChanged())); //m_calendarWidget->disconnect(SIGNAL(clicked(QDate))); layout->addWidget(m_calendarWidget); m_widget->setLayout(layout); connect(m_calendarWidget, &QCustomCalendarTimeWidget;::signalSetCalendarTime, [this](const QDateTime& date){ setText(date.toString("yyyy-MM-dd hh:mm:ss")); hideCalendarTimeWidget(); }); connect(m_calendarWidget, &QCustomCalendarTimeWidget;::signalClearDateTime, [this](){ clear(); hideCalendarTimeWidget(); }); hideCalendarTimeWidget(); } void QCalendarTimeEdit::setCalendarMinDate(const QDateTime &datetime;) { m_minDateTime = datetime; if (m_calendarWidget) { m_calendarWidget->setCalendarMinDate(m_minDateTime); } } void QCalendarTimeEdit::setCalendarMaxDate(const QDateTime &datetime;) { m_maxDateTime = datetime; if (m_calendarWidget) { m_calendarWidget->setCalendarMaxDate(m_maxDateTime); } } void QCalendarTimeEdit::popCalendarTimeWidget() { if (m_calendarWidget == nullptr) { initControlcalendar(); } QRect rect = qApp->desktop()->availableGeometry(0); QPoint gloadPos = mapToGlobal(QPoint(-8, this->height())); if (gloadPos.y() + m_calendarWidget->height() > rect.height()) { int y = gloadPos.y() - this->height() - m_calendarWidget->height(); gloadPos.setY(y); } m_widget->move(gloadPos); m_widget->show(); } void QCalendarTimeEdit::hideCalendarTimeWidget() { m_widget->hide(); } void QCalendarTimeEdit::mousePressEvent(QMouseEvent *event) { __super::mousePressEvent(event); if (event->button() == Qt::LeftButton) { popCalendarTimeWidget(); } } void QCalendarTimeEdit::keyPressEvent(QKeyEvent *event) { Q_UNUSED(event); }
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 2和QML中创建包含传感器配置的页面,具体包括序列号输入、日期选择器、组合框和按钮操作。首先,我需要回忆一下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 }) ```
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龚建波

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

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

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

打赏作者

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

抵扣说明:

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

余额充值