Using the lasso tool when file is not main

Hi,
I'm not an expert so please go easy on me. I am using the pyplot lasso
demo, and have got it to work how I would like. I am having a problem,
however, where I cannot get it to work if my python file is not the
main file (where __name__ is not __main__).

I took the part at the bottom in the "if __name__ == '__main__':" and
put it into a class. Everything works fine if I call the class from
within the same file. However, if I call the class from another file,
the graph loads fine but the lasso tool does not work. Troubleshooting
revealed that LassoManager.onpress is not being called when I click
the mouse. Any suggestions are welcome because this is driving me
crazy!
Thanks,
Mark

I can assure you that it does work from a library because I have done that. Most likely it is some minor error in your code. Is it possible to post a simple example to demonstrate your problem?

Ben Root

···

On Wednesday, September 5, 2012, Mark Budde wrote:

Hi,

I’m not an expert so please go easy on me. I am using the pyplot lasso

demo, and have got it to work how I would like. I am having a problem,

however, where I cannot get it to work if my python file is not the

main file (where name is not main).

I took the part at the bottom in the “if name == ‘main’:” and

put it into a class. Everything works fine if I call the class from

within the same file. However, if I call the class from another file,

the graph loads fine but the lasso tool does not work. Troubleshooting

revealed that LassoManager.onpress is not being called when I click

the mouse. Any suggestions are welcome because this is driving me

crazy!

Thanks,

Mark

Hi Mark,

I can’t seem to reproduce your issue, but it’s a bit difficult without seeing how exactly you wrapped up the “main” part of the code. Just guessing: maybe the two cases aren’t exactly the same.

Is it possible that you have lman (the LassoManager instance) defined in the same block of code as show in one case but not the other? If, for example, lman is defined in a method of your class, but not saved anywhere then it’ll get discarded after the method finishes. So show would need to be called inside that method, or saved as a class attribute.

Like I said, that’s just a wild guess. You should paste the class def if you’re still having problems.

Best

-Tony

P.S. If you’re running matplotlib from Github master, you might be interested in an alternative lasso tool (LassoSelector) that may be simpler to use.:

https://github.com/matplotlib/matplotlib/blob/master/examples/widgets/lasso_selector_demo.py

···

On Wed, Sep 5, 2012 at 7:19 PM, Mark Budde <markbudde@…287…> wrote:

Hi,

I’m not an expert so please go easy on me. I am using the pyplot lasso

demo, and have got it to work how I would like. I am having a problem,

however, where I cannot get it to work if my python file is not the

main file (where name is not main).

I took the part at the bottom in the “if name == ‘main’:” and

put it into a class. Everything works fine if I call the class from

within the same file. However, if I call the class from another file,

the graph loads fine but the lasso tool does not work. Troubleshooting

revealed that LassoManager.onpress is not being called when I click

the mouse. Any suggestions are welcome because this is driving me

crazy!

Thanks,

Mark

On closer inspection the problem isn't calling it from another file,
it is calling it from wxpython. Not exactly sure how to copy code...

lasso_test.py

···

-----
from matplotlib.widgets import Lasso
from matplotlib.nxutils import points_inside_poly
from matplotlib.colors import colorConverter
from matplotlib.collections import RegularPolyCollection

from matplotlib.pyplot import figure, show
from numpy import nonzero
from numpy.random import rand
from scipy import linspace, polyval, polyfit, sqrt, stats, randn

class Datum:
    colorin = colorConverter.to_rgba('red')
    colorout = colorConverter.to_rgba('green')
    def __init__(self, x, y, include=False):
        self.x = x
        self.y = y
        if include: self.color = self.colorin
        else: self.color = self.colorout

class LassoManager:
    def __init__(self, ax, data):
        self.axes = ax
        self.canvas = ax.figure.canvas
        self.data = data

        self.Nxy = len(data)

        facecolors = [d.color for d in data]
        self.xys = [(d.x, d.y) for d in data]
        fig = ax.figure
        self.collection = RegularPolyCollection(
            fig.dpi, 6, sizes=(100,),
            facecolors=facecolors,
            offsets = self.xys,
            transOffset = ax.transData)

        ax.add_collection(self.collection)

        self.cid = self.canvas.mpl_connect('button_press_event', self.onpress)
        self.ind = None

    def callback(self, verts):
        if hasattr(self, 'temp'):
            self.temp.pop(0).remove()

        facecolors = self.collection.get_facecolors()
        ind = nonzero(points_inside_poly(self.xys, verts))[0]
        selected = []
        for i in range(self.Nxy):
            if i in ind:
                facecolors[i] = Datum.colorin
                selected.append(self.xys[i])
            else:
                facecolors[i] = Datum.colorout
        xs = []
        ys = []
        for n in selected:
            xs.append(n[0])
            ys.append(n[1])
        (ar,br) = polyfit(xs,ys,1)
        t = linspace(min(xs),max(xs))
        print('ar=%s br=%s' % (ar,br))
        xr = polyval([ar,br],t)
        self.temp = self.axes.plot(t,xr,'-',c='b')
        self.canvas.draw_idle()
        self.canvas.widgetlock.release(self.lasso)
        del self.lasso
        self.ind = ind
    def onpress(self, event):
        print('clicked')
        if self.canvas.widgetlock.locked(): return
        if event.inaxes is None: return
        self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata),
self.callback)
        # acquire a lock on the widget drawing
# self.canvas.widgetlock(self.lasso)

class start_lasso():
    def __init__(self):
        data = [Datum(*xy) for xy in rand(100, 2)]

        fig = figure()
        ax = fig.add_subplot(111, xlim=(0,1), ylim=(0,1), autoscale_on=False)
        lman = LassoManager(ax, data)
        show()

--------------------------------
call_lasso.py
----
import wx
import lasso_test

class MyFrame(wx.Frame):
    """ We simply derive a new class of Frame. """
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        button = wx.Button(self, label='Open Lasso')
        self.Bind(wx.EVT_BUTTON, self.open_lasso, button)
        self.Show(True)
    def open_lasso(self, event):
        lasso_test.start_lasso()

app = wx.App(False)
frame = MyFrame(None, 'Small editor')
app.MainLoop()

-----------------------------
Thanks,
Mark

On Wed, Sep 5, 2012 at 5:15 PM, Tony Yu <tsyu80@...287...> wrote:

On Wed, Sep 5, 2012 at 7:19 PM, Mark Budde <markbudde@...287...> wrote:

Hi,
I'm not an expert so please go easy on me. I am using the pyplot lasso
demo, and have got it to work how I would like. I am having a problem,
however, where I cannot get it to work if my python file is not the
main file (where __name__ is not __main__).

I took the part at the bottom in the "if __name__ == '__main__':" and
put it into a class. Everything works fine if I call the class from
within the same file. However, if I call the class from another file,
the graph loads fine but the lasso tool does not work. Troubleshooting
revealed that LassoManager.onpress is not being called when I click
the mouse. Any suggestions are welcome because this is driving me
crazy!
Thanks,
Mark

Hi Mark,

I can't seem to reproduce your issue, but it's a bit difficult without
seeing how exactly you wrapped up the "main" part of the code. Just
guessing: maybe the two cases aren't *exactly* the same.

Is it possible that you have ``lman`` (the LassoManager instance) defined in
the same block of code as ``show`` in one case but not the other? If, for
example, ``lman`` is defined in a method of your class, but not saved
anywhere then it'll get discarded after the method finishes. So ``show``
would need to be called inside that method, or saved as a class attribute.

Like I said, that's just a wild guess. You should paste the class def if
you're still having problems.

Best
-Tony

P.S. If you're running matplotlib from Github master, you might be
interested in an alternative lasso tool (LassoSelector) that may be simpler
to use.:
https://github.com/matplotlib/matplotlib/blob/master/examples/widgets/lasso_selector_demo.py

I think I see your problem… see below:

class start_lasso():

def __init__(self):

    data = [Datum(*xy) for xy in rand(100, 2)]

    fig = figure()

    ax = fig.add_subplot(111, xlim=(0,1), ylim=(0,1), autoscale_on=False)

    lman = LassoManager(ax, data)

    show()

You aren’t saving any of the objects created in the “start_lasso” class to your start_lasso object. Luckily, with the way pyplot works, the figure object your create gets implicitly saved to the “pyplot state manager” (a sort of smart global location for the figure objects), and the axes object gets implicitly attached to the figure object. Therefore, when the python execution goes out of this scope, the figure object and the axes do not get garbage-collected. However, the lasso widget that gets created and all the callbacks that were attached are all done with weak references to the figure and axes. So when you leave this scope, the LassoManager no longer exists and the callback fails to execute (as designed).

So, make sure that at least lman (and possibly fig and ax) gets saved to the start_lasso object to solve that part of the problem. Next, you don’t save the start_lasso object your create anywhere, so even if you saved lman to start_lasso, the start_lasso object gets garbage-collected anyway as a temporary.

I hope this is clear. Let me know if you still have more issues.

Cheers!
Ben Root

Thanks Ben, that solved my issue. I guess I got thrown off because the
plot stayed open, as you described. It's hard to troubleshoot when you
don't get any errors. In the future I will attach all of my objects.
-Mark

···

On Fri, Sep 7, 2012 at 12:03 PM, Benjamin Root <ben.root@...1304...> wrote:

I think I see your problem... see below:

class start_lasso():
    def __init__(self):
        data = [Datum(*xy) for xy in rand(100, 2)]

        fig = figure()
        ax = fig.add_subplot(111, xlim=(0,1), ylim=(0,1),
autoscale_on=False)
        lman = LassoManager(ax, data)
        show()

You aren't saving any of the objects created in the "start_lasso" class to
your start_lasso object. Luckily, with the way pyplot works, the figure
object your create gets implicitly saved to the "pyplot state manager" (a
sort of smart global location for the figure objects), and the axes object
gets implicitly attached to the figure object. Therefore, when the python
execution goes out of this scope, the figure object and the axes do not get
garbage-collected. However, the lasso widget that gets created and all the
callbacks that were attached are all done with weak references to the figure
and axes. So when you leave this scope, the LassoManager no longer exists
and the callback fails to execute (as designed).

So, make sure that at least lman (and possibly fig and ax) gets saved to the
start_lasso object to solve that part of the problem. Next, you don't save
the start_lasso object your create anywhere, so even if you saved lman to
start_lasso, the start_lasso object gets garbage-collected anyway as a
temporary.

I hope this is clear. Let me know if you still have more issues.

Cheers!
Ben Root