Update:
This is an MRE based on my original experiment, putting a QSplitter
as the direct child widget of another QSplitter
. I want to achieve a situation with the upper/lower height proportions being 66%-33% and the left/right width proportion of the upper QSplitter
being 66%-33%.
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
MAIN_WINDOW_HEIGHT = 900
TOP_FRAME_PERCENTAGE = 66 # can try with other percentages: 50, 75, 90...
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setMinimumSize(1000, MAIN_WINDOW_HEIGHT)
self.setStyleSheet('background-color: magenta');
main_splitter = QtWidgets.QSplitter(self)
main_splitter.setOrientation(QtCore.Qt.Vertical)
self.setCentralWidget(main_splitter)
# try swapping and using a TopSplitter instead of a QFrame
# top_frame = QtWidgets.QFrame()
top_frame = TopSplitter()
self.setStyleSheet('background-color: red');
main_splitter.addWidget(top_frame)
self.bottom_panel = QtWidgets.QFrame()
main_splitter.addWidget(self.bottom_panel)
self.bottom_panel.setStyleSheet("background-color: green");
print(f'main_splitter.indexOf(self.bottom_panel) {main_splitter.indexOf(self.bottom_panel)}')
def bp_size_hint(*args):
# NB is never called
print('YYY')
return QtCore.QSize(200, (100 - TOP_FRAME_PERCENTAGE) * int(MAIN_WINDOW_HEIGHT/100))
self.bottom_panel.sizeHint = bp_size_hint
main_splitter.setStretchFactor(0, TOP_FRAME_PERCENTAGE)
main_splitter.setStretchFactor(1, 100 - TOP_FRAME_PERCENTAGE)
print(f'A self.bottom_panel.height() {self.bottom_panel.height()}')
def show(self):
print(f'B self.bottom_panel.height() {self.bottom_panel.height()}')
super().show()
print(f'C self.bottom_panel.height() {self.bottom_panel.height()}')
# 305 pixels with a QFrame, but 263 pixels with a TopSplitter
class TopSplitter(QtWidgets.QSplitter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setStyleSheet('background-color: cyan');
top_left_panel = QtWidgets.QFrame()
top_left_panel.setStyleSheet('background-color: yellow');
self.addWidget(top_left_panel)
top_right_panel = QtWidgets.QFrame()
top_right_panel.setStyleSheet('background-color: black');
self.addWidget(top_right_panel)
# these work exactly as you'd expect, to achieve 66%-33%
self.setStretchFactor(0, 20)
self.setStretchFactor(1, 10)
def sizeHint(self):
# NB called frequently
print('XXX')
return QtCore.QSize(200, TOP_FRAME_PERCENTAGE * int(MAIN_WINDOW_HEIGHT/100))
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Previous MRE for this question:
(This was the second thing I tried: making both children of the main splitter QFrame
s, and then putting a left-right QSplitter
under the top one... but again, it doesn't get the height proportions right...)
Here is an MRE:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setMinimumSize(1000, 800)
self.main_splitter = QtWidgets.QSplitter(self)
self.main_splitter.setOrientation(QtCore.Qt.Vertical)
self.setCentralWidget(self.main_splitter)
TopFrame(self.main_splitter)
self.bottom_panel = QtWidgets.QFrame(self.main_splitter)
self.bottom_panel.setStyleSheet("background-color: green");
print(f'self.main_splitter.indexOf(self.bottom_panel) {self.main_splitter.indexOf(self.bottom_panel)}')
self.main_splitter.setStretchFactor(0, 20)
self.main_splitter.setStretchFactor(1, 10)
class TopFrame(QtWidgets.QFrame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# A
self.lr_splitter = QtWidgets.QSplitter(self)
self.lr_splitter.setOrientation(QtCore.Qt.Horizontal)
# B
self.setLayout(QtWidgets.QHBoxLayout())
self.x_panel = QtWidgets.QFrame(self.lr_splitter)
self.x_panel.setStyleSheet("background-color: red");
self.y_panel = QtWidgets.QFrame(self.lr_splitter)
self.y_panel.setStyleSheet("background-color: cyan");
self.lr_splitter.setStretchFactor(0, 20)
self.lr_splitter.setStretchFactor(1, 10)
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
What I'm trying to do is make the children of the left-right splitter extend across the full width and full height of the upper frame. The stretch factor should preserve the ratios of the widths of the two components as the window is made wider or less wide.
It is possible to make a QSplitter
the direct child of another QSplitter
... but my experiments with this seem to show that the setStretchFactor
doesn't then work. For that reason I have chosen to make the upper child of the main splitter a QFrame
.
But there's another problem: if you comment out everything under the line "# A" in __init__
you will see that the stretch factor is as expected: the top frame is twice the height of the lower one. But if you uncomment and display again you'll see that that ratio has been changed mysteriously. Experimentation shows that the culprit line is the line under line "# B", setLayout...
.
So ultimately what I'm looking for is a solution where the top QSplitter
children occupy the full real estate of the TopFrame
and where the ratios set by setStretchFactor
are what one might expect, in both QSplitter
s.
Edit
As per discussion, I then tried this for the TopFrame
class:
class TopFrame(QtWidgets.QSplitter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setOrientation(QtCore.Qt.Horizontal)
h_layout = QtWidgets.QHBoxLayout()
self.setLayout(h_layout)
# this line causes two errors: 1) can't add layout to splitter; 2) can't add parent widget to its child layout
# h_layout.addWidget(self)
self.x_panel = QtWidgets.QFrame()
self.addWidget(self.x_panel)
self.x_panel.setStyleSheet("background-color: red");
self.y_panel = QtWidgets.QFrame()
self.addWidget(self.y_panel)
self.y_panel.setStyleSheet("background-color: cyan");
self.setStretchFactor(0, 20)
self.setStretchFactor(1, 10)