Python Forum
[PyQt] Assign Label Text as number in Hz
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] Assign Label Text as number in Hz
#11
(Jul-10-2018, 04:26 PM)Alfalfa Wrote:
(Jul-07-2018, 02:44 AM)mekha Wrote: It does looping forever until I terminate the graphs window, then they stop.
But if I want to assign this Qtimer to show some frequency data as Label Text in my QtDesigner window, then how I can do that?

I'm not sure to understand, are you asking about how to use the QTimer to refresh the text of a QLabel?

If so, simply add something like
self.ui.yourLabel.setText("frequency data")
into the function connected to your timer..

The purpose of QtDesigner is to organize your widget, but the dynamic content is rather set by using python.

I'm sorry if I confused you, I'm trying my best to tell you since English isn't my primary language Sad
I see... I'll try it if it is can show the freq data as I hope so
But can you take a look of this code for me, I have a problem with this freq analyzer recorder. My question is why in the middle of recording data it's stopped and if I tried to close the data (every single time) there'll be a warning and it said to me that the .py file still running (but the weird is, there's actually no updates at all or any more progress of the data itself, totally stop)

import pyaudio
import os
import struct
import numpy as np
import time
from time import sleep


CHUNK = 2**14 #2**15 #4096
WIDTH = 2
FORMAT = pyaudio.paInt16 
CHANNELS = 2
RATE = 44100
dt = 1.0/RATE


### frequencies of the strings for the violin (tunned in A), in Hz
f4 = 195.998   ## G3
f3 = 293.665   ## D4
f2 = 440.000   ## A4
f1 = 659.255   ## E5

n = CHUNK
freqs = np.fft.rfftfreq(n, d = dt)

def Frequency_of_position(position):
    """ Returns the frequency (Hz) of the note in from its position (halftones)
    relative to A4 in an equal tempered scale. Ex: 0 -> 440 Hz (A4), 
    12 -> 880 Hz (A5)."""
    return 440.0*(2**(1.0/12.0))**position


def Position_to_note(position):
    "A A# B C C# D D# E F F# G G#"
    SCALE = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
    LETTER = SCALE[position % 12]
    NUMBER = str(int((position+57) / 12))
    return LETTER+NUMBER

pos = np.array(range(-36,48))
vnote_freqs = np.vectorize(Frequency_of_position)
note_freqs = vnote_freqs(pos)


def get_frequency( spectrum ):
    return freqs[np.argmax(spectrum)]

class Freq_analysis(object):
    def __init__(self):
        self.pa = pyaudio.PyAudio()
        self.stream = self.open_mic_stream()

    def stop(self):
        self.stream.close()

    def open_mic_stream( self ):
        device_index = self.find_input_device()

        stream = self.pa.open(   format = FORMAT,
                                 channels = CHANNELS,
                                 rate = RATE,
                                 input = True,
                                 input_device_index = device_index,
                                 frames_per_buffer = CHUNK)

        return stream

    def find_input_device(self):
        device_index = None            
        for i in range( self.pa.get_device_count() ):     
            devinfo = self.pa.get_device_info_by_index(i)   
            print( "Device %d: %s"%(i,devinfo["name"]) )

            for keyword in ["mic","input"]:
                if keyword in devinfo["name"].lower():
                    print( "Found an input: device %d - %s"%    (i,devinfo["name"]) )
                    device_index = i
                    return device_index

        if device_index == None:
            print( "No preferred input found; using default input device." )

        return device_index

    def PrintFreq(self, S2):
        dominant = get_frequency( S2 )
        dist = np.abs(note_freqs-dominant)
        closest_pos = pos[np.argmin(dist)]
        closest_note = Position_to_note(closest_pos)
        print(dominant, "(",closest_note, "=",Frequency_of_position(closest_pos),")")

    def listen(self):
        try:
            block = self.stream.read(CHUNK)
        except IOError:
            # An error occurred. 
            print( "Error recording.")
            return
        indata = np.array(struct.unpack("%dh"%(len(block)/2),block))
        n = indata.size
        freqs = np.fft.rfftfreq(n, d = dt)
        data_rfft = np.fft.rfft(indata)
        S2 = np.abs(data_rfft)**2
        #self.PrintFreq(block)
        #self.update_fig(block)
        self.PrintFreq(S2)
        
if __name__ == "__main__":
    Tuner = Freq_analysis()

    for i in range(100):
        Tuner.listen()
And yes, this is the freq data I want to showed in my QT Window since it's running on the back, so if I can injected the label to show the result of this freq recorder I'm truly happy
Reply
#12
(Jul-04-2018, 10:30 AM)DeaD_EyE Wrote: I have no clue about music.
Based on this Article and this Example with matplotlib, I wrote an example.

from itertools import product
import matplotlib.pyplot as plt


def calc_freq_from_midi(m):
    if m < 0 or m > 127:
        raise ValueError('m must be between 0 and 127.')
    notes = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')
    octaves = range(-1, 10)
    notes = product(octaves, notes)
    notes = [note + str(band) for band, note in notes]
    freq = 440 * 2 ** ((m - 69)/12)
    return freq, notes[m]

labels = [calc_freq_from_midi(m) for m in range(128)]
x_ticks, x_labels = zip(*labels) # it's a transpose


frequencies = [110.0, 220.0, 440.0] # A2, A3, A4
magnitude = [2, 3, 4]
plt.title('Spectrum')
plt.xlabel('Notes')
plt.ylabel('Magnitude')
plt.xticks(x_ticks, x_labels, rotation='vertical')
plt.plot(frequencies, magnitude, 'o')
plt.legend(['Signal'])
plt.show()
Just check if the notes are correct.
It should plot A2, A3 and A4 as blue dots.

I guess you can also print two different xticks.
Just explore the matplotlib examples.

If you don't use matplotlib inside of QT, you have to adapt it to the plotting framework you use.

(Jul-10-2018, 04:26 PM)Alfalfa Wrote:
(Jul-07-2018, 02:44 AM)mekha Wrote: It does looping forever until I terminate the graphs window, then they stop.
But if I want to assign this Qtimer to show some frequency data as Label Text in my QtDesigner window, then how I can do that?

I'm not sure to understand, are you asking about how to use the QTimer to refresh the text of a QLabel?

If so, simply add something like
self.ui.yourLabel.setText("frequency data")
into the function connected to your timer..

The purpose of QtDesigner is to organize your widget, but the dynamic content is rather set by using python.

When I calling a class inside the freq.py inside the go.py, the QtWindow (the graphs and others) couldn't showed up it keep loading and the freq analyzer keep running on the back giving results of it's recorder

the updated go.py
from PyQt4 import QtGui,QtCore

import sys
import ui_main
import StrSetting
import numpy as np
import pyqtgraph
import SWHear
import freq
from freq import Freq_analysis

class SoundApp(QtGui.QMainWindow, ui_main.Ui_MainWindow):
    def __init__(self, parent=None):
        pyqtgraph.setConfigOption('background', 'w') #before loading widget
        super(SoundApp, self).__init__(parent)
        self.setupUi(self)
        self.grFFT.plotItem.showGrid(True, True, 0.7)
        self.grPCM.plotItem.showGrid(True, True, 0.7)
        self.maxFFT=0
        self.maxPCM=0
        self.ear = SWHear.SWHear(rate=44100,updatesPerSecond=20)
        self.ear.stream_start()
        

    def update(self):
        if not self.ear.data is None and not self.ear.fft is None:
            pcmMax=np.max(np.abs(self.ear.data))
            if pcmMax>self.maxPCM:
                self.maxPCM=pcmMax
                self.grPCM.plotItem.setRange(yRange=[-pcmMax,pcmMax])
            if np.max(self.ear.fft)>self.maxFFT:
                self.maxFFT=np.max(np.abs(self.ear.fft))
                #self.grFFT.plotItem.setRange(yRange=[0,self.maxFFT])
                self.grFFT.plotItem.setRange(yRange=[0,1])
            self.pbLevel.setValue(1000*pcmMax/self.maxPCM)
            pen=pyqtgraph.mkPen(color='b')
            self.grPCM.plot(self.ear.datax,self.ear.data,pen=pen,clear=True)
            pen=pyqtgraph.mkPen(color='r')
            self.grFFT.plot(self.ear.fftx,self.ear.fft/self.maxFFT,pen=pen,clear=True)
        QtCore.QTimer.singleShot(1, self.update) # QUICKLY repeat
        

if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    form = SoundApp()
    form.show()
    form.update() #start with something
    Tuner = Freq_analysis()

    for i in range(1000):
        Tuner.listen()
    app.exec_()
    print("DONE")
and this is the freq.py
import pyaudio
import os
import struct
import numpy as np
import time
from time import sleep


CHUNK = 2**14 #2**15 #4096
WIDTH = 2
FORMAT = pyaudio.paInt16 
CHANNELS = 2
RATE = 44100
dt = 1.0/RATE


### frequencies of the strings for the violin (tunned in A), in Hz
f4 = 195.998   ## G3
f3 = 293.665   ## D4
f2 = 440.000   ## A4
f1 = 659.255   ## E5

n = CHUNK
freqs = np.fft.rfftfreq(n, d = dt)

def Frequency_of_position(position):
    """ Returns the frequency (Hz) of the note in from its position (halftones)
    relative to A4 in an equal tempered scale. Ex: 0 -> 440 Hz (A4), 
    12 -> 880 Hz (A5)."""
    return 440.0*(2**(1.0/12.0))**position


def Position_to_note(position):
    "A A# B C C# D D# E F F# G G#"
    SCALE = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
    LETTER = SCALE[position % 12]
    NUMBER = str(int((position+57) / 12))
    return LETTER+NUMBER

pos = np.array(range(-36,48))
vnote_freqs = np.vectorize(Frequency_of_position)
note_freqs = vnote_freqs(pos)


def get_frequency( spectrum ):
    return freqs[np.argmax(spectrum)]

class Freq_analysis(object):
    def __init__(self):
        self.pa = pyaudio.PyAudio()
        self.stream = self.open_mic_stream()

    def stop(self):
        self.stream.close()

    def open_mic_stream( self ):
        device_index = self.find_input_device()

        stream = self.pa.open(   format = FORMAT,
                                 channels = CHANNELS,
                                 rate = RATE,
                                 input = True,
                                 input_device_index = device_index,
                                 frames_per_buffer = CHUNK)

        return stream

    def find_input_device(self):
        device_index = None            
        for i in range( self.pa.get_device_count() ):     
            devinfo = self.pa.get_device_info_by_index(i)   
            print( "Device %d: %s"%(i,devinfo["name"]) )

            for keyword in ["mic","input"]:
                if keyword in devinfo["name"].lower():
                    print( "Found an input: device %d - %s"%    (i,devinfo["name"]) )
                    device_index = i
                    return device_index

        if device_index == None:
            print( "No preferred input found; using default input device." )

        return device_index

    def PrintFreq(self, S2):
        dominant = get_frequency( S2 )
        dist = np.abs(note_freqs-dominant)
        closest_pos = pos[np.argmin(dist)]
        closest_note = Position_to_note(closest_pos)
        print(dominant, "(",closest_note, "=",Frequency_of_position(closest_pos),")")

    def listen(self):
        try:
            block = self.stream.read(CHUNK)
        except IOError:
            # An error occurred. 
            print( "Error recording.")
            return
        indata = np.array(struct.unpack("%dh"%(len(block)/2),block))
        n = indata.size
        freqs = np.fft.rfftfreq(n, d = dt)
        data_rfft = np.fft.rfft(indata)
        S2 = np.abs(data_rfft)**2
        #self.PrintFreq(block)
        #self.update_fig(block)
        self.PrintFreq(S2)
        
if __name__ == "__main__":
    Tuner = Freq_analysis()

    for i in range(100):
        Tuner.listen()
Reply
#13
I'm glad to help you get on the right track, but unfortunately I do not have the time to go over your code. So your problem is that you are trying to have a long running code in parallel with your GUI? In that case, instead of QTimer your need to put the long running code in a separate thread, or else the GUI will becomes unresponsive.

Then the right way to send data back from the worker thread to the gui thread is by using signals and slots. To get you started, have a look at this example: https://gitlab.com/snippets/1732597

In your case, the frequency analysis would go into the function run() (class WorkerThread), the result would be sent through the signal/slot mechanism, and the label would be set into the function signalExample() of the gui thread (class Main). Hope that helps.
Reply
#14
(Jul-12-2018, 09:38 PM)Alfalfa Wrote: I'm glad to help you get on the right track, but unfortunately I do not have the time to go over your code. So your problem is that you are trying to have a long running code in parallel with your GUI? In that case, instead of QTimer your need to put the long running code in a separate thread, or else the GUI will becomes unresponsive.

Then the right way to send data back from the worker thread to the gui thread is by using signals and slots. To get you started, have a look at this example: https://gitlab.com/snippets/1732597

In your case, the frequency analysis would go into the function run() (class WorkerThread), the result would be sent through the signal/slot mechanism, and the label would be set into the function signalExample() of the gui thread (class Main). Hope that helps.

I've checked the post you linked but it's for Python 3x and using PyQt5, and I'm using Python 2.7 and PyQt4...
So what I can confirmed for the progress I've been making from my last post is, when I said it's error not showing the graphs window and etc but only the python running box...

It's actually happened that way because when I combined the freq analyzer with my graph sound visualizer and when I run the overall code both the freq analyzer and sound visualizer (it's on the go.py)
from PyQt4 import QtGui,QtCore

import sys
import ui_main
import StrSetting
import numpy as np
import pyqtgraph
import SWHear
import freq
from freq import Freq_analysis

class SoundApp(QtGui.QMainWindow, ui_main.Ui_MainWindow):
    def __init__(self, parent=None):
        pyqtgraph.setConfigOption('background', 'w') #before loading widget
        super(SoundApp, self).__init__(parent)
        self.setupUi(self)
        self.grFFT.plotItem.showGrid(True, True, 0.7)
        self.grPCM.plotItem.showGrid(True, True, 0.7)
        self.maxFFT=0
        self.maxPCM=0
        self.ear = SWHear.SWHear(rate=44100,updatesPerSecond=20)
        self.ear.stream_start()
        

    def update(self):
        if not self.ear.data is None and not self.ear.fft is None:
            pcmMax=np.max(np.abs(self.ear.data))
            if pcmMax>self.maxPCM:
                self.maxPCM=pcmMax
                self.grPCM.plotItem.setRange(yRange=[-pcmMax,pcmMax])
            if np.max(self.ear.fft)>self.maxFFT:
                self.maxFFT=np.max(np.abs(self.ear.fft))
                #self.grFFT.plotItem.setRange(yRange=[0,self.maxFFT])
                self.grFFT.plotItem.setRange(yRange=[0,1])
            self.pbLevel.setValue(1000*pcmMax/self.maxPCM)
            pen=pyqtgraph.mkPen(color='b')
            self.grPCM.plot(self.ear.datax,self.ear.data,pen=pen,clear=True)
            pen=pyqtgraph.mkPen(color='r')
            self.grFFT.plot(self.ear.fftx,self.ear.fft/self.maxFFT,pen=pen,clear=True)
        QtCore.QTimer.singleShot(1, self.update) # QUICKLY repeat
        

if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    form = SoundApp()
    form.show()
    form.update() #start with something
    Tuner = Freq_analysis()

    for i in range(100):
        Tuner.listen()
    app.exec_()
    print("DONE")
so what I want to pointed is, the freq analyzer is had to be runned 1st and finishing it's recording and later the sound visualizer came in (so the sound visualizer window keeps loading whie the freq give it's results) Wall

what can I do to make the both running in the same time?? Huh
Reply
#15
You have to use a QThread for your freq analyzer, then emit a signal to update the gui. For PyQt4 it would be something like this:

import sys
import time
from PyQt4 import QtGui, QtCore

class WorkerThread(QtCore.QObject):
    updateFreq = QtCore.pyqtSignal(int)

    def __init__(self):
        super().__init__()

    @QtCore.pyqtSlot()
    def run(self):
        freq = 0
        while True:
            freq += 1
            self.updateFreq.emit(freq)
            time.sleep(5)

class Main(QtGui.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()  # Move the Worker object to the Thread object
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup
        self.worker.moveToThread(self.workerThread)
        self.worker.updateFreq.connect(self.updateFreq)  # Connect your signals/slots
        self.workerThread.start()

    def updateFreq(self, freq):
        #self.ui.freqLabel.setText(str(freq) + " Hz")
        print(freq)

if __name__== '__main__':
    app = QtGui.QApplication([])
    gui = Main()
    sys.exit(app.exec_())
Reply
#16
(Jul-16-2018, 12:45 PM)Alfalfa Wrote: You have to use a QThread for your freq analyzer, then emit a signal to update the gui. For PyQt4 it would be something like this:

import sys
import time
from PyQt4 import QtGui, QtCore

class WorkerThread(QtCore.QObject):
    updateFreq = QtCore.pyqtSignal(int)

    def __init__(self):
        super().__init__()

    @QtCore.pyqtSlot()
    def run(self):
        freq = 0
        while True:
            freq += 1
            self.updateFreq.emit(freq)
            time.sleep(5)

class Main(QtGui.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()  # Move the Worker object to the Thread object
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup
        self.worker.moveToThread(self.workerThread)
        self.worker.updateFreq.connect(self.updateFreq)  # Connect your signals/slots
        self.workerThread.start()

    def updateFreq(self, freq):
        #self.ui.freqLabel.setText(str(freq) + " Hz")
        print(freq)

if __name__== '__main__':
    app = QtGui.QApplication([])
    gui = Main()
    sys.exit(app.exec_())

Ah thank you very much!
I'll try to run a test now

EDIT:
so I've try to run it to see what it does... so got an error (of course it'll but...)
Traceback (most recent call last):
  File "C:/Users/user/Desktop/Python sound recog/qt_audio_monitor/TEST/QthreadFreq.py", line 35, in <module>
    gui = Main()
  File "C:/Users/user/Desktop/Python sound recog/qt_audio_monitor/TEST/QthreadFreq.py", line 21, in __init__
    super().__init__()
TypeError: super() takes at least 1 argument (0 given)
What's this super() do anyway? sorry if I asking you a lot
Reply
#17
I forgot that python2 need arguments to init super. Simply replace it with:
super(ClassName, self).__init__()

so for the example above:
super(WorkerThread, self).__init__()
super(Main, self).__init__()


I don't know what it does exactly but it is mandatory. Btw, as you are developping a new app, maybe you should consider going for the lastest versions, as PyQt4 is quite old. You'll less likely need to port your code again in the future. If you need help to get started I've posted several examples for PyQt5 on gitlab: https://gitlab.com/users/william.belanger/snippets
Reply
#18
(Jul-16-2018, 04:35 PM)Alfalfa Wrote: I forgot that python2 need arguments to init super. Simply replace it with:
super(ClassName, self).__init__()

so for the example above:
super(WorkerThread, self).__init__()
super(Main, self).__init__()


I don't know what it does exactly but it is mandatory. Btw, as you are developping a new app, maybe you should consider going for the lastest versions, as PyQt4 is quite old. You'll less likely need to port your code again in the future. If you need help to get started I've posted several examples for PyQt5 on gitlab: https://gitlab.com/users/william.belanger/snippets

Thank you very much!
But if I porting it right now, I guess no... because I need to get this app right 1st then I'll consider to porting it when all done. My deadline is getting closer and the app still unable to give what I need
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] The Text in the Label widget Tkinter cuts off the Long text in the view malmustafa 4 4,667 Jun-26-2022, 06:26 PM
Last Post: menator01
  update text variable on label with keypress knoxvilles_joker 3 4,839 Apr-17-2021, 11:21 PM
Last Post: knoxvilles_joker
  How to read text in kivy textinput or Label jadel440 1 5,178 Dec-29-2020, 10:47 AM
Last Post: joe_momma
  Tkinter: How to assign calculated value to a Label LoneStar 7 3,756 Sep-03-2020, 08:19 PM
Last Post: LoneStar
  [Kivy] Kivy text label won't shows up! AVD_01 1 2,894 Jun-21-2020, 04:01 PM
Last Post: AVD_01
  [Tkinter] Python 3 change label text gw1500se 6 4,605 May-08-2020, 05:47 PM
Last Post: deanhystad
  [Tkinter] how to update label text from list Roshan 8 5,370 Apr-25-2020, 08:04 AM
Last Post: Roshan
  [PyQt] Python PyQt5 - Change label text dynamically based on user Input ppel123 1 13,661 Mar-20-2020, 07:21 AM
Last Post: deanhystad
  [Tkinter] Label, align imported text from pandas kundrius 2 4,160 Dec-11-2019, 08:26 AM
Last Post: kundrius
  Make Label Text background (default color) transparent using tkinter in python barry76 1 23,477 Nov-28-2019, 10:19 AM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020