I use Python 3.7 and Pyside2.
I would like to change color, font, background color... but I can not !
I import QtGui for design but I have the same error 'Window' object has no attribute 'setBrush'
from PySide2.QtGui import QColor, QBrush, QPainterPath, QFont
from PySide2.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox, QDesktopWidget
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Convertisseur de devises")
self.setGeometry(700,300,700,300)
self.setBrush(QColor(255, 0, 0, 127))
self.setButton()
self.center()
def setButton(self):
btn = QPushButton("Inverser Devises", self)
btn.move(550,135)
def center(self):
qRect = self.frameGeometry()
centerPoint = QDesktopWidget().availableGeometry().center()
qRect.moveCenter(centerPoint)
self.move(qRect.topLeft())
myapp = QApplication(sys.argv)
window = Window()
window.show()
myapp.exec_()
sys.exit()
For exemple :
setButton Remove the background color, borders, white writing ...
window Change the background color
Thank you for your help
Instead of changing the Painter just use a stylesheet. Qt stylesheets use CSS syntax and can be easily reused for multiple widgets. More info here: https://doc.qt.io/qt-5/stylesheet-syntax.html
In your case for example you could replace
self.setBrush(QColor(255, 0, 0, 127))
with
self.setStyleSheet('background-color: rgb(0, 0, 127)')
to change the background color to Blue.
To make it reusable though it would make sense to put the Stylesheet into a seperate file. Place the stylesheet in the same folder as your Python file.
style.qss:
QWidget {
background-color: rgb(0, 0, 127);
}
QPushButton {
border: none;
color: white;
}
And then replace
self.setBrush(QColor(255, 0, 0, 127))
with
# This gets the folder the Python file is in and creates the path for the stylesheet
stylesheet_path = os.path.join(os.path.dirname(__file__), 'style.qss')
with open(stylesheet_path, 'r') as f:
self.setStyleSheet(f.read())
And since you set the style on the parent widget all child widgets (including your Button) will have the same style aswell.
Related
what i want to achieve
I am learning how to use pyqt5 with this project of mine.
I tried downloading something called BlurWindow but it kept giving me a parameter error so switched back to trying to use QGraphicBlurEffect but it blurs everything inside my MainWindow
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
import sys
from PyQt5.uic import loadUi
from BlurWindow.blurWindow import blur
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
loadUi(r'D:\Workspace\Qt Designer\blur bg\blurtest.ui',self)
self.setAttribute(Qt.WA_TranslucentBackground)
hWnd = self.winId()
print(hWnd)
blur(hWnd)
self.setStyleSheet("background-color: rgba(0, 0, 0, 0)")
app=QApplication(sys.argv)
mainwindow=MainWindow()
widget=QtWidgets.QStackedWidget()
widget.setWindowOpacity(0.5)
widget.addWidget(mainwindow)
widget.setFixedHeight(600)
widget.setFixedWidth(800)
widget.show()
sys.exit(app.exec_())
BlurWindow uses system features to set the background of a top level window.
Your problem is that you're applying it to the wrong widget, which is a child widget, not a top level one. The top level has no "glass effect" set, so the result is that it won't have any effect applied on it.
The solution is simple: apply the effect to the top level window.
import sys
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUi
from BlurWindow.blurWindow import blur
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
loadUi(r'D:\Workspace\Qt Designer\blur bg\blurtest.ui', self)
self.setStyleSheet("""
MainWindow {
background-color: rgba(0, 0, 0, 0);
}
""")
app = QApplication(sys.argv)
mainwindow = MainWindow()
widget = QtWidgets.QStackedWidget()
widget.addWidget(mainwindow)
widget.setFixedHeight(600)
widget.setFixedWidth(800)
blur(widget.winId()) # <---
widget.show()
sys.exit(app.exec_())
Note that:
QMainWindow is not supposed to be used as a child widget. You should switch to a basic QWidget (or other container widgets like QFrame), meaning that you should create a new "widget" in Designer and copy/paste the content of your previous window to it, otherwise loadUi() will throw an exception;
you should never apply generic style sheet properties to parent widgets, as you would get unexpected results (especially with complex widgets, like scroll areas); you should always use proper selectors (as I did above);
I have added a widget in my mainwindows and I would like to give it a show something like box-shadow: 3px 3px 25px #111;
I tried above by going to widget change stylesheet option and adding the code as below:
background-color:#fff;
border:4px solid blue;
box-shadow: 0px -3px 5px #a6a6a6;
The first two attribute give the expected effect but box-shadow don't work.
How to use Python QT Designer and add box shadow to a widget?
Qt StyleSheet is not CSS but it is a technology that implements some features, and among them is not the box-shadow. If you want to implement something similar then you should use QGraphicsDropShadowEffect:
import sys
from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
QApplication,
QGraphicsDropShadowEffect,
QMainWindow,
QVBoxLayout,
QWidget,
)
def main():
app = QApplication(sys.argv)
main_window = QMainWindow()
container = QWidget()
container.setContentsMargins(3, 3, 3, 3)
main_window.setCentralWidget(container)
widget = QWidget()
widget.setAutoFillBackground(True)
lay = QVBoxLayout(container)
lay.addWidget(widget)
effect = QGraphicsDropShadowEffect(
offset=QPoint(3, 3), blurRadius=25, color=QColor("#111")
)
widget.setGraphicsEffect(effect)
main_window.resize(640, 480)
main_window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
It is recommended that you read the Qt Stylesheet references:
https://doc.qt.io/qt-5/stylesheet-reference.html
https://doc.qt.io/qt-5/stylesheet-syntax.html
if I use the setStyleSheet method in order to change the style for a specific widget, the other ones placed inside it, changes their style, but I don't want it! I can bring you two example:
when I change the border/background color for a frame (see the widgets placed inside it):
import PyQt5.QtGui as qtg
import PyQt5.QtCore as qtc
import PyQt5.QtWidgets as qtw
import sys
class MainWindow(qtw.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(520,300)
self.setWindowTitle("Treeview Example")
self.layout = qtw.QVBoxLayout()
self.frame1=qtw.QFrame()
self.frame1layout=qtw.QVBoxLayout()
self.frame1layout.setContentsMargins(5, 5, 5, 5)
self.frame1.setLayout(self.frame1layout)
self.frame1.setStyleSheet("border: 1px solid; border-color:red; background-color:white") # I change the style for the main frame
self.layout.addWidget(self.frame1)
self.frame2=qtw.QFrame()
self.frame2layout=qtw.QVBoxLayout()
self.frame2layout.setContentsMargins(10, 10, 10, 10)
self.frame2.setLayout(self.frame2layout)
self.frame1layout.addWidget(self.frame2)
self.ProgressBar=qtw.QProgressBar()
self.frame2layout.addWidget(self.ProgressBar)
self.setLayout(self.layout)
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
or when I change the border color for a treeview widget (the scrolled treeview widget is became white):
import PyQt5.QtGui as qtg
import PyQt5.QtCore as qtc
import PyQt5.QtWidgets as qtw
import sys
class MainWindow(qtw.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(520,300)
self.setWindowTitle("Treeview Example")
self.layout = qtw.QVBoxLayout()
self.treeview = qtw.QTreeView(self)
self.treeview.setStyleSheet("border: 1px solid; border-color:red") # it destroy the style of the objects inside the treeview widget!
model = qtg.QStandardItemModel()
rootNode = model.invisibleRootItem()
section1 = qtg.QStandardItem("A")
section1.appendRow([qtg.QStandardItem("A1")])
childnode = qtg.QStandardItem("A2")
section1.appendRow([childnode])
section2 = qtg.QStandardItem("B")
section2.appendRow([qtg.QStandardItem("B1")])
section2.appendRow([qtg.QStandardItem("B2")])
rootNode.appendRow([section1])
rootNode.appendRow([section2])
self.treeview.setHeaderHidden(True)
self.treeview.setModel(model)
self.layout.addWidget(self.treeview)
self.setLayout(self.layout)
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
my question is, how can I change the style of a specific widget without modifying the style of the other ones?
###########
UPDATE:
in my first example, if I don't use the instruction self.treeview.setStyleSheet("border: 1px solid; border-color:red") I realized that the progress bar widget doesn't expand as before. see this screenshot:
what is the issue?
I strongly suggest you to more carefully read the style sheet syntax and reference documentation, as everything is clearly specified and explained there:
Style sheets consist of a sequence of style rules. A style rule is made up of a selector and a declaration. The selector specifies which widgets are affected by the rule; the declaration specifies which properties should be set on the widget.
Stylesheets are, by definition, cascading.
The stylesheet set on a widget is propagated on its children, those children inherit the style of the parent.
If you set a generic property like this:
border: 1px solid black;
The result is that all its children will have that border: you only gave the declaration but without the selector, so a universal selector is used as implicit.
This is not just Qt, this is typical of CSS (from which QSS take their main concepts), and it works exactly as any other widget styling property: setting a font or a palette on a widget, propagates them to all its children.
The difference is that with stylesheets (exactly like standard CSS) you can use selectors.
If you want to style the tree widget only, then use that class selector:
self.treeview.setStyleSheet('''
QTreeView {
border: 1px solid;
border-color:red;
}
''')
I am trying to write a CSS style sheet for toy PyQt5 application. I have successfully defined a classes MainWindow(QMainWindow) and DefaultWidget(QWidget), with the latter containing two push buttons, which I am trying to stylize.
Using the C++ docs, I've been trying to take advantage of the QPushButton#evilButton example, provided in: Link
However, it appears that the # notation for an instance name does not translate to the analogous self.setStyleSheet() method for an external CSS file in my python application.
Python file (apologies if there are typos - transcribing from another computer):
import sys
from PyQt5.Widgets import QApplication, QMainWindow, QWidget, QPushButton, QHBoxLayout
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setStyleSheet(open('./style.css').read())
try:
self.setCentralWidget(DefaultWidget())
else:
some_error_stuff_for_troubleshooting
self.show()
class DefaultWidget(QWidget):
def __init__(self):
super().__init__()
okButton = QPushButton('OK')
cancelButton = QPushButton('Cancel')
hbox = QHBoxLayout()
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
self.setLayout(hbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
In the CSS file...
QWidget.MainWindow {
background: blue
}
QPushButton {
background-color: beige
}
QPushButton???okButton {
background-color: green
}
Where I put the ??? I have tried a number of things (trying to walk through the classes QPushButton.okButton, QPushButton.DefaultWidget.okButton, DefaultWidget.okButton, with ., #, :: and : notation, etc.) but at this point it's more random than educated guessing.
I would like for the CSS to handle some naming convention styling, outside of the class name to help pull as much style out of the python file as possible. Even if the notation happened to be correct, am I not seeing my expected color change due to inherited style?
Edit: If anyone stumbles across this, the .setObjectName() method or declaring the object name option when instancing the widget. Also, here's a similar question on why some sort of default name isn't used, if you're interested.
PyQt: is there an better way to set objectName in code?
Try it:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QHBoxLayout
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# open('./style.css').read())
self.setStyleSheet("""
QWidget {
background: blue;
}
QPushButton {
background-color: beige;
}
QPushButton#okButton { /* <--- #okButton */
background-color: green;
}
""")
defaultWidget = DefaultWidget()
self.setCentralWidget(defaultWidget)
self.show()
class DefaultWidget(QWidget):
def __init__(self):
super().__init__()
okButton = QPushButton('OK', objectName="okButton") # + objectName="okButton"
cancelButton = QPushButton('Cancel')
hbox = QHBoxLayout()
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
self.setLayout(hbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
Consider the following example code:
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QLabel, QWidget,
QMainWindow, QVBoxLayout, QTextEdit)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
cwidget = QWidget(self)
cwidget.setStyleSheet("QWidget { background-color: red; }")
self.setCentralWidget(cwidget)
self.resize(100, 100)
vbox = QVBoxLayout(cwidget)
vbox.addWidget(QTextEdit(self))
vbox.addWidget(BlackBar(self))
class BlackBar(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setStyleSheet("* { background-color: black; color: white; }")
hbox = QHBoxLayout(self)
hbox.setSpacing(5)
hbox.addWidget(QLabel(text="eggs"))
hbox.addWidget(QLabel(text="bacon"))
if __name__ == '__main__':
app = QApplication([])
main = MainWindow()
main.show()
app.exec_()
It has:
A QMainWindow, QWidget as central widget (red), QVBoxLayout as a child of the cental widget. Inside there:
A QTextEdit (just as a filler)
A QWidget (black), which contains a QHBoxLayout. Inside that:
Two QLabels
This looks like this:
I'd expect the spaces between the labels to be black, because the QHBoxLayout is a child of BlackBar, but it seems BlackBar is just "invisible" in between and the central widget "shines through". Why is this?
The bugreport has now been answered with a solution that's easier than #ekhumoro's answer and works:
I don't think this is valid. The paint code your are looking for is not drawn in the paintEvent. Look for QWidgetPrivate::paintBackground instead. For performance reasons widgets will ignore style sheets by default, but you can set the WA_StyledBackground attribute on the widget and it should respect style sheet backgrounds.
And indeed, doing this before setting the stylesheet does the trick:
self.setAttribute(Qt.WA_StyledBackground)
Although the Style Sheet Syntax does not mention it, it seems that the QWidget class is treated differently when it comes to stylesheets.
Other widgets will work fine with your example code. For example, if QWidget is replaced everywhere with QFrame, then everything works as expected.
To get stylesheet support for QWidget subclasses, you need to reimplement the paintEvent and enable it explicitly:
class BlackBar(QWidget):
...
def paintEvent(self, event):
option = QStyleOption()
option.initFrom(self)
painter = QPainter(self)
self.style().drawPrimitive(
QStyle.PE_Widget, option, painter, self)