How to draw a pointing arrow progress using QPainter
Unsolved
General and Desktop
-
I'm trying to draw something like this:
So far what I got:
I'm not figuring out how to properly draw the left side of the "middle" widget to fit in the "first" widget, the same applies to the last/middle.
I'm also struggling with properly drawing the ">" shape of the arrow, when I resize the arrows get deformed.
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; } } /* path.moveTo(arrowSize, 0); path.lineTo(0, arrowSize); path.lineTo(0, height() - arrowSize); 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); */ 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; };
-
@Rua3n
Not so easy because widgets overlap ...class ProgressWidget : public QWidget { public: ProgressWidget(QWidget* parent=nullptr) : QWidget(parent) { hLayout=new QHBoxLayout(this); hLayout->setSpacing(0); setFixedHeight(50); } void addStep(const QString& text) { auto label=new QLabel(text); label->setAlignment(Qt::AlignCenter); hLayout->addWidget(label); } void setCurrentStep(int n) { if(n<hLayout->count()) currentStep=n; else currentStep=-1; update(); } private: void paintEvent(QPaintEvent*event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // draw steps in reverse order for(int i=hLayout->count()-1; i>=0; i--) { QRect rec=hLayout->itemAt(i)->geometry(); int h=rec.height(); QPainterPath path; if(hLayout->count()>=2) { if(i!=hLayout->count()-1) { path.moveTo(rec.topLeft()); path.lineTo(rec.topRight()); path.lineTo(rec.topRight()+QPoint(h/2,h/2)); path.lineTo(rec.topRight()+QPoint(0,h)); path.lineTo(rec.bottomLeft()); path.lineTo(rec.topLeft()); } else path.addRect(rec); } painter.drawPath(path); if(i==currentStep) painter.fillPath(path,QColor(50,150,250)); else painter.fillPath(path,QColor(200,200,200)); } } QHBoxLayout* hLayout; int currentStep=-1; }; ... QWidget mainWidget; auto layout=new QHBoxLayout(&mainWidget); auto progressWidget=new ProgressWidget; layout->addWidget(progressWidget); progressWidget->addStep("First"); progressWidget->addStep("Second"); progressWidget->addStep("Third"); progressWidget->setCurrentStep(0); mainWidget.show(); mainWidget.setMinimumWidth(400);
-
@Rua3n
Change:
QRectF rec=hLayout->itemAt(i)->geometry();
andpath.moveTo(rec.topLeft()); path.lineTo(rec.topRight()+QPoint(1,0)); path.lineTo(rec.topRight()+QPoint(h/2+1,h/2)); path.lineTo(rec.topRight()+QPoint(1,h)); path.lineTo(rec.bottomLeft()); path.lineTo(rec.topLeft());
-
@mpergand said in How to draw a pointing arrow progress using QPainter:
path.moveTo(rec.topLeft()); path.lineTo(rec.topRight()+QPoint(1,0)); path.lineTo(rec.topRight()+QPoint(h/2+1,h/2)); path.lineTo(rec.topRight()+QPoint(1,h)); path.lineTo(rec.bottomLeft()); path.lineTo(rec.topLeft());
thank you! and about the border-radius, how i can control it?