axes.cla() in mplot3d - grid lines don't fall back. #750

Closed
sb2nov opened this Issue Mar 9, 2012 · 22 comments

Projects

None yet

6 participants

@sb2nov

Once you do a axes.cla(), the grid lines do not fall back.

set_axisbelow() does not work and neither does resetting the grid function.

@WeatherGod
Matplotlib Developers member

There are some oddities with mplot3d's cla function. However, I am not exactly sure I understand what you mean by "grid lines don't fall back". Could you please produce a simple example that I can run that demonstrates the problem?

@sb2nov

def PlotCanvas(self,xs1,ys1,zs1,size, color):
self.ax.cla()
self.ax.mouse_init(rotate_btn=1, zoom_btn=3)
self.ax.set_axisbelow()

    p=self.ax.scatter(xs1,ys1,zs1,marker='o', alpha=0.5, c=color1, s=size1)

    self.ax.set_xlim(self.XLow, self.XHigh)
    self.ax.set_ylim(self.YLow, self.YHigh)
    self.ax.set_zlim(self.ZLow, self.ZHigh) 

    self.ax.set_xlabel(self.Xfeature)
    self.ax.set_ylabel(self.Yfeature)
    self.ax.set_zlabel(self.Zfeature)

    self.canvas.draw()

The above code is a function that is triggered when I press a button in PyQT. If I remove the ax.cla() line, then the grid lines remain in the background. But then the new plot is drawn over the previous plot.
But with the ax.cla() , it brings the gridlines to the foreground (above the circles in the scatter plot)

@WeatherGod
Matplotlib Developers member

I don't think I can reproduce your problem. A quick note is that ax.set_axisbelow() takes a argument, so the above code should actually fail as written. Second, for mplot3d, set_axisbelow() does absolutely nothing (right now) because the axis lines will always be drawn below everything else. It was only included so that it can override the 2d version. Third, your scatter() call refers to "color1" and "size1", but they aren't the same as what you pass in through your function.

Just to be sure, I am testing using the latest v1.1.x. Which matplotlib version are you using?

@sb2nov

Sorry i just copied a chuck from my existing code and didnt edit it properly. What ever you mentioned is write . I'll post a working code that is complete with PyQt in sometime.

@sb2nov

import sys
from PyQt4 import QtGui, QtCore, Qt
import numpy as np
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar

def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin

class Example(QtGui.QWidget):

def __init__(self):
    super(Example, self).__init__()

    self.initUI()

def initUI(self):
    self.main_frame = QtGui.QWidget()
    self.dpi=100
    self.fig = Figure((5.0, 5.0), dpi=self.dpi)

    self.canvas = FigureCanvas(self.fig)
    self.canvas.setParent(self.main_frame)
    self.ax = self.fig.gca(projection='3d')


    self.btn = QtGui.QPushButton('Button', self)
    self.btn.resize(self.btn.sizeHint())
    self.btn.clicked.connect(self.drawplot)

    self.setWindowTitle('Tooltips')    

    Box =QtGui.QHBoxLayout()
    Box.addWidget(self.canvas)
    Box.addWidget(self.btn)
    self.main_frame.setLayout(Box)
    self.main_frame.show()

def drawplot(self):
    self.ax.cla()

self.ax.mouse_init(rotate_btn=1, zoom_btn=3)

    n = 500
    for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
        xs = randrange(n, 23, 32)
        ys = randrange(n, 0, 100)
        zs = randrange(n, zl, zh)
    self.ax.scatter(xs, ys, zs, c=c, marker=m, s=40)

    self.ax.set_xlabel('X Label')
    self.ax.set_ylabel('Y Label')
    self.ax.set_zlabel('Z Label')

    self.canvas.draw()

def main():

app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

if name == 'main':
main()

Here is a complete code for the problem, It shows how that once you do ax.cla() ... the grid lines come to the foreground.

@WeatherGod
Matplotlib Developers member

Confirmed... very strange. As a temporary work-around, I would suggest keeping a list of the results from your call to scatter. Then before calling scatter, pop through that list and call each item's remove() method.

@sb2nov

Can you give a example code to help me do the work around, I didnt understand what do I exactly need to do.

@WeatherGod
Matplotlib Developers member

So, some psuedo-code

class Example(QtGui.QWidget):
    def __init__(self) :
        self.scatterpts = []

and then further down:

    def drawplot(self) :
        for pt in self.scatterpts :
            pt.remove()
        self.scatterpts = []

        n = 500
        for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
            xs = randrange(n, 23, 32)
            ys = randrange(n, 0, 100)
            zs = randrange(n, zl, zh)
            pt = self.ax.scatter(xs, ys, zs, c=c, marker=m, s=40)
            self.scatterpts.append(pt)

        self.canvas.draw()

Does that make sense?

@sb2nov

The plot is not getting cleared for me, I am pasting the code I ran below for reference. Also I think instead of pt.remove() it should be self.scatterpts.remove(pt).

Please correct me if I am wrong somewhere. The self.ax.cla() line is commented.

import sys
from PyQt4 import QtGui, QtCore, Qt
import numpy as np
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar

def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin

class Example(QtGui.QWidget):

def __init__(self):
    super(Example, self).__init__()
    self.initUI()

def initUI(self):
    self.main_frame = QtGui.QWidget()
    self.dpi=100
    self.fig = Figure((5.0, 5.0), dpi=self.dpi)
    self.scatterpts = []
    self.canvas = FigureCanvas(self.fig)
    self.canvas.setParent(self.main_frame)
    self.ax = self.fig.gca(projection='3d')


    self.btn = QtGui.QPushButton('Button', self)
    self.btn.resize(self.btn.sizeHint())
    self.btn.clicked.connect(self.drawplot)

    self.setWindowTitle('Tooltips')    

    Box =QtGui.QHBoxLayout()
    Box.addWidget(self.canvas)
    Box.addWidget(self.btn)
    self.main_frame.setLayout(Box)
    self.main_frame.show()

def drawplot(self):

self.ax.cla()

    for pt in self.scatterpts :
        self.scatterpts.remove(pt)
    self.scatterpts = []

    n = 500
    for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
        xs = randrange(n, 23, 32)
        ys = randrange(n, 0, 100)
        zs = randrange(n, zl, zh)
    pt = self.ax.scatter(xs, ys, zs, c=c, marker=m, s=40)
    self.scatterpts.append(pt)

    self.ax.set_xlabel('X Label')
    self.ax.set_ylabel('Y Label')
    self.ax.set_zlabel('Z Label')

    self.canvas.draw()

def main():

app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

if name == 'main':
main()

@WeatherGod
Matplotlib Developers member

The remove() method I used has nothing to do with python lists. It means "remove this artist from the axes". Did you try it my way?

@sb2nov

Yeah just got it to work. Thank you so much for helping me out.

@WeatherGod
Matplotlib Developers member

I am not 100% certain (I don't have PyQt installed on this computer), but I believe this issue was inadvertently fixed when I fixed another bug with ax.cla(). When I do a scatter plot in interactive mode, and then do a cla(), the scatter points disappear. Closing.

@WeatherGod WeatherGod closed this Sep 20, 2012
@Ferguzz

This issue is still present for me in v1.2.0 with Tk running on win32.

@WeatherGod
Matplotlib Developers member

Can you please provide sample code to reproduce the problem?

@Ferguzz
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def plot_surface_3d(x,y,z,ax):
    ax.clear()
    ax.set_axisbelow(True)
    surf = ax.plot_surface(X,Y,Z, rstride=1, cstride=1)

X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

fig = plt.figure()
ax = fig.gca(projection = '3d')

plot_surface_3d(X, Y ,Z, ax)

plt.show()
@WeatherGod
Matplotlib Developers member

And that explains that... in mplot3d, I have extended cla() to take care of some extra steps for 3d plots in addition to the normal 2d steps done in the regular cla(). However, I failed to notice the existence of Axes.clear(), and so I didn't extend that in mplot3d. As a work-around, use ax.cla() instead.

@Ferguzz

ax.cla() does not fix the problem. Behavior is identical to that of ax.clear(). Note: I'm now running 1.3.0.

@WeatherGod
Matplotlib Developers member

And that will teach me not to mix C++ OO rules and python rules in my head. Because Axes.clear() is implemented as calling self.cla(), of course it will call the extended Axes3D.cla(). That will also teach me to actually run the code first before commenting. Indeed, I can reproduce your problem, so I will be re-opening this ticket.

@WeatherGod WeatherGod reopened this Aug 20, 2013
@tacaswell tacaswell added this to the unassigned milestone Aug 18, 2014
@AmyTeegarden AmyTeegarden added a commit to AmyTeegarden/matplotlib that referenced this issue Sep 7, 2015
@AmyTeegarden AmyTeegarden Bugfix for issue #750 (gridlines for 3d axes cover a plotted surface …
…after ax.cla() is called)
8c51da3
@WeatherGod
Matplotlib Developers member

This problem appears to have been fixed in #4553. I just tried the example and I didn't see any issues. Closing.

@WeatherGod WeatherGod closed this Sep 8, 2015
@rush719

I think this is related: impossible to hide the axes after clearing the plot and plotting new data (matplotlib==1.4.3). Example code:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

x = np.random.rand(100,100)
y = np.random.rand(100,100)
z = np.random.rand(100,100)
ax.plot_surface(x, y, z)
ax.set_axis_off()

ax.clear()

x = np.random.rand(10,10)
y = np.random.rand(10,10)
z = np.random.rand(10,10)
ax.plot_surface(x, y, z)
ax.set_axis_off()

plt.show()

@WeatherGod
Matplotlib Developers member

This issue was fixed for v1.5.x. I just tried your example, and the axes are not displayed (as expected).

@rush719

thanks, I didn't realize that I am using an outdated version

@QuLogic QuLogic modified the milestone: v1.5.0, unassigned Mar 30, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment