首页 > 解决方案 > 在 PyQt5 中删除动态创建按钮

问题描述

我创建了一个在垂直布局中创建按钮的按钮,称为“elementos”。在每一行中,都有一个标签和一个应该删除该行的 X 按钮。有一个名为“puntos”MRE 的标签文本列表:

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QOpenGLWidget
import sys
from sys import argv, exit


class Renderizador(QOpenGLWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.puntos = []
        self.num_elementos = 0

    def borrar_punto(self):
        self.puntos.pop()
        ui.elementos.removeWidget(self.name)
        ui.elementos.removeWidget(self.borrar)
        self.name.deleteLater()
        self.borrar.deleteLater()
        self.num_elementos -= 1

    def crear_punto(self):
        do = ui.valor_do.value()
        cota = ui.valor_cota.value()
        alejamiento = ui.valor_alej.value()
        self.puntos.append((ui.nombre.toPlainText(), do, cota, alejamiento))
        self.name = QtWidgets.QLabel()
        self.name.setText(ui.nombre.toPlainText() + "({}, {}, {})".format(do, cota, alejamiento))
        self.borrar = QtWidgets.QPushButton()
        self.borrar.setText("X")
        self.borrar.clicked.connect(lambda: self.borrar_punto())

        ui.elementos.addWidget(self.name, self.num_elementos, 0, 1, 1)
        ui.elementos.addWidget(self.borrar, self.num_elementos, 1, 1, 1)
        self.num_elementos += 1
        self.update()


class UiVentana:
    def __init__(self):
        ventana.resize(1500, 1000)
        ventana.setLayoutDirection(QtCore.Qt.LeftToRight)
        ventana.setFixedSize(ventana.size())

        self.widget_central = QtWidgets.QWidget(ventana)
        self.gridLayoutWidget = QtWidgets.QWidget(self.widget_central)
        self.gridLayoutWidget.setGeometry(QtCore.QRect(1000, 60, 280, 50))
        self.Vistas = QtWidgets.QGridLayout(self.gridLayoutWidget)

        self.Visor = Renderizador(self.widget_central)
        self.Visor.setGeometry(QtCore.QRect(0, 0, 1000, 1000))

        self.gridLayoutWidget_2 = QtWidgets.QWidget(self.widget_central)
        self.gridLayoutWidget_2.setGeometry(QtCore.QRect(1004, 105, 300, 400))
        self.Punto = QtWidgets.QGridLayout(self.gridLayoutWidget_2)
        self.Punto.setContentsMargins(5, 5, 5, 5)
        self.Punto.setVerticalSpacing(4)
        self.texto_nombre = QtWidgets.QLabel(self.gridLayoutWidget_2)
        self.texto_nombre.setText("Nombre:")
        self.Punto.addWidget(self.texto_nombre, 3, 0, 1, 1)
        self.crear_punto = QtWidgets.QPushButton(self.gridLayoutWidget_2)
        self.crear_punto.setText("Crear")
        self.crear_punto.clicked.connect(self.Visor.crear_punto)

        self.Punto.addWidget(self.crear_punto, 3, 2, 1, 1)
        self.texto_cota = QtWidgets.QLabel(self.gridLayoutWidget_2)

        self.nombre = QtWidgets.QTextEdit(self.gridLayoutWidget_2)
        self.nombre.setMaximumSize(QtCore.QSize(100, 24))
        self.Punto.addWidget(self.nombre, 3, 1, 1, 1)

        self.scroll = QtWidgets.QScrollArea()
        self.scroll.setWidgetResizable(True)
        self.scroll_widget = QtWidgets.QWidget()
        self.scroll_widget.resize(200, 300)
        self.elementos_widget = QtWidgets.QWidget()
        self.vbox = QtWidgets.QVBoxLayout(self.scroll_widget)
        self.vbox.setContentsMargins(0, 0, 0, 0)
        self.vbox.addWidget(self.elementos_widget)
        self.vbox.addStretch()
        self.elementos = QtWidgets.QGridLayout()
        self.elementos_widget.setLayout(self.elementos)
        self.scroll.setWidget(self.scroll_widget)
        self.scroll_widget.setLayout(self.elementos)
        self.scroll.setWidget(self.scroll_widget)
        self.Punto.addWidget(self.scroll, 4, 0, 1, 3)

        self.valor_do = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
        self.Punto.addWidget(self.valor_do, 2, 0, 1, 1)
        self.valor_alej = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
        self.Punto.addWidget(self.valor_alej, 2, 1, 1, 1)
        self.valor_cota = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
        self.Punto.addWidget(self.valor_cota, 2, 2, 1, 1)

        ventana.setCentralWidget(self.widget_central)
        ventana.show()


def except_hook(cls, exception, traceback):
    sys.__excepthook__(cls, exception, traceback)


if __name__ == "__main__":
    app = QtWidgets.QApplication(argv)
    ventana = QtWidgets.QMainWindow()
    ui = UiVentana()
    sys.excepthook = except_hook
    exit(app.exec_())

pop 语句应从列表中删除有关标签的相应信息。我得到一个超出索引范围的错误,但如果我弹出最后一条语句,我得到一个“RuntimeError:QLabel 类型的包装 C/C++ 对象已被删除”错误。完整代码:https ://github.com/Jaime02/Proyecto-de-investigacion-2019-Dibujo-tecnico/blob/experimental/error (第 120-140 行)

编辑:删除按钮仅在创建两行或多行时才有效

标签: pythonpyqt5qpushbutton

解决方案


您的代码至少有以下错误:

  • 如果要在循环中创建对象,请不要使其成为成员,例如在您的情况下 self.name 和 self.borrar 是始终指向最后一个 QLabel 和 QPushButton 的变量,因此当您只删除最后一行将被一次性删除。

  • 您的代码非常混乱,因为它们正在修改变量的地方,因为它们可能会导致跟踪错误等问题,例如变量 window 和 ui。

考虑到上述情况,我重写了您的代码,实现了传递小部件和直接删除小部件的逻辑。

import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets


class UiVentana(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(UiVentana, self).__init__(parent)

        self.puntos = []

        self.visor = QtWidgets.QOpenGLWidget()

        self.valor_do = QtWidgets.QSpinBox()
        self.valor_cota = QtWidgets.QSpinBox()
        self.valor_alej = QtWidgets.QSpinBox()

        self.texto_nombre = QtWidgets.QLabel("Nombre")
        self.nombre = QtWidgets.QLineEdit()
        self.crear_punto = QtWidgets.QPushButton("Crear", clicked=self.crear_punto)

        elementos_widget = QtWidgets.QWidget()

        scroll_widget = QtWidgets.QWidget()
        scroll = QtWidgets.QScrollArea(widgetResizable=True)
        scroll.setWidget(scroll_widget)

        vbox = QtWidgets.QVBoxLayout(scroll_widget)
        vbox.addWidget(elementos_widget)
        vbox.addStretch()

        self.elementos = QtWidgets.QGridLayout(elementos_widget)

        grid_layout = QtWidgets.QGridLayout()
        grid_layout.addWidget(self.valor_do, 0, 0)
        grid_layout.addWidget(self.valor_cota, 0, 1)
        grid_layout.addWidget(self.valor_alej, 0, 2)
        grid_layout.addWidget(self.texto_nombre, 1, 0)
        grid_layout.addWidget(self.nombre, 1, 1)
        grid_layout.addWidget(self.crear_punto, 1, 2)
        grid_layout.addWidget(scroll, 2, 0, 1, 3)

        for i in range(3):
            grid_layout.setColumnStretch(i, 1)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        lay = QtWidgets.QHBoxLayout(central_widget)
        lay.addWidget(self.visor, stretch=1)
        lay.addLayout(grid_layout)

        self.resize(1280, 960)


    def crear_punto(self):
        do = self.valor_do.value()
        cota = self.valor_cota.value()
        alejamiento = self.valor_alej.value()
        name = QtWidgets.QLabel(
            "{}({}, {}, {})".format(self.nombre.text(), do, cota, alejamiento)
        )

        punto = (self.nombre.text(), do, cota, alejamiento)

        borrar = QtWidgets.QPushButton("X")

        wrapper = partial(self.borrar_punto, (name, borrar), punto)
        borrar.clicked.connect(wrapper)

        row = self.elementos.rowCount()
        self.elementos.addWidget(name, row, 0)
        self.elementos.addWidget(borrar, row, 1)

        self.puntos.append(punto)

    def borrar_punto(self, widgets, punto):
        if self.puntos:
            name, borrar = widgets
            name.deleteLater()
            borrar.deleteLater()
            self.puntos.remove(punto)
            print(self.puntos)


def except_hook(cls, exception, traceback):
    sys.__excepthook__(cls, exception, traceback)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    sys.excepthook = except_hook
    w = UiVentana()
    w.show()
    exit(app.exec_())

推荐阅读