自定義的Qt日期選擇控制元件
阿新 • • 發佈:2022-11-29
此控制元件也能作為日曆控制元件使用。實現了Windows系統日曆控制元件的滑鼠懸停有白色漸變的效果(見於下圖18日周圍的白色漸變效果)。從中可以學習到Qt日期類的常用方法,和漸變畫刷的使用。作者的審美不行,不能很好地美化介面,所以介面不太好看。控制元件裡的QSpinBox也沒有用樣式表修改外觀使其與整體風格保持一致。此控制元件在VS2015和Qt5.9上測試通過。下面是效果圖:
上程式碼,標頭檔案:
class QSpinBox; class MDatePicker : public QWidget { Q_OBJECT public: MDatePicker(QWidget* parent = 0); void setCurrentDate(const QDate& idate); QDate currentDate() const; signals: void dateChanged(const QDate&); private slots: void sbDateValueChanged(); private: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; void calcFillDayRect(qreal cellw, qreal cellh); /* 填充dayInfos */ void drawHoverEffect(QPainter* painter); QRectF getBorderRect(const QRectF& cell); QRectF getFilledRect(const QRectF& cell); struct DayInfo { QRectF rect; QDate d; }; private: static const QMargins margin; /* 日期表格到控制元件的邊距 */ static const int texth; /* 週一到週日文字高度 */ QDate date; QPoint hover; QPoint pressPt; QVarLengthArray<DayInfo, 42> dayInfos; QSpinBox* sbYear; QSpinBox* sbMonth; };
CPP檔案:
const QMargins MDatePicker::margin(6, 32, 6, 6); const int MDatePicker::texth = 24; MDatePicker::MDatePicker(QWidget* parent) : QWidget(parent), date(QDate::currentDate()) { setMouseTracking(true); QHBoxLayout* lay = new QHBoxLayout(this); lay->setContentsMargins(6, 6, 6, 6); lay->setAlignment(Qt::AlignTop); sbYear = new QSpinBox(this); sbYear->setMinimumWidth(70); sbYear->setMinimum(1900); sbYear->setMaximum(2100); sbYear->setValue(date.year()); lay->addWidget(sbYear); QLabel* lbYear = new QLabel(u8"年", this); lay->addWidget(lbYear); sbMonth = new QSpinBox(this); sbMonth->setMinimumWidth(70); sbMonth->setMinimum(1); sbMonth->setMaximum(12); sbMonth->setValue(date.month()); lay->addWidget(sbMonth); QLabel* lbMonth = new QLabel(u8"月", this); lay->addWidget(lbMonth); QSpacerItem* spacer = new QSpacerItem(24, 12, QSizePolicy::Expanding); lay->addSpacerItem(spacer); setLayout(lay); connect(sbYear, QOverload<int>::of(&QSpinBox::valueChanged), this, &MDatePicker::sbDateValueChanged); connect(sbMonth, QOverload<int>::of(&QSpinBox::valueChanged), this, &MDatePicker::sbDateValueChanged); } void MDatePicker::setCurrentDate(const QDate& idate) { date = idate; update(); } QDate MDatePicker::currentDate() const { return date; } void MDatePicker::sbDateValueChanged() { int year = sbYear->value(); int month = sbMonth->value(); date.setDate(year, month, 1); emit dateChanged(date); update(); } inline QRectF MDatePicker::getBorderRect(const QRectF& cell) { return cell.adjusted(2, 2, -2, -2); } inline QRectF MDatePicker::getFilledRect(const QRectF& cell) { return cell.adjusted(4, 4, -4, -4); } void MDatePicker::calcFillDayRect(qreal cellw, qreal cellh) { dayInfos.clear(); for (int i = 0; i < 6; i++) { for (int j = 0; j < 7; j++) { qreal startx = margin.left() + j * cellw; qreal starty = margin.top() + texth + i * cellh; dayInfos.append({ QRectF(startx, starty, cellw, cellh), QDate() }); } } int dayCount = date.daysInMonth(); QDate firstDay = QDate(date.year(), date.month(), 1); int currMonthBegin = firstDay.dayOfWeek() - 1; for (int i = currMonthBegin; i < currMonthBegin + dayCount; i++) { dayInfos[i].d = firstDay.addDays(i - currMonthBegin); } int prevMonthDay = 1; for (int i = currMonthBegin - 1; i >= 0; i--) { dayInfos[i].d = firstDay.addDays(-prevMonthDay); prevMonthDay++; } int nextMonthDay = 0; for (int i = currMonthBegin + dayCount; i < 42; i++) { dayInfos[i].d = firstDay.addMonths(1).addDays(nextMonthDay); nextMonthDay++; } } void MDatePicker::drawHoverEffect(QPainter* painter) { QRadialGradient gradient; gradient.setFocalPoint(hover); gradient.setFocalRadius(0); gradient.setCenter(hover); gradient.setRadius(100); gradient.setColorAt(0, QColor(255, 255, 255, 173)); gradient.setColorAt(1, QColor(255, 255, 255, 0)); painter->setPen(QPen(gradient, 2, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin)); painter->setBrush(Qt::NoBrush); for (auto& item : dayInfos) { painter->drawRect(getBorderRect(item.rect)); } auto hit = std::find_if(dayInfos.begin(), dayInfos.end(), [this](const DayInfo& x) { return x.rect.contains(hover); }); if (hit != dayInfos.end()) { painter->setPen(QPen(QColor(255, 255, 255), 2, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin)); painter->drawRect(getBorderRect(hit->rect)); } } void MDatePicker::paintEvent(QPaintEvent *) { QPainter painter(this); int tableWidth = width() - margin.left() - margin.right(); int tableHeight = height() - margin.top() - texth - margin.bottom(); qreal cellWidth = tableWidth / 7.0; qreal cellHeight = tableHeight / 6.0; painter.fillRect(margin.left(), margin.top(), tableWidth, texth, QColor(0x36, 0x4f, 0x6b)); painter.fillRect(margin.left(), margin.top() + texth, tableWidth, tableHeight, QColor(0xe8, 0xe8, 0xe8)); const QString weekdays[] = { u8"一", u8"二", u8"三", u8"四", u8"五", u8"六", u8"日" }; painter.setPen(Qt::white); for (int i = 0; i < 7; i++) { qreal start = margin.left() + i * cellWidth; QTextOption option; option.setAlignment(Qt::AlignCenter); painter.drawText(QRectF(start, margin.top(), cellWidth, texth), weekdays[i], option); } /* 計算前介面42個日期的資訊 */ calcFillDayRect(cellWidth, cellHeight); /* 繪製滑鼠懸停漸變特效 */ if (!hover.isNull()) { drawHoverEffect(&painter); } /* 給當前日期繪製背景 */ auto found = std::find_if(dayInfos.begin(), dayInfos.end(), [this](const DayInfo& x) { return x.d.month() == date.month() && x.d.day() == date.day(); }); if (found != dayInfos.end()) { painter.fillRect(getFilledRect(found->rect), QColor(0xfc, 0x51, 0x85)); } /* 繪製日期數字 */ for (auto& item : dayInfos) { painter.setPen((item.d.month() == date.month()) ? Qt::black : Qt::gray); QTextOption option; option.setAlignment(Qt::AlignCenter); painter.drawText(item.rect, QString::number(item.d.day()), option); } } void MDatePicker::mouseMoveEvent(QMouseEvent* event) { hover = event->pos(); update(); } void MDatePicker::mousePressEvent(QMouseEvent *event) { pressPt = event->pos(); } void MDatePicker::mouseReleaseEvent(QMouseEvent *event) { if (pressPt == event->pos()) { auto clicked = std::find_if(dayInfos.begin(), dayInfos.end(), [this](const DayInfo& x) { return x.rect.contains(pressPt); }); if (clicked != dayInfos.end()) { date = clicked->d; sbYear->blockSignals(true); sbYear->setValue(date.year()); sbYear->blockSignals(false); sbMonth->blockSignals(true); sbMonth->setValue(date.month()); sbMonth->blockSignals(false); emit dateChanged(date); update(); } } } void MDatePicker::enterEvent(QEvent *event) { // nothing. } void MDatePicker::leaveEvent(QEvent *event) { hover = QPoint(); update(); }