自定义的Qt时间轴控件

这是一个垂直的时间轴,显示效果非常简单,但不能显示有格式的文本。如果想显示有格式文本可以把右侧显示本文的位置换成QLabel控件去显示有格式文本。

如果想改变文本行间距,根据我目前在网上搜索的答案,只能用QLabel配合qss样式表(即控件的styleSheet属性)实现。根据此控件可以学习Qt多行文本尺寸的测量和绘制方法。此控件使用方法是new MTimeLine,然后用append函数添加项,或removeAt函数删除项就可以了。

此代码在VS2015和Qt5.9上测试通过。

下面是效果图:

上代码,头文件:

class MTimeLinePrivate : public QWidget
{
    Q_OBJECT

public:
    MTimeLinePrivate(QWidget* parent = 0);
    void append(const QDate& date, const QString& str);
    void removeAt(int index);

private:
    void paintEvent(QPaintEvent *event) override;
    void resizeEvent(QResizeEvent*) override;
    void showEvent(QShowEvent*) override;
    QSize calcContentSize();
    QSize calcDateStringSize(const QFontMetrics& fm) const;
    void paintDateBkShape(QPainter* painter, const QRect& rect, int whereisy);
    void paintTextBkShape(QPainter* painter, const QRect& rect, int whereisy);

    struct EventInfo
    {
        QDate date;
        QString content;
    };

private:
    static const QMargins contentm; /* 控件内容的边距 */
    static const QMargins datep; /* 日期文本的边距 */
    static const QMargins datem; /* 日期文本背景矩形的外边距 */
    static const QMargins textp; /* 事件文本的边距 */
    static const QMargins textm; /* 事件文本背景矩形的外边距 */
    QVector<EventInfo> events;
};

class MTimeLine : public QWidget
{
    Q_OBJECT

public:
    MTimeLine(QWidget* parent = 0);
    void append(const QDate& date, const QString& str);
    void removeAt(int index);

private:
    MTimeLinePrivate* tlReal;
};

CPP文件:

const QMargins MTimeLinePrivate::contentm(10, 6, 10, 6);
const QMargins MTimeLinePrivate::datep(10, 10, 10, 10);
const QMargins MTimeLinePrivate::datem(0, 6, 14, 6);
const QMargins MTimeLinePrivate::textp(12, 12, 12, 12);
const QMargins MTimeLinePrivate::textm(14, 6, 0, 6);

MTimeLinePrivate::MTimeLinePrivate(QWidget* parent) :
    QWidget(parent)
{
}

void MTimeLinePrivate::append(const QDate& date, const QString& str)
{
    events.push_back({ date, str });
    if (isVisible())
    {
        QSize mini = calcContentSize();
        setMinimumSize(mini);
        update();
    }
}

void MTimeLinePrivate::removeAt(int index)
{
    events.remove(index);
    if (isVisible())
    {
        QSize mini = calcContentSize();
        setMinimumSize(mini);
        update();
    }
}

void MTimeLinePrivate::showEvent(QShowEvent*)
{
    QSize mini = calcContentSize();
    setMinimumSize(mini);
}

void MTimeLinePrivate::resizeEvent(QResizeEvent*)
{
    QSize mini = calcContentSize();
    setMinimumSize(mini);
}

QSize MTimeLinePrivate::calcContentSize()
{
    QFontMetrics fm = fontMetrics();
    QSize datesz = calcDateStringSize(fm);
    int ally = contentm.top();
    int datex = contentm.left();
    int midx = contentm.left() + datem.left() + datep.left() + datesz.width() + datep.right() + datem.right();
    int textx = midx;
    for (const auto &item : events)
    {
        QRect dateOuterRect;
        dateOuterRect.setX(datex + datem.left());
        dateOuterRect.setY(ally + datem.top());
        dateOuterRect.setWidth(datep.left() + datesz.width() + datep.right());
        dateOuterRect.setHeight(datep.top() + datesz.height() + datep.bottom());
        QRect dateRect;
        dateRect.setX(dateOuterRect.x() + datep.left());
        dateRect.setY(dateOuterRect.y() + datep.top());
        dateRect.setSize(datesz);

        int charx = textx + textp.left() + textm.left();
        int chary = ally + textm.top() + textp.top();
        int charw = width() - charx - (textp.right() + textm.right() + contentm.right());
        QRect textRect = fm.boundingRect(charx, chary, charw, 12000, Qt::TextWordWrap, item.content);
        QRect textOuterRect;
        textOuterRect.setX(textx + textm.left());
        textOuterRect.setY(ally + textm.top());
        textOuterRect.setWidth(textp.left() + textRect.width() + textp.right());
        textOuterRect.setHeight(textp.top() + textRect.height() + textp.bottom());

        int midh1 = datem.top() + dateOuterRect.height() + datem.bottom(); /* 左侧高度 */
        int midh2 = textm.top() + textOuterRect.height() + textm.bottom(); /* 右侧高度 */
        int midy2 = ally + qMax(midh1, midh2);

        ally = midy2;
    }
    return QSize(2 * midx, ally + contentm.bottom());
}

void MTimeLinePrivate::paintDateBkShape(QPainter* painter, const QRect& rect, int whereisy)
{
    const QPoint triangle[] =
    {
        { 0, -6 },
        { 7, 0 },
        { 0, 6 },
    };
    painter->setPen(Qt::NoPen);
    painter->setBrush(QColor(0x34, 0x98, 0xdb));
    painter->drawRoundedRect(rect, 6, 6);
    painter->save();
    painter->translate(rect.right(), whereisy);
    painter->drawPolygon(triangle, 3);
    painter->restore();
}

void MTimeLinePrivate::paintTextBkShape(QPainter* painter, const QRect& rect, int whereisy)
{
    const QPoint triangle[] =
    {
        { 0, -6 },
        { -7, 0 },
        { 0, 6 },
    };
    painter->setPen(Qt::NoPen);
    painter->setBrush(QColor(0xec, 0xf0, 0xf1));
    painter->drawRoundedRect(rect, 6, 6);
    painter->save();
    painter->translate(rect.x(), whereisy);
    painter->drawPolygon(triangle, 3);
    painter->restore();
}

QSize MTimeLinePrivate::calcDateStringSize(const QFontMetrics& fm) const
{
    QDate today = QDate::currentDate();
    QSize datesz = fm.size(0, today.toString(u8"yyyy年MM月dd日"));
    return datesz;
}

void MTimeLinePrivate::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(0, 0, width(), height(), Qt::white); /* 白色背景 */
    QFontMetrics fm = painter.fontMetrics();
    QSize datesz = calcDateStringSize(fm);
    int ally = contentm.top();
    int datex = contentm.left();
    int midx = contentm.left() + datem.left() + datep.left() + datesz.width() + datep.right() + datem.right();
    int textx = midx;
    bool odd = true; /* 奇偶数,用来区分绘制点的样式 */
    for (const auto &item : events)
    {
        /* 绘制左侧日期 */
        QRect dateOuterRect;
        dateOuterRect.setX(datex + datem.left());
        dateOuterRect.setY(ally + datem.top());
        dateOuterRect.setWidth(datep.left() + datesz.width() + datep.right());
        dateOuterRect.setHeight(datep.top() + datesz.height() + datep.bottom());
        const int doty = dateOuterRect.center().y();
        paintDateBkShape(&painter, dateOuterRect, doty);
        QRect dateRect;
        dateRect.setX(dateOuterRect.x() + datep.left());
        dateRect.setY(dateOuterRect.y() + datep.top());
        dateRect.setSize(datesz);
        painter.setPen(Qt::black);
        painter.drawText(dateRect, 0, item.date.toString(u8"yyyy年MM月dd日"));
        /* 绘制右侧字符串 */
        int charx = textx + textp.left() + textm.left();
        int chary = ally + textm.top() + textp.top();
        int charw = width() - charx - (textp.right() + textm.right() + contentm.right());
        QRect textRect = fm.boundingRect(charx, chary, charw, 12000, Qt::TextWordWrap, item.content);
        QRect textOuterRect;
        textOuterRect.setX(textx + textm.left());
        textOuterRect.setY(ally + textm.top());
        textOuterRect.setWidth(textp.left() + textRect.width() + textp.right());
        textOuterRect.setHeight(textp.top() + textRect.height() + textp.bottom());
        paintTextBkShape(&painter, textOuterRect, doty);
        painter.setPen(Qt::black);
        painter.drawText(textRect, Qt::TextWordWrap, item.content);
        /* 绘制中间的线条和点 */
        int midh1 = datem.top() + dateOuterRect.height() + datem.bottom(); /* 左侧高度 */
        int midh2 = textm.top() + textOuterRect.height() + textm.bottom(); /* 右侧高度 */
        int midy2 = ally + qMax(midh1, midh2);
        painter.setPen(QPen(QColor(0, 170, 255), 2));
        painter.drawLine(QPoint(midx, ally), QPoint(midx, midy2));
        painter.setPen(QPen(QColor(0, 170, 255), 8, Qt::SolidLine, odd ? Qt::SquareCap : Qt::RoundCap));
        painter.drawPoint(midx, doty);

        odd = !odd;
        ally = midy2;
    }
}

/

MTimeLine::MTimeLine(QWidget* parent) :
    QWidget(parent)
{
    QGridLayout* lay = new QGridLayout(this);
    lay->setContentsMargins(0, 0, 0, 0);
    QScrollArea* area = new QScrollArea(this);
    tlReal = new MTimeLinePrivate(this);
    area->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    area->setWidget(tlReal);
    area->setWidgetResizable(true);
    lay->addWidget(area);
    setLayout(lay);
}

void MTimeLine::append(const QDate& date, const QString& str)
{
    tlReal->append(date, str);
}

void MTimeLine::removeAt(int index)
{
    tlReal->removeAt(index);
}

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(Qt实战项目,C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

猜你喜欢

转载自blog.csdn.net/m0_73443478/article/details/132474582
今日推荐