I have this simple code, but every time I try to close one of my proxy 
widgets, I get this error below:

import sys
import numpy as np
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QGraphicsProxyWidget, QGridLayout
)
from PySide6.QtCore import Signal, Qt
import pyqtgraph as pg


class YawSweepPlot(pg.PlotWidget):
    """
    Represents Plot A: Yaw Sweep Plot.    Displays Clf vs Yaw with a 
movable vertical cursor.    Emits a yawChanged signal when the cursor is 
moved.    """
    yawChanged = Signal(float)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setBackground('w')
        self.setTitle("Clf vs Yaw")
        self.setLabel('left', "Clf")
        self.setLabel('bottom', "Yaw")
        self.setXRange(-12, 10)
        self.setYRange(-2.5, -1.5)

        # Simulated Clf data
        yaw = np.linspace(-12, 10, 500)
        clf = -2 + 0.5 * np.sin(0.5 * yaw)
        self.plot(yaw, clf, pen=pg.mkPen('b', width=2))

        # Add Vertical Cursor
        self.cursor = pg.InfiniteLine(angle=90, movable=True, 
pen=pg.mkPen('r', width=2))
        self.addItem(self.cursor)
        self.cursor.setPos(0)  # Initial Yaw position

        # Connect cursor movement to signal       
 self.cursor.sigPositionChanged.connect(self.emit_yaw_changed)

    def emit_yaw_changed(self):
        current_yaw = self.cursor.value()
        self.yawChanged.emit(current_yaw)


class FloorPlot(pg.PlotWidget):
    """
    Represents each Floor Plot (Plot B).    Displays pressure data based on 
yaw.    """

    def __init__(self, yaw=0.0, parent=None):
        super().__init__(parent)
        self.setBackground('w')
        self.setContentsMargins(0, 0, 0, 0)
        self.setTitle(f"Floor Plot at Yaw = {yaw:.2f}")
        self.setXRange(-1, 1)
        self.setYRange(-1, 1)
        self.yaw = yaw
        self.pressure_data = self.generate_pressure_data(yaw)
        self.plot_pressure()

    def generate_pressure_data(self, yaw):
        """
        Generates simulated pressure data based on yaw.        Ensures the 
seed is within [0, 2**32 - 1].        """
        seed = int(yaw * 10)
        seed = abs(seed) % (2 ** 32)
        rng = np.random.default_rng(seed)
        num_points = 50
        x = rng.uniform(-1, 1, num_points)
        y = rng.uniform(-1, 1, num_points)
        pressure = rng.uniform(0, 100, num_points)
        return np.column_stack((x, y, pressure))

    def plot_pressure(self):
        """
        Plots the pressure data on the PlotWidget.        """
        self.clear()
        x = self.pressure_data[:, 0]
        y = self.pressure_data[:, 1]
        pressure = self.pressure_data[:, 2]

        # Normalize pressure for color mapping
        norm_pressure = (pressure - pressure.min()) / (pressure.max() - 
pressure.min())
        colors = [pg.intColor(int(p * 255)) for p in norm_pressure]

        scatter = pg.ScatterPlotItem(x, y, size=10, brush=colors)
        self.addItem(scatter)

    def update_data(self, yaw):
        """
        Updates the pressure plot based on the new yaw value.        """
        self.yaw = yaw
        self.setTitle(f"Floor Plot at Yaw = {yaw:.2f}")
        self.pressure_data = self.generate_pressure_data(yaw)
        self.plot_pressure()


class FloorPlotWidget(QWidget):
    """
    Encapsulates a FloorPlot along with control buttons.    """
    duplicateRequested = Signal()
    closeRequested = Signal(QWidget)

    def __init__(self, yawChanged_signal, parent=None):
        super().__init__(parent)
        self.is_frozen = False
        self.yawChanged_signal = yawChanged_signal

        # Layout Setup
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(main_layout)

        # Initialize FloorPlot
        self.floor_plot = FloorPlot(yaw=0.0)
        main_layout.addWidget(self.floor_plot)

        # Buttons Layout
        buttons_layout = QHBoxLayout()
        main_layout.addLayout(buttons_layout)

        # Freeze Button
        self.freeze_button = QPushButton("Freeze")
        self.freeze_button.clicked.connect(self.toggle_freeze)
        buttons_layout.addWidget(self.freeze_button)

        # Duplicate Button
        self.duplicate_button = QPushButton("Duplicate")
        self.duplicate_button.clicked.connect(self.duplicateRequested.emit)
        buttons_layout.addWidget(self.duplicate_button)

        # Close Button
        self.close_button = QPushButton("Close")
        self.close_button.clicked.connect(self.close_plot)
        buttons_layout.addWidget(self.close_button)

        # Connect to yawChanged_signal
        self.yawChanged_signal.connect(self.handle_yaw_changed)

    def handle_yaw_changed(self, yaw):
        if not self.is_frozen:
            self.floor_plot.update_data(yaw)

    def toggle_freeze(self):
        if self.is_frozen:
            # Unfreeze: Start listening
            self.is_frozen = False
            self.freeze_button.setText("Freeze")
            self.yawChanged_signal.connect(self.handle_yaw_changed)
        else:
            # Freeze: Stop listening
            self.is_frozen = True
            self.freeze_button.setText("Unfreeze")
            self.yawChanged_signal.disconnect(self.handle_yaw_changed)

    def close_plot(self):
        """
        Emits a signal to request closing this plot.        """
        self.closeRequested.emit(self)


class FloorPlotGrid(pg.GraphicsLayoutWidget):
    """
    Manages a grid layout of FloorPlotWidget instances.    """

    def __init__(self, yawChanged_signal, parent=None):
        super().__init__(parent)
        self.yawChanged_signal = yawChanged_signal
        self.setLayout(QGridLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(0)
        self.color_bar = pg.ColorBarItem(interactive=False)
        color_map = pg.colormap.get('CET-D9')
        self.color_bar.setColorMap(color_map)
        self.color_bar.setLevels((-0.2, 0.2))
        self.max_columns = 3
        self.floor_plots = []
        self.proxies = []

        self.setStyleSheet("background: red")

        # Initialize with one FloorPlotWidget
        self.add_floor_plot()

    def update_color_bar(self):
        if len(self.floor_plots) == 0:
            return

        if self.ci.getItem(0, self.max_columns) is not None:
            self.removeItem(self.color_bar)

        rows_span, _ = self.get_row_col()
        col = self.max_columns if len(self.floor_plots) < self.max_columns 
else len(self.floor_plots)
        self.addItem(self.color_bar, 0, col, rowspan=rows_span + 1)



    def add_floor_plot(self, floor_plot_widget=None):
        """
        Adds a new FloorPlotWidget to the grid.        If floor_plot_widget 
is None, creates a new instance.        """
        if floor_plot_widget is None:
            floor_plot_widget = FloorPlotWidget(self.yawChanged_signal)

            # Connect signals
        floor_plot_widget.duplicateRequested.connect(self.add_floor_plot)
        floor_plot_widget.closeRequested.connect(self.remove_floor_plot)

        row, col =  self.get_row_col()

        # Create QGraphicsProxyWidget to embed FloorPlotWidget
        proxy = QGraphicsProxyWidget()
        proxy.setContentsMargins(0, 0, 0, 0)
        proxy.setWidget(floor_plot_widget)
        self.addItem(proxy, row, col)

        # Track the FloorPlotWidget and its proxy
        self.floor_plots.append(floor_plot_widget)
        self.proxies.append(proxy)
        self.update_color_bar()

    def get_row_col(self):
        # Calculate row and column
        index = len(self.floor_plots)
        return  divmod(index, self.max_columns)

    def remove_floor_plot(self, floor_plot_widget):
        """
        Removes a FloorPlotWidget from the grid.        """
        if floor_plot_widget in self.floor_plots:
            index = self.floor_plots.index(floor_plot_widget)
            proxy = self.proxies[index]

            # Remove from GraphicsLayoutWidget
            self.removeItem(proxy)

            # Remove references
            self.floor_plots.pop(index)
            self.proxies.pop(index)

            # Delete the proxy and widget
            floor_plot_widget.setParent(None)
            del floor_plot_widget
            del proxy
            # floor_plot_widget.deleteLater()
            # proxy.deleteLater()

            # Rearrange the remaining plots
            self.rearrange_plots()
            self.update_color_bar()

    def rearrange_plots(self):
        """
        Rearranges the plots in the grid after removal.        """
        if len(self.ci.items) > 1:
            self.ci.clear()

            # Reset proxies list
        self.proxies = []


        # Re-add FloorPlotWidgets
        for idx, floor_plot_widget in enumerate(self.floor_plots):
            row, col = divmod(idx, self.max_columns)
            proxy = QGraphicsProxyWidget()
            proxy.setWidget(floor_plot_widget)
            self.addItem(proxy, row, col)
            self.proxies.append(proxy)


class MainLayoutWidget(QWidget):
    """
    Main layout widget containing YawSweepPlot on the left and 
FloorPlotGrid on the right.    """

    def __init__(self, parent=None):
        super().__init__(parent)

        # Main Layout
        main_layout = QHBoxLayout()
        self.setLayout(main_layout)

        # Initialize YawSweepPlot
        self.yaw_sweep_plot = YawSweepPlot()
        main_layout.addWidget(self.yaw_sweep_plot, stretch=1)

        # Initialize FloorPlotGrid
        self.floor_plot_grid = FloorPlotGrid(self.yaw_sweep_plot.yawChanged)
        main_layout.addWidget(self.floor_plot_grid, stretch=2)


class MainWindow(QMainWindow):
    """
    The main application window.    """

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Yaw Sweep and Floor Plots")
        self.resize(1600, 800)

        # Set Central Widget
        self.main_layout_widget = MainLayoutWidget()
        self.setCentralWidget(self.main_layout_widget)


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

I'm getting this error:

Traceback (most recent call last):
  File "C:\dev\personal\LearningPyQT\final_floor_prototype.py", line 243, 
in remove_floor_plot
    self.rearrange_plots()
  File "C:\dev\personal\LearningPyQT\final_floor_prototype.py", line 260, 
in rearrange_plots
    proxy.setWidget(floor_plot_widget)
RuntimeError: Internal C++ object (FloorPlotWidget) already deleted.

I'm unable to figure out the exact issue, anyone faced this before

-- 
You received this message because you are subscribed to the Google Groups 
"pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/pyqtgraph/8911a4c2-2030-44c1-94cf-88f516d202f6n%40googlegroups.com.

Reply via email to