如何在Kivy中防止按钮动作发生得太快?

时间:2018-04-07 09:14:21

标签: python bash python-2.7 shell kivy

嘿伙计们,我和Kivy一起大约10分钟,然后随便用蟒蛇嬉戏。

我试图创建一个调用我写的bash脚本的按钮来启动一个radio接收器(rtl-sdr)。单独调用会导致GUI冻结,所以我决定使用线程。这很好,但后来我发现我无法停止我的进程(rtl_fm) - 我需要做的事情才能调用另一个频率。

我决定在Kivy中使用on_presson_release方法停止我的所有无线电进程并尝试启动一个新的频率线程 - 理论上运行良好,但是杀戮过程花费的时间太长而且我认为在它有机会开始之前它会杀死我的on_release进程。我必须按两次按钮。 (将两个os.system进程放在一个线程中没有帮助,也没有使用Timer库中的threading方法)这就是我设置它的方式 - 我可能不需要killRadio()中的帖子,但我抓住了稻草(提前抱歉):

(使用subprocess.call也没有工作)

main.kv

FloatLayout:
    size_hint: 1, 1
    Label:
            markup: True
            text: '[size=25]This app is for testing purposes.[/size]'
            pos: 0, 0

    MDRaisedButton:
            text: "106.9"
            font_size: 25
            on_press: app.killRadio()
            on_release: app.tuneRadio("106.9M")
            pos: 20, 255
            size_hint: .3, .3

    MDRaisedButton:
            text: "93.3"
            font_size: 25
            on_press: app.killRadio()
            on_release: app.tuneRadio("93.3M")
            pos: 305, 255
            size_hint: .3, .3

main.py

def killRadio(obj):
    de = Thread(target = lambda: os.system("kill -9 $(ps -ef | grep rtl_fm | grep -v grep | awk '{print $2}')"))
    try:
       de.start() 
    except:
        print ("can't kill")

def tuneRadio(obj, station):
    th = Thread(target = lambda: os.system('radio ' + station))
    # time.sleep(.3)
    th.start()

无线电广播

#!/bin/sh

if [ "$1" = "stop" ] #thought this would kill the script - it just frees the cmd line
then
    echo "Stopping Radio Playback"
    exit 0
fi
if [ "$#" -gt 0  ]
then
    echo "playing {$1}"
    rtl_fm -f $1 -M wbfm -g 42 -s 200000 -r 192000 - | aplay -D hw:1,0 -c 2 -r 48000 -f S32_LE
else
    exit 0
fi

使用Clock.schedule_once并添加延迟似乎会冻结GUI,因此我无法在按下其他按钮时测试它是否有效。对不起,上面的代码是一个绝望的尝试尝试一切 - 我只是想要一个按钮来杀死无线电,如果它打开并调用另一个频率。

我厌倦了阅读Kivy和Python文档 - 有没有人看到丢失的内容?而且我确定我做错了什么......

由于

修改

def control_radio(self, *args):
    global radio
    radio_lock = threading.Lock()  # a mutex for the radio control
    if radio_lock.acquire(False):  # make sure other threads silently fail
        print("aquired")
        print(args)
        if radio and radio.poll() is None:  # kill the radio process if running
            print("radio and poll")
            radio.kill()  # use radio.terminate() for a milder version
        if args[0] == "tune":  # start the new radio process if requested
            print("tuning")
            radio = subprocess.Popen(["radio", args[1]])
        radio_lock.release()  # release the lock

我修改了@zwer控制功能以使其在我的Main类中工作。

1 个答案:

答案 0 :(得分:0)

首先,你应该真正使用subprocess.Popen(),这样你就可以在不依赖脆弱的外部命令的情况下实际控制你的过程,最基本的方法是:

import subprocess

radio = None  # a reference to hold our radio process

def kill_radio(obj):
    if radio and radio.poll() is None:
        radio.kill()  # use radio.terminate() for a milder version

def tune_radio(obj, station):
    global radio
    if radio and radio.poll() is None:
        radio.kill()  # use radio.terminate() for a milder version
    radio = subprocess.Popen(["radio", station])

旁注:我将整个.sh脚本转换为Python,以便更快地执行和更轻松地控制,而不会出现孤立进程的危险。

现在,问题是subprocess.Popen()subprocess.Popen.kill()都是阻塞方法,所以在分别启动和终止进程之前,函数不会返回。您可以尝试使用“即发即弃”线程,但最终会运行大量线程等待轮到您(此外您必须处理排队),因此您需要决定应该发生什么当用户发出太多命令时 - 在进程启动/终止之前禁用其控件可能是最好的,否则您的应用将代表用户进行假设。

这纯粹取决于你如何布置你的控件和应用程序GUI,所以我建议的唯一其他选择(除了排队命令)只是忽略对底层无线电的调用,无论是调整还是杀死,虽然它已经开始/被终止。类似的东西:

import subprocess
import threading

radio = None  # a reference to radio
radio_lock = threading.Lock()  # a mutex for the radio control

def control_radio(command, *args):
    global radio
    if radio_lock.acquire(False):  # make sure other threads silently fail
        if radio and radio.poll() is None:  # kill the radio process if running
            radio.kill()  # use radio.terminate() for a milder version
        if command == "tune":  # start the new radio process if requested
            radio = subprocess.Popen(["radio", args[0]])
        radio_lock.release()  # release the lock

def kill_radio(obj):
    threading.Thread(target=control_radio, args=("kill", )).start()

def tune_radio(obj, station):
    threading.Thread(target=control_radio, args=("tune", station)).start()

我仍然会阻止这样的使用,因为如果你强行杀死你的应用程序或者你不等待执行无线电终止,它可能最终会出现一个oprhaned进程。您只想在退出应用程序时添加阻止if radio and radio.poll() is None: radio.terminate()调用,以确保生成的子进程被终止。