首页 > 其他分享 >自定义的Qt日期选择控件

自定义的Qt日期选择控件

时间:2022-11-29 20:23:58浏览次数:41  
标签:MDatePicker 控件 const Qt 自定义 dayInfos void date painter

此控件也能作为日历控件使用。实现了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();
}

 

标签:MDatePicker,控件,const,Qt,自定义,dayInfos,void,date,painter
From: https://www.cnblogs.com/mengxiangdu/p/16936562.html

相关文章

  • vs2019 配置 qt 库
    如标题所说,在vs编译器中调用qt库首先安装一个适合vs的qt版本不知道怎么安装的,可以参考:VisualStudio+Qt配置开发环境qt安装之后,我们只需要在vs编译器中设置......
  • QT程序新建一个线程
    1、创建好一个QT应用程序2、手动创建新线程类,继承QThread我这里新建的是下面newthread.h和newthread.cpp文件。newthread.h#ifndefNEWTHREAD_H#define......
  • .Net Core中自定义认证实现
    一、起因最近项目中需要对项目同时支持JWT认证,以及自定义的认证校验方式认证。通过对官方文档了解,得到认证实现主要通过继承 IAuthenticationHandler 或 Authenticat......
  • ArcObjects SDK开发 007 自定义App-Command-Tool框架
    1、为什么再设计一套App-Command-Tool框架为什么我们要自己再设计一套App-Command框架,而不直接使用AOAPI中的AxControl-ICommand这套已经非常好的框架呢?1、宿主不同。我......
  • WPF控件模板(6)
    什么是ControlTemplate?ControlTemplate(控件模板)不仅是用于来定义控件的外观、样式,还可通过控件模板的触发器(ControlTemplate.Triggers)修改控件的行为、响应动画等......
  • VS2019使用Qt4.8.7
    取消系统变量中的Qt_INCLUDEPATH_。C:\Users\octob\AppData\Local\QtMsBuild中添加qt4.natvis.xml,qt4.natvisforvisualstudio2015forqt4IverifiedQString,Q......
  • 解决Qt msvc编译器中文乱码
    编码知识科普参考彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)_利白的博客-CSDN博客_qt中文乱码解决方法上文讲到了QString显示中文乱码的原因。我的理解qt先对输入......
  • 汇编实验:自定义键盘中断的处理函数
    汇编实验报告-键盘中断1.实验任务:采用键盘中断方式,当输入是字符或数字的时候,回显输入并回车换行;否则退出。2.运行环境:Windows11+MASM3.题目分析:在课上我们刚刚学习......
  • android自定义view实现progressbar的效果
    一键清理是很多Launcher都会带有的功能,其效果也比较美观。实现方式也许有很多中,其中常见的是使用图片drawable来完成的,具体可以参考这篇文章:​​模仿......
  • Windows10+VS2019从源码编译 Qt5
    参考Windows10+MSVC(VS2022)从源码编译QT5.12.11-知乎(zhihu.com)qt-labs/vstools~qt-labs/vstools(github.com)BuildingQt5fromGit/zh-QtWikiQtConfi......