This question already has answers here:
Transparent Background in QWebEnginePage
(2 answers)
Closed 1 year ago.
I am trying to display some html in PyQT5 with QWebEngine. The problem is that the background (body) of the html is set to 'transparent', but QT won't display it as transparent.
I have been able to make the window frameless, and have been able to make the white part around the html that QT adds transparent, but no where on the internet can I find a way to have QT correctly render the transparent body background as actually transparent (instead of white).
If anyone can help, I would really appreciate it!
import os
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from PyQt5.QtWebChannel import QWebChannel
my_html = """<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
background-color: transparent;
}
#square {
width: 200px;
height: 200px;
background-color: red;
position: absolute;
top: 100px;
left: 100px;
}
</style>
</head>
<body>
<div id="square"></div>
</body>
</html>
"""
class Browser(QtWebEngineWidgets.QWebEngineView):
def __init__(self, html):
super().__init__()
self.url = QtCore.QUrl.fromLocalFile(os.getcwd() + os.path.sep)
self.page().setHtml(html, baseUrl=self.url)
class Window(QtWidgets.QMainWindow):
def __init__(self, html):
super().__init__()
self.html = html
self.init_widgets()
self.init_layout()
self.setFixedSize(400, 400)
# these make the border QT adds transparent, and removes the title bar
# but doesn't affect the body background of the html which is set to transparent
self.setStyleSheet("background: transparent; border: transparent;")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setAutoFillBackground(True) # don't know what this does, as far as I know, nothing
def init_widgets(self):
self.browser = Browser(self.html)
def init_layout(self):
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.browser)
central_widget = QtWidgets.QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def start():
app = QtWidgets.QApplication(sys.argv)
window = Window(my_html)
window.show()
app.exec_()
if __name__ == '__main__':
start()
Picture for the result of the code above
I found answer in old question for Qt and C/C++:
Transparent Background in QWebEnginePage
page has default background white and you have to change it
page = self.page()
page.setBackgroundColor(QtCore.Qt.transparent)
page.setHtml(html, baseUrl=self.url)
Related
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;
}
''')
Python 3.6 PYQT 5.12.1
I am ready to show the style I need by pyqt5 and I knew that the QTextEdit in pyqt5 can display the html code pretty good (I have some experience in web development), so I decided to use html/css to show my style . However , it may have some problem in showing the code in css . What can I do to let it can show the css/javascript ? If it can‘t , can recommend other methods to modify the style?
It can show some style like width = "100" height = "100" when I code it in the html but not css and some can't display like border-radius:50%;. It won't get any effect when I code the style in css . By the way , I've imported CSS code.
The CSS code do nothing in QTextEdit (but it is ok in html)
.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class WINDOW(QMainWindow):
def __init__(self):
super().__init__()
self.init()
def init(self):
w_width,w_height,w_x,w_y = 700,640,700,200
self.set_size_pos(w_width,w_height,w_x,w_y);
self.set_message_textedit()
self.message_textedit.setHtml(self.get_html())
self.show()
def set_size_pos(self,width,height,x,y):
'''
set window's width , height and position
'''
self.resize(width,height)
self.move(x,y)
def set_message_textedit(self):
self.message_textedit = QTextEdit(self)
self.message_textedit.setFont(QFont("Microsoft YaHei",12))
self.message_textedit.resize(680,420)
self.message_textedit.move(10,50)
self.message_textedit.setReadOnly(True)
def get_html(self):
html = ""
with open("./chat-style/chat.html","r",encoding = "utf-8") as f:
html = f.read()
return html
if __name__ == '__main__':
app = QApplication(sys.argv)
test = WINDOW()
sys.exit(app.exec_())
.html
<!doctype html>
<html lange="zh-CN">
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="./chat.css">
<script src = "chat.js"></script>
</head>
<body>
<img class = "tx" src="E:\study\assiataant\picture\icon.jpg">
<div>Welcome ~ Don!</div>
</body>
</html>
.css
.tx
{
border-radius:50%;
width: 30;
height: 30;
}
QTextEdit only supports CSS 2.1 as indicated by the docs:
All CSS 2.1 selector classes are supported except pseudo-class selectors such as :first-child, :visited and :hover.
But border-radius was introduced in CSS3. So you can not use it unfortunately. I recommend you read the following link so that you know the allowed tags.
Another alternative is to use QWebEngineView that supports these tags:
*.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
view = QtWebEngineWidgets.QWebEngineView()
file = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"chat-style/chat.html"
)
view.load(QtCore.QUrl.fromLocalFile(file))
self.setCentralWidget(view)
self.resize(640, 480)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
If you do not have QWebEngineView installed, you must install it with the following command:
python -m pip install PyQtWebEngine
I want to display a border around a QWidget that wraps a QLabel (this is practice for a more complicated widget). I'm using setStyleSheet to create the border. When I did this manually, it worked as expected. However, when I moved the code into its own class (derived from QWidget), the padding is different, and I can't figure out why.
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication, QMainWindow, QVBoxLayout
class WrappedLabel(QWidget):
def __init__(self, text=''):
super().__init__()
self.text = QLabel(text)
layout = QVBoxLayout()
layout.addWidget(self.text)
self.setLayout(layout)
self.setStyleSheet('padding: 2px; border: 2px solid red;')
class Shell(QMainWindow):
def __init__(self): # constructor
super().__init__() # call the parent's constructor
w = QWidget() # Create the main window content widget
self.setCentralWidget(w)
# First label
unwrapped_label = QLabel('This is a normal QLabel with a border and no padding.')
unwrapped_label.setStyleSheet('border: 2px solid gray; padding: 2px;')
# Second label
wrapped_label = QLabel('This QLabel is manually wrapped in a styled QWidget. ' +
'There is a slight indent compared to the normal QLabel due to padding.')
wrapped_layout = QVBoxLayout()
wrapped_layout.addWidget(wrapped_label)
manual_wrapper = QWidget()
manual_wrapper.setObjectName('wrapper')
manual_wrapper.setLayout(wrapped_layout)
self.setStyleSheet('QWidget#wrapper { border: 2px solid gray; padding: 2px; }')
# Third label
derived_wrapper = WrappedLabel('This class derives from QWidget and wraps a QLabel like above, but is indented even further and the border is in the wrong spot.')
vbox = QVBoxLayout()
vbox.addWidget(unwrapped_label)
vbox.addWidget(manual_wrapper)
vbox.addWidget(derived_wrapper)
vbox.addStretch(1) # Squish them together to better see the spacing
w.setLayout(vbox)
# Setup the rest of the main window appearance
self.setGeometry(300,300,640,180)
self.setWindowTitle('Testing wrapped labels')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
shell = Shell() # create and show the main window
sys.exit(app.exec_())
To begin with, the code in the custom class WrappedLabel is not exactly the same as for the manual widget. For the manual widget you make sure that the stylesheet is only applied to the widget itself, but not to any child widgets via QWidget#wrapper. For you custom class you simply apply the stylesheet to the WrappedLabel instance which will cause it to cascade to all its child widgets (and also to the QLabel instance). This is why your QLabel instance ends up with the padding and the red border.
So why doesn't the same happen for the wrapper? Apparently custom base classes of QWidgets reject all applied style sheets by default (see this answer). You can make this work by adding self.setAttribute(QtCore.Qt.WA_StyledBackground) in WrappedLabel.__init__. Now you'll see that you end up with two borders, one for the wrapper and one for the label. To restrict the stylesheet to the wrapper you need to apply a similar identifier as for the manual widget: self.setStyleSheet('WrappedLabel { padding: 2px; border: 2px solid red; }').
So to make it work you can add this to WrappedLabel.__init__:
self.setAttribute(QtCore.Qt.WA_StyledBackground)
self.setStyleSheet('WrappedLabel { padding: 2px; border: 2px solid red; }')
I did not yet find out how to make the icons in my QMenu larger. I have tried to define a stylesheet in which the icon size is enlarged. But it doesn't work. Here is my code:
menuStyleSheet = ("""
QMenu {
font-size: 18px;
color: black;
border: 2px solid black;
left: 20px;
background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #cccccc, stop: 1 #ffffff);
}
QMenu::item {
padding: 2px 20px 2px 30px;
border: 1px solid transparent; /* reserve space for selection border */
spacing: 20px;
height: 60px;
}
QMenu::icon {
padding-left: 20px;
width: 50px; /* <- unfortunately, doesn't work */
height: 50px; /* <- unfortunately, doesn't work */
}
""")
#####################################################
# THE PYQT APPLICATION #
#####################################################
class GMainWindow(QMainWindow):
def __init__(self, title):
super(GMainWindow, self).__init__()
...
def setCustomMenuBar(self):
myMenuBar = self.menuBar()
global menuStyleSheet
myMenuBar.setStyleSheet(menuStyleSheet)
# Now add Menus and QActions to myMenuBar..
The result of this code is as follows:
I know that there is an old StackOverflow question about a similar topic, but it assumes that one is coding the Qt application in C++. So the situation is different. Here is the link: How to make Qt icon (in menu bar and tool bar) larger?
Any help is greatly appreciated :-)
EDIT :
Here are some details about my machine:
OS: Windows 10
Python: v3 (Anaconda package)
Qt: PyQt5
After a long search, I finally found the solution.
Just copy-paste the code below, and paste it in a *.py file. Of course, you should replace the path to the icon with a valid path on your local computer. Just provide a complete path ("C:\..") to be 100% sure that Qt finds the icon drawing.
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
# Create a custom "QProxyStyle" to enlarge the QMenu icons
#-----------------------------------------------------------
class MyProxyStyle(QProxyStyle):
pass
def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
if QStyle_PixelMetric == QStyle.PM_SmallIconSize:
return 40
else:
return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
# This is the main window class (with a simple QMenu implemented)
# ------------------------------------------------------------------
class TestWindow(QMainWindow):
def __init__(self):
super(TestWindow, self).__init__()
# 1. Set basic geometry and color.
self.setGeometry(100, 100, 400, 400)
self.setWindowTitle('Hello World')
palette = QPalette()
palette.setColor(QPalette.Window, QColor(200, 200, 200))
self.setPalette(palette)
# 2. Create the central frame.
self.centralFrame = QFrame()
self.centralFrame.setFrameShape(QFrame.NoFrame)
self.setCentralWidget(self.centralFrame)
# 3. Create a menu bar.
myMenuBar = self.menuBar()
fileMenu = myMenuBar.addMenu("&File")
testMenuItem = QAction(QIcon("C:\\my\\path\\myFig.png"), "&Test", self)
testMenuItem.setStatusTip("Test for icon size")
testMenuItem.triggered.connect(lambda: print("Menu item has been clicked!"))
fileMenu.addAction(testMenuItem)
# 4. Show the window.
self.show()
# Start your Qt application based on the new style
#---------------------------------------------------
if __name__== '__main__':
app = QApplication(sys.argv)
myStyle = MyProxyStyle('Fusion') # The proxy style should be based on an existing style,
# like 'Windows', 'Motif', 'Plastique', 'Fusion', ...
app.setStyle(myStyle)
myGUI = TestWindow()
sys.exit(app.exec_())
You will see a window like this, with a huge icon:
So how did I solve it? Well, apparently you cannot increase the icon size of a QMenu item in the usual way - defining the size in the stylesheet. The only way to do it is to provide a new QStyle, preferably derived from an existing QStyle. I found sources on how to do that in C++ (see http://www.qtcentre.org/threads/21187-QMenu-always-displays-icons-aty-16x16-px), but no explanation for PyQt.
After long trials and errors, I got it working :-)
Apparently more people struggle with this situation: http://www.qtcentre.org/threads/61860-QMenu-Icon-Scale-in-PyQt