要求:
使用QSplitter并没有帮助,因为QSplitter保持固定宽度,并且调整其任何 splits 的大小都会导致其他 splits 缩小。 [1] [2] [3]
当然可以通过创建自定义窗口小部件,添加用于指示可拖动区域的可视条以及侦听拖动事件以通过代码调整窗口小部件大小来完成此操作。有没有更简单的解决方案?
答案 0 :(得分:0)
我遇到了同样的问题。想出了一个讨厌的黑客:
它对我有用(目前)。但是它很笨拙,并且其中有一些令人讨厌的神奇数字可以使其发挥作用。 因此,如果有人提出更好的解决方案,那就太好了! 无论如何 - 如果有人觉得它有用,代码(在 Python3 和 PySide2 中)
import sys
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QWidget, QScrollArea, QSplitter
from PySide2.QtWidgets import QApplication, QMainWindow, QLabel, QFrame
class ScrollSplitter(QScrollArea):
def __init__(self, orientation, parent=None):
super().__init__(parent)
# Orientation = Qt.Horizontal or Qt.Vertical
self.orientation = orientation
# Keep track of all the sizes of all the QSplitter's child widgets BEFORE the latest resizing,
# so that we can reinstate all of them (except the widget that we wanted to resize)
self.old_sizes = []
self._splitter = QSplitter(orientation, self)
# TODO - remove magic number. This is required to avoid zero size on first viewing.
if orientation == Qt.Horizontal :
self._splitter.setMinimumWidth(500)
else :
self._splitter.setMinimumHeight(500)
# In a default QSplitter, the bottom widget doesn't have a drag handle below it.
# So create an empty widget which will always sit at the bottom of the splitter,
# so that all of the user widgets have a handle below them
#
# I tried playing with the max width/height of this bottom widget - but the results were crummy. So gave up.
bottom_widget = QWidget(self)
self._splitter.addWidget(bottom_widget)
# Use the QSplitter.splitterMoved(pos, index) signal, emitted every time the splitter's handle is moved.
# When this signal is emitted, the splitter has already resized all child widgets to keep its total size constant.
self._splitter.splitterMoved.connect(self.resize_splitter)
# Configure the scroll area.
if orientation == Qt.Horizontal :
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
else :
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setWidgetResizable(True)
self.setWidget(self._splitter)
# Called every time a splitter handle is moved
# We basically undo the QSplitter resizing of all the other children,
# and resize the QSplitter (using setMinimumHeight() or setMinimumWidth() ) instead.
def resize_splitter(self, pos, index):
# NOTE: index refs the child widget AFTER the moved splitter handle.
# pos is position relative to the top of the splitter, not top of the widget.
# TODO - find a better way to initialise the old_sizes list.
# Ideally whenever we add/remove a widget.
if not self.old_sizes :
self.old_sizes = self._splitter.sizes()
# The 'index' arg references the QWidget below the moved splitter handle.
# We want to change the QWidget above the moved splitter handle, so...
index_above = index - 1
# Careful with the current sizes - QSplitter has already mucked about with the sizes of all other child widgets
current_sizes = self._splitter.sizes()
# The only change in size we are interested in is the size of the widget above the splitter
size_change = current_sizes[index_above] - self.old_sizes[index_above]
# We want to keep the old sizes of all other widgets, and just resize the QWidget above the splitter.
# Update our old_list to hold the sizes we want for all child widgets
self.old_sizes[index_above] = current_sizes[index_above]
# Increase/decrease the(minimum) size of the QSplitter object to accommodate the total new, desired size of all of its child widgets (without resizing most of them)
if self.orientation == Qt.Horizontal :
self._splitter.setMinimumWidth(max(self._splitter.minimumWidth() + size_change, 0))
else :
self._splitter.setMinimumHeight(max(self._splitter.minimumHeight() + size_change, 0))
# and set the sizes of all the child widgets back to their old sizes, now that the QSplitter has grown/shrunk to accommodate them without resizing them
self._splitter.setSizes(self.old_sizes)
#print(self.old_sizes)
# Add a widget at the bottom of the user widgets
def addWidget(self, widget):
self._splitter.insertWidget(self._splitter.count()-1, widget)
# Insert a widget at 'index' in the splitter.
# If the widget is already in the splitter, it will be moved.
# If the index is invalid, widget will be appended to the bottom of the (user) widgets
def insertWidget(self, index, widget):
if index >= 0 and index < (self._splitter.count() - 1) :
self._splitter.insertWidget(index, widget)
self.addWidget(widget)
# Replace a the user widget at 'index' with this widget. Returns the replaced widget
def replaceWidget(self, index, widget):
if index >= 0 and index < (self._splitter.count() - 1) :
return self._splitter.replaceWidget(index, widget)
# Return the number of (user) widgets
def count(self):
return self._splitter.count() - 1
# Return the index of a user widget, or -1 if not found.
def indexOf(self, widget):
return self._splitter.indexOf(widget)
# Return the (user) widget as a given index, or None if index out of range.
def widget(self, index):
if index >= 0 and index < (self._splitter.count() - 1) :
return self._splitter.widget(index)
return None
# Save the splitter's state into a ByteArray.
def saveState(self):
return self._splitter.saveState()
# Restore the splitter's state from a ByteArray
def restoreState(self, s):
return self._splitter.restoreState(s)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("ScrollSplitter Test")
self.resize(640, 400)
self.splitter = ScrollSplitter(Qt.Vertical, self)
self.setCentralWidget(self.splitter)
for color in ["Widget 0", "Widget 1", "Widget 2", "Some other Widget"]:
widget = QLabel(color)
widget.setFrameStyle(QFrame.Panel | QFrame.Raised)
self.splitter.addWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()