单列QVBoxLayout如何修改为多列?
How to modify single column QVBoxLayout to be multi-column?
我有一列自动生成的按钮,如果按钮太多,可以挤压 window 中的 UI 个元素。因此,我想通过以下方式自动将单列按钮 - 名义上在 QVBoxLayout
内称为 self.main_layout
- 转换为多列事件:
- 正在删除
self.main_layout
中的按钮
- 将它们添加到由
QVBoxLayout
s 表示的交替新列中
- 将
self.main_layout
更改为 QHBoxLayout
- 将新列添加到此布局
我的尝试只会导致按钮停留在一列中,但现在甚至不调整大小以填充它们占据的 QSplitter
框架:
app = QApplication(sys.argv)
window = TestCase()
app.exec_()
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.layout = QVBoxLayout()
test.setLayout(self.layout)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.pressed.connect(self.multi_col)
self.layout.addWidget(temp_btn)
self.setCentralWidget(test)
@pyqtSlot()
def multi_col(self):
cols = [QVBoxLayout(), QVBoxLayout()]
while self.layout.count():
child = self.layout.takeAt(0)
if child.widget():
self.layout.removeItem(child)
cols[0].addItem(child)
cols[1], cols[0] = cols[0], cols[1]
self.layout = QHBoxLayout()
self.layout.addLayout(cols[0])
self.layout.addLayout(cols[1])
我这里有什么明显的错误吗?
替换 QWidget 的布局并不是那么简单,只需将另一个对象分配给存储其他布局引用的变量即可。在你正在做的几行代码中:
self.layout = Foo()
widget.setLayout(self.layout)
self.layout = Bar()
对象与变量不同,对象本身是执行动作的实体,而变量只是存储对象引用的地方。例如,对象可以是人和变量我们的名字,所以如果他们改变了我们的名字,并不意味着他们改变了我们这个人。
解决方案是使用 sip.delete 删除 QLayout,然后设置新布局:
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
import sip
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.setCentralWidget(test)
layout = QVBoxLayout(test)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.pressed.connect(self.multi_col)
layout.addWidget(temp_btn)
@pyqtSlot()
def multi_col(self):
cols = [QVBoxLayout(), QVBoxLayout()]
old_layout = self.centralWidget().layout()
while old_layout.count():
child = old_layout.takeAt(0)
widget = child.widget()
if widget is not None:
old_layout.removeItem(child)
cols[0].addWidget(widget)
cols[1], cols[0] = cols[0], cols[1]
sip.delete(old_layout)
lay = QHBoxLayout(self.centralWidget())
lay.addLayout(cols[0])
lay.addLayout(cols[1])
def main():
app = QApplication(sys.argv)
window = TestCase()
window.show()
app.exec_()
if __name__ == "__main__":
main()
我想提出一个替代解决方案,即使用 QGridLayout 并仅更改小部件的列而不是每次都设置新布局。 “技巧”是 addWidget()
总是在指定位置添加小部件,即使它已经是布局的一部分,所以您不需要删除布局项。
显然,这种方法的缺点是,如果小部件具有不同的高度,则每一行都取决于该行中所有小部件所需的最小高度,但由于 OP 是关于使用按钮的,因此情况不应如此。
这样做的主要好处是可以通过一个函数自动完成切换,可能通过设置最大列数以提供进一步的实现。
在下面的示例中,multi_col
函数实际上增加了列数,直到达到最大数,然后它再次重置为一列。
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.layout = QGridLayout()
test.setLayout(self.layout)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.clicked.connect(self.multi_col)
self.layout.addWidget(temp_btn)
self.setCentralWidget(test)
self.multiColumns = 3
self.columns = 1
def multi_col(self):
maxCol = 0
widgets = []
for i in range(self.layout.count()):
item = self.layout.itemAt(i)
if item.widget():
widgets.append(item.widget())
row, col, rSpan, cSpan = self.layout.getItemPosition(i)
maxCol = max(col, maxCol)
if maxCol < self.multiColumns - 1:
self.columns += 1
else:
self.columns = 1
row = col = 0
for widget in widgets:
self.layout.addWidget(widget, row, col)
col += 1
if col > self.columns - 1:
col = 0
row += 1
注意:我更改为 clicked
信号,因为它通常比 pressed
更受青睐,因为按钮的标准约定(只有在释放鼠标按钮时才会“接受”点击 在里面),在你的情况下它也会产生两个问题:
- 在视觉上,UI 造成混乱,按下的按钮在不同的位置变为未按下的状态(因为鼠标按钮在其实际几何形状之外释放);
- 概念上,因为如果用户在释放鼠标之前再次将鼠标移动到先前按下的按钮内,它将再次触发
pressed
;
我有一列自动生成的按钮,如果按钮太多,可以挤压 window 中的 UI 个元素。因此,我想通过以下方式自动将单列按钮 - 名义上在 QVBoxLayout
内称为 self.main_layout
- 转换为多列事件:
- 正在删除
self.main_layout
中的按钮
- 将它们添加到由
QVBoxLayout
s 表示的交替新列中
- 将
self.main_layout
更改为QHBoxLayout
- 将新列添加到此布局
我的尝试只会导致按钮停留在一列中,但现在甚至不调整大小以填充它们占据的 QSplitter
框架:
app = QApplication(sys.argv)
window = TestCase()
app.exec_()
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.layout = QVBoxLayout()
test.setLayout(self.layout)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.pressed.connect(self.multi_col)
self.layout.addWidget(temp_btn)
self.setCentralWidget(test)
@pyqtSlot()
def multi_col(self):
cols = [QVBoxLayout(), QVBoxLayout()]
while self.layout.count():
child = self.layout.takeAt(0)
if child.widget():
self.layout.removeItem(child)
cols[0].addItem(child)
cols[1], cols[0] = cols[0], cols[1]
self.layout = QHBoxLayout()
self.layout.addLayout(cols[0])
self.layout.addLayout(cols[1])
我这里有什么明显的错误吗?
替换 QWidget 的布局并不是那么简单,只需将另一个对象分配给存储其他布局引用的变量即可。在你正在做的几行代码中:
self.layout = Foo()
widget.setLayout(self.layout)
self.layout = Bar()
对象与变量不同,对象本身是执行动作的实体,而变量只是存储对象引用的地方。例如,对象可以是人和变量我们的名字,所以如果他们改变了我们的名字,并不意味着他们改变了我们这个人。
解决方案是使用 sip.delete 删除 QLayout,然后设置新布局:
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
import sip
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.setCentralWidget(test)
layout = QVBoxLayout(test)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.pressed.connect(self.multi_col)
layout.addWidget(temp_btn)
@pyqtSlot()
def multi_col(self):
cols = [QVBoxLayout(), QVBoxLayout()]
old_layout = self.centralWidget().layout()
while old_layout.count():
child = old_layout.takeAt(0)
widget = child.widget()
if widget is not None:
old_layout.removeItem(child)
cols[0].addWidget(widget)
cols[1], cols[0] = cols[0], cols[1]
sip.delete(old_layout)
lay = QHBoxLayout(self.centralWidget())
lay.addLayout(cols[0])
lay.addLayout(cols[1])
def main():
app = QApplication(sys.argv)
window = TestCase()
window.show()
app.exec_()
if __name__ == "__main__":
main()
我想提出一个替代解决方案,即使用 QGridLayout 并仅更改小部件的列而不是每次都设置新布局。 “技巧”是 addWidget()
总是在指定位置添加小部件,即使它已经是布局的一部分,所以您不需要删除布局项。
显然,这种方法的缺点是,如果小部件具有不同的高度,则每一行都取决于该行中所有小部件所需的最小高度,但由于 OP 是关于使用按钮的,因此情况不应如此。
这样做的主要好处是可以通过一个函数自动完成切换,可能通过设置最大列数以提供进一步的实现。
在下面的示例中,multi_col
函数实际上增加了列数,直到达到最大数,然后它再次重置为一列。
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.layout = QGridLayout()
test.setLayout(self.layout)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.clicked.connect(self.multi_col)
self.layout.addWidget(temp_btn)
self.setCentralWidget(test)
self.multiColumns = 3
self.columns = 1
def multi_col(self):
maxCol = 0
widgets = []
for i in range(self.layout.count()):
item = self.layout.itemAt(i)
if item.widget():
widgets.append(item.widget())
row, col, rSpan, cSpan = self.layout.getItemPosition(i)
maxCol = max(col, maxCol)
if maxCol < self.multiColumns - 1:
self.columns += 1
else:
self.columns = 1
row = col = 0
for widget in widgets:
self.layout.addWidget(widget, row, col)
col += 1
if col > self.columns - 1:
col = 0
row += 1
注意:我更改为 clicked
信号,因为它通常比 pressed
更受青睐,因为按钮的标准约定(只有在释放鼠标按钮时才会“接受”点击 在里面),在你的情况下它也会产生两个问题:
- 在视觉上,UI 造成混乱,按下的按钮在不同的位置变为未按下的状态(因为鼠标按钮在其实际几何形状之外释放);
- 概念上,因为如果用户在释放鼠标之前再次将鼠标移动到先前按下的按钮内,它将再次触发
pressed
;