首先觉得Qt自带的日历控件不好看,跟现在的设计风格有些不符了;其次自带的控件没法满足目前的功能需求。所以只能参考网上的大神们的帖子,结合自己的项目需求,基于QCalendarWidget开发自定义日历控件。最终效果就是如下图所示了。
代码实现
BaseCalendarWidget类就是我们的自定义日历控件,主要的定义如下,继承于QCalendarWidget。下面分析每个主要成员函数的作用。
class BaseCalendarWidget : public QCalendarWidget
{
Q_OBJECT
public:
explicit BaseCalendarWidget(QWidget *parent = nullptr);
~BaseCalendarWidget();
protected:
void paintCell(QPainter *painter, const QRect &rect, const QDate &date) const;
private:
void initUI();
void initSetting();
};
1. 日历导航栏的修改
导航栏的修改也是参考了众多帖子,发现主要是两种方法:
- 隐藏原生的导航栏,完全自己实现导航栏,方法不难,但是比较繁琐,需要绑定各种信号槽才能实现原生导航栏的功能。适用于大量关于日历功能的开发。
- 获取原生的导航栏中的各个控件,直接修改原生控件的样式。开发量比较小,不会影响原生导航栏的功能逻辑。当然我们也不能灵活的新增导航栏的功能。
我比较懒,所以采用了方法2。当然方法2也满足了我的需求。
initUI函数主要是改变Qt原生日历中导航栏的样式,即年份、月份按钮所在的那一行。去掉了月份按钮自带的下拉菜单,修改了下一个月份和上一个月份的按钮图标,最后修改了导航栏的背景颜色。
void BaseCalendarWidget::initUI()
{
QMenu *monthMenu = QCalendarWidget::findChild<QMenu*>();
Q_ASSERT( nullptr != monthMenu);
QToolButton *monthBtn = qobject_cast<QToolButton*>(monthMenu->parentWidget());
Q_ASSERT( nullptr != monthBtn);
//monthBtn->setMenu(nullptr);
monthBtn->setStyleSheet("QToolButton::menu-indicator {image: none;}");
QToolButton *prevBtn = QCalendarWidget::findChild<QToolButton*>(QLatin1String("qt_calendar_prevmonth"));
Q_ASSERT( nullptr != prevBtn);
QSize prevSize = prevBtn->iconSize();
prevSize.rwidth() -= 2;
prevSize.rheight() -= 2;
prevBtn->setIconSize(prevSize);
prevBtn->setIcon(QIcon(":/BackDisplay/Resource/BackDisplay/arrow_left.png"));
QToolButton *nextBtn = QCalendarWidget::findChild<QToolButton*>(QLatin1String("qt_calendar_nextmonth"));
Q_ASSERT( nullptr != nextBtn);
QSize nextSize = nextBtn->iconSize();
nextSize.rwidth() -= 2;
nextSize.rheight() -= 2;
nextBtn->setIconSize(nextSize);
nextBtn->setIcon(QIcon(":/BackDisplay/Resource/BackDisplay/arrow_right.png"));
setStyleSheet("QCalendarWidget QWidget#qt_calendar_navigationbar {background-color: #696969;}");
}
2. 整体风格的设置
initSetting函数主要是设置日历的显示风格,这个根据个人的喜好和需求自由设置,没啥好说的。重点说下setWeekdayTextFormat函数的功能,设置日历表头中星期几的显示风格。
void BaseCalendarWidget::initSetting()
{
setLocale(QLocale(QLocale::Chinese));
setSelectionMode(QCalendarWidget::SingleSelection);
setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames);
QTextCharFormat format;
format.setForeground(QColor(160, 160, 160));
format.setBackground(QColor(255, 255, 255));
setHeaderTextFormat(format);
setWeekdayTextFormat(Qt::Saturday, format);
setWeekdayTextFormat(Qt::Sunday, format);
setWeekdayTextFormat(Qt::Monday, format);
setWeekdayTextFormat(Qt::Tuesday, format);
setWeekdayTextFormat(Qt::Wednesday,format);
setWeekdayTextFormat(Qt::Thursday, format);
setWeekdayTextFormat(Qt::Friday, format);
}
3. 日期的绘制
最重要的就是paintCell函数了,但是官方文档说明却很简单。
[virtual protected] void QCalendarWidget::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
Paints the cell specified by the given date, using the given painter and rect.
说明下每个参数的作用
- QPainter *painter : 控件的画笔,提供给我们自绘用的。
- const QRect &rect :当前绘制的区域,此区域限定第三个参数data显示的区域。
- const QDate &date :当前绘制时的日期,也就是我们要自定义绘制的日期。
主要的工作,就是设置当前需要绘制的日期背景颜色,显示的日期数字的颜色、大小、是否加粗等等。
/* 需要根据自己的业务逻辑处理当前日期绘制的逻辑 */
void BaseCalendarWidget::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
/* 绘制背景 */
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0, 145, 255));
painter->drawRoundedRect(rect.x()+1, rect.y()+1, rect.width()-2, rect.height()-2, 3, 3);
/* 绘制前景 */
QFont dateFont = painter->font();
dateFont.setPixelSize(rect.height()-10);
painter->setFont(dateFont);
painter->setPen(QColor(255, 255, 255));
QRect dateRect = QRect(rect.x()+3, rect.y()+3, rect.width()-6, rect.height()-6);
painter->drawText(dateRect, Qt::AlignCenter, QString::number(date.day()));
painter->restore();
}
PS: 由于是项目中的代码,所以不能贴上完整代码,但是关键的地方都已经贴出来了,只要根据自己的实际需求修改。