pythonqvtkwidgetpyvista

PyVista: Why does the callback only draw the 3d entities and not the 2d one on the second time?


I have a question and hope that you can help me. I put together a small MCVE below.

When you click the button for the first time, you can see that the 3d data (add_mesh, add_points) and 2d data (chart.scatter) show up. If you click it again only the 3d data shows up.

Does someone have an idea why this happens?

import sys
import os
os.environ["QT_API"] = "pyqt5"

from qtpy import QtWidgets
import numpy as np
from pyvista import Chart2D
import pyvista as pv
from pyvistaqt import QtInteractor, MainWindow

class MyMainWindow(MainWindow):

    def __init__(self, parent=None, show=True):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.frame = QtWidgets.QFrame()
        vlayout = QtWidgets.QVBoxLayout()
        self.plotter = QtInteractor(self.frame)
        btn1 = QtWidgets.QPushButton("Button 1", self)
        vlayout.addWidget(btn1)
        btn1.clicked.connect(self.buttonClicked)
        vlayout.addWidget(self.plotter)
        self.frame.setLayout(vlayout)
        self.setCentralWidget(self.frame)
        self.show()


    def buttonClicked(self):

        self.plotter.clear()
        data = np.random.standard_normal(size=(100, 3))
        actor = self.plotter.add_points(data)
        chart = Chart2D()
        _x = np.random.standard_normal(100)
        _y = np.random.standard_normal(100)
        chart.scatter(_x, _y, color="tab:blue", style="d", label="Scores")
        self.plotter.add_chart(chart)
        self.plotter.add_mesh(pv.Sphere())

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MyMainWindow()

    sys.exit(app.exec_())

Solution

  • I think you've found a bug, thanks for the great example! This is now fixed in PyVista version 0.35.2, updating the package should make things working. Original (pre-fix) version of the answer below.


    What happens with charts is that they are stored in a private attribute called __charts, accessible via a private property called _charts on a plotter's Renderer. I.e. self.plotter.renderer._charts will give you a private pyvista.plotting.charts.Charts object. This is what actually keeps track of individual charts added via plotter.add_chart().

    But calling plotter.clear() doesn't clear this list of charts. How exactly this messes with replotting is not yet clear to me (I suspect that somehow earlier charts prevent new ones from being added, but new ones have already been removed from the plotter). But it makes sense to clear the charts too on plotter.clear(), and if I do that by editing PyVista's library code, your issue goes away.

    I've opened a pull request in PyVista to fix this (or to learn if I'm wrong about this being a bug). Until that happens and the fix is released, you can hack the equivalent step in your own code, in the callback:

            self.plotter.clear()
            self.plotter.renderer._charts.deep_clean()  # <- hacky workaround
    

    Needless to say, this is a temporary measure and very bad form to have in your own code. As soon as we have a new release with this fixed, you should remove this line.