You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:02:05