如何用QPainter绘制衔接式指向箭头进度条?问题求助
问题:衔接式指向箭头进度条绘制故障
想要绘制的衔接式箭头进度条目标效果:
当前实现效果:
存在两个核心问题:
- 中间部件左侧无法与第一个部件的右侧箭头衔接,末尾部件与中间部件的衔接也有同样问题;
- 箭头形状在窗口缩放时会变形,无法保持比例。
实现代码如下:
class ArrowWidget : public QWidget { Q_OBJECT public: QString text; enum Position { FIRST, MIDDLE, LAST }; Position position; ArrowWidget(QWidget* parent, QString text, Position position) : QWidget(parent), text(text), position(position) { } void paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; // Adjust to correctly draw the arrow point. int borderRadius = 12; int arrowSize = 12; switch (position) { case FIRST: { path.moveTo(borderRadius, 0); path.lineTo(width() - arrowSize, 0); path.lineTo(width(), arrowSize); path.lineTo(width(), height() - arrowSize); path.lineTo(width() - arrowSize, height()); path.lineTo(borderRadius, height()); path.quadTo(0, height(), 0, height() - borderRadius); path.lineTo(0, borderRadius); path.quadTo(0, 0, borderRadius, 0); break; } case MIDDLE: { path.moveTo(borderRadius, 0); path.lineTo(width() - arrowSize, 0); path.lineTo(width(), arrowSize); path.lineTo(width(), height() - arrowSize); path.lineTo(width() - arrowSize, height()); path.lineTo(borderRadius, height()); path.quadTo(0, height(), 0, height() - borderRadius); path.lineTo(0, borderRadius); path.quadTo(0, 0, borderRadius, 0); break; } case LAST: { path.moveTo(width(), 0); path.lineTo(width() - borderRadius, 0); path.quadTo(width(), 0, width(), borderRadius); // Bottom right corner path.lineTo(width(), height() - borderRadius); path.quadTo(width(), height(), width() - borderRadius, height()); path.lineTo(0, height()); path.lineTo(0, 0); break; } } painter.fillPath(path, QColor("#4699ea")); painter.setPen(QColor(255, 255, 255)); painter.drawText(0, 0, width(), height(), Qt::AlignCenter, text); painter.end(); } }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent), ui(new Ui::MainWindowClass()) { ui->setupUi(this); setStyleSheet("#centralWidget { background-color: rgba(80, 80, 80, 80); }"); QWidget* widget = new QWidget(this); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); ArrowWidget* arrowWidget = new ArrowWidget(widget, "First", ArrowWidget::FIRST); layout->addWidget(arrowWidget); arrowWidget = new ArrowWidget(widget, "Middle", ArrowWidget::MIDDLE); layout->addWidget(arrowWidget); arrowWidget = new ArrowWidget(widget, "Last", ArrowWidget::LAST); layout->addWidget(arrowWidget); setContentsMargins(32, 32, 32, 32); setCentralWidget(widget); } private: Ui::MainWindowClass *ui; };
解决方案
1. 修复部件衔接问题
当前MIDDLE部件的路径与FIRST完全一致,左侧为圆角,但实际需要做成与前一个部件右侧箭头匹配的凹形。同时LAST部件的左侧也需要适配MIDDLE的右侧箭头,修改路径绘制逻辑如下:
- MIDDLE部件路径:
case MIDDLE: { // 左侧箭头凹槽,匹配前一个部件的右侧箭头 path.moveTo(arrowSize, 0); path.lineTo(0, height() / 2); path.lineTo(arrowSize, height()); // 右侧箭头凸形,与FIRST部件一致 path.lineTo(width() - arrowSize, height()); path.lineTo(width(), height() / 2); path.lineTo(width() - arrowSize, 0); path.lineTo(arrowSize, 0); break; }
- LAST部件路径:
case LAST: { // 左侧箭头凹槽 path.moveTo(arrowSize, 0); path.lineTo(0, height() / 2); path.lineTo(arrowSize, height()); // 右侧圆角 path.lineTo(width() - borderRadius, height()); path.quadTo(width(), height(), width(), height() - borderRadius); path.lineTo(width(), borderRadius); path.quadTo(width(), 0, width() - borderRadius, 0); path.lineTo(arrowSize, 0); break; }
2. 修复箭头变形问题
固定的arrowSize会导致窗口缩放时箭头比例失调,改为基于部件高度动态计算箭头尺寸,保证比例一致:
// 替换固定arrowSize,改为基于高度比例计算 int arrowSize = height() / 4; // 可根据需求调整比例,比如1/3或1/4 arrowSize = qMax(arrowSize, 8); // 限制最小箭头尺寸,避免过小
完整修改后的paintEvent代码
void paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; int borderRadius = 12; // 动态计算箭头尺寸,基于高度比例 int arrowSize = height() / 4; arrowSize = qMax(arrowSize, 8); // 最小尺寸限制 switch (position) { case FIRST: { path.moveTo(borderRadius, 0); path.lineTo(width() - arrowSize, 0); path.lineTo(width(), height() / 2); path.lineTo(width() - arrowSize, height()); path.lineTo(borderRadius, height()); path.quadTo(0, height(), 0, height() - borderRadius); path.lineTo(0, borderRadius); path.quadTo(0, 0, borderRadius, 0); break; } case MIDDLE: { path.moveTo(arrowSize, 0); path.lineTo(0, height() / 2); path.lineTo(arrowSize, height()); path.lineTo(width() - arrowSize, height()); path.lineTo(width(), height() / 2); path.lineTo(width() - arrowSize, 0); path.lineTo(arrowSize, 0); break; } case LAST: { path.moveTo(arrowSize, 0); path.lineTo(0, height() / 2); path.lineTo(arrowSize, height()); path.lineTo(width() - borderRadius, height()); path.quadTo(width(), height(), width(), height() - borderRadius); path.lineTo(width(), borderRadius); path.quadTo(width(), 0, width() - borderRadius, 0); path.lineTo(arrowSize, 0); break; } } painter.fillPath(path, QColor("#4699ea")); painter.setPen(QColor(255, 255, 255)); painter.drawText(0, 0, width(), height(), Qt::AlignCenter, text); }
额外优化建议
- 将
borderRadius和箭头比例设置为类成员变量,方便统一调整样式; - 给ArrowWidget设置最小尺寸,避免部件过小导致文字和箭头显示异常:
ArrowWidget(QWidget* parent, QString text, Position position) : QWidget(parent), text(text), position(position) { setMinimumSize(100, 40); // 可根据需求调整 }
内容的提问来源于stack exchange,提问作者Jabu
相关产品推荐
相关产品推荐

