简体   繁体   中英

How to use Tkinter OptionMenu to select microphone input

In my code, I am using Tkinter, PyAudio and the SpeechRecognition library. The speech recognition library uses PyAudio for interfacing with the PC's microphones (im on Win 10), and allows you to select the microphone input to use through an index value. This index refers to a list of microphones the system has available. This list contains multiple dictionaries, each dictionary containing information on each audio device connected to the system. Each of these dictionaries contains a field called "index", which stores an integer value representing where that dictionary lays in the overall list. This list is fetched by the SpeechRecognition library through the PyAudio library.

I can use the same method the speech recognition library does with the PyAudio library to generate my own list of microphones using

def getMicrophones(self):
        import pyaudio
        p = pyaudio.PyAudio()
        temp = []
        for i in range(p.get_device_count()):
            if p.get_device_info_by_index(i).get("maxInputChannels") > 0: #if maxInputChannels > 0, then device is a microphone
                temp.append(p.get_device_info_by_index(i).get("name"))
        return temp

This function fetches each dictionary separately from this list, and, if the maxInputChannels field has a value > 0, the value of the name field is stored in a separate list. I can then display this list in a tkinter OptionMenu with the below code:

self.micOptions = self.getMicrophones()
self.selectedMicrophone = tk.StringVar() #Tkinter variable needs to be made so that the dropdown can hold a value
self.selectedMicrophone.set(self.micOptions[0]) #Set default OptionMenu value
self.micDropDown = tk.OptionMenu(self, self.selectedMicrophone, *self.micOptions)

Here's the problem: These microphone names are taken from a dictionary that is returned by p.get_device_info_by_index(i) using.get("name"), as seen above. The names are then placed into a list to be used, as explained.

Overall, my question would be, how can i use the selected string (name) of the microphone from the OptionMenu in order to get back to the original dictionary it was taken from, so that i can then access its index field to use later on. Any help is much appreciated, as i feel like there would definitely be some best practices for this.

Extends a tk.OptionMenu to return a selected audio device.

return self._devices.get(self.selected.get(), None)

Core Point :

Instead of saving the index of a device, which is not in sync with get_device_count() , store the whole device into a dict with key == device['name'] .

                device = p.get_device_info_by_index(i)
                devices[device.get("name")] = device

Imports, Simulated PyAudio :

import tkinter as tk


# Simulating PyAudio
class PyAudio:
    def __init__(self):
        self._devices = [{'name': 'Device 1', 'maxInputChannels': 0},
                         {'name': 'Device 2', 'maxInputChannels': 1},
                         {'name': 'Device 3', 'maxInputChannels': 0},
                         {'name': 'Device 4', 'maxInputChannels': 2},
                         {'name': 'Device 5', 'maxInputChannels': 3},
                         ]

    def get_device_count(self):
        return len(self._devices)

    def get_device_info_by_index(self, i):
        return self._devices[i]

Extends tk.OptionMenu :

class Microphones(tk.OptionMenu):
    def __init__(self, parent, **kwargs):
        self.selected = tk.StringVar()
        self._devices = kwargs.pop('devices', ())
        options = tuple(self._devices.keys())

        super().__init__(parent, self.selected, *options, **kwargs)
        self.selected.set(options[0])

    @property
    def device(self):
        return self._devices.get(self.selected.get(), None)

Usage :

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.micDropDown = Microphones(self, devices=self.getMicrophones(), command=self.on_selected)
        self.micDropDown.pack()

    def on_selected(self, event):
        print(f'on_selected({self.micDropDown.selected.get()}, '
              f'device={self.micDropDown.device})')

    def getMicrophones(self):
        """
        import pyaudio
        p = pyaudio.PyAudio()
        """
        p = PyAudio()
        devices = {}
        for i in range(p.get_device_count()):
            # if maxInputChannels > 0, then device is a microphone
            if p.get_device_info_by_index(i).get("maxInputChannels") > 0:
                device = p.get_device_info_by_index(i)
                devices[device.get("name")] = device
        return devices


if __name__ == '__main__':
    App().mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM