似乎很管用。我在找关于如何加速这个计划的建议。
尝试:在没有GUI的情况下运行(稍微快一点,但不是我想要的)
严重程度:可怕(每400毫秒更新一次,不包括等待时间)加上非常高的CPU使用率
程序的目的:使用麦克风测量一段时间内的噪声暴露量。
tkinter.ttk版本:
import os, errno
import pyaudio
from scipy.signal import lfilter
import numpy
from tkinter import *
from threading import Thread
from tkinter.ttk import *
from tk_tools import *
import time
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from tkinter import messagebox
import sys
from idlelib.tooltip import Hovertip
try:
from winreg import *
except:
reg_present=False
messagebox.askokcancel('Limited Features', "Registry not present. Dosimeter disabled. OK to continue, Cancel to quit.", icon='warning')
else:
reg_present=True
def get_resource_path(relative_path):
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def toggle_dosi():
global dosi_enabled
dosi_enabled=enable_dosi.instate(['selected'])
def reset():
global start, dosimeter_times, runTime, x, y, plot1
dosimeter_times={'82dB':0, '85dB':0, '88dB':0, '91dB':0, '94dB':0, '97dB':0, '100dB':0, '103dB':0, '106dB':0, '109dB':0, '112dB':0, '115dB':0, '118dB':0}
x=[]
y=[]
runTime=0
win=Tk()
win.title('Decibel Meter v1.2 (c) sserver')
win.grid()
win.resizable(False, False)
if reg_present:
CreateKeyEx(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver\Decibel Meter', reserved=0)
try:
dosi_enabled=bool(QueryValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled')[0])
except OSError:
SetValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled', 0, REG_DWORD, 0)
dosi_enabled=False
messagebox.showwarning('Registry Error', 'Error reading settings. Resetting to default...')
else:
dosi_enabled=False
dosi_enabled_first=dosi_enabled
tabControl = Notebook(win)
root=Frame(tabControl)
runTime=0
sub=Frame(tabControl)
sub1=Frame(tabControl)
tabControl.add(root, text ='Meter')
measure=False
start=time.time()
tabControl.add(sub, text ='Dosimeter')
tabControl.add(sub1, text ='Recording')
led = Led(root, size=20)
led.grid(column=2, row=14)
Hovertip(led,'1 dB\nAll is OK\nThreshold of hearing')
tabControl.pack(expand = 1, fill ="both")
gaugedb=SevenSegmentDigits(root, digits=3, digit_color='#00ff00', background='black')
gaugedb.grid(column=1, row=1)
dosidb=SevenSegmentDigits(sub, digits=3, digit_color='#00ff00', background='black')
dosidb.grid(column=1, row=2)
graphdb=SevenSegmentDigits(sub1, digits=3, digit_color='#00ff00', background='black')
graphdb.grid(column=1, row=2)
Hovertip(gaugedb,"Current dBA level")
led0 = Led(root, size=20)
led0.grid(column=2, row=13)
Hovertip(led0,'10 dB\nAll is OK\nEquivalent to rustling leaves in the distance')
led1 = Led(root, size=20)
led1.grid(column=2, row=12)
Hovertip(led1,'20 dB\nAll is OK\nEquivalent to a background in a movie studio')
led2 = Led(root, size=20)
led2.grid(column=2, row=11)
Hovertip(led2,'30 dB\nAll is OK\nEquivalent to a quiet bedroom')
led3 = Led(root, size=20)
led3.grid(column=2, row=10)
Hovertip(led3,'40 dB\nAll is OK\nEquivalent to a whisper')
led4 = Led(root, size=20)
led4.grid(column=2, row=9)
Hovertip(led4,'50 dB\nAll is OK\nEquivalent to a quiet home')
led5 = Led(root, size=20)
led5.grid(column=2, row=8)
Hovertip(led5,'60 dB\nAll is OK\nEquivalent to a quiet street')
led6 = Led(root, size=20)
led6.grid(column=2, row=7)
Hovertip(led6,'70 dB\nAll is OK\nEquivalent to a normal conversation')
led7 = Led(root, size=20)
led7.grid(column=2, row=6)
Hovertip(led7,'80 dB\nA little loud, may cause hearing damage in sensitive people.\nEquivalent to loud singing')
led8 = Led(root, size=20)
led8.grid(column=2, row=5)
Hovertip(led8,'90 dB\nLoud; repeated and/or long term exposure at this level may damage hearing.\nEquivalent to a motorcycle')
led9 = Led(root, size=20)
led9.grid(column=2, row=4)
Hovertip(led9,'100 dB\nCritically loud, even short exposure to this level can damage hearing.\nEquivalent to a subway')
led10 = Led(root, size=20)
led10.grid(column=2, row=3)
Hovertip(led10,'110 dB\nDangerous, even short exposure to this level can damage hearing.\nEquivalent to a helicopter overheaad')
led11 = Led(root, size=20)
led11.grid(column=2, row=2)
win.iconbitmap(get_resource_path('snd.ico'))
Hovertip(led11,"120 dB\nDangerous, even short exposure to this level can damage hearing.\nYou might feel pain at this level.\nEquivalent to a rock concert")
Label(root, text='120').grid(column=1, row=2)
Label(sub, text='Instantaneous dBA level').grid(column=1, row=1)
Label(sub1, text='Instantaneous dBA level').grid(column=1, row=1)
style=Style(win)
style.configure("1.Horizontal.TProgressbar", background='green')
style.configure("2.Horizontal.TProgressbar", background='yellow')
style.configure("3.Horizontal.TProgressbar", background='red')
style = Style(root)
style.layout('text.Horizontal.TProgressbar',
[('Horizontal.Progressbar.trough',
{'children': [('Horizontal.Progressbar.pbar',
{'side': 'left', 'sticky': 'ns'})],
'sticky': 'nswe'}),
('Horizontal.Progressbar.label', {'sticky': ''})])
style.configure('text.Horizontal.TProgressbar', text='0 %')
db_levels=[82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118]
niosh_limits=[57600, 28800, 14400, 7200, 3600, 1800, 900, 450, 225, 120, 60, 30, 15]
db_levels.reverse()
niosh_limits.reverse()
enable_dosi=Checkbutton(sub, text='Enable Dosimeter (restart app to apply)', command=toggle_dosi)
enable_dosi.grid(column=1, row=3)
enable_dosi.state(['!alternate'])
def record_frame():
global recording, rec_start, db, f
time_trunc='%.1f' % (time.time()-rec_start)
f.write(time_trunc+','+str(int(db))+'\n')
if recording:
win.after(500, record_frame)
else:
f.close()
def record():
global recording, f, rec_start
if not recording:
rec.config(text='Stop')
rec_start=time.time()
recording=True
f=open(os.getenv('HOMEPATH')+'\\Music\\mic_levels.csv', 'w')
f.write('Seconds,Decibels\n')
record_frame()
else:
recording=False
rec.config(text='Record')
if not reg_present:
enable_dosi.config(state=DISABLED)
enable_dosi.state(['!alternate'])
if dosi_enabled:
enable_dosi.state(['selected'])
Label(sub, text='Dose:', width=40).grid(column=1, row=4)
dosebar=Progressbar(sub, maximum=100, mode='determinate', length=200, style='text.Horizontal.TProgressbar')
dosebar.grid(column=2, row=4)
timeLabel=Label(sub, text='0 sec', width=30)
timeLabel.grid(column=2, row=2)
pdose=Label(sub, text='Projected dose: 0 sec', width=40)
Button(sub, text='Reset', command=reset).grid(column=2, row=3)
Label(sub1, text='Recording to Music\\mic_levels.csv.\nMake sure the file does not exist, or it will be overwritten.').grid(column=1, row=3)
rec=Button(sub1, text='Record', command=record)
rec.grid(column=1, row=4)
for i in range(len(db_levels)):
db_level=db_levels[i]
exec("label_"+str(db_level)+"=Label(sub, width=40, text='"+str(db_level)+"dBA: 0/"+str(niosh_limits[i])+" sec')")
exec("label_"+str(db_level)+".grid(column=1, row="+str(i+5)+")")
exec("bar_"+str(db_level)+'=Progressbar(sub, length=200, style="1.Horizontal.TProgressbar", mode="determinate")')
exec("bar_"+str(db_level)+'.grid(column=2, row='+str(i+5)+')')
else:
Label(sub, text='Dosimeter is not enabled', width=60).grid(column=1, row=4)
Label(sub1, text='Dosimeter must be enabled', width=60).grid(column=1, row=4)
Label(root, text='-').grid(column=1, row=3)
Label(root, text='-').grid(column=1, row=5)
Label(root, text='-').grid(column=1, row=7)
Label(root, text='-').grid(column=1, row=9)
Label(root, text='-').grid(column=1, row=11)
Label(root, text='-').grid(column=1, row=13)
Label(root, text='100').grid(column=1, row=4)
Label(root, text='Danger').grid(column=3, row=4)
Label(root, text='80').grid(column=1, row=6)
Label(root, text='Loud').grid(column=3, row=6)
Label(root, text='60').grid(column=1, row=8)
Label(root, text='40').grid(column=1, row=10)
Label(root, text='20').grid(column=1, row=12)
Label(root, text='dBA').grid(column=1, row=14)
Label(root, text='OK').grid(column=3, row=14)
Label(root, text='Max').grid(column=3, row=0)
Label(root, text='dBA').grid(column=1, row=0)
Label(root, text='dB Offset').grid(column=2, row=0)
maxdb_display=SevenSegmentDigits(root, digits=3, digit_color='#00ff00', background='black')
maxdb_display.grid(column=3, row=1)
dosimeter_times={'82dB':0, '85dB':0, '88dB':0, '91dB':0, '94dB':0, '97dB':0, '100dB':0, '103dB':0, '106dB':0, '109dB':0, '112dB':0, '115dB':0, '118dB':0}
Hovertip(maxdb_display,"Max dBA level since program start")
CHUNKS = [4096, 9600]
CHUNK = CHUNKS[1]
FORMAT = pyaudio.paInt16
CHANNEL = 1
RATES = [44300, 48000]
RATE = RATES[1]
offset=StringVar()
offset.set('0')
spinbox=Spinbox(root, from_=-20, to=20, textvariable=offset, state='readonly', width=5)
spinbox.grid(column=2, row=1)
Hovertip(spinbox,"dB offset (Calibration)\nUse this if the meter is not accurate.\nUse a reliable reference meter (such as a dedicated SPL meter).")
appclosed=False
start_check=time.time()
from scipy.signal import bilinear
db=0
x=[]
y=[]
def change_color(index):
global db
bar=db_levels[index]
temp=dosimeter_times[str(bar)+'dB']/niosh_limits[index]
if temp>=0.9:
exec("bar_"+str(db_level)+".config(style='3.Horizontal.TProgressbar')")
elif temp>=0.5:
exec("bar_"+str(db_level)+".config(style='2.Horizontal.TProgressbar')")
else:
exec("bar_"+str(db_level)+".config(style='1.Horizontal.TProgressbar')")
def timer_dosi():
global runTime, y, x, start_check
runTime+=time.time()-start_check
if db>=82 and db<85:
dosimeter_times['82dB']+=time.time()-start_check
if db>=85 and db<88:
dosimeter_times['85dB']+=time.time()-start_check
if db>=88 and db<91:
dosimeter_times['88dB']+=time.time()-start_check
if db>=91 and db<94:
dosimeter_times['91dB']+=time.time()-start_check
if db>=94 and db<97:
dosimeter_times['94dB']+=time.time()-start_check
if db>=97 and db<100:
dosimeter_times['97dB']+=time.time()-start_check
if db>=100 and db<103:
dosimeter_times['100dB']+=time.time()-start_check
if db>=103 and db<106:
dosimeter_times['103dB']+=time.time()-start_check
if db>=106 and db<109:
dosimeter_times['106dB']+=time.time()-start_check
if db>=109 and db<112:
dosimeter_times['109dB']+=time.time()-start_check
if db>=112 and db<115:
dosimeter_times['112dB']+=time.time()-start_check
if db>=115 and db<118:
dosimeter_times['115dB']+=time.time()-start_check
if db>=118:
dosimeter_times['118dB']+=time.time()-start_check
start_check=time.time()
win.after(200, timer_dosi)
def close():
global appclosed
win.destroy()
if dosi_enabled:
SetValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled', 0, REG_DWORD, 1)
else:
SetValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled', 0, REG_DWORD, 0)
appclosed=True
stream.stop_stream()
stream.close()
pa.terminate()
def A_weighting(fs):
f1 = 20.598997
f2 = 107.65265
f3 = 737.86223
f4 = 12194.217
A1000 = 1.9997
NUMs = [(2*numpy.pi * f4)**2 * (10**(A1000/20)), 0, 0, 0, 0]
DENs = numpy.polymul([1, 4*numpy.pi * f4, (2*numpy.pi * f4)**2],
[1, 4*numpy.pi * f1, (2*numpy.pi * f1)**2])
DENs = numpy.polymul(numpy.polymul(DENs, [1, 2*numpy.pi * f3]),
[1, 2*numpy.pi * f2])
return bilinear(NUMs, DENs, fs)
NUMERATOR, DENOMINATOR = A_weighting(RATE)
def rms_flat(a):
return numpy.sqrt(numpy.mean(numpy.absolute(a)**2))
pa = pyaudio.PyAudio()
stream = pa.open(format = FORMAT,
channels = CHANNEL,
rate = RATE,
input = True,
frames_per_buffer = CHUNK)
max_decibel=0
def returnSum(myDict):
mylist = []
for i in myDict:
mylist.append(myDict[i])
final = sum(mylist)
return final
def listen(old=0, error_count=0, min_decibel=100):
global appclosed
global max_decibel
global measure
global db
global start
if not appclosed:
try:
try:
block = stream.read(CHUNK)
except IOError as e:
if not appclosed:
error_count += 1
messagebox.showerror("Error, ", " (%d) Error recording: %s" % (error_count, e))
else:
decoded_block = numpy.frombuffer(block, numpy.int16)
y = lfilter(NUMERATOR, DENOMINATOR, decoded_block)
new_decibel = 20*numpy.log10(rms_flat(y))+int(offset.get())
if runTime>0:
runt=runTime
else:
runt=0.1
if new_decibel<0:
new_decibel=0
old = new_decibel
db=new_decibel
gaugedb.set_value(str(int(float('{:.2f}'.format(new_decibel)))))
dosidb.set_value(str(int(float('{:.2f}'.format(new_decibel)))))
graphdb.set_value(str(int(float('{:.2f}'.format(new_decibel)))))
if new_decibel>max_decibel:
max_decibel=new_decibel
if int(db)>0:
led.to_green(on=True)
else:
led.to_grey(on=True)
maxdb_display.set_value(str(int(float(str(max_decibel)))))
for i in range(0, 12):
if int(new_decibel)>=(10*(i+1)):
if i>=9:
exec("led"+str(i)+".to_red(on=True)")
elif i>=7:
exec("led"+str(i)+".to_yellow(on=True)")
else:
exec("led"+str(i)+".to_green(on=True)")
else:
exec("led"+str(i)+".to_grey(on=True)")
if dosi_enabled_first:
percent=(returnSum(dosimeter_times)/runt)*100
dosebar['value']=float(percent)
timeLabel.config(text=str(int(runt))+' sec')
style.configure('text.Horizontal.TProgressbar',text='%.1f %%' % percent)
pdose.config(text='Projected dose: '+str(int((returnSum(dosimeter_times)*8)/runt))+' sec')
for i in range(len(db_levels)):
db_level=db_levels[i]
exec("label_"+str(db_level)+".config(text='"+str(db_level)+"dBA: "+str(int(dosimeter_times[str(db_level)+'dB']))+'/'+str(niosh_limits[i])+" sec')")
exec("bar_"+str(db_level)+"['value']="+str((dosimeter_times[str(db_level)+'dB']/niosh_limits[i])*100))
change_color(i)
win.after(20, listen)
except TclError:
pass
win.protocol('WM_DELETE_WINDOW', close)
if __name__ == '__main__':
if dosi_enabled:
timer_dosi()
listen()
win.mainloop()定制版本(https://github.com/TomSchimansky/CustomTkinter库):
import os, errno
import pyaudio
from scipy.signal import lfilter
import numpy
from tkinter import *
from threading import Thread
from tk_tools import *
from customtkinter import *
import time
from tkinter import messagebox
import sys
from idlelib.tooltip import Hovertip
set_appearance_mode("System") # Modes: system (default), light, dark
set_default_color_theme("blue")
try:
from winreg import *
except:
reg_present=False
messagebox.askokcancel('Limited Features', "Registry not present. Dosimeter disabled. OK to continue, Cancel to quit.", icon='warning')
else:
reg_present=True
def get_resource_path(relative_path):
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def toggle_dosi():
global dosi_enabled
dosi_enabled=enable_dosi.get()
def reset():
global start, dosimeter_times, runTime, x, y, plot1
dosimeter_times={'82dB':0, '85dB':0, '88dB':0, '91dB':0, '94dB':0, '97dB':0, '100dB':0, '103dB':0, '106dB':0, '109dB':0, '112dB':0, '115dB':0, '118dB':0}
x=[]
y=[]
runTime=0
win=CTk()
win.title('Decibel Meter v1.2 (c) sserver')
win.grid()
win.resizable(False, False)
if reg_present:
CreateKeyEx(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver\Decibel Meter', reserved=0)
try:
dosi_enabled=bool(QueryValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled')[0])
except OSError:
SetValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled', 0, REG_DWORD, 0)
dosi_enabled=False
messagebox.showwarning('Registry Error', 'Error reading settings. Resetting to default...')
else:
dosi_enabled=False
dosi_enabled_first=dosi_enabled
runTime=0
measure=False
recording=False
start=time.time()
tabview =CTkTabview(win)
tabview.pack(padx=20, pady=20)
tabview.add("Meter") # add tab at the end
tabview.add("Dosimeter")
tabview.add("Recording")
root=tabview.tab("Meter")
sub=tabview.tab("Dosimeter")
sub1=tabview.tab("Recording")
led = Led(root, size=20)
led.grid(column=2, row=14)
Hovertip(led,'1 dB\nAll is OK\nThreshold of hearing')
gaugedb=SevenSegmentDigits(root, digits=3, digit_color='#00ff00', background='black')
gaugedb.grid(column=1, row=1)
dosidb=SevenSegmentDigits(sub, digits=3, digit_color='#00ff00', background='black')
dosidb.grid(column=1, row=2)
graphdb=SevenSegmentDigits(sub1, digits=3, digit_color='#00ff00', background='black')
graphdb.grid(column=1, row=2)
Hovertip(gaugedb,"Current dBA level")
led0 = Led(root, size=20)
led0.grid(column=2, row=13)
Hovertip(led0,'10 dB\nAll is OK\nEquivalent to rustling leaves in the distance')
led1 = Led(root, size=20)
led1.grid(column=2, row=12)
Hovertip(led1,'20 dB\nAll is OK\nEquivalent to a background in a movie studio')
led2 = Led(root, size=20)
led2.grid(column=2, row=11)
Hovertip(led2,'30 dB\nAll is OK\nEquivalent to a quiet bedroom')
led3 = Led(root, size=20)
led3.grid(column=2, row=10)
Hovertip(led3,'40 dB\nAll is OK\nEquivalent to a whisper')
led4 = Led(root, size=20)
led4.grid(column=2, row=9)
Hovertip(led4,'50 dB\nAll is OK\nEquivalent to a quiet home')
led5 = Led(root, size=20)
led5.grid(column=2, row=8)
Hovertip(led5,'60 dB\nAll is OK\nEquivalent to a quiet street')
led6 = Led(root, size=20)
led6.grid(column=2, row=7)
Hovertip(led6,'70 dB\nAll is OK\nEquivalent to a normal conversation')
led7 = Led(root, size=20)
led7.grid(column=2, row=6)
Hovertip(led7,'80 dB\nA little loud, may cause hearing damage in sensitive people.\nEquivalent to loud singing')
led8 = Led(root, size=20)
led8.grid(column=2, row=5)
Hovertip(led8,'90 dB\nLoud; repeated and/or long term exposure at this level may damage hearing.\nEquivalent to a motorcycle')
led9 = Led(root, size=20)
led9.grid(column=2, row=4)
Hovertip(led9,'100 dB\nCritically loud, even short exposure to this level can damage hearing.\nEquivalent to a subway')
led10 = Led(root, size=20)
led10.grid(column=2, row=3)
Hovertip(led10,'110 dB\nDangerous, even short exposure to this level can damage hearing.\nEquivalent to a helicopter overheaad')
led11 = Led(root, size=20)
led11.grid(column=2, row=2)
win.iconbitmap(get_resource_path('snd.ico'))
Hovertip(led11,"120 dB\nDangerous, even short exposure to this level can damage hearing.\nYou might feel pain at this level.\nEquivalent to a rock concert")
CTkLabel(sub, text='Instantaneous dBA level').grid(column=1, row=1)
CTkLabel(sub1, text='Instantaneous dBA level').grid(column=1, row=1)
db_levels=[82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118]
niosh_limits=[57600, 28800, 14400, 7200, 3600, 1800, 900, 450, 225, 120, 60, 30, 15]
db_levels.reverse()
niosh_limits.reverse()
enable_dosi=CTkSwitch(sub, text='Enable Dosimeter (restart app to apply)', command=toggle_dosi)
enable_dosi.grid(column=1, row=3)
def record_frame():
global recording, rec_start, db, f
time_trunc='%.1f' % (time.time()-rec_start)
f.write(time_trunc+','+str(int(db))+'\n')
if recording:
win.after(500, record_frame)
else:
f.close()
def record():
global recording, f, rec_start
if not recording:
rec.configure(text='Stop')
rec_start=time.time()
recording=True
f=open(os.getenv('HOMEPATH')+'\\Music\\mic_levels.csv', 'w')
f.write('Seconds,Decibels\n')
record_frame()
else:
recording=False
rec.configure(text='Record')
if not reg_present:
enable_dosi.configure(state=DISABLED)
if dosi_enabled:
enable_dosi.select()
doseLabel=CTkLabel(sub, text=r'Dose: 0.0%', width=40)
doseLabel.grid(column=1, row=4)
dosebar=CTkProgressBar(sub, mode='determinate')
dosebar.grid(column=2, row=4)
timeLabel=CTkLabel(sub, text='0 sec', width=30)
timeLabel.grid(column=2, row=2)
pdose=CTkLabel(sub, text='Projected dose: 0 sec', width=40)
CTkButton(sub, text='Reset', command=reset).grid(column=2, row=3)
CTkLabel(sub1, text='Recording to Music\\mic_levels.csv.\nMake sure the file does not exist, or it will be overwritten.').grid(column=1, row=3)
rec=CTkButton(sub1, text='Record', command=record)
rec.grid(column=1, row=4)
for i in range(len(db_levels)):
db_level=db_levels[i]
exec("label_"+str(db_level)+"=CTkLabel(sub, width=40, text='"+str(db_level)+"dBA: 0/"+str(niosh_limits[i])+" sec')")
exec("label_"+str(db_level)+".grid(column=1, row="+str(i+5)+")")
exec("bar_"+str(db_level)+'=CTkProgressBar(sub, mode="determinate")')
exec("bar_"+str(db_level)+'.grid(column=2, row='+str(i+5)+')')
else:
CTkLabel(sub, text='Dosimeter is not enabled', width=60).grid(column=1, row=4)
CTkLabel(sub1, text='Dosimeter must be enabled', width=60).grid(column=1, row=4)
CTkLabel(root, text='120').grid(column=1, row=2)
CTkLabel(root, text='100').grid(column=1, row=4)
CTkLabel(root, text='Danger').grid(column=3, row=4)
CTkLabel(root, text='80').grid(column=1, row=6)
CTkLabel(root, text='Loud').grid(column=3, row=6)
CTkLabel(root, text='60').grid(column=1, row=8)
CTkLabel(root, text='40').grid(column=1, row=10)
CTkLabel(root, text='20').grid(column=1, row=12)
CTkLabel(root, text='dB').grid(column=1, row=14)
CTkLabel(root, text='OK').grid(column=3, row=14)
CTkLabel(root, text='Max').grid(column=3, row=0)
CTkLabel(root, text='dBA').grid(column=1, row=0)
CTkLabel(root, text='-').grid(column=1, row=3)
CTkLabel(root, text='-').grid(column=1, row=5)
CTkLabel(root, text='-').grid(column=1, row=7)
CTkLabel(root, text='-').grid(column=1, row=9)
CTkLabel(root, text='-').grid(column=1, row=11)
CTkLabel(root, text='-').grid(column=1, row=13)
CTkLabel(root, text='dB Offset').grid(column=2, row=0)
maxdb_display=SevenSegmentDigits(root, digits=3, digit_color='#00ff00', background='black')
maxdb_display.grid(column=3, row=1)
dosimeter_times={'82dB':0, '85dB':0, '88dB':0, '91dB':0, '94dB':0, '97dB':0, '100dB':0, '103dB':0, '106dB':0, '109dB':0, '112dB':0, '115dB':0, '118dB':0}
Hovertip(maxdb_display,"Max dBA level since program start")
CHUNKS = [4096, 9600]
win.geometry('502x586')
CHUNK = CHUNKS[1]
FORMAT = pyaudio.paInt16
CHANNEL = 1
RATES = [44300, 48000]
RATE = RATES[1]
offset=StringVar()
offset.set('0')
spinbox=CTkOptionMenu(root, variable=offset, width=100, values=tuple(reversed(['-20','-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'])), state='readonly')
spinbox.grid(column=2, row=1)
Hovertip(spinbox,"dB offset (Calibration)\nUse this if the meter is not accurate.\nUse a reliable reference meter (such as a dedicated SPL meter).")
appclosed=False
from scipy.signal import bilinear
start_check=time.time()
db=0
x=[]
y=[]
def timer_dosi():
global runTime, y, x, start_check
runTime+=time.time()-start_check
if db>=82 and db<85:
dosimeter_times['82dB']+=time.time()-start_check
if db>=85 and db<88:
dosimeter_times['85dB']+=time.time()-start_check
if db>=88 and db<91:
dosimeter_times['88dB']+=time.time()-start_check
if db>=91 and db<94:
dosimeter_times['91dB']+=time.time()-start_check
if db>=94 and db<97:
dosimeter_times['94dB']+=time.time()-start_check
if db>=97 and db<100:
dosimeter_times['97dB']+=time.time()-start_check
if db>=100 and db<103:
dosimeter_times['100dB']+=time.time()-start_check
if db>=103 and db<106:
dosimeter_times['103dB']+=time.time()-start_check
if db>=106 and db<109:
dosimeter_times['106dB']+=time.time()-start_check
if db>=109 and db<112:
dosimeter_times['109dB']+=time.time()-start_check
if db>=112 and db<115:
dosimeter_times['112dB']+=time.time()-start_check
if db>=115 and db<118:
dosimeter_times['115dB']+=time.time()-start_check
if db>=118:
dosimeter_times['118dB']+=time.time()-start_check
start_check=time.time()
win.after(200, timer_dosi)
def close():
global appclosed
win.destroy()
if dosi_enabled:
SetValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled', 0, REG_DWORD, 1)
else:
SetValueEx(OpenKey(OpenKey(OpenKey(HKEY_CURRENT_USER, 'Software', reserved=0, access=KEY_ALL_ACCESS), 'sserver', reserved=0, access=KEY_ALL_ACCESS), 'Decibel Meter', reserved=0, access=KEY_ALL_ACCESS), 'DosimeterEnabled', 0, REG_DWORD, 0)
appclosed=True
stream.stop_stream()
stream.close()
pa.terminate()
def A_weighting(fs):
f1 = 20.598997
f2 = 107.65265
f3 = 737.86223
f4 = 12194.217
A1000 = 1.9997
NUMs = [(2*numpy.pi * f4)**2 * (10**(A1000/20)), 0, 0, 0, 0]
DENs = numpy.polymul([1, 4*numpy.pi * f4, (2*numpy.pi * f4)**2],
[1, 4*numpy.pi * f1, (2*numpy.pi * f1)**2])
DENs = numpy.polymul(numpy.polymul(DENs, [1, 2*numpy.pi * f3]),
[1, 2*numpy.pi * f2])
return bilinear(NUMs, DENs, fs)
NUMERATOR, DENOMINATOR = A_weighting(RATE)
def rms_flat(a):
return numpy.sqrt(numpy.mean(numpy.absolute(a)**2))
pa = pyaudio.PyAudio()
stream = pa.open(format = FORMAT,
channels = CHANNEL,
rate = RATE,
input = True,
frames_per_buffer = CHUNK)
max_decibel=0
def returnSum(myDict):
mylist = []
for i in myDict:
mylist.append(myDict[i])
final = sum(mylist)
return final
def listen(old=0, error_count=0, min_decibel=100):
global appclosed
global max_decibel
global measure
global db
global start
if not appclosed:
try:
try:
block = stream.read(CHUNK)
except IOError as e:
if not appclosed:
error_count += 1
messagebox.showerror("Error, ", " (%d) Error recording: %s" % (error_count, e))
else:
decoded_block = numpy.frombuffer(block, numpy.int16)
y = lfilter(NUMERATOR, DENOMINATOR, decoded_block)
new_decibel = 20*numpy.log10(rms_flat(y))+int(offset.get())
if runTime>0:
runt=runTime
else:
runt=0.1
if new_decibel<0:
new_decibel=0
old = new_decibel
db=new_decibel
gaugedb.set_value(str(int(float('{:.2f}'.format(new_decibel)))))
dosidb.set_value(str(int(float('{:.2f}'.format(new_decibel)))))
graphdb.set_value(str(int(float('{:.2f}'.format(new_decibel)))))
if new_decibel>max_decibel:
max_decibel=new_decibel
if int(db)>0:
led.to_green(on=True)
else:
led.to_grey(on=True)
maxdb_display.set_value(str(int(float(str(max_decibel)))))
for i in range(0, 12):
if int(new_decibel)>=(10*(i+1)):
if i>=9:
exec("led"+str(i)+".to_red(on=True)")
elif i>=7:
exec("led"+str(i)+".to_yellow(on=True)")
else:
exec("led"+str(i)+".to_green(on=True)")
else:
exec("led"+str(i)+".to_grey(on=True)")
if dosi_enabled_first:
dosebar.set(returnSum(dosimeter_times)/runt)
pdose.configure(text='Projected dose: '+str(int((returnSum(dosimeter_times)*8)/runt))+' sec')
timeLabel.configure(text=str(int(runt))+' sec')
doseLabel.configure(text='Dose: '+str(round(returnSum(dosimeter_times)/runt*1000)/10)+'%')
for i in range(len(db_levels)):
db_level=db_levels[i]
exec("label_"+str(db_level)+".configure(text='"+str(db_level)+"dBA: "+str(int(dosimeter_times[str(db_level)+'dB']))+'/'+str(niosh_limits[i])+" sec')")
exec("bar_"+str(db_level)+".set("+str((dosimeter_times[str(db_level)+'dB']/niosh_limits[i]))+')')
win.after(20, listen)
except TclError:
pass
win.protocol('WM_DELETE_WINDOW', close)
if __name__ == '__main__':
if dosi_enabled:
timer_dosi()
listen()
win.mainloop()发布于 2023-04-17 13:07:29
您的代码需要主要的重构。
为了调查您的性能问题,您需要能够分析执行情况,识别缓慢的部分,独立地处理它们。
事实上,这是不可能的,整个代码是如此错综复杂,以至于遵循逻辑是不可能的,而且我也不愿意花那么多精力去理解正在发生的事情。
首先,我建议阅读PEP 8- Python代码样式指南和PEP 257 - Docstring公约,并在编写代码时牢记这一点。这对性能没有帮助,但至少可以使代码具有足够的可读性,从而能够获得关于您的问题的有用反馈。
下面列出了一些需要改进的地方,以便合理地优化您的逻辑:
您可以从多个模块(包括非标准模块)进行import *。这意味着,当使用这些模块中的函数/类时,您无法知道它从何而来,以及它应该做什么。
这还可能导致无意中用您自己的定义或在不同模块之间覆盖来自模块的类或函数定义。
import *来自tkinter,然后import messagebox来自同一个模块,这是无用的。
您导入不使用的内容,包括Thread和matplotlib中的内容。把那些拿掉。
整个代码中没有一个空行,因此很难看到函数从哪里开始和结束,以及逻辑块是什么。
在运算符周围添加空格(如=、<、>=、.)也有助于提高可读性。
您将已执行的语句与函数定义混合在一起,这使得执行顺序很难遵循。一些导入就在代码的中间添加。而且,大多数代码总是被执行,但是有一小部分代码在if __name__ == '__main__'保护程序后面,原因我无法理解。
您应该在开始时拥有所有导入,然后是所有常量定义,然后是函数定义,然后执行代码,最好是在if __name__ == '__main__'保护后面。
使用docstring来记录您的函数所做的事情、所期望的参数以及它们返回的内容。
全球化使得执行顺序难以遵循,并给bug带来了风险。
虽然有些try/except块确实捕捉到一个特定的错误并处理它,但另一些块捕获了所有的异常,隐藏了潜在的问题。例如,您只应该在尝试导入ImportError时捕获winreg S。
有很多重复的代码,特别是用于初始化GUI小部件。使用循环来简化代码。
分离
看起来,您的主要逻辑发生在listen函数中。但是,在该函数中,GUI有许多依赖项,因此无法测试和分析您的逻辑以查找bug或瓶颈。
https://codereview.stackexchange.com/questions/284457
复制相似问题