I came across a button style which I like, I wanted to recreate it for use in pyqt5, using the QPushButton class, and styling it with css. If I can recreate it, I can change it’s colours in cool ways.
In CSS terms, the button has a “solid” border of 1px all around, with a “border radius” of 2px, and I found it at 21px tall, however obviously it’s resizeable. If anyone is wondering, it is the humble “Vista” button:
Vista Button
The PyQt framework implements a good number of of CSS-3 features (however not all of them) so somebody with knowledge of only CSS may know how this is done.
My inexperience with CSS is letting me down, unfortunately. Usually I can brute-force my way through a problem, and then tidy up and optimise my implementation, however here I have hit a bit of a stump.
90% of the look of the button can be created with a simple linear gradient for the background-color, however there is an “inner border” of 1px which is flush with the real border and goes all the way around the button, I am having trouble re-creating this look.
I tried messing with border styles, even with using gradient colours for the border, and I am not having any luck. Something that seemed useful called the “double” border style unfortunately was of no use to me, since you cannot control both border colours seperately very well.
Here is a crude diagram to explain the gist of what I need to do:
Crude diagram explaining issue
In this diagram, (c) is a gradient fill which doesn’t share colours with anything else on the diagram.
Now, I’m aware that using linear-gradients means that if something is “one pixel” wide or tall, this will change if the object is ever resized, and the ratios are recalculated, I am less concerned about this for now, however it’s something I’d probably consider as the next problem.
Step 1), Just the button, no styling attempts apart from the border.
Minimum reproducible code:
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout)
app = QApplication([])
window_container = QWidget()
window_container.setWindowTitle("Buttons")
window_container.setMinimumSize(200,125)
window_container.setObjectName("window_container")
window_container.setStyleSheet("QPushButton {"
+ "border-style: solid;"
+ "border-width: 1px 1px 1px 1px;"
+ "border-radius: 2px;"
+ "border-color: #000000;"
+ "font: 9;"
+ "padding: 3px;"
+ "}"
)
button_1 = QPushButton("Top")
button_2 = QPushButton("Middle")
button_3 = QPushButton("Bottom")
layout = QVBoxLayout()
layout.addWidget(button_1)
layout.addWidget(button_2)
layout.addWidget(button_3)
window_container.setLayout(layout)
window_container.show()
sys.exit(app.exec())
This creates a very basic window like this (apologies, transparency effects were on):
basic “unstyled” buttons example
Now, with a simple “qlineargradient” (because everything has Q in front of it…), you can actually get 95% accurate in recreating the button.
Something like this as a crude simple example, added to the above “setStyleSheet”:
+ "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, "
+ "stop:0 #F2F2F2, "
+ "stop:0.11 #F1F1F1, stop:0.47 #EBEBEB, stop:0.53 #DDDDDD, "
+ "stop:1.0 #CFCFCF);"
Which, with a few more “stops” carefully tuned, creates the following:
Buttons with one vertical linear gradient applied
(Close, but no sigar)
In this example I created the top and bottom “inner border” colours (252,252,252 & 243,243,243) as well as the main inner gradients using just one linear gradient, which is obviously a bit hacky, but it does the job for illustrative purposes, however doesn’t cover the left and right sides. I actually think I have reached the limit of what qlineargradient will let me imitate, so now am wondering if there’s another way, or maybe I am just bad with the linear gradient tool.
Obviously there’s loads of things you can do, in theory, for example you could slap a second object on top of the first and if they resize together nobody would know the difference, but I am trying to make this as one object, styled with one stylesheet snippet.
Maybe someone finds this fun or a challenge, I am happy to try anything (within reason).
(for anyone wondering what the difference is, look at the left and right edges of the first screenshot and the last, the first has “whitish” inner borders on the sides and mine does not).
bfh47 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.