Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
codes [2017/04/07 14:05] beate_jung-mint1997 angelegt |
codes [2017/04/07 14:15] (aktuell) beate_jung-mint1997 |
||
---|---|---|---|
Zeile 190: | Zeile 190: | ||
</code> | </code> | ||
+ | |||
+ | ====== fouriersb.py ====== | ||
+ | <code python> | ||
+ | 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 | ||
+ | |||
+ | |||
+ | |||
+ | </code> | ||
+ | ====== microlistener.py ====== | ||
+ | |||
+ | <code python> | ||
+ | #!/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 | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ====== microlistenerdftnew.py ====== | ||
+ | <code python> | ||
+ | #!/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) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ====== moving_circles.py ====== | ||
+ | <code python> | ||
+ | # -*- 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() | ||
+ | | ||
+ | </code> | ||
+ | |||