c++ 如何在具有圆形边界的QWidget上应用QLinearGradient

efzxgjgh  于 5个月前  发布在  其他
关注(0)|答案(2)|浏览(66)

我尝试在QPushButton边框上使用样式表和QLinearGradient来动画计时器进度。
我是这么做的

#include <QApplication>
#include <QPushButton>
#include <QTimer>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton button("Animation Button");

    float greenPosition = 0.99;
    float whitePosition = 0.01;
    QTimer timer;
    enum Border {Top = 1, Left = 2, Bottom = 3, Right = 4};
    int border = 1;

    button.connect(&timer, &QTimer::timeout, [&button, &greenPosition, &whitePosition, &border]()
    {
        //Left or Bottom
        if(border == 2 || border == 3)
        {
            if(whitePosition + 0.01 > 1)
            {
                whitePosition = 0;
                border = border++ % 4 + 1;
            }
            whitePosition += 0.01;
        }
        else
        {
            if(greenPosition - 0.01 < 0.01)
            {
                greenPosition = 1;
                border = border % 4 + 1;
            }
            greenPosition -= 0.01;
        }

        switch (border)
        {
            case Top:
                button.setStyleSheet(QString("border: 2px solid white;"
                                           //"border-radius: 5px;"
                                           "border-top: 2px solid qlineargradient(x0:0, x2:1,"
                                           "stop: 0 green,"
                                           "stop: %1 green,"
                                           "stop: %2 white,"
                                           "stop: 1 white);").arg(greenPosition).arg(greenPosition + 0.01));
                break;
            case Left:
                button.setStyleSheet(QString("border: 2px solid white;"
                                           //"border-radius: 5px;"
                                           "border-left: 2px solid qlineargradient(y0:0, y2:1,"
                                           "stop: 0 white,"
                                           "stop: %1 white,"
                                           "stop: %2 green,"
                                           "stop: 1 green);").arg(whitePosition).arg(whitePosition + 0.01));

                break;
            case Bottom:
                button.setStyleSheet(QString("border: 2px solid white;"
                                           //"border-radius: 5px;"
                                           "border-bottom: 2px solid qlineargradient(x0:0, x2:1,"
                                           "stop: 0 white,"
                                           "stop: %1 white,"
                                           "stop: %2 green,"
                                           "stop: 1 green);").arg(whitePosition).arg(whitePosition + 0.01));
                break;
            case Right:
                button.setStyleSheet(QString("border: 2px solid white;"
                                           //"border-radius: 5px;"
                                           "border-right: 2px solid qlineargradient(y0:0, y2:1,"
                                           "stop: 0 green,"
                                           "stop: %1 green,"
                                           "stop: %2 white,"
                                           "stop: 1 white);").arg(greenPosition).arg(greenPosition + 0.01));
                break;
        }
    });

    timer.start(50);
    button.show();

    return a.exec();
}

字符串
这是我想出的逻辑,来创建这个动画:
greenPositionwhitePosition是控制QLinearGradient中的绿色和白色范围的值,绿色是进度,白色是实际边框颜色。
基本的想法是,我让白色后退,绿色前进,反之亦然。
它们的值之间有一小部分是为了避免梯度效应,因此我添加了0.01。如果greenPositionwhitePosition达到[1.00 - 0.00]范围的限制,它们将重置为默认值。
border是一个变量,它允许我在交换机的情况下遍历4边界,只是一个简单的循环计数器。
所有这些循环都使用QTimer
结果如下:
x1c 0d1x的数据
我需要圆形边框,但当我使用它们时,它看起来像这样:



边框似乎自己形成了按钮的另一面,就像圆形边框一样,按钮现在有8个边。
这是因为圆形边框上的动画似乎与当前正在设置动画的边框同步,而不是在动画到达它们时才设置动画。这看起来也像是一种镜像效果。

**注:**这只是一个观察,以解释它看起来如何,而不是实际发生了什么。

下面是在使用圆形边框时应用不带QLinearGradient的绿色颜色的效果:



如何以与非圆形边界相同的方式设置圆形边界的动画?

flvtvl50

flvtvl501#

由于使用样式表设置边框半径会产生不必要的效果,因此您可以在没有样式表的情况下将小部件边框设置为圆形,并使用paintEvent;方法如下:
你可以画一个圆角矩形,颜色和小部件的父背景一样,这就好像它隐藏了那些尖角,让它们看起来是圆的。
这种方法的一个缺点是它限制了角的圆度,因为如果画家画了一个太圆的矩形,它会掩盖小部件本身或导致尖锐的边缘。
可以通过增加边框大小来稍微解决这个问题,以获得更多的空间,因此,在使用绘图器时可以增加边框半径,而不会破坏小部件边缘。
使用这种方法有一个好处,它可以防止窗口小部件的背景突出边框。
下面是一个从QPushButton派生的子类的例子,它带有一个自定义的paintEvent

class button : public QPushButton
{
    Q_OBJECT

public:
    button (QWidget *parent = nullptr) : QPushButton(parent) {}

protected:
    void paintEvent(QPaintEvent *event) override
    {
        QPushButton::paintEvent(event);

        QRectF r = rect();
        r.adjust(-1,-1,+1,+1);

        QPainter p(this);
        QColor color = parentWidget()->palette().brush(QPalette::Window).color();
        p.setPen(QPen(color,2));
        p.setRenderHint(QPainter::Antialiasing);
        p.drawRoundedRect(r, 6, 6);
    }
};

字符串
下面是结果,底部按钮是一个普通的QPushButton,没有边界半径,只是为了增加差异的可见性:
x1c 0d1x的数据

gajydyqb

gajydyqb2#

对于这些类型的功能,QML提供了一个非常灵活的工具集。
我已经实现了我认为你在QML中寻找的东西:

Canvas {
        id: border_animation
        property real progress: 0
        onProgressChanged: {
            border_animation.requestPaint()
        }
        anchors.centerIn: parent
        height: parent.height*0.10
        width: parent.width*0.65
        z: 2
        Text {
            text: "start timer"
            color: "white"
            font.bold: true
            font.pointSize: 15
            anchors.centerIn: parent
        }
        NumberAnimation on progress {
            id: startTimer
            from: 0
            to: 1
            running: false
            duration: 1000
            onFinished: {
                startTimer.start()
            }
        }
        onPaint: {
            var ctx = border_animation.getContext('2d')

            ctx.strokeStyle = "green"
            ctx.fillStyle = "black"
            ctx.lineWidth = 2

            ctx.beginPath()
            ctx.rect(0,0,width,height)
            ctx.fill()

            var Lcenterx = width*0.25
            var Lcentery = height*0.50
            var Rcenterx = width*0.75
            var Rcentery = height*0.50
            var radius = height*0.40

            //each section is 25% of the border so we break it up that way
            if(progress<=0.25){
                var topProgress = progress/0.25
                var rightProgress = 0
                var bottomProgress = 0
                var leftProgress = 0
            }else if(progress<=0.50){
                topProgress = 1
                rightProgress = (progress-0.25)/0.25
                bottomProgress = 0
                leftProgress = 0
            }else if(progress<=0.75){
                topProgress = 1
                rightProgress = 1
                bottomProgress = (progress-0.50)/0.25
                leftProgress = 0
            }else{
                topProgress = 1
                rightProgress = 1
                bottomProgress = 1
                leftProgress = (progress-0.75)/0.25
            }

            //top line
            ctx.strokeStyle = "orange"
            ctx.beginPath();
            ctx.moveTo(Lcenterx, Lcentery-radius);
            ctx.lineTo(Lcenterx + (Rcenterx - Lcenterx)*topProgress, Rcentery-radius);
            ctx.stroke();

            ctx.strokeStyle = "yellow"
            ctx.beginPath();
            ctx.arc(Rcenterx, Rcentery, radius, 1.5*Math.PI, 1.5*Math.PI + Math.PI * rightProgress);
            ctx.stroke();

            //bottom line
            ctx.strokeStyle = "cyan"
            ctx.beginPath();
            ctx.moveTo(Rcenterx, Lcentery+radius);
            ctx.lineTo(Rcenterx - (Rcenterx - Lcenterx)*bottomProgress, Rcentery+radius);
            ctx.stroke();

            ctx.strokeStyle = "green"
            ctx.beginPath();
            ctx.arc(Lcenterx, Lcentery, radius, 0.5*Math.PI, 0.5*Math.PI + Math.PI * leftProgress);
            ctx.stroke();

        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                startTimer.start()


            }
        }
    }

字符串


的数据


相关问题