How to apply stylesheets to menu of QPushButton - python

How do I apply css style sheet to a menu of QPushButton. I have made numerous attempts but Nothing seems to work. I found a similar discussion here
Here is my attempt:
from PyQt5 import QtWidgets
import sys
_style = """
#ButtonMenu > QPushButton{
background-color: blue;
}
/* #ButtonMenu > QPushButton::menu{
background-color: blue;
}*/
QMenu{
background-color: red;
}
/* #ButtonMenu > QMenu{
background-color: red;
}
*/
"""
class ButtonMenu(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(ButtonMenu, self).__init__(*args, **kwargs)
self.setObjectName("ButtonMenu")
layout = QtWidgets.QVBoxLayout()
button = QtWidgets.QPushButton()
menu = QtWidgets.QMenu()
menu.addAction("hello")
menu.addAction("world")
button.setMenu(menu)
layout.addWidget(button)
self.setLayout(layout)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = ButtonMenu()
win.setStyleSheet(_style)
win.show()
sys.exit(app.exec_())

Try it:
import sys
from PyQt5 import QtWidgets, QtGui
class ButtonMenu(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(ButtonMenu, self).__init__(*args, **kwargs)
self.setObjectName("ButtonMenu")
button = QtWidgets.QPushButton()
menu = QtWidgets.QMenu()
menu.addAction("hello")
menu.addAction("world")
button.setMenu(menu)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(button)
_style = """
#ButtonMenu {
background-color: blue;
}
QPushButton {
background-color: #f0f;
}
QMenu {
background: #ff0;
border: 2px solid red;
border-radius: 2px;
}
QMenu::item {
color: green;
font-size: 22px;
}
QMenu::item:selected {
background: #0ff;
color: red;
}
"""
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(_style) # +
win = ButtonMenu()
# win.setStyleSheet(_style)
win.show()
sys.exit(app.exec_())

Related

QSplitter() doesn't treat a QScrollArea() and QFrame() equally

1. Problem explained
I'm experimenting with Qt's QSplitter() widget. I've built a very simple sample project in PyQt5 showing a QSplitter() encapsulating a QScrollArea() on the left and a QFrame() on the right:
I've given both the QScrollArea() and QFrame() equal stretch factors, but the QSplitter() doesn't treat them equally. The QScrollArea() always gets most space. I have no idea why.
Minimal, Reproducible Example
Simply copy-paste the code below in a .py script and run it. I've got Python 3.7 with PyQt5 running on a Windows 10 machine.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Scroller(QScrollArea):
'''
The Scroller(), will be first widget in the Splitter().
'''
def __init__(self):
super().__init__()
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.setStyleSheet("""
QScrollArea {
background-color:#fce94f;
border-color:#c4a000;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
}
""")
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
# 1. FRAME
self.__frm = QFrame()
self.__frm.setStyleSheet("QFrame { background: #ff25292d; border: none; }")
self.__frm.setMinimumHeight(100)
# 2. LAYOUT
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
# 3. SELF
self.setWidget(self.__frm)
self.setWidgetResizable(True)
return
class Frame(QFrame):
'''
The Frame(), will be second widget in the Splitter()
'''
def __init__(self):
super().__init__()
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
# 1. FRAME
self.setStyleSheet("""
QFrame {
background-color:#fcaf3e;
border-color:#ce5c00;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
}
""")
self.__lyt = QVBoxLayout()
self.__lyt.setAlignment(Qt.AlignTop)
self.__lyt.setSpacing(0)
self.__lyt.setContentsMargins(10, 10, 10, 10)
self.setLayout(self.__lyt)
return
class Splitter(QSplitter):
'''
The Splitter().
'''
def __init__(self, widg1, widg2):
super().__init__()
self.setOrientation(Qt.Horizontal)
self.addWidget(widg1)
self.addWidget(widg2)
self.setStretchFactor(0, 5)
self.setStretchFactor(1, 5)
return
def createHandle(self):
return QSplitterHandle(self.orientation(), self)
class CustomMainWindow(QMainWindow):
'''
CustomMainWindow(), a QMainWindow() to start the whole setup.
'''
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 600, 300)
self.setWindowTitle("QSPLITTER TEST")
# 1. OUTER FRAME
self.__frm = QFrame()
self.__frm.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.__frm.setStyleSheet("""
QFrame {
background-color: #eeeeec;
border-color: #2e3436;
}
""")
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.show()
# 2. WIDGETS TO BE PUT IN SPLITTER
self.__widg1 = Scroller()
self.__widg2 = Frame()
# 3. SPLITTER
self.__splitter = Splitter(self.__widg1, self.__widg2)
self.__lyt.addWidget(self.__splitter)
return
if __name__== '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
QSplitter not only takes the stretch factor as a reference, it also takes into account the sizeHint(). If the following is added:
# ...
# 3. SPLITTER
self.__splitter = Splitter(self.__widg1, self.__widg2)
self.__lyt.addWidget(self.__splitter)
print(self.__widg1.sizeHint(), self.__widg2.sizeHint())
return
You get the following:
PyQt5.QtCore.QSize(38, 22) PyQt5.QtCore.QSize(20, 20)
Where we see that the QScrollArea has a greater width in the sizeHint() than the QFrame, and that explains why the observed behavior.
The solution is to establish the same width of sizeHint(), that is, not depend on what it contains.
class Scroller(QScrollArea):
# ...
def sizeHint(self):
s = super().sizeHint()
s.setWidth(20) # same width
return s
class Frame(QFrame):
# ...
def sizeHint(self):
s = super().sizeHint()
s.setWidth(20) # same width
return s
# ...
Output:

Is it possible to change the height of a QProgressBar?

I'm trying to change the height of the inner bar.
I already tried to change the height of the chunk, but the hole bar just vanished.
I also tried to change the chunks padding, but nothing happend.
from PySide2 import QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setStyleSheet("""
QProgressBar {
background-color: #C0C6CA;
border: 0px;
padding-top: 11px;
padding-bottom: 10px;
}
QProgressBar::chunk {
background: #7D94B0;
}
""")
self.progress_bar()
def progress_bar(self):
layout = QtWidgets.QHBoxLayout()
progress = QtWidgets.QProgressBar()
progress.setTextVisible(False)
progress.setValue(35)
layout.addWidget(progress)
self.setLayout(layout)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I want a chunk with the same height of the progressbar itself.
The ::chunk subcontrol is used to change the chunk progress (i.e. the rectangles in the progress bar).
If you want to have a progress bar with the same size of its background, remove the padding:
self.setStyleSheet("""
QProgressBar {
background-color: #C0C6CA;
border: 0px;
padding: 0px;
// height: 100px; // To change the progress bar height
}
QProgressBar::chunk {
background: #7D94B0;
width:5px
}
""")
It will display:

Why does a class inhering another class not produce the same results as 'another class'?

I am developing an application in Python using PyQt5. Here is the code in question:
class Dialog(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QGridLayout()
self.main = QtWidgets.QWidget()
self.main.setLayout(self.layout)
self.setStyleSheet(QMainWindowStyle)
self.setCentralWidget(self.main)
self.show()
class AppearanceTab(QtWidgets.QWidget):
def __init__(self):
super().__init__()
class SettingsDialog(Dialog):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget(self)
self.tabs.setStyleSheet(QTabWidgetStyle)
self.layout.addWidget(self.tabs)
self.tab_appearance = AppearanceTab()
self.tab_appearance.setStyleSheet(QWidgetStyle)
self.tab_appearance_layout = QtWidgets.QGridLayout()
self.tab_appearance.setLayout(self.tab_appearance_layout)
self.tabs.addTab(self.tab_appearance, "Appearance")
self.tab_server = QtWidgets.QWidget()
self.tab_server.setStyleSheet(QWidgetStyle)
self.tab_server_layout = QtWidgets.QGridLayout()
self.tab_server.setLayout(self.tab_server_layout)
self.tabs.addTab(self.tab_server, "Server")
Why is it that when self.tab_appearance is an AppearanceTab instance (which should be a copy of QWidget) it has a different style to self.tab_server (i.e. background colour changes) when self.tab_server is an instance of QWidget?
The stylesheet just defines background-color: #333333 and color: #dddddd.
Thanks in advance.
EDIT:
I believe that the stylesheet is not being properly applied to AppearanceTab, however I don;t know why that would be seeing as it just inherits from QWidget.
EDIT 2:
A MCVE (along with the rest of my project) can be found on github.
In the documentation, there is a paragraph the paragraph about inheritance and style:
Inheritance
In classic CSS, when font and color of an item is not explicitly set, it gets automatically inherited from the parent. When using Qt Style Sheets, a widget does not automatically inherit its font and color setting from its parent widget.
If we want to set the color on a QGroupBox and its children, we can write:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
So you probably want to to change
QMainWindowStyle = QMainWindow {color: #dddddd; background-color: #333333;}
to
QMainWindowStyle = QMainWindow, QMainWindow * {color: #dddddd; background-color: #333333;}
so that all the child widgets of the main window have the same style.
Try it:
from PyQt5 import QtWidgets
class Dialog(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QGridLayout()
self.main = QtWidgets.QWidget()
self.main.setLayout(self.layout)
# self.setStyleSheet(QMainWindowStyle)
self.setCentralWidget(self.main)
self.show()
class AppearanceTab(QtWidgets.QWidget):
def __init__(self):
super().__init__()
class SettingsDialog(Dialog):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget(self)
# self.tabs.setStyleSheet(QTabWidgetStyle)
self.layout.addWidget(self.tabs)
self.tab_appearance = AppearanceTab()
# self.tab_appearance.setStyleSheet(QWidgetStyle)
## self.tab_appearance.setStyleSheet("QWidget, QWidget * {color: #dddddd; background-color: #333333;}") #note: Tried this however it didn't work.
self.tab_appearance_layout = QtWidgets.QGridLayout()
self.tab_appearance.setLayout(self.tab_appearance_layout)
self.tabs.addTab(self.tab_appearance, "Appearance")
self.tab_server = QtWidgets.QWidget()
# self.tab_server.setStyleSheet(QWidgetStyle)
self.tab_server_layout = QtWidgets.QGridLayout()
self.tab_server.setLayout(self.tab_server_layout)
self.tabs.addTab(self.tab_server, "Server")
style = """
QWidget {
color: #dddddd;
background-color: #333333;
}
QMainWindow {
color: #dddddd;
background-color: #333333;
}
QTabWidget {
background-color: #333333;
color: #dddddd;
}
QTabBar {
color: #dddddd;
background-color: #333333;
}
"""
if __name__ == "__main__":
QtWidgets.QApplication.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
app = QtWidgets.QApplication([])
app.setStyleSheet(style) # < ---
d = SettingsDialog()
print(app.exec_())

Setting combo box's scroll area border/background color in PyQt5

I'm having trouble getting the drop down of a QComboBox to have the same color all around. When I click on my combo box, this appears
I do not want the white border color that appears to be part of the scroller of this drop down. However, I can't figure out what this white border belongs to! Is it the QAbstractScrollArea of the QComboBox? Is it the QListView? Is it the QScrollArea, QScroller or QScrollBar? I've tried setting background colors/border colors for all of these properties but to no avail. The following is a stripped down version of my app so that you can just copy and paste this into python and have it run (unfortunately I really couldn't shorten it any further, so sorry for how long it is!)
import sys
import os
import urllib.request
from PyQt5.QtWidgets import QApplication, QComboBox, QMainWindow, QVBoxLayout, QWidget
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPixmap, QIcon
class MainWindow(QMainWindow):
def __init__(self, app):
super().__init__()
self.vlayout = QVBoxLayout()
self.main_widget = QWidget()
self.init_combobox()
self.main_widget.setLayout(self.vlayout)
self.setCentralWidget(self.main_widget)
self.show()
def init_combobox(self):
data_solid = urllib.request.urlopen('https://i.imgur.com/fbrdSkv.png').read()
pixmap_solid = QPixmap()
pixmap_solid.loadFromData(data_solid)
data_dashed = urllib.request.urlopen('https://i.imgur.com/gI9oHKm.png').read()
pixmap_dashed = QPixmap()
pixmap_dashed.loadFromData(data_dashed)
data_dotted = urllib.request.urlopen('https://i.imgur.com/AAu4TrX.png').read()
pixmap_dotted = QPixmap()
pixmap_dotted.loadFromData(data_dotted)
cbox = QComboBox()
cbox.setObjectName('linestylecbox')
cbox.setFixedSize(75, 25)
cbox.addItem(QIcon(pixmap_solid), '')
cbox.addItem(QIcon(pixmap_dashed), '')
cbox.addItem(QIcon(pixmap_dotted), '')
cbox.setIconSize(QSize(75, 25))
cbox.setContentsMargins(0, 0, 50, 0)
cbox.setStyleSheet('''
QComboBox#linestylecbox
{
background-color: black;
color: black;
font: 10px Lucida Sans;
border: 0px solid black;
margin: 0px;
}
QComboBox#linestylecbox::drop-down
{
border: 0px;
border-top: 0px;
border-color: black;
background-color: black;
font: 10px Lucida Sans;
}
QComboBox#linestylecbox QListView
{
background-color: black;
color: black;
}
''')
self.vlayout.addWidget(cbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = MainWindow(app)
sys.exit(app.exec_())

How to iterate all QPushButton in a QGroupBox

I have a QDialog which contains a QGroupBox which in turn contains some push-buttons. I want to differentiate the background-color of the push-button which is clicked and all the remaining push-buttonx. How to achieve this?
Make sure the buttons are all children of the group-box, and then use findChildren to iterate over them. You could also use a QButtonGroup to help manage the buttons.
Here's a demo script to show how it could be done:
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.groupBox = QtWidgets.QGroupBox(self)
layout.addWidget(self.groupBox)
layout = QtWidgets.QVBoxLayout(self.groupBox)
for index in range(5):
button = QtWidgets.QPushButton('Button %d' % index, self.groupBox)
layout.addWidget(button)
self.buttonGroup = QtWidgets.QButtonGroup(self)
self.buttonGroup.buttonClicked.connect(self.handleButtonClicked)
self.updateButtonGroup()
def updateButtonGroup(self):
for button in self.groupBox.findChildren(QtWidgets.QPushButton):
if self.buttonGroup.id(button) < 0:
self.buttonGroup.addButton(button)
def handleButtonClicked(self, button):
for item in self.buttonGroup.buttons():
if button is item:
item.setStyleSheet('background-color: orange')
else:
item.setStyleSheet('')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Dialog()
window.show()
sys.exit(app.exec_())
If reason to use class QtGui.QGroupBox is select button just like radio button, You can use Qt Style Sheet to set difference background color;
import sys
from PyQt4 import QtGui
class QCustomDialog (QtGui.QDialog):
def __init__(self, *args, **kwargs):
QtGui.QDialog.__init__(self, *args, **kwargs)
myQVBoxLayout = QtGui.QVBoxLayout()
for text in ['PyQt', 'Stack', 'Overflow']:
myQPushButton = QtGui.QPushButton(text)
myQPushButton.setCheckable(True)
myQPushButton.setAutoExclusive(True)
myQVBoxLayout.addWidget(myQPushButton)
myQVBoxLayout.addStretch(1)
myQGroupBox = QtGui.QGroupBox()
myQGroupBox.setStyleSheet('''
QPushButton {
border: 0px;
color: rgb(255, 255, 255);
background-color: rgb(0, 0, 0);
}
QPushButton:checked {
border: 0px;
color: rgb(255, 255, 255);
background-color: rgb(255, 0, 0);
}
''')
myQGroupBox.setLayout(myQVBoxLayout)
allQVBoxLayout = QtGui.QVBoxLayout()
allQVBoxLayout.addWidget(myQGroupBox)
self.setLayout(allQVBoxLayout)
myQApplication = QtGui.QApplication([])
myQCustomDialog = QCustomDialog()
myQCustomDialog.show()
sys.exit(myQApplication.exec_())

Categories

Resources