aRaMinet
aRaMinet

Reputation: 103

QPushButton rounded gradient border with QSS

I'm trying to make some "complex" button styling with Qt, using QSS, but I'm facing an issue that I can't resolve.

I want to do a gradient rounded border, for example going from blue on the left side to red on the right side:

result wanted

So, here is the stylesheet applied to a QPushButton:

background:
    white;
border-radius:
    30px;
border-style:
    solid;
border-width:
    10px;
border-color: 
    qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 blue, stop: 1 red)
    red
    qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 blue, stop: 1 red)
    blue;

And here is the result.

Pretty ugly, right?

Upvotes: 3

Views: 8194

Answers (3)

Asad Naqvi
Asad Naqvi

Reputation: 11

This might helpful for you, late but might help somebody

import sys

from PyQt5.QtCore import pyqtSignal
import sys
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, 
QWidget, QFrame

class StyledButton(QFrame):
    clicked = pyqtSignal()

    def __init__(self, parent=None, size_=(150, 50), border_=3, 
radius_=10):
    super().__init__(parent)
    self.size_ = size_
    self.border_ = border_
    self.radius_ = radius_
    self.init_ui()

def init_ui(self):
    if self.size_ is not None:
        self.setFixedSize(self.size_[0] + (self.border_ * 2), self.size_[1] + (self.border_ * 2))
    self.setFrameStyle(QFrame.Box | QFrame.Plain)
    self.setStyleSheet(f"border-radius: {self.radius_}px;")  # Set rounded corners

    gradient_style = f"border: none; border-radius: {self.radius_ * 0.5}px; background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #f00, stop: 0.25 #f0f, stop: 0.5 #f00, stop: 0.75 #00f, stop: 1 #0f0);"
    self.setStyleSheet(gradient_style)

    self.button = QPushButton('Button')
    self.button.setFixedSize(self.size_[0], self.size_[1])
    self.button.setStyleSheet('background-color: black; color: white;')

    self.layout = QVBoxLayout(self)
    self.layout.setContentsMargins(self.border_, self.border_, self.border_, self.border_)

    self.layout.addWidget(self.button)

    # Connect QPushButton's clicked signal to emit the custom signal from StyledButton
    self.button.clicked.connect(self.emit_custom_signal)

def emit_custom_signal(self):
    self.clicked.emit()

def fun():
    print("Button is clicked")

def main():
    app = QApplication(sys.argv)

    layout = QVBoxLayout()
    combined_widget = StyledButton()
    combined_widget1 = StyledButton()
    combined_widget1.button.setStyleSheet('background-color: green; color: red;')
    combined_widget2 = StyledButton()
    combined_widget3 = StyledButton()

    combined_widget.clicked.connect(fun)  # Connect the custom signal to the function
    layout.addWidget(combined_widget)
    layout.addWidget(combined_widget1)
    layout.addWidget(combined_widget2)
    layout.addWidget(combined_widget3)

    window = QWidget()
    window.setLayout(layout)
    window.show()

    sys.exit(app.exec_())

 if __name__ == "__main__":
    main()

round button with gradient

round button with gradient

Upvotes: 0

Link H.
Link H.

Reputation: 808

This issue has been reported to Qt as a bug and there is no indication that they will ever fix it: https://bugreports.qt.io/browse/QTBUG-2221

I was able to work around it by creating a .png image on paint.net (you can use any image creation program) of this exact border. I set the background to transparent, and made sure the border of the image was the border I wanted on the QPushButton. I then set the .png file up as a resource and entered this in the QPushButton stylesheet:

border: none;
border-image: url(:/icons/images/blue-red-gradient.png);
background-color: rgb(255, 255, 255);
border-radius: 15px;

Here is what the final result looked like on my QMainWindow: enter image description here

Another thing that you can do is to subclass a QPushButton and override it's paint event. Paint your border there and promote all of your QPushButtons to this new class. This would be more of a pain though, so I prefer my first solution personally.

Upvotes: 3

Neil
Neil

Reputation: 400

I searched a little and i saw two ways, but the second uses border-image wich is not compatible with border radius, so you will have to deal with :before and :after.

just to link you the answer from camilo martin :

See the answer here

Upvotes: 0

Related Questions