Python 线程仅在从另一个模块调用时工作

Python threading only working when called from another module

我正在开发一个接受 Kivy 应用程序中的 MIDI 键盘输入(使用 Mido)的应用程序。目标是让一个线程不断轮询 MIDI 输入并将事件路由到 pyfluidsynth,而传统的 Kivy 应用程序是 运行ning 并行。我需要某种并行处理,否则只要 midi poll while 循环 运行ning.

,Kivy UI 就会冻结。

经过多次调整后,我让它工作了,但我有点担心代码。我尝试在 [if name == "main"] 下启动线程,但只能 运行 一个进程,其次是另一个。

然后一不小心把mido_midi.py里的最后两行代码留下来得到了想要的效果,本来只是为了测试。现在,当我 运行 main.py 时,我得到了应用程序和键盘输入。除了关闭应用程序时出现一些丑陋的行为外,一切似乎都按照我想要的方式进行。

我担心的是,我似乎无法通过调用 main 中的所有内容来使线程工作。由于一切正常,我不明白为什么,而且我觉得这不对。我想我会把它扔给更聪明的人以获取见识。

我做的对吗?如果不是,我需要更改什么?谢谢

main.py:

from kivy.app import App
from kivy.uix.widget import Widget
from mido_midi import start_synth, KeyboardInput

class MyApp(App):
    def build(self):
        return GameWidget()

class GameWidget(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        ...
        self.midikeyboard = KeyboardInput()
        ...

if __name__ == "__main__":
    app = MyApp()
    app.run()

mido_midi.py:

import mido
import fluidsynth
import threading

def start_synth(driver, sound, channel=0):
    fs = fluidsynth.Synth(samplerate=24000, gain=0.8)
    fs.start(driver)
    sfid = fs.sfload(sound)
    fs.program_select(channel, sfid, 0, 0)
    # print("Midi loaded...")
    return fs

class KeyboardInput(threading.Thread):
    def __init__(self, device='Keystation 88 Port 1', driver='coreaudio', sound='sounds/Wii_Grand_Piano.sf2', channel=0):
        super(KeyboardInput, self).__init__()
        self.played_notes = []
        self.device = device
        self.driver = driver
        self.sound = sound
        self.channel = channel
        self.inport = mido.open_input(self.device)
        self.fs = start_synth(self.driver, self.sound)

    def run(self):
        for msg in self.inport:
            print(msg)
            note = msg.note
            velocity = msg.velocity
            self.fs.noteon(self.channel, note, velocity)

# Code outside of the class, intended for testing
m = KeyboardInput()
m.start()

当您执行 from mido_midi import start_synth, KeyboardInput 并且 "testing" 行在那个时候执行时,您的代码正在启动 KeyboardInput 线程。 if __name__ == "__main__": 是一种结构,旨在防止在导入包含该结构的文件时发生这种情况。另外,请注意您有两个不同的 KeyboardInput 实例。不确定这是否是您的意图。

您应该能够在 if __name__ == "__main__": 块中启动线程,只需在该块中添加相同的两行:

m = KeyboardInput()
m.start()

如果你真的只想要 KeyboardInput 的一个实例,你应该可以做到

self.midikeyboard.start()

GameWidget 中的 __init__() 方法中。

此外,如果您想要更轻松地关闭,请将 daemon=True 添加到构造函数调用中,或者:

m = KeyboardInput(daemon=True)   

self.midikeyboard = KeyboardInput(daemon=True)

daemon=True表示应用一结束线程就会被杀死。