1. 程式人生 > 其它 >自定義的Qt日期選擇控制元件

自定義的Qt日期選擇控制元件

此控制元件也能作為日曆控制元件使用。實現了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();
}