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

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:

Related

setGeometry not workingin pyqt5?

I am trying to set the width height of a QWidget percentage wise. But since we cannot do something like min-height:20% using QSS. I tried to use setGeometry function and recall this function on window resize event, but somehow it don't work, below is my code:
class mainwindow(QMainWindow):
def __init__(self):
super().__init__()
stylesheet = """
#mainwindow{
border: 0px;
background-color: #222;
}
"""
scanelestylesheet = """
#scanelecontainer{
border: 1px solid #1a1a1a;
background-color: #222;
}
"""
scanstusconstylesheet = """
#scanstuscon{
border: 1px solid #1a1a1a;
background-color: #fff;
}
"""
container = QWidget()
container.setObjectName("mainwindow")
container.setContentsMargins(100, 100, 100, 100)
self.setCentralWidget(container)
self.setStyleSheet(stylesheet)
self.setWindowTitle("Neehack")
widget = QWidget()
widget.setObjectName("scanelecontainer")
widget.setAutoFillBackground(True)
lay = QVBoxLayout(container)
lay.addWidget(widget)
effect = QGraphicsDropShadowEffect(
offset=QPoint(3, 3), blurRadius=25, color=QColor("#1a1a1a")
)
widget.setGraphicsEffect(effect)
widget.setGeometry(0, 0,200, 20)
widget.setStyleSheet(scanelestylesheet)
scanstuscon = QWidget()
scanstuscon.setObjectName("scanstuscon")
scanstuscon.setAutoFillBackground(False)
scanstusconlay = QVBoxLayout(widget)
scanstusconlay.addWidget(scanstuscon)
scanstuscon.setGeometry(10,10, 200,200)
scanstuscon.setStyleSheet(scanstusconstylesheet)
self.resize(1200, 800)
How do I set a QWidget size based on its parent's percentage or why isn't setGeomtry not working here?
The objective of the layouts is to handle the geometry of the widgets so setGeometry will not work. On the other hand, using setGeometry to modify the size together with resizeEvent to establish a percentage of the size is unnecessary since it is enough to set the stretch factor in the layout:
class Mainwindow(QMainWindow):
def __init__(self):
super().__init__()
stylesheet = """
#mainwindow{
border: 0px;
background-color: #222;
}
"""
scanelestylesheet = """
#scanelecontainer{
border: 1px solid #1a1a1a;
background-color: #222;
}
"""
scanstusconstylesheet = """
#scanstuscon{
border: 1px solid #1a1a1a;
background-color: #fff;
}
"""
container = QWidget()
container.setObjectName("mainwindow")
container.setContentsMargins(100, 100, 100, 100)
self.setCentralWidget(container)
self.setStyleSheet(stylesheet)
self.setWindowTitle("Neehack")
widget = QWidget()
widget.setObjectName("scanelecontainer")
widget.setAutoFillBackground(True)
lay = QVBoxLayout(container)
lay.addWidget(widget)
effect = QGraphicsDropShadowEffect(
offset=QPoint(3, 3), blurRadius=25, color=QColor("#1a1a1a")
)
widget.setGraphicsEffect(effect)
widget.setStyleSheet(scanelestylesheet)
scanstuscon = QWidget()
scanstuscon.setObjectName("scanstuscon")
scanstuscon.setAutoFillBackground(False)
scanstuscon.setStyleSheet(scanstusconstylesheet)
# 20% = 20/(20 + 80)
lay = QVBoxLayout(widget)
lay.addWidget(scanstuscon, stretch=20)
lay.addStretch(80)
self.resize(1200, 800)

Button doesn't show up

i'm trying to do a simple GUI for a python script that convert some text into a specific format but buttons doesn't show up in the window.
I first create the button class
class Button(QPushButton):
def __init__(self, btn_name=None):
super().__init__()
self.button = QPushButton(btn_name)
self.button.setCursor(
QCursor(QtCore.Qt.CursorShape.PointingHandCursor))
self.button.setStyleSheet(
"""*{border: 4px solid 'green';
border-radius: 45px;
font-size: 35px;
color: 'white';
padding: 25px 0;
margin: 100px, 200px}
*:hover{background: 'green'}
*:pressed{background: 'lightgreen'}"""
)
Then create the window class like this
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.window = QWidget()
self.window.resize(500, 500)
self.window.setWindowTitle("Pantuflis Software")
self.window.setFixedWidth(1000)
self.window.setStyleSheet("background: 'black';")
self.grid = QGridLayout()
self.window.setLayout(self.grid)
self.button = Button("Play")
self.grid.addWidget(self.button)
self.window.show()
Finally add the rest
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())
But the button doesn't show up, only the main window does. I also tried the same but without creataing the button from my own class and works. Must be something wrong in the button class but i can't see what is.
If you are going to implement inherence then you have to apply the changes to the class. In your case it has a class that inherits from QPushButton but where you create the custom button which is not necessary, the same with the main window. My recommendation is that the OP should review his notes about inheritance.
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QCursor
from PyQt6.QtWidgets import QApplication, QGridLayout, QPushButton, QWidget
class Button(QPushButton):
def __init__(self, btn_name=""):
super().__init__(btn_name)
self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
self.setStyleSheet(
"""*{border: 4px solid 'green';
border-radius: 45px;
font-size: 35px;
color: 'white';
padding: 25px 0;
margin: 100px, 200px}
*:hover{background: 'green'}
*:pressed{background: 'lightgreen'}"""
)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.resize(500, 500)
self.setWindowTitle("Pantuflis Software")
self.setFixedWidth(1000)
self.setStyleSheet("background: 'black';")
self.grid = QGridLayout(self)
self.button = Button("Play")
self.grid.addWidget(self.button)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())

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_())

PyQt4 - Applying a stylesheet on an object which inherits from QWidget

I am having an issue with PyQt4. I have a class which inherits from QWidget. This class uses a layout to store a QLabel and a QLineEdit. Here is the code:
class SearchBar(QtGui.QWidget):
def __init__(self, parent=None):
super(SearchBar, self).__init__(parent)
self.setStyleSheet(SEARCHBAR_STYLE)
layout = QtGui.QHBoxLayout()
layout.setSpacing(0)
layout.setMargin(0)
layout.addStrut(SEARCHAR_HEIGHT)
lbl_notification = QtGui.QLabel('Hi')
lbl_notification.setStyleSheet(SEARCHBAR_NOTIFICATION_STYLE)
layout.addSpacing(10)
layout.addWidget(lbl_notification)
searchbox = QLineEdit('Search')
layout.addStretch()
layout.addWidget(searchbox)
layout.addSpacing(10)
self.setLayout(layout)
and here is the stylesheet:
SEARCHBAR_STYLE = """
QWidget {
background: #424a7d;
}
.QWidget {
border: 1px solid grey;
}
QLabel {
border-top: 1px solid grey;
border-bottom: 1px solid grey;
}
"""
Now, my problem is that the stylesheet does not apply the way I would like it to. It only applies on my QLabel when the border should be around the whole object:
When I had a function creating my search bar as a QWidget, it worked perfectly, but now that I changed that to a class it does not work. What am I doing wrong?
EDIT: I am trying to achieve this:
EDIT 2: The previous code, before I change it to a class, was this:
def create_bar():
layout = QtGui.QHBoxLayout()
layout.setSpacing(0)
layout.setMargin(0)
layout.addStrut(SEARCHAR_HEIGHT)
lbl_notification = QtGui.QLabel('Hi')
lbl_notification.setStyleSheet(SEARCHBAR_NOTIFICATION_STYLE)
layout.addSpacing(10)
layout.addWidget(lbl_notification)
search_bar = QtGui.QLineEdit('Search')
search_bar.setMinimumSize(200, 25)
search_bar.setMaximumSize(200, 25)
search_bar.setStyleSheet(SEARCHBOX_STYLE)
layout.addStretch()
layout.addWidget(search_bar)
layout.addSpacing(10)
widget = QtGui.QWidget()
widget.setStyleSheet(SEARCHBAR_STYLE)
widget.setLayout(layout)
return widget
Change the base class of SearchBar from QWidget to QFrame, or alternatively implement a style sheet aware paintEvent:
def paintEvent(self, event):
opt = QStyleOption()
opt.initFrom(self)
painter = QPainter(self)
self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
Then change the style sheet to
SEARCHBAR_STYLE = """
SearchBar {
background: #424a7d;
border: 1px solid grey;
}
"""

Categories

Resources