[英]Python threading only working when called from another module
I am working on an app that accepts MIDI keyboard input (using Mido) from within a Kivy application. 我正在开发一个可从Kivy应用程序中接受MIDI键盘输入(使用Mido)的应用程序。 The goal is to have one thread that constantly polls the MIDI input and routes events to pyfluidsynth, while a conventional Kivy app is running in parallel.
目标是拥有一个线程,该线程不断轮询MIDI输入并将事件路由到pyfluidsynth,而传统的Kivy应用程序正在并行运行。 I need some sort of parallel process, or else the Kivy UI freezes for as long as the midi poll while loop is running.
我需要某种并行过程,否则Kivy UI会冻结,直到运行循环时进行midi poll。
After much fiddling, I got it to work, but I'm a bit concerned about the code. 经过多番摆弄后,我开始使用它了,但是我对代码有些担心。 I tried starting the threads under [if name == " main "], but was only ever able to run one process, followed by the other.
我尝试在[if name ==“ main ”]下启动线程,但是只能运行一个进程,然后再运行另一个进程。
Then by accident I was able to get the desired effect by leaving in the last 2 lines of code in mido_midi.py, which were originally just for testing. 然后偶然地,我通过将mido_midi.py的最后两行代码保留在原来仅用于测试的代码中,从而获得了预期的效果。 Now, when I run main.py, I get the app plus the keyboard input.
现在,当我运行main.py时,我得到了该应用程序以及键盘输入。 Other than some ugly behaviour when I close the app, things appear to be working the way I wanted.
除了我关闭应用程序时的某些丑陋行为外,其他事情似乎都按照我想要的方式工作。
My concern is that I can't seem to get the threading to work by calling everything from main. 我担心的是,我似乎无法通过从main调用所有内容来使线程正常工作。 Since things are working, and I don't understand why, and it looks wrong to me.
既然一切正常,我不明白为什么,对我来说,这似乎是错误的。 I thought I'd throw it to smarter people for insight.
我以为我会把它扔给更聪明的人以获得见识。
Am I doing it right? 我做对了吗? If not, what do I need to change?
如果没有,我需要更改什么? Thanks.
谢谢。
main.py: 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: 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()
Your code is starting the KeyboardInput
thread when you do the from mido_midi import start_synth, KeyboardInput
and the "testing" lines are executed at that time. 当您执行
from mido_midi import start_synth, KeyboardInput
您的代码正在启动KeyboardInput
线程,此时会执行from mido_midi import start_synth, KeyboardInput
和“ testing”行。 The if __name__ == "__main__":
is a construct designed to prevent exactly that from happening when a file containing that construct is imported. if __name__ == "__main__":
是一种构造,旨在防止在导入包含该构造的文件时发生这种情况。 Also, note that you have two different instance of KeyboardInput
. 另外,请注意,您有两个不同的
KeyboardInput
实例。 Not sure if that is your intention. 不确定这是否是您的意图。
You should be able to start the thread inside your if __name__ == "__main__":
block by just adding the same two lines inside that block: 您只要能够在
if __name__ == "__main__":
块中添加相同的两行,就可以启动该线程:
m = KeyboardInput()
m.start()
If you really only want one instance of KeyboardInput
, you should be able to do 如果您确实只想要
KeyboardInput
一个实例,则应该能够
self.midikeyboard.start()
inside your __init__()
method in GameWidget
. 在
GameWidget
__init__()
方法中。
Also, if you want easier shutdown add daemon=True
to the constructor call, either: 另外,如果您想更轻松地关闭,请在构造函数调用中添加
daemon=True
,或者:
m = KeyboardInput(daemon=True)
or 要么
self.midikeyboard = KeyboardInput(daemon=True)
The daemon=True
means that the thread will be killed as soon as the app is finished. daemon=True
表示该线程将在应用程序完成后立即终止。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.