简体   繁体   中英

How to remove empty space(void) in the tab widget after removing a PlotWidget?

I have a small dashboard app where I can add and remove graphs dynamically. The problem is that when I click the x-button (kill graph widget), the graph disappears from the view, but leaves an void above the top remaining graph (see attached screenshot).

I am pretty much a complete beginner on this, but I'm suspecting I'm not dealing with the removal of the widgets, and objects correctly?

In my simplified code below I have class dashboard_tab , where through a button click I create a add_new_graph object which creates a new graph widget self.graphWidget = pg.PlotWidget() .

When initiating a graph widget close(or kill) I call the following code:

def kill_widget(self):
        self.lay.removeWidget(self.graphWidget)
        self.graphWidget.deleteLater()
        self.graphWidget = None

This somehow seems to work, since the graph widget disappears from the view, but not the unwanted void.

My idea was then to also kill the add_new_graph object...should it have anything to do with my issue...by perhaps creating a signal from the add_new_graph object back to the dashboard_tab object...but it can't get that to work.

So my questions are:

  • What is the most likely reason for the unwanted void after deleting a widget?
  • Do I also have to delete the object to have proper coding?

Any points in the right direction would be appreciated!

class MainWindow(QtWidgets.QWidget):
    def __init__(self, parent):
        super(QtWidgets.QWidget, self).__init__(parent)
        self.layout = QtWidgets.QVBoxLayout(self)

        self.tab_holder = QtWidgets.QTabWidget()
        tab1 = dashboard_tab()

        self.tab_holder.addTab(tab1, "Dashboard")
        self.layout.addWidget(self.tab_holder)


class dashboard_tab(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.lay = QtWidgets.QVBoxLayout(self)

        self.numGraphs = 0

        addGraphBtn = QtWidgets.QToolButton()
        addGraphBtn.setText("  Add Graph")

        addGraphBtnMenu = QtWidgets.QMenu()
        addGraphBtnMenu.addAction('Add Temp Graph', self.add_graph)
        addGraphBtn.setMenu(addGraphBtnMenu)

        self.lay.addWidget(addGraphBtn)

    def add_graph(self):
        # Create a unique graph name
        self.name = ("graph" + str(self.numGraphs))

        self.graph = add_new_graph(self.name)
        self.lay.addWidget(self.graph)

        # Up graph name index every time a new graph is created
        self.numGraphs += 1


class add_new_graph(QtWidgets.QWidget):

    def __init__(self, name):
        super().__init__()
        self.name = name

        self.lay = QtWidgets.QHBoxLayout(self)

        self.graphWidget = pg.PlotWidget()
        self.killWidgetBtn = QtWidgets.QPushButton('')
        self.killWidgetBtn.clicked.connect(self.kill_widget)

        self.lay.addWidget(self.killWidgetBtn)
        self.lay.addWidget(self.graphWidget)

    def kill_widget(self):
        self.lay.removeWidget(self.killWidgetBtn)
        self.killWidgetBtn.deleteLater()
        self.killWidgetBtn = None

        self.lay.removeWidget(self.graphWidget)
        self.graphWidget.deleteLater()
        self.graphWidget = None


if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Dashboard App Screenshot

What is happening is that you are only removing the child widgets (the graph and the button), but not the container widget you added to the layout.

Even after deleting those objects, the container has a minimum size given by its layout's contents margins (depending on the Operating System and current style, they are between 5 and 10 pixels usually), and that's the "void" you can see.

Instead of deleting the children widgets, you can delete the container.


class add_new_graph(QtWidgets.QWidget):
    def __init__(self, name):
        # ...
        self.killWidgetBtn.clicked.connect()

Anyway, you might prefer to be warned when such a widget is going to be removed, so I'd suggest to make the parent's responsibility to do so:


class dashboard_tab(QtWidgets.QWidget):
    # ...
    def add_graph(self):
        # ...
        graph = add_new_graph(self.name)
        graph.deleteRequested.connect(self.deleteGraph)

    def deleteGraph(self, graph):
        # some warning message box, maybe?
        if QtWidgets.QMessageBox.warning(self, 'Close?', 
            'Delete the graph?', 
            QtWidgets.QMessageBox.Ok|QtWidgets.QMessageBox.Cancel
            ) != QtWidgets.QMessageBox.Ok:
                return
        graph.deleteLater()

class add_new_graph(QtWidgets.QWidget):
    deleteRequested = QtCore.pyqtSignal(object)

    def __init__(self, name):
        # ...
        self.killWidgetBtn.clicked.connect(
            lambda: )

A suggestion, if I may: use uppercase names for classes (so that they can easily be distinguished from functions), and don't use "action descriptions" for their names: classes don't usually "do" things by themselves, their methods do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM