08 Qt自绘制日历控件:摆脱丑的让人无语的原生QCalendarWidget

前言

在QtGui中,日历控件是一种非常常用的用户界面元素,用于显示和选择日期。

Qt提供了一个原生的日历控件类QCalendarWidget。但是,原生的日历控件真的丑的让人窒息!那么在这时候,也就需要我们不得不对其外观进行自定义绘制,以满足特定的设计需求。在本文中,我们将介绍如何使用Qt的自定义绘制功能来对原生日历控件进行个性化定制。

一、示意图

 Qt原生式样风格:

原生风格
原生风格

Qt自绘制目标效果图:

目标效果图

二、实现思路

 概述

日历组件自绘制共可拆解为以下几部分:

 日历导航栏

日历的周信息

日历详情信息

部分绘制说明

绘制日期

为了沿用QCalendarWidget的日历关联功能接口,所以我们要继承QCalendarWidget。
在这个类中,我们将重写QCalendarWidget::paintCell()方法来实现自定义绘制。

这一步骤是自绘制日历控件最最简单、明了的步骤!

示例代码参考:


void QUiCalendarControl::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
{
    QWidget* pTargetWgt = dynamic_cast<QWidget*>(painter->device());
    if (pTargetWgt)
    {
        QPoint pt = pTargetWgt->mapFromGlobal(QCursor::pos());
        m_bHovered = rect.contains(pt);
    }
    painter->setRenderHint(QPainter::TextAntialiasing);
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setRenderHint(QPainter::SmoothPixmapTransform);
    //1.绘制背景色(圆形或其他图形)
    QPainterPath pathBK;
    pathBK.addEllipse(rcCircle);
    painter->fillPath(pathBK, backgroundColor(date));
    //2.绘制边框根据日期的特殊性做特殊绘制
    if (是今天的日期)
    {
        painter->save();
        QPainterPath pathBorder;
        pathBorder.addEllipse(rcCircle);
        QPen penBorder;
        penBorder.setColor(borderColor());
        penBorder.setWidth(1);
        painter->setPen(penBorder);
        painter->drawPath(pathBorder);
        painter->restore();
    }
    if( 是当前选中的日期)
    {
        
    }
    //3.其他一般日期
    QString strDay = QString::number(date.day());
    painter->save();
    painter->drawText(rcCircle, Qt::AlignCenter, strDisplayTxt);
    painter->restore();
    //其他绘制
    *********************省略***************
}

绘制周信息栏

如果用过QCalendarWidget日历控件,我们会发现,该日历控件的周信息标题定制性极差!也没有任何虚接口可供业务开发者使用。为了达到定制的目标,我们需要结合QCalendarWidget源码去做。

Qt关联源码如下:

QCalendarWidget::QCalendarWidget(QWidget *parent)
    : QWidget(*new QCalendarWidgetPrivate, parent, { })
{
    Q_D(QCalendarWidget);

    setAutoFillBackground(true);
    setBackgroundRole(QPalette::Window);

    QVBoxLayout *layoutV = new QVBoxLayout(this);
    layoutV->setContentsMargins(QMargins());
    d->m_model = new QCalendarModel(this);
    QTextCharFormat fmt;
    fmt.setForeground(QBrush(Qt::red));
    d->m_model->m_dayFormats.insert(Qt::Saturday, fmt);
    d->m_model->m_dayFormats.insert(Qt::Sunday, fmt);
    //关键代码!!!!!
    d->m_view = new QCalendarView(this);
    d->m_view->setObjectName(QLatin1String("qt_calendar_calendarview"));
    d->m_view->setModel(d->m_model);
    d->m_model->setView(d->m_view);
    d->m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
    d->m_view->setSelectionMode(QAbstractItemView::SingleSelection);
    d->m_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    d->m_view->horizontalHeader()->setSectionsClickable(false);
    d->m_view->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    d->m_view->verticalHeader()->setSectionsClickable(false);
    d->m_selection = d->m_view->selectionModel();
    d->createNavigationBar(this);
    d->m_view->setFrameStyle(QFrame::NoFrame);
    d->m_delegate = new QCalendarDelegate(d, this);
    d->m_view->setItemDelegate(d->m_delegate);
    d->update();
    d->updateNavigationBar();
    setFocusPolicy(Qt::StrongFocus);
    setFocusProxy(d->m_view);
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    ***************省略******************
}

class QCalendarView : public QTableView
{
    Q_OBJECT
public:
    QCalendarView(QWidget *parent = nullptr);
    ********省略***********
}

 从源码我们可以看出:

1.所谓的QCalendarWidget日历控件实际上是一个内嵌了QTableView的 widget组件!

2.这个QTableView控件的objectName是qt_calendar_calendarview

所以,我们可以知道在自绘代码中可以通过objectName获取这个tableView对象, 并且可以通过自定义这个tableView的HeadView可以实现定制表头(也就是周信息)

绘制日历导航栏 

关于定制日历导航栏,这里不再赘述了,其实就是几个按钮控件水平布局一下,而后摆在自绘日历控件的上方构成垂直布局即可!


总结

以上就是今天要分享的:Qt如何实现定制日历组件的内容!

既聊思路,也说代码!我们下次继续分享自定义风格扩展组件!

PS:本专栏所有篇幅涉及的UI扩展组件类,会封装成插件动态库,感兴趣的同学可以留言哦

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

峭桑岱司

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

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

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

打赏作者

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

抵扣说明:

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

余额充值