Merlin
Merlin

Reputation: 297

Plot not updaing correctly using draw() (duplicated axes) in wxPython

I am new to Python and wxPython I am trying to simulate particles in a box with random velocities in random directions. I created a simple GUI in wxFormBuilder where I have a panel to show a plot of the paricles. The particles are set to a position and plotted onto the panel, then I start the simulation and update the x and y positons of the particles. When redrawing the positions The axes appear 'thicker' as before, it looks like if there are several axes ontop of eachother.

I cant find anything about this problem, I hope somebody could help me with this?

This is the code that creates the Plot:

import wx
import particles


import random
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
        FigureCanvasWxAgg as FigCanvas, \
        NavigationToolbar2WxAgg as NavigationToolbar
matplotlib.rcParams.update({'font.size': 8})

class MyFrameSub( particles.GUI_MainFrame ):
    def __init__( self, parent ):
        particles.GUI_MainFrame.__init__( self, parent )

    def InitData(self):
        self.npart = int(self.m_npart.GetValue())
        self.nsteps = int(self.m_steps.GetValue())
        self.ndt = float(self.m_dt.GetValue())

        self.x= [random.random() for I in range(self.npart)]
        self.y= [2*random.random()-1 for I in range(self.npart)]
        self.vx= [self.ndt*(2*random.random()-1) for I in range(self.npart)]
        self.vy= [self.ndt*(2*random.random()-1) for I in range(self.npart)]
        return

    def CreatePlot(self):
        panelsize = self.m_PlotPanel.GetClientSize()
        self.figure = Figure(figsize=(panelsize[0]/100.0,panelsize[1]/100.0), dpi=100, frameon=False)

        self.canvas = FigCanvas(self.m_PlotPanel, wx.ID_ANY, self.figure)

        self.axes = self.figure.add_subplot(111)
        self.axes.axis((-1,1,-1,1))

        self.partplot, = self.axes.plot(self.x, self.y, 'ro')

        self.canvas.draw()
        return

    def UpdateData(self):
        for i in range(self.nsteps):
            for j in range(self.npart):
                self.x[j]=self.x[j]+self.vx[j]
                self.y[j]=self.y[j]+self.vy[j]

                if abs(self.x[j])>1:
                    self.vx[j]=-self.vx[j]
                if abs(self.y[j])>1:
                    self.vy[j]=-self.vy[j]

            self.partplot.set_xdata(self.x)
            self.partplot.set_ydata(self.y)
            self.canvas.draw()
        return

followed by the button definitions, it looks like this: Before running the simulation: www.merlinvs.de/before.jpg

enter image description here

and after running the simulation: www.merlinvs.de/after.jpg

enter image description here

As you see the axes got ugly and I have no idea why.

Another question I was thinking about is the following: When I run a loop that takes a while the UI is unresponsive, is it possible to have the UI active to cancel a loop if desired?

Upvotes: 1

Views: 582

Answers (1)

lukecampbell
lukecampbell

Reputation: 15266

As for the unresponsive UI, I used greenlets for my Matplotlib stuff while updating it

from gevent.greenlet import Greenlet
from gevent import sleep
import matplotlib.plot as plt

# Plot stuff

def event_handler():
  # Can update plot
  sleep(1) # Simulate handling or number crunching (numpy WILL block)

g = Greenlet(event_handler)
g.start()
plt.plot(0,0) # Works immediately and updates

Some things of note is that for serious applications you need to add some protection against race coditions with the plot. Numpy and external science libraries typically cause the entire application to become unresponsive (in my experience) because they are blocking system calls outside of the greenlet context switcher's reach. For something simple though the above pattern works well.

Upvotes: 0

Related Questions