这是一个垂直的时间轴,显示效果非常简单,但不能显示有格式的文本。如果想显示有格式文本可以把右侧显示本文的位置换成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); }
标签:控件,const,自定义,int,top,时间轴,MTimeLinePrivate,painter,left From: https://www.cnblogs.com/mengxiangdu/p/16945503.html