日历(QCalendarWidget)的使用及风格样式的设置——Qt

前言

之前项目中没涉及过日历控件(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 

 

结束语

好了,下班了,就这样了,周末愉快。


版权声明:本文为xiaopei_yan原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。