Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Modificación de rejilla en Serial Plotter de Pyrhon
#1
Saludos, hace varios días que estoy intentando modificar la escala de la grilla de un software en Python pero por más que lo intento no lo logro, agradecería muchísimo si alguien me ayuda a realizar la modificación o si me guía en el proceso, lo que necesito es que la grilla esté dividida en cuadros de 40 mili segundos, en la pantalla se muestran 5 segundos, eso quiere decir que dentro de cada segundo tendría que haber 25 cuadritos. 

Esta es la primera parte del código. El programa tiene varios archivos. 


Código:
"""
diyECG GUI for monitoring live ECG through sound card input (by Scott Harden).
If you haven't built the circuit, test this software by playing demo_ecg.wav
while you run this software. If you don't use a virtual audio cable to connect
the output (speaker jack) to the input (default microphone jack), consider
running an actual cable to connect these two.
"""

from PyQt4 import QtGui,QtCore
import sys
import ui_main
import numpy as np
import pyqtgraph
import swhear
import time
import pyqtgraph.exporters
import webbrowser

class ExampleApp(QtGui.QMainWindow, ui_main.Ui_MainWindow):
   def __init__(self, parent=None):
       pyqtgraph.setConfigOption('background', 'w') #before loading widget
       super(ExampleApp, self).__init__(parent)
       self.setupUi(self)
       self.grECG.plotItem.showGrid(True, True, 0.7)
       self.btnSave.clicked.connect(self.saveFig)
       self.btnSite.clicked.connect(self.website)
       stamp="DIY ECG by Scott Harden"
       self.stamp = pyqtgraph.TextItem(stamp,anchor=(-.01,1),color=(150,150,150),
                                       fill=pyqtgraph.mkBrush('w'))
       self.ear = swhear.Ear(chunk=int(100)) # determines refresh rate
       # optionally you can manually set the audio input device to use like this:
       # self.ear = swhear.Ear(chunk=int(100), device=5) # use audio input device 5
       if len(self.ear.valid_input_devices()):
           self.ear.stream_start()
           self.lblDevice.setText(self.ear.msg)
           self.update()

   def closeEvent(self, event):
       self.ear.close()
       event.accept()

   def saveFig(self):
       fname="ECG_%d.png"%time.time()
       exp = pyqtgraph.exporters.ImageExporter(self.grECG.plotItem)
       exp.parameters()['width'] = 1000
       exp.export(fname)
       print("saved",fname)

   def update(self):
       t1,timeTook=time.time(),0
       if len(self.ear.data) and not self.btnPause.isChecked():
           freqHighCutoff=0
           if self.spinLowpass.value()>0:
               freqHighCutoff=self.spinLowpass.value()
           data=self.ear.getFiltered(freqHighCutoff)
           if self.chkInvert.isChecked():
               data=np.negative(data)
           if self.chkAutoscale.isChecked():
               self.Yscale=np.max(np.abs(data))*1.1
           self.grECG.plotItem.setRange(xRange=[0,self.ear.maxMemorySec],
                           yRange=[-self.Yscale,self.Yscale],padding=0)
           self.grECG.plot(np.arange(len(data))/float(self.ear.rate),data,clear=True,
                           pen=pyqtgraph.mkPen(color='r'),antialias=True)
           self.grECG.plotItem.setTitle(self.lineTitle.text(),color=(0,0,0))
           self.stamp.setPos(0,-self.Yscale)
           self.grECG.plotItem.addItem(self.stamp)
           timeTook=(time.time()-t1)*1000
           print("plotting took %.02f ms"%(timeTook))
       msTillUpdate=int(self.ear.chunk/self.ear.rate*1000)-timeTook
       QtCore.QTimer.singleShot(max(0,msTillUpdate), self.update)

   def website(self):
       webbrowser.open("http://www.SWHarden.com")

if __name__=="__main__":
   app = QtGui.QApplication(sys.argv)
   form = ExampleApp()
   form.show()
   app.exec_()



Esta es la segunda parte. 

Código:
"""
The core SWHEar class for continuously monitoring microphone data.

The Ear class is the primary method used to access microphone data.
It has extra routines to audomatically detec/test sound card, channel,
and rate combinations to maximize likelyhood of finding one that
works on your system (all without requiring user input!)

Although this was designed to be a package, it's easy enough to work
as an entirely standalone python script. Just drop this .py file in
your project and import it by its filename. Done!
"""

import pyaudio
import time
import numpy as np
import threading
import scipy.io.wavfile

def FFT(data,rate):
   """given some data points and a rate, return [freq,power]"""
   data=data*np.hamming(len(data))
   fft=np.fft.fft(data)
   fft=10*np.log10(np.abs(fft))
   freq=np.fft.fftfreq(len(fft),1/rate)
   return freq[:int(len(freq)/2)],fft[:int(len(fft)/2)]

class Ear(object):
   def __init__(self,device=None,rate=None,chunk=4096,maxMemorySec=5):
       """
       Prime the Ear class to access audio data. Recording won't start
       until stream_start() is called.
       - if device is none, will use first valid input (not system default)
       - if a rate isn't specified, the lowest usable rate will be used.
       """

       # configuration
       self.chunk = chunk # doesn't have to be a power of 2
       self.maxMemorySec = maxMemorySec # delete if more than this around
       self.device=device
       self.rate=rate

       # internal variables
       self.chunksRecorded=0
       self.p=pyaudio.PyAudio() #keep this forever
       self.t=False #later will become threads

   ### SOUND CARD TESTING

   def valid_low_rate(self,device):
       """set the rate to the lowest supported audio rate."""
       for testrate in [8000, 9600, 11025, 12000, 16000, 22050, 24000,
                        32000, 44100, 48000, 88200, 96000, 192000]:
           if self.valid_test(device,testrate):
               return testrate
       print("SOMETHING'S WRONG! I can't figure out how to use DEV",device)
       return None

   def valid_test(self,device,rate=44100):
       """given a device ID and a rate, return TRUE/False if it's valid."""
       try:
           self.info=self.p.get_device_info_by_index(device)
           if not self.info["maxInputChannels"]>0:
               return False
           stream=self.p.open(format=pyaudio.paInt16,channels=1,
              input_device_index=device,frames_per_buffer=self.chunk,
              rate=int(self.info["defaultSampleRate"]),input=True)
           stream.close()
           return True
       except:
           return False

   def valid_input_devices(self):
       """
       See which devices can be opened for microphone input.
       call this when no PyAudio object is loaded.
       """
       mics=[]
       for device in range(self.p.get_device_count()):
           if self.valid_test(device):
               mics.append(device)
       if len(mics)==0:
           print("no microphone devices found!")
       else:
           print("found %d microphone devices: %s"%(len(mics),mics))
       return mics

   ### SETUP AND SHUTDOWN

   def initiate(self):
       """
       run this after changing settings (like rate) before recording.
       mostly just ensures the sound card settings are good.
       """
       if self.device is None:
           self.device=self.valid_input_devices()[0] #pick the first one
       if self.rate is None:
           self.rate=self.valid_low_rate(self.device)
       if not self.valid_test(self.device,self.rate):
           print("guessing a valid microphone device/rate...")
           self.device=self.valid_input_devices()[0] #pick the first one
           self.rate=self.valid_low_rate(self.device)
       self.msg='recording from "%s" '%self.info["name"]
       self.msg+='(device %d) '%self.device
       self.msg+='at %d Hz'%self.rate
       self.data=np.array([])
       print(self.msg)


   def close(self):
       """gently detach from things."""
       print(" -- sending stream termination command...")
       self.keepRecording=False #the threads should self-close
       if self.t:
           while(self.t.isAlive()):
               time.sleep(.1) #wait for all threads to close
           self.stream.stop_stream()
       self.p.terminate()

   ### LIVE AUDIO STREAM HANDLING

   def stream_readchunk(self):
       """reads some audio and re-launches itself"""
       try:
           data = np.fromstring(self.stream.read(self.chunk),dtype=np.int16)
           self.data=np.concatenate((self.data,data))
           self.chunksRecorded+=1
           self.dataFirstI=self.chunksRecorded*self.chunk-len(self.data)
           if len(self.data)>self.maxMemorySec*self.rate:
               pDump=len(self.data)-self.maxMemorySec*self.rate
               #print(" -- too much data in memory! dumping %d points."%pDump)
               self.data=self.data[pDump:]
               self.dataFirstI+=pDump
       except Exception as E:
           print(" -- exception! terminating...")
           print(E,"\n"*5)
           self.keepRecording=False
       if self.keepRecording==True:
           self.stream_thread_new()
       else:
           self.stream.close()
           self.p.terminate()
           self.keepRecording=None
           print(" -- stream STOPPED")

   def stream_thread_new(self):
       self.t=threading.Thread(target=self.stream_readchunk)
       self.t.start()

   def stream_start(self):
       """adds data to self.data until termination signal"""
       self.initiate()
       print(" -- starting stream")
       self.keepRecording=True # set this to False later to terminate stream
       self.dataFiltered=None #same
       self.stream=self.p.open(format=pyaudio.paInt16,channels=1,
                     rate=self.rate,input=True,frames_per_buffer=self.chunk)
       self.stream_thread_new()

   def stream_stop(self,waitForIt=True):
       """send the termination command and (optionally) hang till its done"""
       self.keepRecording=False
       if waitForIt==False:
           return
       while self.keepRecording is False:
           time.sleep(.1)

   ### WAV FILE AUDIO

   def loadWAV(self,fname):
       """Add audio into the buffer (self.data) from a WAV file"""
       self.rate,self.data=scipy.io.wavfile.read(fname)
       print("loaded %.02f sec of data (rate=%dHz)"%(len(self.data)/self.rate,
                                                    self.rate))
       self.initiate()
       return

   ### DATA RETRIEVAL
   def getPCMandFFT(self):
       """returns [data,fft,sec,hz] from current memory buffer."""
       if not len(self.data):
           return
       data=np.array(self.data) # make a copy in case processing is slow
       sec=np.arange(len(data))/self.rate
       hz,fft=FFT(data,self.rate)
       return data,fft,sec,hz

   def softEdges(self,data,fracEdge=.05):
       """multiple edges by a ramp of a certain percentage."""
       rampSize=int(len(data)*fracEdge)
       mult = np.ones(len(data))
       window=np.hanning(rampSize*2)
       mult[:rampSize]=window[:rampSize]
       mult[-rampSize:]=window[-rampSize:]
       return data*mult

   def getFiltered(self,freqHighCutoff=50):
       if freqHighCutoff<=0:
           return self.data
       fft=np.fft.fft(self.softEdges(self.data)) #todo: filter needed?
       trim=len(fft)/self.rate*freqHighCutoff
       fft[int(trim):-int(trim)]=0
       return np.real(np.fft.ifft(fft))


if __name__=="__main__":
   print("This script is intended to be imported, not run directly!")

Esta la tercera parte. 

Código:
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'ui_main.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
   _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
   def _fromUtf8(s):
       return s

try:
   _encoding = QtGui.QApplication.UnicodeUTF8
   def _translate(context, text, disambig):
       return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
   def _translate(context, text, disambig):
       return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
   def setupUi(self, MainWindow):
       MainWindow.setObjectName(_fromUtf8("MainWindow"))
       MainWindow.resize(993, 692)
       self.centralwidget = QtGui.QWidget(MainWindow)
       self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
       self.verticalLayout_2 = QtGui.QVBoxLayout(self.centralwidget)
       self.verticalLayout_2.setMargin(10)
       self.verticalLayout_2.setSpacing(10)
       self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
       self.frame_4 = QtGui.QFrame(self.centralwidget)
       self.frame_4.setFrameShape(QtGui.QFrame.StyledPanel)
       self.frame_4.setFrameShadow(QtGui.QFrame.Raised)
       self.frame_4.setObjectName(_fromUtf8("frame_4"))
       self.horizontalLayout = QtGui.QHBoxLayout(self.frame_4)
       self.horizontalLayout.setMargin(0)
       self.horizontalLayout.setSpacing(0)
       self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
       self.label_3 = QtGui.QLabel(self.frame_4)
       font = QtGui.QFont()
       font.setPointSize(12)
       font.setBold(True)
       font.setWeight(75)
       self.label_3.setFont(font)
       self.label_3.setObjectName(_fromUtf8("label_3"))
       self.horizontalLayout.addWidget(self.label_3)
       self.label_4 = QtGui.QLabel(self.frame_4)
       self.label_4.setEnabled(True)
       self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
       self.label_4.setObjectName(_fromUtf8("label_4"))
       self.horizontalLayout.addWidget(self.label_4)
       spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
       self.horizontalLayout.addItem(spacerItem)
       self.btnSite = QtGui.QPushButton(self.frame_4)
       self.btnSite.setStyleSheet(_fromUtf8("color: rgb(0, 0, 255);"))
       self.btnSite.setCheckable(False)
       self.btnSite.setFlat(True)
       self.btnSite.setObjectName(_fromUtf8("btnSite"))
       self.horizontalLayout.addWidget(self.btnSite)
       self.verticalLayout_2.addWidget(self.frame_4)
       self.frame_5 = QtGui.QFrame(self.centralwidget)
       self.frame_5.setFrameShape(QtGui.QFrame.StyledPanel)
       self.frame_5.setFrameShadow(QtGui.QFrame.Raised)
       self.frame_5.setObjectName(_fromUtf8("frame_5"))
       self.horizontalLayout_3 = QtGui.QHBoxLayout(self.frame_5)
       self.horizontalLayout_3.setMargin(0)
       self.horizontalLayout_3.setSpacing(0)
       self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3"))
       self.lblDevice = QtGui.QLabel(self.frame_5)
       self.lblDevice.setEnabled(False)
       self.lblDevice.setObjectName(_fromUtf8("lblDevice"))
       self.horizontalLayout_3.addWidget(self.lblDevice)
       spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
       self.horizontalLayout_3.addItem(spacerItem1)
       self.verticalLayout_2.addWidget(self.frame_5)
       self.frame = QtGui.QFrame(self.centralwidget)
       self.frame.setFrameShape(QtGui.QFrame.NoFrame)
       self.frame.setFrameShadow(QtGui.QFrame.Plain)
       self.frame.setObjectName(_fromUtf8("frame"))
       self.verticalLayout = QtGui.QVBoxLayout(self.frame)
       self.verticalLayout.setMargin(0)
       self.verticalLayout.setSpacing(10)
       self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
       self.frame_2 = QtGui.QFrame(self.frame)
       self.frame_2.setFrameShape(QtGui.QFrame.StyledPanel)
       self.frame_2.setFrameShadow(QtGui.QFrame.Raised)
       self.frame_2.setObjectName(_fromUtf8("frame_2"))
       self.horizontalLayout_2 = QtGui.QHBoxLayout(self.frame_2)
       self.horizontalLayout_2.setMargin(0)
       self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
       self.chkInvert = QtGui.QCheckBox(self.frame_2)
       self.chkInvert.setObjectName(_fromUtf8("chkInvert"))
       self.horizontalLayout_2.addWidget(self.chkInvert)
       self.chkAutoscale = QtGui.QCheckBox(self.frame_2)
       self.chkAutoscale.setChecked(True)
       self.chkAutoscale.setObjectName(_fromUtf8("chkAutoscale"))
       self.horizontalLayout_2.addWidget(self.chkAutoscale)
       self.line = QtGui.QFrame(self.frame_2)
       self.line.setFrameShape(QtGui.QFrame.VLine)
       self.line.setFrameShadow(QtGui.QFrame.Sunken)
       self.line.setObjectName(_fromUtf8("line"))
       self.horizontalLayout_2.addWidget(self.line)
       self.label_2 = QtGui.QLabel(self.frame_2)
       sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred)
       sizePolicy.setHorizontalStretch(0)
       sizePolicy.setVerticalStretch(0)
       sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
       self.label_2.setSizePolicy(sizePolicy)
       self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
       self.label_2.setObjectName(_fromUtf8("label_2"))
       self.horizontalLayout_2.addWidget(self.label_2)
       self.spinLowpass = QtGui.QSpinBox(self.frame_2)
       self.spinLowpass.setPrefix(_fromUtf8(""))
       self.spinLowpass.setMinimum(0)
       self.spinLowpass.setMaximum(999999)
       self.spinLowpass.setSingleStep(1)
       self.spinLowpass.setProperty("value", 45)
       self.spinLowpass.setObjectName(_fromUtf8("spinLowpass"))
       self.horizontalLayout_2.addWidget(self.spinLowpass)
       self.label_5 = QtGui.QLabel(self.frame_2)
       sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred)
       sizePolicy.setHorizontalStretch(0)
       sizePolicy.setVerticalStretch(0)
       sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
       self.label_5.setSizePolicy(sizePolicy)
       self.label_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
       self.label_5.setObjectName(_fromUtf8("label_5"))
       self.horizontalLayout_2.addWidget(self.label_5)
       self.lineTitle = QtGui.QLineEdit(self.frame_2)
       self.lineTitle.setObjectName(_fromUtf8("lineTitle"))
       self.horizontalLayout_2.addWidget(self.lineTitle)
       self.line_2 = QtGui.QFrame(self.frame_2)
       self.line_2.setFrameShape(QtGui.QFrame.VLine)
       self.line_2.setFrameShadow(QtGui.QFrame.Sunken)
       self.line_2.setObjectName(_fromUtf8("line_2"))
       self.horizontalLayout_2.addWidget(self.line_2)
       self.btnPause = QtGui.QPushButton(self.frame_2)
       self.btnPause.setCheckable(True)
       self.btnPause.setObjectName(_fromUtf8("btnPause"))
       self.horizontalLayout_2.addWidget(self.btnPause)
       self.btnSave = QtGui.QPushButton(self.frame_2)
       self.btnSave.setObjectName(_fromUtf8("btnSave"))
       self.horizontalLayout_2.addWidget(self.btnSave)
       self.verticalLayout.addWidget(self.frame_2)
       self.frame_3 = QtGui.QFrame(self.frame)
       self.frame_3.setFrameShape(QtGui.QFrame.StyledPanel)
       self.frame_3.setFrameShadow(QtGui.QFrame.Plain)
       self.frame_3.setObjectName(_fromUtf8("frame_3"))
       self.verticalLayout_3 = QtGui.QVBoxLayout(self.frame_3)
       self.verticalLayout_3.setMargin(0)
       self.verticalLayout_3.setSpacing(0)
       self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
       self.grECG = PlotWidget(self.frame_3)
       self.grECG.setFrameShape(QtGui.QFrame.NoFrame)
       self.grECG.setFrameShadow(QtGui.QFrame.Plain)
       self.grECG.setLineWidth(0)
       self.grECG.setObjectName(_fromUtf8("grECG"))
       self.verticalLayout_3.addWidget(self.grECG)
       self.verticalLayout.addWidget(self.frame_3)
       self.verticalLayout_2.addWidget(self.frame)
       MainWindow.setCentralWidget(self.centralwidget)

       self.retranslateUi(MainWindow)
       QtCore.QMetaObject.connectSlotsByName(MainWindow)

   def retranslateUi(self, MainWindow):
       MainWindow.setWindowTitle(_translate("MainWindow", "diyECG", None))
       self.label_3.setText(_translate("MainWindow", "diyECG", None))
       self.label_4.setText(_translate("MainWindow", "   open-source live soundcard monitor with realtime iFFT filtering by Scott Harden", None))
       self.btnSite.setText(_translate("MainWindow", "www.SWHarden.com", None))
       self.lblDevice.setText(_translate("MainWindow", "!!! ERROR !!! no valid input sound devices found. Plug in a microphone and restart this program!", None))
       self.chkInvert.setText(_translate("MainWindow", "invert", None))
       self.chkAutoscale.setText(_translate("MainWindow", "autoscale", None))
       self.label_2.setText(_translate("MainWindow", "lowpass:", None))
       self.spinLowpass.setToolTip(_translate("MainWindow", "set to 0 to disable", None))
       self.spinLowpass.setSuffix(_translate("MainWindow", " Hz", None))
       self.label_5.setText(_translate("MainWindow", "title:", None))
       self.lineTitle.setText(_translate("MainWindow", "DIY ECG", None))
       self.btnPause.setText(_translate("MainWindow", "Pause", None))
       self.btnSave.setText(_translate("MainWindow", "Save Figure", None))

from pyqtgraph import PlotWidget

Básicamente necesito que se vea así. 


Ya que originalmente se ve así. 




Este es el software en github. 

En la documentación aquí se explica cómo lograrlo, pero aún así no ha sido posible. 

Muchas gracias de ante mano Smile.
Responder
#2
Hola Antonio, ¿qué problema estás teniendo con el método que ofrece la documentación?
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
Responder
#3
(24-01-2019, 09:36 PM)Francisco escribió: Hola Antonio, ¿qué problema estás teniendo con el método que ofrece la documentación?

Hola Francisco, pues que simplemente no funciona o no sé cómo implementarlo, agrego self.ctrl.logXCheck.setChecked(40) en el archivo swhear y al ejecutarlo me devuelve   File "go.py", line 81, in <module>
    form = ExampleApp()
  File "go.py", line 34, in __init__
    self.ear = swhear.Ear(chunk=int(100)) # determines refresh rate
  File "D:\Escritorio\software\software\swhear.py", line 30, in __init__
    self.ctrl.logXCheck.setChecked(40)
AttributeError: 'Ear' object has no attribute 'ctrl'

Entonces me voy a go.py y edito ese 100 pero solamente me varía el tiempo de muestreo de la gráfica y no la grilla, no he logrado variar la grilla de ninguna manera.

Cómo podría hacer para modificar la escala de la grilla?

También me serviría saber cómo agregar una cuadrícula nueva en este software.
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)