0

我有一列自动生成的按钮,如果有太多,可以挤压窗口中的 UI 元素。因此,我想通过以下方式自动将单列按钮(名义上在QVBoxLayout称为内部的按钮)self.main_layout转换为多列事件:

  • 从中删除按钮self.main_layout
  • QVBoxLayout将它们添加到由s表示的交替新列
  • 更改self.main_layoutQHBoxLayout
  • 将新列添加到此布局

我的尝试只是导致按钮停留在单列中,但现在甚至不调整大小以填充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])

我在这里做错了什么明显的事情吗?

4

2 回答 2

2

将另一个对象分配给存储另一个布局引用的变量,替换 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()
于 2021-07-21T00:19:54.257 回答
1

我想提出一个替代解决方案,即使用 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

  1. 从视觉上看,UI 会造成混乱,按下的按钮在不同的位置变得未按下(因为鼠标按钮在其实际位置之外释放,并且在那时,不同的几何形状);
  2. 从概念上讲,因为如果用户在释放鼠标之前再次在先前按下的按钮内移动鼠标,它将pressed再次触发;
于 2021-07-21T00:49:52.583 回答