摘自:https://blog.csdn.net/xiaopei_yan/article/details/108007941
前言
之前项目中没涉及过日历控件(QCalendarWidget),一般我是用简单的日期编辑器代替,对它的印象只存在于QtDesigner中丑的不行的日历控件。这次我要写类似企业微信的日程(如下图)功能的东西,这个就必须用到日历了(此篇博客只介绍日历的写法)。
有两种方式实现:一是自己全部重写,二呢是,修改Qt自带的控件QCalendarWidget,难易程度我肯定选二个了,不过后续我想自己重写一个日历,这样自己更有自主权。
效果图
这次先把效果图放到前边吧,对比着代码看,更容易讲解。
代码及说明
代码部分参考大佬的博客
https://blog.csdn.net/ly305750665/article/details/80092040。
因为用的是Qt自带的日历控件,所以就不用操心基本功能(比如时间的管理),我们只需要将日历控件的外貌设置为我们想要的就行了。
导航栏的重写
日历自带的导航栏真心丑,所以我将原始的导航栏隐藏起来自己又重新写了一个。左右按钮我没设置风格,因为写的是Demo,就犯懒了。
setNavigationBarVisible(false);
void MyCalendarWidget::initTopWidget()
{
QWidget* topWidget = new QWidget(this);
topWidget->setObjectName("CalendarTopWidget");
topWidget->setFixedHeight(40);
topWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
QHBoxLayout* hboxLayout = new QHBoxLayout;
hboxLayout->setContentsMargins(12, 0, 12, 0);
hboxLayout->setSpacing(4);
m_leftMonthBtn = new QPushButton(this);
m_leftMonthBtn->setText("<");
m_rightMonthBtn = new QPushButton(this);
m_rightMonthBtn->setText(">");
m_dataLabel = new QLabel(this);
m_leftMonthBtn->setObjectName("CalendarLeftMonthBtn");
m_rightMonthBtn->setObjectName("CalendarRightMonthBtn");
m_dataLabel->setObjectName("CalendarDataLabel");
m_leftMonthBtn->setFixedSize(16, 16);
m_rightMonthBtn->setFixedSize(16, 16);
hboxLayout->addStretch();
hboxLayout->addWidget(m_leftMonthBtn);
hboxLayout->addWidget(m_dataLabel);
hboxLayout->addWidget(m_rightMonthBtn);
hboxLayout->addStretch();
topWidget->setLayout(hboxLayout);
QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
vBodyLayout->insertWidget(0, topWidget);
connect(m_leftMonthBtn,&QPushButton::clicked,this,&MyCalendarWidget::onbtnClicked);
connect(m_rightMonthBtn,&QPushButton::clicked,this,&MyCalendarWidget::onbtnClicked);
setDataLabelTimeText(selectedDate().year(), selectedDate().month());
}
void MyCalendarWidget::onbtnClicked()
{
QPushButton *senderBtn = qobject_cast<QPushButton *>(sender());
if (senderBtn == m_leftMonthBtn)
{
showPreviousMonth();
}
else if (senderBtn == m_rightMonthBtn)
{
showNextMonth();
}
}
一些基础设置
日历这这样首先设置为中文(setLocale),这为行表头(tHorizontalHeader)的名字的中英文做铺垫:中文就是周几,星期几,英文Monday Mon.之类。我还设置了单选和周六日的风格,这些东西不仅只是表面意思,还有其他的作用。
void MyCalendarWidget::initControl()
{
//layout()->setSizeConstraint(QLayout::SetFixedSize);//大小不随布局变化,我不需要
setLocale(QLocale(QLocale::Chinese));//设置中文
setNavigationBarVisible(false);//隐藏导航条
setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);//去掉列表头
setHorizontalHeaderFormat(QCalendarWidget::LongDayNames);//我想要星期几的行表头
setSelectionMode(QCalendarWidget::SingleSelection);//单选
//设置星期风格
QTextCharFormat format;
format.setForeground(QColor(51, 51, 51));
format.setBackground(QColor(247,247,247));
format.setFontFamily("Microsoft YaHei");
format.setFontPointSize(9);
format.setFontWeight(QFont::Medium);
setWeekdayTextFormat(Qt::Saturday, format);
setWeekdayTextFormat(Qt::Sunday, format);
initTopWidget();
connect(this, &QCalendarWidget::currentPageChanged, [this](int year, int month){
setDataLabelTimeText(year, month);
});
}
先说我上面设置了日历是单选的,这个还有一个深层次的原因:我将选中的矩形改为圆形后,出现了一个问题就是,刚show出来后,在当天圆圈圈住后,前一天被矩形框住了,也就是有两个被选中了,前一天用的样式是原始的,而当天的是我新写的。我不知道什么原因造成的,所以只能将其设为单选解决此问题了。
void MyCalendarWidget::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
{
if (date == selectedDate())
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(118, 178, 224));
painter->drawEllipse(QRect(rect.x()+rect.width()/2-10, rect.y() + rect.height()/2-10, 20, 20));
painter->setPen(QColor(255, 255, 255));
painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
painter->restore();
}
else if (date == QDate::currentDate())
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QColor(118, 178, 224));
painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
painter->restore();
}
else if (date < minimumDate() || date > maximumDate())
{
qDebug()<< minimumDate()<<maximumDate();
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(249, 249, 249));
painter->drawRect(rect.x(), rect.y() + 3, rect.width(), rect.height() - 6);
//painter->drawEllipse(QRect(rect.x()+rect.width()/2-10, rect.y() + rect.height()/2-10, 20, 20));
painter->setPen(QColor(255,0,0));
painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
painter->restore();
}
else
{
QCalendarWidget::paintCell(painter, rect, date);
}
}
QSS设置
我在上面曾说道,我通过代码只更改了周六日的风格,这是为什么呢?因为qss中我没找到将周六日红色字体改为正常的语句。然后还有一点指的注意的是:设置了weekday 的format,那么表头的format将无效。(If you also set a weekday text format, this format's foreground and background color will take precedence over the header's format.)所以这就造成我为了将周六日变为正常色,但是我就没办法将表头设为其他色了,有点遗憾。
我看了一点QCalendarWidget的源码,视图部分是QCalendarView控制的,它继承的QTableView;我之前以为QCalendarWidget的表头即为QHeaderView,后来发现不是,QCalendarView将表头隐藏,然后将第一行作为horizontalHeader,第一列作为verticalHeader,涉及到的代码有:
enum {
RowCount = 6,
ColumnCount = 7,
HeaderColumn = 0,
HeaderRow = 0,
MinimumDayOffset = 1
};
QCalendarView::QCalendarView(QWidget *parent)
: QTableView(parent),
readOnly(false),
validDateClicked(false)
{
setTabKeyNavigation(false);
setShowGrid(false);
verticalHeader()->setVisible(false);
horizontalHeader()->setVisible(false);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
QCalendarWidget QAbstractItemView{
font-size:12px;
font-family:Microsoft YaHei;
font-weight:500;
outline:0px;
}
QCalendarWidget QAbstractItemView:disabled{
color:#D3D3D3;
}
QCalendarWidget QAbstractItemView:enabled{
color:#333333;
}
QCalendarView#qt_calendar_calendarview {
background-color: #F7F7F7; /*背景色*/
alternate-background-color:#F7F7F7; {/* 表头的背景颜色,为什么设置这个就有效,我不清楚*/
}
为了更好地写qss,我学会了一个dump方法,这样可以看到控件的结构和objectName.
ui->calendarWidget->dumpObjectTree();
将打印出如下结果:
QCalendarWidget::calendarWidget
QVBoxLayout::
QCalendarModel::
QCalendarView::qt_calendar_calendarview
QWidget::qt_scrollarea_viewport
QWidget::qt_scrollarea_hcontainer
QScrollBar::
QBoxLayout::
QWidget::qt_scrollarea_vcontainer
QScrollBar::
QBoxLayout::
QStyledItemDelegate::
QHeaderView::
QWidget::qt_scrollarea_viewport
QWidget::qt_scrollarea_hcontainer
QScrollBar::
QBoxLayout::
QWidget::qt_scrollarea_vcontainer
QScrollBar::
QBoxLayout::
QItemSelectionModel::
QHeaderView::
QWidget::qt_scrollarea_viewport
QWidget::qt_scrollarea_hcontainer
QScrollBar::
QBoxLayout::
QWidget::qt_scrollarea_vcontainer
QScrollBar::
QBoxLayout::
QItemSelectionModel::
QTableCornerButton::
QItemSelectionModel::
QWidget::qt_calendar_navigationbar
QPrevNextCalButton::qt_calendar_prevmonth
QPrevNextCalButton::qt_calendar_nextmonth
QToolButton::qt_calendar_monthbutton
QMenu::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QAction::
QToolButton::qt_calendar_yearbutton
QSpinBox::qt_calendar_yearedit
QLineEdit::qt_spinbox_lineedit
QWidgetLineControl::
QValidator::qt_spinboxvalidator
QHBoxLayout::
QCalendarDelegate::
QCalendarTextNavigator::
QProxyStyle::
QWidget::CalendarTopWidget
QHBoxLayout::
QPushButton::CalendarLeftMonthBtn
QLabel::CalendarDataLabel
QPushButton::CalendarRightMonthBtn
QCalendarWidget 样式设置
修改日历的样式,使用QSS设置,做个记录
图是借用的网友的,如图显示:需要修改的地方有大致这么5处,我们一个一个来说:
1. 左右两边的箭头
这个我试了qss还是没成功,因为这两个箭头跟我贴的图就重叠了,最后我就在代码中实现了,效果是一样的,如果有人知道在qss中实现,也麻烦告诉我一声
代码:
QToolButton *prevBtn = calendar->findChild<QToolButton*>(QLatin1String("qt_calendar_prevmonth"));
QToolButton *bextBtn = calendar->findChild<QToolButton*>(QLatin1String("qt_calendar_nextmonth"));
prevBtn->setIcon("你自己的图标");
bextBtn->setIcon("你自己的图标");
"qt_calendar_prevmonth"名字是查看源码知道的
2. 中间白色部分
QCalendarWidget QTableView
{
alternate-
background-color
:
rgb
(
128
,
128
,
128
); //颜色自己可以改
}
3. 背景色
QCalendarWidget QTableView
{
alternate-
background-color
:
rgb
(
128
,
128
,
128
); //颜色自己可以改
background-color: #2F2F3E;
}
4&5. 月份 和年份
QToolButton#qt_calendar_monthbutton,#qt_calendar_yearbutton{
color: #9ea5a9; //修改字体颜色
font: 9pt simHei; //也可以修改字体
}
6. 显示月份和年份所在的导航条
QCalendarWidget QWidget#qt_calendar_navigationbar{
//可以自己添加一些其他设置,比如边框
background-color: #2F2F3E;//这个一般设置渐变色比较多,可以自行修改
}
到这就基本说完了,最后再说下,鼠标点击月份会弹出一个下拉框,默认的也不好看,其实那是个菜单,qss中设置QMenu就可以改变其背景,
还有年份旁边有个QSpinBox也可以修改,这些都简单,网上搜下都有介绍
————————————————
版权声明:本文为CSDN博主「Ray_Chang_988」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wuchalilun/article/details/73613604