PySide & QWT object disable/destroy

113 Views Asked by At

I am just learning OOP and PySide. I have created a code as below. The application doesn't do anything much (it's a development project in learning stages).

import numpy as np
import sys

from qtpy.QtWidgets import (
    QWidget,
    QMainWindow,
    QVBoxLayout,
    QAction,
    QMenu,
    QLabel,
    QApplication,
    QMessageBox,
    QDesktopWidget,
)
from qtpy.QtCore import Qt, Slot, QPoint, QObject
from qwt import (
    QwtPlot,
    QwtPlotMarker,
    QwtPlotGrid,
    QwtLegend,
    QwtPlotCurve,
    QwtLegendData,
)


class contexMenuHelper(QObject):
    def __init__(self, plot, legend, legendItem):
        super(contexMenuHelper, self).__init__()
        self.plot = plot
        self.legend = legend
        self.legendItem = legendItem
        

    @Slot(QPoint)
    def contextMenuSlot(self, pos):       
        context = QMenu(self.legendItem)
        context.addAction(QAction("Delete", self))
        context.exec_(self.legendItem.mapToGlobal(pos))


class Plot(QwtPlot, QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setAxisTitle(QwtPlot.xBottom, "X-axis")
        self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
        self.setCanvasBackground(Qt.white)
        self.setAxisScale(QwtPlot.yLeft, -2, 2)
        QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)

        legend = QwtLegend()
        legend.setDefaultItemMode(QwtLegendData.Checkable)
        self.insertLegend(legend, QwtPlot.RightLegend)

        x = np.arange(-5.0, 5.0, 0.1)
        curves = []
        curves.append(
            QwtPlotCurve.make(
                x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
            )
        )
        curves.append(
            QwtPlotCurve.make(
                x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
            )
        )
        self.helpers = dict()
        for a in curves:
            legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
            h = contexMenuHelper(self, legend, legend.legendWidget(a))
            self.helpers[a]  = h
            legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)

        QwtPlotMarker.make(
            align=Qt.AlignRight | Qt.AlignTop,
            linestyle=QwtPlotMarker.HLine,
            color="black",
            plot=self,
        )
        for keys, value in self.helpers.items():
            print(keys)
            print(value)    
        # insert a vertical marker at x = 0
        QwtPlotMarker.make(
            align=Qt.AlignRight | Qt.AlignTop,
            linestyle=QwtPlotMarker.VLine,
            color="black",
            plot=self,
        )

        legend.checked.connect(self.showCurve)
        self.replot()

    @Slot(object, bool, int)
    def showCurve(self, obj, condition, num):
        obj.setVisible(not condition)
        self.replot()

    @Slot(object, bool, int)
    def __del__(self,  obj, condition):
        print('Destructor called, vehicle deleted.')


class SimplePlot(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        plot = Plot()
        plot.setTitle("Trigonometric")
        self.setWindowTitle("Trigonometric")
        layout.addWidget(plot)
        label = QLabel("Press the legend to en/disable a curve")
        layout.addWidget(label)
        self.center()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def closeEvent(self, event):
        reply = QMessageBox.question(
            self,
            "Message",
            "Are you sure to quit?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No,
        )

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = SimplePlot()
    window.show()
    window.resize(800, 600)
    sys.exit(app.exec_())

I made the active legend and the context menu:

enter image description here

I want to make it so that when I select "Delete" from the context menu, the corresponding function waveform in the graph and the corresponding object in the legend will be deleted.

1

There are 1 best solutions below

0
VRI - Opcjonalny - Optional On

I have implemented it as follows. Perhaps someone will find my thinking useful. It works correctly and as I expected although there is a tiny error in the operation itself .

Do you see what error I mean?

class contexMenuHelper(QObject):
    def __init__(self, plot, legend, legendItem):
        super(contexMenuHelper, self).__init__()
        self.plot = plot
        self.legend = legend
        self.legendItem = legendItem
        self.emlSel = QAction("Delete")


    @Slot(QPoint)
    def contextMenuSlot(self, pos):       
        context = QMenu(self.legendItem)
        context.addAction(self.emlSel)        
        context.exec_(self.legendItem.mapToGlobal(pos))
        self.emlSel.triggered.connect(self.destroy())
    
    @Slot()
    def destroy(self):
        QwtPlotCurve.detach(self.legend)

     
        

class Plot(QwtPlot, QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setAxisTitle(QwtPlot.xBottom, "X-axis")
        self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
        self.setCanvasBackground(Qt.white)
        self.setAxisScale(QwtPlot.yLeft, -2, 2)
        QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)

        legend = QwtLegend()
        legend.setDefaultItemMode(QwtLegendData.Checkable)
        legend.resize(100,100)
        self.insertLegend(legend, QwtPlot.RightLegend)
        
        x = np.arange(-5.0, 5.0, 0.1)
        curves = []
        curves.append(
            QwtPlotCurve.make(
                x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
            )
        )
        curves.append(
            QwtPlotCurve.make(
                x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
            )
        )

        self.helpers = dict()
        for a in curves:
            legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
            h = contexMenuHelper(self, a, legend.legendWidget(a))
            self.helpers[a]  = h
            legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)


        QwtPlotMarker.make(
            align=Qt.AlignRight | Qt.AlignTop,
            linestyle=QwtPlotMarker.HLine,
            color="black",
            plot=self,
        )

        QwtPlotMarker.make(
            align=Qt.AlignRight | Qt.AlignTop,
            linestyle=QwtPlotMarker.VLine,
            color="black",
            plot=self,
        )

        legend.checked.connect(self.showCurve)
        self.replot()

    @Slot(object, bool, int)
    def showCurve(self, obj, condition, num):
        obj.setVisible(not condition)
        self.replot()


class SimplePlot(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        plot = Plot()
        plot.setTitle("Trigonometric")
        self.setWindowTitle("Trigonometric")
        layout.addWidget(plot)
        label = QLabel("Press the legend to en/disable a curve")
        layout.addWidget(label)
        self.center()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def closeEvent(self, event):
        reply = QMessageBox.question(
            self,
            "Message",
            "Are you sure to quit?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No,
        )

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = SimplePlot()
    window.show()
    window.resize(850, 600)
    sys.exit(app.exec_())