schallwerkzeuge.py
#!/usr/bin/python
# coding=utf8
## Verhalten von / wie in Python 3 a/b = float
from __future__ import division
from math import pi
### Audio-Module
import pyaudio
from scipy.io import wavfile
### Fourier, Numerik
import numpy.fft as FFT
import numpy as np
##############
# Graphik
import matplotlib.pyplot as plt
###
CHANNELS=1
RATE=44100
DAUER=1
ANZAHL=DAUER*RATE # ANZAHL der Frames,
# globale Variablen, schlechter Programmierstil, hier aber ganz
# praktisch: Nach Aufnahme oder Lesen werden diese Variablen
# entsprechend gesetzt.
def recordsnd(filename, time):
'''
Nimmt eine time Sekunden lange Schallsequenz
auf (mit der Samplerate 48000 Hz) auf und speichert,
speichert sie in der Datei filename und gibt die
Aufnahme als numpy-array mit Werten zwischen -1 und 1 zurück
'''
global ANZAHL
global RATE
global DAUER
global CHANNELS
DAUER=time
ANZAHL=int(RATE*DAUER)
p = pyaudio.PyAudio()
raw_input("Aufnehmen "+str(time)+ " Sekunden): Eingabetaste drücken...")
stream = p.open(format =pyaudio.paInt16 ,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = 1024)#ANZAHL)
yy=stream.read(ANZAHL)
y=np.fromstring(yy,dtype=np.short)
if CHANNELS==2:
y=y.reshape((y.shape[0]//2,2))
print("Aufgenomen: " + str(ANZAHL) + " Frames")
stream.close()
p.terminate()
yy=np.array(y,dtype='d')/32768.0
if filename!=None:
wavwrite(filename, yy)
return yy
def wavwrite(filename, y):
'''Schreibt ein wav-File aus einem numpy-array'''
wavfile.write(filename, RATE, np.array(y*2**15, dtype=int))
def wavread(filename):
'''Liest ein wav-File in ein numpy-Array'''
global ANZAHL
global RATE
global DAUER
global CHANNELS
RATE,y=wavfile.read(filename)
ANZAHL=y.shape[0]
if len(y.shape)==2:
CHANNELS=y.shape[1]
else:
CHANNELS=1
DAUER=ANZAHL/RATE
return np.array(y,dtype=np.float)/2**15
def playsnd(y, r):
'''
spielt das numpy-array (bzw. Vektor) y als Klang
mit der Samplerate r [Hz] ab.
'''
def callback(in_data, frame_count, time_info, status):
data = (32767*klanggen.generate(frame_count/RATE)).astype(np.int16)
return (data.copy(), pyaudio.paContinue)
p = pyaudio.PyAudio()
CHUNK=2**12
stream = p.open(format =pyaudio.paInt16 ,
channels = CHANNELS,
rate = r,
output = True,
frames_per_buffer=CHUNK)
#callback=callback)#ANZAHL)
yy=np.array(y.flatten()*2**15,dtype=np.short)
for i in range(0,len(yy),CHUNK):
stream.write(yy[i:i+CHUNK].tostring(order='F'))
stream.close()
p.terminate()
def inspectsnd(y):
'''Zeigt einen Plot von y'''
n=y.shape[0]
fig=plt.Figure(figsize=(6,4),dpi=100)
ax=fig.add_subplot(111)
x=np.arange(0,n,1)/np.float32(RATE)
plt.plot(x,y)
plt.show()
def inspectspec(y):
'''zeigt einen Plot des DFT-Spektrums von y'''
global RATE
# Vorbereitungen, um ein Fenster zum Plotten zu kriegen
plt.figure(1)
n=len(y)
window=np.blackman(n)
sumw=sum(window*window)
A=FFT.fft(y*window)
B2=(A*np.conjugate(A)).real
sumw*=2.0
sumw/=1/np.float(RATE) # sample rate
B2=B2/sumw
x=np.arange(0,n/2,1)/np.float(n)*RATE
eps=1e-8
plt.plot(x, np.log10(B2[0:n/2]+eps))
plt.show()
def sinewave(f,r,d):
'''erzeugt die Daten einer Sinusschwingung mit Frequenz f, Dauer d
und Samplerate r'''
global RATE
global DAUER
global ANZAHL
RATE=r
DAUER=d
ANZAHL=DAUER*RATE
y=np.zeros(r*d)
for i in range(0,r*d):
y[i]=np.sin(2*pi*f*i/float(r))
return y
fouriersb.py
from __future__ import division
# -*- coding: utf-8 -*-
import scipy
import numpy.fft as FFT
import matplotlib.pyplot as plt
from matplotlib.ticker import FormatStrFormatter
execfile("schallwerkzeuge.py")
def calc_hamming_weights(T,fs,framesamp, hopsamp):
halfwindow=framesamp//2
x=scipy.zeros(int(T*fs))
w = scipy.hamming(framesamp)
for i in range(halfwindow, len(x)-halfwindow, hopsamp):
x[i-halfwindow:i+halfwindow+1] += w
for i in range(0,int(T*fs)):
if x[i]!=0:
x[i]=1./x[i]
else:
x[i]=1.
return x
def stft(x, fs, framesz, hop):
"""Short time Fourier transformation: stft(x, RATE, fensterdauer, fensterverschiebung)
Berechnet die diskrete Fourier-Transformation jeweils von einem Signalstück der
Dauer "fensterdauer". Die betrachteten Stücke sind jeweils um "fensterverschiebung"
gegeneinander verschoben.
Vor der Transformation werden sie mit einem Hamming-Window multipliziert.
Die Funktion liefert ein Array zurück, dessen Zeilen die einzelnen Fouriertransformationen
enthalten.
"""
#x=x.copy()
halfwindow = int(framesz*fs/2)
framesamp=2*halfwindow+1
hopsamp = int(hop*fs)
w = scipy.hamming(framesamp)
X = scipy.array([scipy.fft(w*x[i-halfwindow:i+halfwindow+1])
for i in range(halfwindow, len(x)-halfwindow, hopsamp)])
return X
def istft(X, fs, T, hop):
""" istft(X, RATE, DAUER, fensterverschiebung)
Umkehrung von stft, nicht ganz perfekt.
Interpretiert die Matrix X so, dass die Zeilen
die Fouriertransformierte von Stücken enthalten,
die jeweils um "fensterverschiebung" gegeneinander
verschoben sind.
"""
print fs, T, int(T*fs)
x = np.zeros(int(T*fs),dtype=np.complex)
framesamp = X.shape[1]
halfwindow=framesamp//2
hopsamp = int(hop*fs)
w=calc_hamming_weights(T,fs,framesamp,hopsamp)
#ww = scipy.hamming(framesamp)
#for i in range(len(ww)):
# try:
# ww[i]=1./ww[i]
# except:
# ww[i]=1.
#print "halfwindow " , halfwindow, " hopsamp ", hopsamp , "len(x)", len(x)
for n,i in enumerate(range(halfwindow, len(x)-halfwindow, hopsamp)):
#print "n: ",n, "i: ", i#print len(FFT.ifft(X[n]))
#print len(x[i-halfwindow:i+halfwindow+1])
x[i-halfwindow:i+halfwindow+1] += scipy.ifft(X[n])
return x*w
def ersterplot(filename):
global DAUER
global RATE
global ANZAHL
sig=wavread(filename)
plt.subplot(2,2,1)
timescale=linspace(0,DAUER,len(sig))
plt.plot(timescale,sig)
plt.xlabel("Zeit [s]")
plt.ylabel("Amplitude des Signals")
plt.title("Das Signal")
plt.subplot(2,2,2)
plt.plot(timescale,scipy.bartlett(len(sig)))
plt.xlabel("Zeit [s]")
plt.ylabel("Faktor")
plt.title("Das Bartlett-Fenster")
sigf=FFT.fft(sig)
plt.subplot(2,2,3)
frqscale=linspace(0,len(sig)/DAUER, len(sig))
plt.plot(frqscale,np.abs(sigf))
plt.xlabel("Frequenz [Hz]")
plt.ylabel("Amplitude ")
plt.title("Das Spektrum")
w=scipy.bartlett(len(sig))
sigfhanning=FFT.fft(w*sig)
plt.subplot(2,2,4)
plt.plot(frqscale,np.abs(sigfhanning))
plt.xlabel("Frequenz [Hz]")
plt.ylabel("Amplitude ")
plt.title("Spektrum mit Bartlett-Fenster")
plt.show()
def zweiterplot():
# hier nun wird etwas ausprobiert, das Ihr letztes Mal
# angesprochen habt: Immer in kleinen Zeitintervallen
# das Spektrum berechnen
global DAUER
global RATE
global ANZAHL
fensterdauer=0.2
fensterueberlappung=0.05 # jeweils in Sekunden
sig=wavread("lala.wav")
A=stft(sig,RATE,fensterdauer,fensterueberlappung)
rsig=istft(A,RATE,DAUER,fensterueberlappung)
eps=1e-8 # Offset, um logarithmieren zu koennen
r,s=A.shape
yl=linspace(0,DAUER, r)
xl=linspace(0,RATE/2,s/2)
X,Y=meshgrid(xl,yl)
plt.figure(1)
plt.pcolor(X,Y,np.log10(np.absolute(A[:,:s/2]+eps)))
plt.show()
def dritterplot():
global DAUER
global RATE
global ANZAHL
# Hier vergleichen wir die Rekonstruktion mit dem
# ursprünglichen Signal
fensterdauer=0.2
fensterueberlappung=0.05 # jeweils in Sekunden
sig=wavread("lala.wav")
A=stft(sig,RATE,fensterdauer,fensterueberlappung)
rsig=istft(A,RATE,DAUER,fensterueberlappung)
plt.subplot(2,1,1)
plt.plot(np.abs(sig))
plt.subplot(2,1,2)
plt.plot(np.abs(rsig))
print len(sig), len(rsig)
plt.show()
def vierterplot(fensterdauer=0.2,fensterueberlappung=0.05):
global DAUER
global RATE
global ANZAHL
#
# Heisenbergsche Unschärferelation
#
sig=np.append(sinewave(200,RATE,1),sinewave(500,RATE,1))
DAUER=2
A=stft(sig,RATE,fensterdauer,fensterueberlappung)
jump=int(fensterdauer/2*RATE/(DAUER/fensterueberlappung))
eps=1e-8 # Offset, um logarithmieren zu koennen
r,s=A.shape
yl=linspace(0,DAUER, r)
xl=linspace(0,RATE/2,s/2)
X,Y=meshgrid(xl,yl)
plt.figure(1)
plt.pcolor(X,Y,np.log10(np.absolute(A[:,:s/2]+eps)))
plt.show()
def fuenfterplot():
global DAUER
global RATE
global ANZAHL
fensterdauer=0.2
fensterueberlappung=0.05 # jeweils in Sekunden
eps=1e-8 # Offset, um logarithmieren zu koennen
#
# Nun die Spektren von i und a zum Vergleich
#
sig1=wavread("a.wav")
A1=stft(sig1,RATE,fensterdauer,fensterueberlappung)
sig2=wavread("i.wav")
A2=stft(sig2,RATE,fensterdauer,fensterueberlappung)
r,s=A1.shape
yl=linspace(0,DAUER, r)
xl=linspace(0,RATE/2,s/2)
X,Y=meshgrid(xl,yl)
plt.subplot(2,1,1)
plt.pcolor(X,Y,np.log10(np.absolute(A1[:,:s/2]+eps)))
r,s=A2.shape
yl=linspace(0,DAUER, r)
xl=linspace(0,RATE/2,s/2)
X,Y=meshgrid(xl,yl)
plt.subplot(2,1,2)
plt.pcolor(X,Y,np.log10(np.absolute(A2[:,:s/2]+eps)))
plt.show()
if __name__=="__main__":
ersterplot("zweisignale.wav")
#ersterplot("nochzweisignale.wav")
#zweiterplot()
#dritterplot()
#vierterplot(fensterdauer=0.2, fensterueberlappung=0.05)
#vierterplot(fensterdauer=0.05, fensterueberlappung=0.02)
#fuenfterplot()
### jetzt wärt ihr dran
microlistener.py
#!/usr/bin/python
# coding=utf8
from __future__ import division
import pyaudio
import time
### Diese Klasse wird im Modul microlistener definiert.
### Sie kann in allen Projekten verwendet werden, die eine
### kontinuierliche Audio-Eingabe brauchen.
class MicroListener(object):
def __init__(self,rate,channels,chunk,callback,playback=False):
'''kreiert einen Stream, der mit der Samplerate 'rate'
und 'channels' Kanälen über das Microphon Audio aufnimmt.
Nach jeweils 'chunk' Frames wird die callback-Funktion
aufgerufen.
Falls playback==True, werden die Daten an den Lautsprecher
durchgereicht.
Die Callback-Funktion nimmt als Argumente
(in_data, frame_count, time_info, status)
und gibt ein Tupel zurück, dessen erster Eintrag die
Daten (für Playback), dessen zweiter Eintrag ein Signal
ist, sollte pyaudio.paContinue sein.
Also z. B. (mit einer globalen Variable CHANNELS)
def micro_callback(in_data, frame_count, time_info)
y=np.fromstring(in_data,dtype=np.short)
if CHANNELS==2:
y=y.reshape((y.shape[0]//2,2))
else:
y=y.reshape((y.shape[0],1))
## y enthält danach die Daten des letzten Abschnitts
## aus chunk samples.
## jetzt irgendwas mit den Daten machen
##
return (in_data, pyaudio.paContinue)
'''
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16 ,
channels=channels,
rate=rate,
input=True,
output=playback,
stream_callback=callback,
frames_per_buffer=chunk)
self.stream.start_stream()
time.sleep(0.5)
def __del__(self):
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
#########################################################################
### Test-Sektion #######################################################
#########################################################################
if __name__=='__main__':
import numpy as np
###########################
### GLOBALE VARIABLEN
CHANNELS=2
RATE=44100
CHUNK=2**11
def micro_callback(in_data, frame_count, time_info,status):
'''callback Funktion: wird vom PyAudio-Objekt aufgerufen,
wenn CHUNK Frames von der Soundkarte (Mikrophon) gelesen wurden'''
y=np.array(np.fromstring(in_data,dtype=np.short),dtype=np.float)
if CHANNELS==2:
y=y.reshape((y.shape[0]//2,2))
else:
y=y.reshape((y.shape[0],1))
print int(np.linalg.norm(y[:,0])/2000.)*'*'
return (in_data, status)
listen=MicroListener(RATE,CHANNELS,CHUNK,micro_callback)
while True:
pass
microlistenerdftnew.py
#!/usr/bin/python
# coding=utf8
from __future__ import division
import pyaudio
import time
### Diese Klasse wird im Modul microlistener definiert.
### Sie kann in allen Projekten verwendet werden, die eine
### kontinuierliche Audio-Eingabe brauchen.
class MicroListener(object):
def __init__(self,rate,channels,chunk,callback,playback=False):
'''kreiert einen Stream, der mit der Samplerate 'rate'
und 'channels' Kanälen über das Microphon Audio aufnimmt.
Nach jeweils 'chunk' Frames wird die callback-Funktion
aufgerufen.
Falls playback==True, werden die Daten an den Lautsprecher
durchgereicht.
Die Callback-Funktion nimmt als Argumente
(in_data, frame_count, time_info, status)
und gibt ein Tupel zurück, dessen erster Eintrag die
Daten (für Playback), dessen zweiter Eintrag ein Signal
ist, sollte pyaudio.paContinue sein.
Also z. B. (mit einer globalen Variable CHANNELS)
def micro_callback(in_data, frame_count, time_info)
y=np.fromstring(in_data,dtype=np.short)
if CHANNELS==2:
y=y.reshape((y.shape[0]//2,2))
else:
y=y.reshape((y.shape[0],1))
## y enthält danach die Daten des letzten Abschnitts
## aus chunk samples.
## jetzt irgendwas mit den Daten machen
##
return (in_data, pyaudio.paContinue)
'''
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16 ,
channels=channels,
rate=rate,
input=True,
output=playback,
stream_callback=callback,
frames_per_buffer=chunk)
self.stream.start_stream()
time.sleep(0.5)
def __del__(self):
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
#########################################################################
### Test-Sektion #######################################################
#########################################################################
if __name__=='__main__':
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
plt.ion()
###########################
### GLOBALE VARIABLEN
CHANNELS=1
RATE=44100
CHUNK=2**11
fig,ax = plt.subplots(1,1)
graf, = plt.plot(np.linspace(0.0, (2.0*RATE), CHUNK/2),(CHUNK//2-1)*[0]+[100000])
def micro_callback(in_data, frame_count, time_info,status):
'''callback Funktion: wird vom PyAudio-Objekt aufgerufen,
wenn CHUNK Frames von der Soundkarte (Mikrophon) gelesen wurden'''
y=np.array(np.fromstring(in_data,dtype=np.short),dtype=np.float)
yf = scipy.fftpack.fft(y)[:CHUNK/2]
xf = np.linspace(0.0, (2.0*RATE), CHUNK/2)
graf.set_data(xf,np.abs(yf))
#plt.pause(0.001)
#plt.show()
if CHANNELS==2:
y=y.reshape((y.shape[0]//2,2))
else:
y=y.reshape((y.shape[0],1))
#print int(np.linalg.norm(y[:,0])/2000.)*'*'
return (in_data, status)
listen=MicroListener(RATE,CHANNELS,CHUNK,micro_callback)
while True:
fig.canvas.draw()
plt.pause(0.001)
moving_circles.py
# -*- coding: utf-8 -*-
# uses:
# code under Copyright (c) 2014, Vispy Development Team.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
#
# most of it: Copyright 2017, Stefan Born
#
# License: GPL, version 2
'''Package moving_circles provides two backends for drawing
and moving circles on a canvas. You select the backend by setting
the variable PREFERRED_BACKEND in the calling module to
either 'VISPY' or 'MATPLOTLIB'.'''
from __future__ import division
# Analyse code of importing file:
import inspect, re
try:
filename = inspect.getfile(inspect.currentframe().f_back)
with open(filename, 'r') as f:
importing_module = f.read()
PREFERRED_BACKEND = re.search ("PREFERRED\_BACKEND\s*=\s*['\"](?P<preferred>\w*)['\"]", importing_module).group('preferred')
except:
PREFERRED_BACKEND = 'MATPLOTLIB'
# import depends on PREFERRED_BACKEND
import sys
import numpy as np
import time
AVAILABLE_BACKENDS = []
try:
from vispy import app, gloo, visuals, scene
from vispy.visuals import transforms
AVAILABLE_BACKENDS.append('VISPY')
except:
pass
try:
import matplotlib
matplotlib.use('GTKAgg')
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
import matplotlib.figure
import matplotlib.patches
import matplotlib.axes
import gtk
AVAILABLE_BACKENDS.append('MATPLOTLIB')
except Exception as e:
print e
if PREFERRED_BACKEND in AVAILABLE_BACKENDS:
BACKEND = PREFERRED_BACKEND
else:
try:
BACKEND = AVAILABLE_BACKENDS[0]
except:
raise Exception("NO BACKEND AVAILABLE")
print "Backend: ", BACKEND
if BACKEND == 'VISPY':
class Canvas(scene.SceneCanvas):
'''Canvas for drawing.
Is created with a certain initial width and height.
The coordinate system for Drawing (implemented: Circles) refers
to xlim and ylim.
:param width: integer, width of canvas
:param height: integer, height of canvas
:param xlim: tuple of float, limits of x-axis
:param ylim: tuple of float, limits of y-axis'''
def __init__(self, height=800, width=800, xlim = (-1.,1.),ylim=(-1.,1.)):
scene.SceneCanvas.__init__(self, keys='interactive',bgcolor='w')
self.size = (width,height)
self.original_size = self.size
self.visuals = []
self.xlim = xlim
self.ylim = ylim
self.center = (0,0)
self.scale = (width/(self.xlim[1]-self.xlim[0]), height/(self.ylim[1]-self.ylim[0]))
self.translate = ((-self.xlim[0])*self.scale[0],(-self.ylim[0])*self.scale[1])
self.transform = transforms.STTransform(
scale = self.scale,
translate = self.translate )
self.show()
self.set_current()
self.action = lambda x,y:0
self.objects = []
self.app.create()
def on_draw(self, ev):
print "ond"
gloo.set_clear_color(self.bgcolor)
gloo.set_viewport(0, 0, *self.size)
gloo.clear()
for vis in self.visuals:
vis.draw(vis.tr_sys)
def update_canvas(self):
'''manually update canvas'''
self.app.process_events()
self.update()
def set_action(self,action,objects):
'''registers an action to be performed on certain objects at each update
:param action: function (objects, event)
:param objects: list of objects
'''
self.action = action
self.objects = objects
def on_timer(self, event):
self.action(self.objects,event)
self.update()
def on_resize(self,event):
width, height = self.size
self.scale = np.array((self.original_size[0]/width*self.scale[0],self.original_size[1]/height*self.scale[1],1,1))
self.transform = transforms.STTransform(
scale = self.scale,
translate = self.translate )
for vis in self.visuals:
vis.transform = self.transform
vis.tr_sys.visual_to_document = vis.transform
self.original_size = self.size
def run(self):
self.on_draw(None)
time.sleep(1)
self._timer = app.Timer(interval=0.05, connect=self.on_timer, start=True)
class Circle(object):
def __init__(self, canvas, **params):
self.params = dict( pos =canvas.center, radius = (0.1,0.1),
color=(0.2, 0.2, 0.8, 1),
border_color=(1, 1, 1, 1)
)
for key in params:
if key!='radius':
self.params[key] = params[key]
else:
try:
self.params[key] = (params[key],params[key])
except:
pass
self.visual = visuals.EllipseVisual(**self.params)
self.visual.transform = canvas.transform
self.visual.tr_sys = transforms.TransformSystem(canvas)
self.visual.tr_sys.visual_to_document = self.visual.transform
self.canvas = canvas
canvas.visuals.append(self.visual)
def set_radius(self, r):
self.visual.radius = (r,r)
def move(self,dx,dy):
self.visual.pos = (self.visual.pos[0]+dx, self.visual.pos[1]+dy)
def set_color(self, color):
self.visual.color = color
if BACKEND == 'MATPLOTLIB':
from collections import namedtuple
Event = namedtuple("Event",("dt", "elapsed"))
class Canvas(object):
'''Canvas for drawing.
Is created with a certain initial width and height.
The coordinate system for Drawing (implemented: Circles) refers
to xlim and ylim.
:param width: integer, width of canvas
:param height: integer, height of canvas
:param xlim: tuple of float, limits of x-axis
:param ylim: tuple of float, limits of y-axis'''
def __init__(self, height=800, width=800, xlim = (-1,1),ylim=(-1,1)):
self.figure = matplotlib.figure.Figure()
self.figure.add_axes((0,0,1,1))
self.axes = self.figure.axes[0]
self.canvas = FigureCanvas(self.figure)
self.xlim = xlim
self.ylim = ylim
self.axes.set_xlim(xlim)
self.axes.set_ylim(ylim)
self.size = (width,height)
self.original_size = (width, height)
self.gtkwin = gtk.Window()
self.gtkwin.set_default_size(width, height)
self.gtkwin.add(self.canvas)
self.gtkwin.show_all()
self.action = lambda x,y:0
self.objects = []
self.visuals = []
self.center = (0,0)
self.start_time = time.time()
self.last_time = time.time()
self.timer = self.canvas.new_timer(interval = 50)
self.timer.add_callback(self.update)
self.timer.start()
self.canvas.mpl_connect('resize_event', self.on_resize)
def set_action(self,action,objects):
'''registers an action to be performed on certain objects at each update
:param action: function (objects, event)
:param objects: list of objects
'''
self.action = action
self.objects = objects
def update(self):
now = time.time()
self.action(self.objects,Event(dt=now-self.last_time,elapsed=now-self.start_time))
self.last_time = now
self.axes.figure.canvas.draw()
def update_canvas(self):
'''manually update canvas'''
self.update()
def on_resize(self, ev):
width = ev.width
height = ev.height
self.size = (width, height)
self.xlim = ((self.xlim[0]-self.center[0])*width/self.original_size[0]+self.center[0],
(self.xlim[1]-self.center[0])*width/self.original_size[0]+self.center[0])
self.ylim = ((self.ylim[0]-self.center[1])*height/self.original_size[1]+self.center[1],
(self.ylim[1]-self.center[1])*height/self.original_size[1]+self.center[1])
self.original_size = self.size
self.axes.set_xlim(self.xlim)
self.axes.set_ylim(self.ylim)
def run(self):
gtk.main()
class Circle(object):
def __init__(self, canvas, **params):
self.params = dict( pos =canvas.center, radius = 0.1,
color=(0.2, 0.2, 0.8, 1),
border_color=(1, 1, 1, 1))
for key in params:
self.params[key] = params[key]
self.visual = matplotlib.patches.Circle(self.params['pos'],radius=self.params['radius'],
facecolor=self.params['color'], edgecolor = self.params['border_color'])
self.canvas = canvas
self.canvas.axes.add_patch(self.visual)
self.canvas.visuals.append(self.visual)
def move(self,dx,dy):
self.visual.center = (self.visual.center[0]+dx, self.visual.center[1]+dy)
def set_color(self, color):
self.visual._facecolor = color
def set_radius(self, r):
self.visual.radius = r
def action(objects, event):
dt, elapsed = event.dt, event.elapsed
for i,circ in enumerate(objects):
circ.move(0.5*dt*np.sin(elapsed*(i+1)), 0.5*dt*np.cos(elapsed*(i+1)))
circ.set_color( (np.abs(np.sin(elapsed*(i+1))),np.abs(np.sin(elapsed*2*(i+1))), np.abs(np.sin(elapsed*3*(i+1))),0.3) )
if __name__ == '__main__':
print BACKEND
win = Canvas()
objects = []
for i in range(10):
circ = Circle(win, pos=(-0.6+0.05*i,0), radius=0.1,color = (1,0,0,0.2))
objects.append(circ)
win.set_action(action, objects)
win.run()