-
-
Notifications
You must be signed in to change notification settings - Fork 92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Theme customization #54
Comments
Hi, thanks for the idea! Unfortunately, there is no way to easily customize stylesheets currently. You need to hack generated stylesheets. But I'm interested in it.
Yes, it is easy to add another placeholders. Do you have any ideas what kind of placeholders we should include? I have an another idea to improve Sample codeimport sys
from PyQt5.QtWidgets import QApplication, QFrame, QGridLayout, QLabel, QMainWindow, QWidget
import qdarktheme
app = QApplication(sys.argv)
# Widgets
main_win = QMainWindow()
panel_frame = QFrame()
box_frame = QFrame()
no_frame = QFrame()
# Setup widgets
panel_frame.setFrameShape(QFrame.Panel)
box_frame.setFrameShape(QFrame.Box)
no_frame.setFrameShape(QFrame.NoFrame)
# Layout
layout = QGridLayout()
layout.addWidget(QLabel("Panel"), 0, 0)
layout.addWidget(QLabel("Box"), 0, 1)
layout.addWidget(QLabel("NoFrame"), 0, 2)
layout.addWidget(panel_frame, 1, 0)
layout.addWidget(box_frame, 1, 1)
layout.addWidget(no_frame, 1, 2)
central_widget = QWidget()
central_widget.setLayout(layout)
main_win.setCentralWidget(central_widget)
# Setup style
stylesheet = qdarktheme.load_stylesheet()
custom_stylesheet = """
QFrame {
border: 1px solid gray;
padding: 1px;
border-radius: 4px;
}
/* NoFrame */
QFrame[frameShape="0"] {
border: none;
padding: 0;
}
/* Panel */
QFrame[frameShape="2"] {
background-color: gray
}
"""
app.setStyleSheet(stylesheet + custom_stylesheet)
main_win.show()
app.exec() But this code is not working in Qt6. Maybe this is bug of Qt6. So we need to use custom property(See qt documentation). Sample codeimport sys
from PySide6.QtWidgets import QApplication, QFrame, QGridLayout, QLabel, QMainWindow, QWidget
import qdarktheme
app = QApplication(sys.argv)
# Widgets
main_win = QMainWindow()
panel_frame = QFrame()
box_frame = QFrame()
no_frame = QFrame()
# Setup widgets
panel_frame.setFrameShape(QFrame.Panel)
box_frame.setFrameShape(QFrame.Box)
no_frame.setFrameShape(QFrame.NoFrame)
# Setup custom property
panel_frame.setProperty("shape", "panel")
no_frame.setProperty("shape", "no_frame")
# Layout
layout = QGridLayout()
layout.addWidget(QLabel("Panel"), 0, 0)
layout.addWidget(QLabel("Box"), 0, 1)
layout.addWidget(QLabel("NoFrame"), 0, 2)
layout.addWidget(panel_frame, 1, 0)
layout.addWidget(box_frame, 1, 1)
layout.addWidget(no_frame, 1, 2)
central_widget = QWidget()
central_widget.setLayout(layout)
main_win.setCentralWidget(central_widget)
# Setup style
stylesheet = qdarktheme.load_stylesheet()
custom_stylesheet = """
QFrame {
border: 1px solid gray;
padding: 1px;
border-radius: 4px;
}
/* NoFrame */
QFrame[shape="no_frame"] {
border: none;
padding: 0;
}
/* Panel */
QFrame[shape="panel"] {
background-color: gray
}
/* Override QFrame style */
QLabel {
border: none;
}
"""
app.setStyleSheet(stylesheet + custom_stylesheet)
main_win.show()
app.exec() By including these custom properties into template stylesheets, we can easily change the style with
Can you provide a minimal code example that replicate this issue? |
I found a bug report in Qt Bug Tracker. |
Regarding placeholders, I'm not sure which will be useful to include, so the more the better? But since I'm looking for ways to style grouped and nested widgets (
This or just plain css classes seems interesting and should do the job. But I'm fine with waiting for upstream fix since the Python bindings for Qt6 are still relatively new and have missing features.
I came across a few issues with the borders of QPlainTextEdit
produces a missing border around the scrollbar: QScrollArea
produces missing corners of the border: I'm not 100% sure if I'm not doing something wrong or it's the platform/version of Qt or something else though. Could also be the selector affecting all |
Thanks for your some ideas.
I think this can basically be solve with property selector. I found a solution to use property selector in Qt6. It seems that the Panel styleimport sys
from PySide6.QtWidgets import QApplication, QFrame, QGridLayout, QLabel, QMainWindow, QWidget
import qdarktheme
app = QApplication(sys.argv)
# Widgets
main_win = QMainWindow()
panel_frame = QFrame()
box_frame = QFrame()
no_frame = QFrame()
# Setup widgets
panel_frame.setFrameShape(QFrame.Panel)
box_frame.setFrameShape(QFrame.Box)
no_frame.setFrameShape(QFrame.NoFrame)
# Layout
layout = QGridLayout()
layout.addWidget(QLabel("Panel"), 0, 0)
layout.addWidget(QLabel("Box"), 0, 1)
layout.addWidget(QLabel("NoFrame"), 0, 2)
layout.addWidget(panel_frame, 1, 0)
layout.addWidget(box_frame, 1, 1)
layout.addWidget(no_frame, 1, 2)
central_widget = QWidget()
central_widget.setLayout(layout)
main_win.setCentralWidget(central_widget)
# Setup style
stylesheet = qdarktheme.load_stylesheet()
custom_stylesheet = """
QFrame {
border: 1px solid gray;
padding: 1px;
border-radius: 4px;
}
/* NoFrame, Qt5 is "0" */
QFrame[frameShape="NoFrame"] {
border: none;
padding: 0;
}
/* Panel, Qt5 is "2" */
QFrame[frameShape="Panel"] {
background-color: gray
}
"""
app.setStyleSheet(stylesheet + custom_stylesheet)
main_win.show()
app.exec() QPushButton and QGroupBox have the similar properties for flat style. The flat style of QPushButton has already been included in qdarktheme. You can check using # After change the style
widget.style().unpolish(widget)
widget.style().polish(widget)
These may need placeholders. I think these are very good options, and accent colors and padding are especially useful.
This is the stylesheet problems.
This can be removed with the following code. QPlainTextEdit > .QWidget {
background-color: transparent;
} And the second issue is similar to the first issue. /* scroll_area > QScrollArea widget container(built-in class) > scroll_area_contents */
QScrollArea > .QWidget > .QWidget {
background-color: transparent;
} Here is the fixed codes. QPlainTextEditimport sys
from PySide6 import QtCore, QtGui, QtWidgets
import qdarktheme
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
central_widget = QtWidgets.QWidget(self)
text_area = QtWidgets.QPlainTextEdit(central_widget)
text_area.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
for i in range(20):
text_area.appendPlainText(f"Line {i}")
self.text_area = text_area
central_widget_layout = QtWidgets.QVBoxLayout(central_widget)
central_widget_layout.addWidget(self.text_area)
central_widget.setLayout(central_widget_layout)
self.central_widget = central_widget
self.central_widget_layout = central_widget_layout
self.setCentralWidget(self.central_widget)
def main():
app = QtWidgets.QApplication(sys.argv)
stylesheet = qdarktheme.load_stylesheet("dark")
custom_stylesheet = """
QFrame {
padding: 0;
border-color: transparent;
}
QPlainTextEdit > .QWidget {
background-color: transparent;
}
"""
app.setStyleSheet(stylesheet + custom_stylesheet)
mw = MainWindow()
mw.show()
sys.exit(app.exec())
if __name__ == "__main__":
main() QScrollAreaimport sys
from PySide6 import QtCore, QtGui, QtWidgets
import qdarktheme
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
central_widget = QtWidgets.QWidget(self)
scroll_area = QtWidgets.QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
scroll_area_contents = QtWidgets.QWidget(scroll_area)
scroll_area_contents_layout = QtWidgets.QVBoxLayout(scroll_area_contents)
scroll_area_contents.setLayout(scroll_area_contents_layout)
scroll_area.setWidget(scroll_area_contents)
self.labels = []
for i in range(20):
label = QtWidgets.QLabel(scroll_area_contents, text=f"Label {i}")
self.labels.append(label)
scroll_area_contents_layout.addWidget(label)
self.scroll_area = scroll_area
self.scroll_area_contents = scroll_area_contents
self.scroll_area_contents_layout = scroll_area_contents_layout
central_widget_layout = QtWidgets.QVBoxLayout(central_widget)
central_widget_layout.addWidget(self.scroll_area)
central_widget.setLayout(central_widget_layout)
self.central_widget = central_widget
self.central_widget_layout = central_widget_layout
self.setCentralWidget(self.central_widget)
def main():
app = QtWidgets.QApplication(sys.argv)
stylesheet = qdarktheme.load_stylesheet("dark")
custom_stylesheet = """
QFrame {
padding: 0;
border-color: transparent;
}
QScrollArea {
border: 1px solid #fff;
}
QScrollArea > .QWidget {
background-color: transparent;
}
/* scroll_area > QScrollArea widget container(built-in class) > scroll_area_contents */
QScrollArea > .QWidget > .QWidget {
background-color: transparent;
}
"""
app.setStyleSheet(stylesheet + custom_stylesheet)
mw = MainWindow()
mw.show()
sys.exit(app.exec())
if __name__ == "__main__":
main() I will try to add this fix to qdarktheme. |
If the property selector values are consistent for all Qt 5.* and 6.* versions it shouldn't be difficult to support them. As I'm currently focusing on Qt6, I'll try to report any inconsistent behaviour that I find.
That's great, I'll keep it in mind.
I see. I'm still not fully familiar with what child widgets there are and which color & border properties are set by default. But thanks for providing working examples! |
Looks good so far. The only small issue I noticed is that frameless widgets still have invisible border that creates 1 pixel gap, where without the theme there's no border at all. QFrame NoFrameimport sys
import qdarktheme
from PySide6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
qdarktheme_stylesheet = qdarktheme.load_stylesheet('dark')
custom_stylesheet = '''
/* Uncomment to emulate the default behaviour */
/*
QFrame[frameShape="NoFrame"] {
border: none;
}
*/
#button_1,
#button_2,
#button_3 {
padding: 4px;
color: #fff;
background-color: #606060;
border: none;
}
#container {
background-color: #ff6060;
}
'''
app.setStyleSheet(qdarktheme_stylesheet + custom_stylesheet)
main_window = QtWidgets.QMainWindow()
central_widget = QtWidgets.QWidget(main_window)
central_widget_layout = QtWidgets.QVBoxLayout(central_widget)
central_widget_layout.setContentsMargins(10, 10, 10, 10)
container = QtWidgets.QFrame(central_widget, objectName='container')
container.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
container_layout = QtWidgets.QVBoxLayout(container)
container_layout.setContentsMargins(0, 0, 0, 0)
button_1 = QtWidgets.QPushButton(central_widget, text='QPushButton 1', objectName='button_1')
button_1.setFlat(True)
button_2 = QtWidgets.QPushButton(central_widget, text='QPushButton 2', objectName='button_2')
button_2.setFlat(True)
button_3 = QtWidgets.QPushButton(central_widget, text='QPushButton 3', objectName='button_3')
button_3.setFlat(True)
container_layout.addWidget(button_1)
container_layout.addWidget(button_2)
container_layout.addWidget(button_3)
container.setLayout(container_layout)
central_widget_layout.addWidget(container)
central_widget.setLayout(central_widget_layout)
main_window.setCentralWidget(central_widget)
main_window.show()
sys.exit(app.exec()) Image 1 - I think the 3rd image is the expected result, as it also retains the same window size, because there is no border. Another example on a flat Image 1 - Flat |
qdarktheme cannot remove flat QPushButton.checkable = Trueimport sys
from PySide6 import QtWidgets
import qdarktheme
app = QtWidgets.QApplication(sys.argv)
qdarktheme_stylesheet = qdarktheme.load_stylesheet("dark")
custom_stylesheet = """
#button_1,
#button_2,
#button_3 {
padding: 4px;
color: #fff;
background-color: #606060;
border: none;
}
#container {
background-color: #ff6060;
}
"""
app.setStyleSheet(qdarktheme_stylesheet + custom_stylesheet)
main_window = QtWidgets.QMainWindow()
central_widget = QtWidgets.QWidget(main_window)
central_widget_layout = QtWidgets.QVBoxLayout(central_widget)
central_widget_layout.setContentsMargins(10, 10, 10, 10)
container = QtWidgets.QFrame(central_widget, objectName="container")
container.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
container_layout = QtWidgets.QVBoxLayout(container)
container_layout.setContentsMargins(0, 0, 0, 0)
for i in range(1, 4):
button = QtWidgets.QPushButton(
central_widget,
text=f"QPushButton {i}",
objectName=f"button_{i}",
flat=True,
checkable=True,
)
container_layout.addWidget(button)
container.setLayout(container_layout)
central_widget_layout.addWidget(container)
central_widget.setLayout(central_widget_layout)
main_window.setCentralWidget(central_widget)
main_window.show()
sys.exit(app.exec()) left: set the line of The QPushButtons of qdarktheme need the border even when style is flat. |
I think it's a good way to have the button style follow Google material design. text-button(flat) > States | Google material design |
Yeah, I didn't think of checkable QPushButton and other widgets that might use the border as highlight. Mainly because I'm used to testing against the default style without a theme.
That sounds good, but it's up to you to decide wether to change a lot of the core style of the theme.
Actually now that I think, I didn't need to change the style of the If you remove the block The flat |
The background color of the flat QPushButton is set to transparent. Therefore, the border color must also be set to transparent. Flat QPushButton color should always match the color of the widget behind it, so I think transparent is the best choice. |
Oh, I'm sorry. I changed the background color of flat QPushButton to transparent in base.qss | PR #66. |
This issue resolved on #187. |
I like this theme because it's simpler and lighter, but is or will there be a way to easily customize it? Perhaps the generated stylesheets can still have placeholders which are formatted on load after the user of the library passes the wanted customizations.
Mainly the default accent color and maybe the borders and padding of widgets.
QFrame
andQGroupBox
for example have border and padding even with the frame set toNoFrame
which makes grouping widgets a bit cluttered. Otherwise manually applying a stylesheet after qdarktheme creates issues with spacing and borders near scrollbars.The text was updated successfully, but these errors were encountered: