一、构想
自定义日历弹窗的制作主要是分为两部分,1、自定义日历,2、点击LineEdit时,将日历窗口弹出来。首先针对如何自定义日历制定思路,通过上网查询 QT自带了一个日历类QCalendarWidget,那就好办了,只需要自定义日历时继承这个日历类,再通过QPainter重新绘制。针对点击LineEdit时日历弹出来,要知道LineEdit没有点击事件的,那这样的话我们需要给它安装事件过滤器installEventFilter。
二、效果展示
图左为QT自带的日历,图右则是自定义的日历效果图
三、实现过程
日历跟lineEdit的数据交互是通过信号与槽的方式。
3.1 自定义日历的主要代码
#include "qcustomcalendartimewidget.h"
#include <QLocale>
#include <QPainter>
#include <QTextCharFormat>
#include <QProxyStyle>
#include <QTableView>
#include <QLayout>
#include <QPushButton>
#include <QLabel>
#include <QDebug>
#include <QMouseEvent>
#include <QCalendarWidget>
#include <QLabel>
QCustomCalendarTimeWidget::QCustomCalendarTimeWidget(QWidget *parent)
: QCalendarWidget (parent)
{
this->resize(350,250);
initControl();
}
//鼠标事件 作用是让日历弹窗能够移动
void QCustomCalendarTimeWidget::mouseMoveEvent(QMouseEvent *e)
{
if (mousePressed & (e->buttons() & Qt::LeftButton)) {
this->move(e->globalPos() - mousePoint);
e->accept();
}
}
void QCustomCalendarTimeWidget::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) {
mousePressed = true;
mousePoint = e->globalPos() - this->pos();
e->accept();
}
}
void QCustomCalendarTimeWidget::mouseReleaseEvent(QMouseEvent *)
{
mousePressed = false;
}
//初始化日历界面
void QCustomCalendarTimeWidget::initControl()
{
//设置日历为中文
this->setLocale(QLocale::Chinese);
setNavigationBarVisible(false);
setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames);
//设置第一天为星期一
QCalendarWidget::setFirstDayOfWeek(Qt::Monday);
QTextCharFormat format;
format.setForeground(QColor(160, 160, 160));
format.setBackground(QColor(255, 255, 255));
setHeaderTextFormat(format);
//重绘日历上部栏
initTopWidget();
connect(this, &QCalendarWidget::currentPageChanged, [this](int year, int month){
setDataLabelTimeText(year, month);
});
//日历双击响应事件
connect(this, &QCalendarWidget::activated , this, &QCustomCalendarTimeWidget::setDate);
}
//发送时间给调用者
void QCustomCalendarTimeWidget::setDate()
{
//通过信号将选择的时间发送出去
emit signalSetCalendarTime(this->selectedDate());
qDebug()<<"时间为: "<<this->selectedDate();
}
void QCustomCalendarTimeWidget::setDataLabelTimeText(int year, int month)
{
m_dataLabel->setText(QStringLiteral("%1年%2月").arg(year).arg(month));
}
//绘制日历上部栏
void QCustomCalendarTimeWidget::initTopWidget()
{
QWidget* topWidget = new QWidget(this);
topWidget->setFixedHeight(40);
topWidget->setSizePolicy(QSizePolicy::Preferred,
QSizePolicy::Fixed);
QHBoxLayout* hboxLayout = new QHBoxLayout;
hboxLayout->setContentsMargins(12, 0, 12, 0);
hboxLayout->setSpacing(4);
m_leftYearBtn = new QPushButton(this);
m_leftMonthBtn = new QPushButton(this);
m_rightYearBtn = new QPushButton(this);
m_rightMonthBtn = new QPushButton(this);
m_dataLabel = new QLabel(this);
QString qss = "QLabel{font-size:18px;font-family:PingFang SC;}";
m_dataLabel->setStyleSheet(qss);
qss = "QPushButton{border-image:url(:/image/previousyear.png);}"
"QPushButton:hover{border-image: url(:/image/previousyear-hover.png);}";
m_leftYearBtn->setFixedSize(16, 16);
m_leftYearBtn->setStyleSheet(qss);
m_leftYearBtn->setToolTip(tr("上一年"));
qss = "QPushButton{border-image:url(:/image/previousmonth.png);}"
"QPushButton:hover{border-image: url(:/image/previous-hover.png);}";
m_leftMonthBtn->setFixedSize(16, 16);
m_leftMonthBtn->setStyleSheet(qss);
m_leftMonthBtn->setToolTip(tr("上一月"));
m_rightYearBtn->setFixedSize(16, 16);
qss = "QPushButton{border-image:url(:/image/nextyear.png);}"
"QPushButton:hover{border-image: url(:/image/nextyear-hover.png);}";
m_rightYearBtn->setStyleSheet(qss);
m_rightYearBtn->setToolTip(tr("下一年"));
m_rightMonthBtn->setFixedSize(16, 16);
qss = "QPushButton{border-image:url(:/image/nextmonth.png);}"
"QPushButton:hover{border-image: url(:/image/nextmonth-hover.png);}";
m_rightMonthBtn->setStyleSheet(qss);
m_rightMonthBtn->setToolTip(tr("下一月"));
hboxLayout->addWidget(m_leftYearBtn);
hboxLayout->addWidget(m_leftMonthBtn);
hboxLayout->addStretch();
hboxLayout->addWidget(m_dataLabel);
hboxLayout->addStretch();
hboxLayout->addWidget(m_rightMonthBtn);
hboxLayout->addWidget(m_rightYearBtn);
topWidget->setStyleSheet("background-color: rgb(170, 170, 170");
topWidget->setLayout(hboxLayout);
QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
vBodyLayout->insertWidget(0, topWidget);
connect(m_leftYearBtn, SIGNAL(clicked()), this, SLOT(onbtnClicked()));
connect(m_leftMonthBtn, SIGNAL(clicked()), this, SLOT(onbtnClicked()));
connect(m_rightYearBtn, SIGNAL(clicked()), this, SLOT(onbtnClicked()));
connect(m_rightMonthBtn, SIGNAL(clicked()), this, SLOT(onbtnClicked()));
//显示当前的年 月
setDataLabelTimeText(selectedDate().year(), selectedDate().month());
}
//响应上部栏四个按钮的事件
void QCustomCalendarTimeWidget::onbtnClicked()
{
QPushButton *senderBtn = qobject_cast<QPushButton *>(sender());
if (senderBtn == m_leftYearBtn)
{
showPreviousYear();
}
else if (senderBtn == m_leftMonthBtn)
{
showPreviousMonth();
}
else if (senderBtn == m_rightYearBtn)
{
showNextYear();
}
else if (senderBtn == m_rightMonthBtn)
{
showNextMonth();
}
}
//绘制日历中间部分 当前点击的时间,当前时间的样式,通过QPainter
void QCustomCalendarTimeWidget::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(0, 145, 255));
painter->drawEllipse(rect.x()+9, rect.y()+1, rect.width()-17, rect.height()-2);
painter->setPen(QColor(255, 255, 255));
painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
painter->restore();
}
else if (date < minimumDate() || date > 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->setPen(QColor(220, 220, 220));
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(0, 161, 255));
painter->drawEllipse(rect.x()+9, rect.y()+1, rect.width()-17, rect.height()-2);
painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
painter->restore();
}
else
{
QCalendarWidget::paintCell(painter, rect, date);
}
}
3.2 点击LineEdit弹出日历的主要代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
datePickerWidget = nullptr;
initUI();
}
MainWindow::~MainWindow()
{
delete ui;
}
//LineEdit的左边贴一张图
void MainWindow::initUI()
{
QLabel *iconLabel = new QLabel(this);
iconLabel->setFixedSize(24,24);
iconLabel->setPixmap(QPixmap(":/image/calendarico.png"));
QHBoxLayout *hLayout = new QHBoxLayout();
hLayout->addSpacing(12);
hLayout->addWidget(iconLabel);
hLayout->addSpacing(10);
hLayout->addStretch();
ui->lineEdit->setLayout(hLayout);
ui->lineEdit->installEventFilter(this);
}
//时间过滤响应函数
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
//lineEdit被点击
if(qobject_cast<QLineEdit *>(obj) == ui->lineEdit &&
event->type() == QEvent::MouseButtonPress) {
if(!datePickerWidget)
datePickerWidget = new QCustomCalendarTimeWidget(this);
connect(datePickerWidget, &QCustomCalendarTimeWidget::signalSetCalendarTime, this, &MainWindow::SetDate);
datePickerWidget->show();
}
return QObject::eventFilter(obj,event);
}
//接收数据的槽函数
void MainWindow::SetDate(const QDate& date)
{
ui->lineEdit->setText(date.toString("yyyy-MM-dd"));
datePickerWidget->close();
}
问题咨询及项目源码请加群:
QQ群
名称:IT项目交流群
群号:245022761