Plot figure problems with python and matplotlib - python

I'm making an app that plots a figure after some processing. This is done after the user has introduced some values and pushes a button. However I don't get the figure plotted. Below there is a simplified code. This works fine if I plot directly the values of t and s, but not if it is done after pushing the button. What am I missing? Is there another better way to do so?
from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
import wx
class Input_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Input variables
self.button = wx.Button(self, label="Go")
# Set sizer for the panel content
self.sizer = wx.GridBagSizer(1, 1)
self.sizer.Add(self.button, (1, 2), (3, 6), flag=wx.EXPAND)
self.SetSizer(self.sizer)
class Output_Panel_Var(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Output variables
self.tittle = wx.StaticText(self, label="OUTPUTS:")
self.font = wx.Font(12, wx.DECORATIVE, wx.BOLD, wx.NORMAL)
self.tittle.SetFont(self.font)
self.lblt = wx.StaticText(self, label="t:")
self.resultt = wx.StaticText(self, label="", size=(100, -1))
self.lbls = wx.StaticText(self, label="s:")
self.results = wx.StaticText(self, label="", size=(100, -1))
# Set sizer for the panel content
self.sizer = wx.GridBagSizer(2, 2)
self.sizer.Add(self.tittle, (1, 3))
self.sizer.Add(self.lblt, (3, 1))
self.sizer.Add(self.resultt, (3, 2))
self.sizer.Add(self.lbls, (4, 1))
self.sizer.Add(self.results, (4, 2))
self.SetSizer(self.sizer)
class Output_Panel_Fig(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
def draw(self,t,s):
self.axes.plot(t, s)
class Main_Window(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, pos = (0, 0), size = wx.DisplaySize())
# Set variable panels
self.main_splitter = wx.SplitterWindow(self)
self.out_splitter = wx.SplitterWindow(self.main_splitter)
self.inputpanel = Input_Panel(self.main_splitter)
self.inputpanel.SetBackgroundColour('#c4c4ff')
self.outputpanelvar = Output_Panel_Var(self.out_splitter)
self.outputpanelvar.SetBackgroundColour('#c2f1f5')
self.outputpanelfig = Output_Panel_Fig(self.out_splitter)
self.main_splitter.SplitVertically(self.inputpanel, self.out_splitter)
self.out_splitter.SplitHorizontally(self.outputpanelvar, self.outputpanelfig)
# Set event handlers
self.inputpanel.button.Bind(wx.EVT_BUTTON, self.OnButton)
def OnButton(self, e):
t = arange(0.0, 1.0, 0.01)
s = sin(2 * pi * t)
#self.outputpanelvar.resultt.SetLabel('%.5f' % t)
#self.outputpanelvar.resultt.SetLabel('%.5f' % s)
self.outputpanelfig.draw(t,s)
def main():
app = wx.App(False)
frame = Main_Window(None, "T-Matrix Codes GUI")
frame.Show()
app.MainLoop()
if __name__ == "__main__" :
main()

I think you are missing a redraw of the canvas. It is not enough to do a new plot but a refresh of the drawing pane must be done! Add a self.canvas.draw() after your plot command in the draw method of the Output_Panel_Fig this should help.
import ...
class Input_Panel(wx.Panel):
def __init__(self, parent):
...
class Output_Panel_Var(wx.Panel):
def __init__(self, parent):
...
class Output_Panel_Fig(wx.Panel):
def __init__(self, parent):
...
def draw(self,t,s):
self.axes.plot(t, s)
self.canvas.draw()
class Main_Window(wx.Frame):
def __init__(self, parent, title):
...
def OnButton(self, e):
...
def main():
...
if __name__ == "__main__" :
main()

Related

wxPython ScrolledWindow not working when used in a panel

I am having trouble nesting a ScrolledWindow inside a wx.Panel.
If I create a scrolled window on its own it seems to work, however when I create it inside a wx.Panel and add the wx.Panel to the frames sizer it does not. Is there anything that I am missing?
Note:
#pa = AScrolledWindow(self) <-- if uncommented this works
pa = ScrolledWindowHolder(self) # However this does not!
import wx
class ScrolledWindowHolder(wx.Panel):
def __init__(self, parent):
super(ScrolledWindowHolder, self).__init__(parent=parent)
mysizer = wx.GridBagSizer()
self.myscrolledWindow = AScrolledWindow(self)
mysizer.Add(self.myscrolledWindow, pos=(0, 0), flag=wx.EXPAND)
self.SetSizerAndFit(mysizer)
class AScrolledWindow(wx.ScrolledWindow):
def __init__(self, parent):
super(AScrolledWindow, self).__init__(parent)
gb = wx.GridBagSizer()
self.sizer = gb
self._labels = []
for y in xrange(1, 30):
self._labels.append(wx.StaticText(self, -1, "Label #%d" % (y,)))
gb.Add(self._labels[-1], (y, 1), (1, 1))
self.SetSizer(self.sizer)
self.SetScrollRate(5, 5)
self.EnableScrolling(True, True)
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Programmatic size change')
sz = wx.BoxSizer(wx.VERTICAL)
#pa = AScrolledWindow(self)
pa = ScrolledWindowHolder(self)
sz.Add(pa, 1, wx.EXPAND)
self.SetSizer(sz)
def main():
wxapp = wx.App()
fr = TestFrame()
fr.Show(True)
wxapp.MainLoop()
if __name__ == '__main__':
main()
Not sure why but the issue appears to be with the fact that you are using a GridBagSizer with a single widget in ScrolledWindowHolder, which itself contains a GridBagSizer.
If you change ScrolledWindowHolder to use a BoxSizer it works, as expected.
class ScrolledWindowHolder(wx.Panel):
def __init__(self, parent):
super(ScrolledWindowHolder, self).__init__(parent=parent)
mysizer = wx.BoxSizer(wx.HORIZONTAL)
self.myscrolledWindow = AScrolledWindow(self)
mysizer.Add(self.myscrolledWindow, 1, wx.EXPAND,0)
self.SetSizerAndFit(mysizer)
Also, change the value of y to for y in range(1, 60): will demonstrate the scrolled window more effectively.

Weird black squares appear when switching tabs using wxpython

I'm using wxpython to create a GUI.
The idea is that whenever I select a row, something will happen on notebook1 and notebook 2, and different tabs will appear with different related information.
However, when I bind an event when selecting a row, weird beird black squares appear on the tab titles. What's wrong?
import wx
import threading
from time import sleep
class VAR():
def __init__(self):
self.result_row = ''
var = VAR()
class TabOne(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
t = wx.StaticText(self, -1, "This is the first tab", (20, 20))
class TabTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
t = wx.StaticText(self, -1, "This is the second tab", (20, 20))
class GUI(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(1000, 1000), style=wx.DEFAULT_FRAME_STYLE &
~wx.MAXIMIZE_BOX ^ wx.RESIZE_BORDER, pos=(100, 0))
self.panel = wx.Panel(self)
self.hsizer = wx.BoxSizer(wx.VERTICAL)
first_panel = wx.Panel(self.panel, size=(1000, 420))
self.hsizer.Add(first_panel, 1)
self.second_panel = wx.Panel(self.panel, size=(1000, 600))
self.notebook1 = wx.Notebook(self.second_panel, size=(1000, 230))
self.notebook2 = wx.Notebook(self.second_panel, size=(1000, 400))
self.hsizer.Add(self.second_panel, 1)
self.second_panel_sizer = wx.BoxSizer(wx.VERTICAL)
self.second_panel_sizer.Add(self.notebook1, 1, wx.EXPAND)
self.second_panel_sizer.Add(self.notebook2, 2, wx.EXPAND)
self.second_panel.SetSizerAndFit(self.second_panel_sizer)
self.panel.SetSizerAndFit(self.hsizer)
var.result_row = wx.ListCtrl(
first_panel, -1, style=wx.LC_REPORT, size=(980, 245), pos=(0, 175))
var.result_row.InsertColumn(0, "No.")
var.result_row.InsertColumn(1, "2 ")
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.SelectRow, var.result_row)
def SelectRow(self, event):
while (self.notebook1.GetPageCount()):
self.notebook1.DeletePage(0)
while (self.notebook2.GetPageCount()):
self.notebook2.DeletePage(0)
tab1 = TabOne(self.notebook1)
self.notebook1.AddPage(tab1, "Tab 1")
sizer = wx.BoxSizer()
sizer.Add(self.notebook1, 1, wx.EXPAND)
self.second_panel.SetSizer(sizer)
tab2 = TabTwo(self.notebook2)
self.notebook2.AddPage(tab2, "Tab 2")
sizer = wx.BoxSizer()
sizer.Add(self.notebook2, 1, wx.EXPAND)
self.second_panel.SetSizer(sizer)
def InfiniteProcess():
for i in range(100):
sleep(0.1)
var.result_row.Append(str(i))
finish = False
a = threading.Thread(target=InfiniteProcess)
a.setDaemon(1)
a.start()
app = wx.App()
frame = GUI(None, -1, "a")
frame.Show()
app.MainLoop()
sample

wxPython : Update the label on Panel with GridBagSizer and Timer

I want to update the panel "label", but I think I am wrong with Refresh/Update/Remove method .
I write 2 python file, the "WriteData.py" would auto-update a txt file, and the "Main.py" want to show the txt value on wx.panel.
I run the 2 python file at the same time, use Timer to auto update data every 3 sec .
And I use the GridBagSizer hope to arrange these panel position.
But I don't know how to arrange the new updating panel position, Also don't know how to remove previous panel .
Hope you give me some advice, or even point out my mistake.
I also appreciate for some example code about this !
Here is the "Main.py"
import wx
import time
def ReadData():
with open('RealTime.txt') as f:
for line in f:
data = line.split()
results = map(float, data)
return results
class BlockWindow(wx.Panel):
# code on book "wxPython in action" Listing 11.1
def __init__(self, parent, ID=-1, label="",
pos = wx.DefaultPosition, size = (100, 25)):
wx.Panel.__init__(self, parent, ID, pos, size,
wx.RAISED_BORDER, label)
self.label = label
self.SetMinSize(size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
sz = self.GetClientSize()
dc = wx.PaintDC(self)
w,h = dc.GetTextExtent(self.label)
dc.SetFont(self.GetFont())
dc.DrawText(self.label, (sz.width-w)/2, (sz.height-h)/2)
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, size=(0,0))
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.timer.Start(3000)
def OnTimer(self, evt):
Data = ReadData()
sizer = wx.GridBagSizer(hgap=5, vgap=-1)
bw = BlockWindow(self, label="Item 1" )
sizer.Add(bw, pos=(4, 2))
#bw.Refresh()
bw = BlockWindow(self, label="Updated : %.3f" % Data[0])
sizer.Add(bw, pos=(5, 2))
bw.Refresh()
#bw.Update(self, label ="Updated : %.3f" % Data[0] )
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(sizer, 0, wx.EXPAND|wx.ALL, 10)
self.SetSizer(mainSizer)
self.Fit()
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title=' Frame Title')
mypanel = MyPanel(self)
self.SetSize(wx.Size(800,600))
self.Centre()
app = wx.App(False)
MyFrame().Show()
app.MainLoop()
Here is the 'WriteData.py',
import sched, time
from datetime import datetime as dt
data = ['5.564', '3.4', '2.176', '7.3', '4.4', '5.5', '2.3', '4.4', '5.1']
index = 0
while True:
start = dt.now().hour
stop = dt.now().hour + 1
if index >7 : index=1
if dt.now().hour in range(start, stop): # start, stop are integers (eg: 6, 9)
# call to your scheduled task goes here
f2 = open('RealTime.txt', 'w')
f2.write("%s " % data[index])
index = index + 1
f2.close()
time.sleep(3)
else:
time.sleep(3)
When I run the 2 .py file , I got this situation Running example
Hope you help me solve this .
I use python2.7 on win10.
Best regards, Kuo-Ting Tang
You don't need to recreate everything from scratch each time an update is needed. Just move the initialization code (where you create BlockWindows and sizers to the constructor of MyPanel. It seems that all you want to do is update the label of the second panel, to achieve this you could write a method in BlockWindow that will update the label and call Refresh so that OnPaint will be triggered and will take care of the rest.
class BlockWindow(wx.Panel):
# code on book "wxPython in action" Listing 11.1
def __init__(self, parent, ID=-1, label="",
pos = wx.DefaultPosition, size = (100, 25)):
wx.Panel.__init__(self, parent, ID, pos, size,
wx.RAISED_BORDER, label)
self.label = label
self.SetMinSize(size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
sz = self.GetClientSize()
dc = wx.PaintDC(self)
w,h = dc.GetTextExtent(self.label)
dc.SetFont(self.GetFont())
dc.DrawText(self.label, (sz.width-w)/2, (sz.height-h)/2)
def UpdateLabel(self, label):
self.label = label
self.Refresh()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, size=(0,0))
sizer = wx.GridBagSizer(hgap=5, vgap=-1)
bw = BlockWindow(self, label="Item 1" )
sizer.Add(bw, pos=(4, 2))
self.block = BlockWindow(self, label="")
sizer.Add(self.block, pos=(5, 2))
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(sizer, 0, wx.EXPAND|wx.ALL, 10)
self.SetSizer(mainSizer)
self.Fit()
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.timer.Start(3000)
def OnTimer(self, evt):
Data = ReadData()
self.block.UpdateLabel("Updated : %.3f" % Data[0])

Matplotlib: Label points on mouseover

I have a scatter plot with several thousand points. This post tells me how to label them:
Matplotlib: How to put individual tags for a scatter plot
But that will look like a disaster with so many points. What I would like instead is to have a "tool tip" type label that pops up when you mouseover a point. Is that possible using matplotlib?
Once you get the coords of the point you can show them or any object-linked info in a textctrl in the toolbar. For this you have to instantiate a toolbar (NavigationToolbar2Wx()) in your canvas and add the textcontrol there. This is not as nice as a popup but it does the job.
Here you have an example of customizing your toolbar (only showing the x coordinate in the txtctrl):
#!/usr/bin/env python
#-*- coding: utf-8 -*-
#
"""
jvisor_spectrum_panel (visor_07)
25 julio 2010
"""
#
import wx
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
#
#
class SpectrumPanel(wx.Panel):
def __init__(self, parent, xlabel='m/z', ylabel='Intensity'):
wx.Panel.__init__(self, parent)
#
self.parent = parent
self.xlabel = xlabel
self.ylabel = ylabel
self.SetBackgroundColour("white")
#
self.figure = Figure()
self.canvas = FigureCanvas(self, -1, self.figure)
#
self.add_toolbar()
#
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP| wx.GROW| wx.EXPAND)
sizer.Add(self.toolbar, 0, wx.LEFT)
self.canvas.mpl_connect('motion_notify_event', self.on_motion)
self.SetSizer(sizer)
self.Fit()
self.clean()
#
def add_toolbar(self):
""
self.toolbar = NavigationToolbar2Wx(self.canvas)
mass_txt = wx.StaticText(self.toolbar, label='m/z', pos=(230, 7),
size=(25, 17))
mass_txt.SetBackgroundColour("light gray")
self.mass = wx.TextCtrl(self.toolbar, pos=(260,4), size=(50, 22),
style=wx.TE_READONLY)
#
self.toolbar.SetToolBitmapSize(wx.Size(24, 25))
self.toolbar.SetMinSize((1500, 31))
self.toolbar.Realize()
self.toolbar.Update()
#
def clean(self):
""
self.figure.clear()
self.axes = self.figure.add_subplot(111)
#
def dibuja(self):
"dibuja el canvas"
self.axes.set_xlabel(self.xlabel)
self.axes.set_ylabel(self.ylabel)
self.canvas.draw()
#
def on_motion(self, evt):
if evt.inaxes:
xpos = evt.xdata
self.mass.SetValue(' %0.1f' % (xpos))
if __name__ == '__main__':
""
class TestFrame(wx.Frame):
def __init__(self, *args, **kargs):
wx.Frame.__init__(self, *args, **kargs)
self.panel = SpectrumPanel(self)
self.Fit()
#
app = wx.PySimpleApp()
fr = TestFrame(None)
fr.Show()
app.MainLoop()
And here you can see the new control in the toolbar:

How to update a plot with python and Matplotlib

I have been bashing my head against the wall trying to update a graph using matplotlib with python and wxpython. I want to press a button and add data to a graph nested in a wx.notebook. Below is the code.
Thanks for the help
import wx
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
class Plot(wx.Panel):
def __init__(self, parent, id = -1, dpi = None, **kwargs):
wx.Panel.__init__(self, parent, id=id, **kwargs)
self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2,2))
self.canvas = Canvas(self, -1, self.figure)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas,1,wx.EXPAND)
self.SetSizer(sizer)
class JBC(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(600,600))
self.SetBackgroundColour(wx.Colour(236, 233, 216))
self.nbG = wx.Notebook(self, -1, style=0, size=(400,400), pos=(0,0))
self.gSheet1 = self.add("Test").gca()
calcButton = wx.Button(self, wx.NewId(), "Update", pos=(0, self.nbG.Position.y+400))
#self.gSheet1.hold(False)
#self.gSheet1.set_xlim(0,20)
#self.gSheet1.set_ylim(0,20)
#for i in range (2):
# self.gSheet1.plot([0,10],[1*i,1+i])
#axes2 = plotter.add('figure 2').gca()
#axes2.plot([1,2,3,4,5],[2,1,4,2,3])
self.Bind(wx.EVT_BUTTON, self.OnCalculate, calcButton)
self.Show(True)
def OnCalculate(self, event):
self.gSheet1.set_xlim(0,20)
self.gSheet1.set_ylim(0,20)
self.gSheet1.plot([1,2,3,4,5],[2,1,4,2,3])
self.Update()
def add(self,name="plot"):
page = Plot(self.nbG)
self.nbG.AddPage(page,name)
return page.figure
def Update(self):
self.gSheet1.clear()
plt.draw()
print "Tried to redraw"
app = wx.App()
JBC(None, -1, "Test Title")
app.MainLoop()
Using this example as a guide, perhaps try this:
import wx
import matplotlib as mpl
mpl.use('WXAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
class Plot(wx.Panel):
def __init__(self, parent, id = -1, dpi = None, **kwargs):
wx.Panel.__init__(self, parent, id=id, **kwargs)
self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2,2))
self.canvas = Canvas(self, -1, self.figure)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas,1,wx.EXPAND)
self.SetSizer(sizer)
class JBC(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(600,600))
self.SetBackgroundColour(wx.Colour(236, 233, 216))
self.nbG = wx.Notebook(self, -1, style=0, size=(400,400), pos=(0,0))
self.gSheet1 = self.add("Test").gca()
calcButton = wx.Button(self, wx.NewId(), "Update", pos=(0, self.nbG.Position.y+400))
#self.gSheet1.hold(False)
#self.gSheet1.set_xlim(0,20)
#self.gSheet1.set_ylim(0,20)
#for i in range (2):
# self.gSheet1.plot([0,10],[1*i,1+i])
#axes2 = plotter.add('figure 2').gca()
#axes2.plot([1,2,3,4,5],[2,1,4,2,3])
self.Bind(wx.EVT_BUTTON, self.OnCalculate, calcButton)
# self.Show(True)
def OnCalculate(self, event):
self.gSheet1.set_xlim(0,20)
self.gSheet1.set_ylim(0,20)
self.gSheet1.plot([1,2,3,4,5],[2,1,4,2,3])
self.Update()
def add(self,name="plot"):
page = Plot(self.nbG)
self.nbG.AddPage(page,name)
return page.figure
def Update(self):
self.gSheet1.clear()
plt.draw()
print "Tried to redraw"
if __name__ == '__main__':
app = wx.App()
frame=JBC(None, -1, "Test Title")
frame.Show()
app.MainLoop()
It is also possible to use matplotlib to draw an animated figure:
"""
Based on Tkinter bouncing ball code:
http://stackoverflow.com/q/13660042/190597 (arynaq) and
http://eli.thegreenplace.net/2008/08/01/matplotlib-with-wxpython-guis/
"""
import wx
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.figure as mplfig
import scipy.spatial.distance as dist
import matplotlib.backends.backend_wxagg as mwx
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, size = (800, 600))
self.panel = wx.Panel(self)
self.fig = mplfig.Figure(figsize = (5, 4), dpi = 100)
self.ax = self.fig.add_subplot(111)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.canvas = mwx.FigureCanvasWxAgg(self.panel, wx.ID_ANY, self.fig)
self.toolbar = mwx.NavigationToolbar2WxAgg(self.canvas)
self.button = wx.Button(self.panel, wx.ID_ANY, "Quit")
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.vbox.Add(self.toolbar, 0, wx.EXPAND)
self.vbox.Add(
self.button, 0, border = 3,
flag = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
self.toolbar.update()
self.update = self.animate().next
self.timer = wx.Timer(self)
self.timer.Start(1)
self.Bind(wx.EVT_BUTTON, self.OnCloseWindow, self.button)
self.Bind(wx.EVT_TIMER, lambda event: self.update())
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
def OnCloseWindow(self, evt):
self.timer.Stop()
del self.timer
self.Destroy()
def animate(self):
N = 100 #Number of particles
R = 10000 #Box width
pR = 5 #Particle radius
r = np.random.randint(0, R, (N, 2)) #Position vector
v = np.random.randint(-R/100, R/100, (N, 2)) #velocity vector
a = np.array([0, -10]) #Forces
v_limit = R/2 #Speedlimit
line, = self.ax.plot([], 'o')
line2, = self.ax.plot([], 'o') #Track a particle
self.ax.set_xlim(0, R+pR)
self.ax.set_ylim(0, R+pR)
while True:
v = v+a #Advance
r = r+v
#Collision tests
r_hit_x0 = np.where(r[:, 0]<0) #Hit floor?
r_hit_x1 = np.where(r[:, 0]>R) #Hit roof?
r_hit_LR = np.where(r[:, 1]<0) #Left wall?
r_hit_RR = np.where(r[:, 1]>R) #Right wall?
#Stop at walls
r[r_hit_x0, 0] = 0
r[r_hit_x1, 0] = R
r[r_hit_LR, 1] = 0
r[r_hit_RR, 1] = R
#Reverse velocities
v[r_hit_x0, 0] = -0.9*v[r_hit_x0, 0]
v[r_hit_x1, 0] = -v[r_hit_x1, 0]
v[r_hit_LR, 1] = -0.95*v[r_hit_LR, 1]
v[r_hit_RR, 1] = -0.99*v[r_hit_RR, 1]
#Collisions
D = dist.squareform(dist.pdist(r))
ind1, ind2 = np.where(D < pR)
unique = (ind1 < ind2)
ind1 = ind1[unique]
ind2 = ind2[unique]
for i1, i2 in zip(ind1, ind2):
eps = np.random.rand()
vtot = v[i1, :]+v[i2, :]
v[i1, :] = -(1-eps)*vtot
v[i2, :] = -eps*vtot
line.set_ydata(r[:, 1])
line.set_xdata(r[:, 0])
line2.set_ydata(r[:N/5, 1])
line2.set_xdata(r[:N/5, 0])
self.canvas.draw()
yield True
def main():
app = wx.App(False)
frame = Frame()
frame.Show(True)
app.MainLoop()
if __name__ == '__main__':
main()

Categories

Resources