当前位置: 代码迷 >> python >> 通过使用Tkinter中的按钮终止线程
  详细解决方案

通过使用Tkinter中的按钮终止线程

热度:114   发布时间:2023-07-16 10:02:27.0

在我的GUI代码中,我尝试通过单击一个按钮同时运行loop1和loop2。 因此,我使用Thread实现了这一点。 但是我也试图通过单击另一个按钮来停止它,但失败了。 在搜索stackoverflow之后,我发现没有直接杀死Thread 这是代码的一部分:

def loop1():
    while True:
        call (["raspivid -n -op 150 -w 640 -h 480 -b 2666666.67 -t 5000 -o test.mp4"],shell=True)
        call (["raspivid -n -op 150 -w 640 -h 480 -b 2666666.67 -t 5000 -o test1.mp4"],shell=True)

def loop2():
    while True:
        call (["arecord -D plughw:1 --duration=5 -f cd -vv rectest.wav"],shell=True)
        call (["arecord -D plughw:1 --duration=5 -f cd -vv rectest1.wav"],shell=True)

def combine():
    Thread(target = loop1).start()
    Thread(target = loop2).start()

def stop():
    Thread(target = loop1).terminate()
    Thread(target = loop2).terminate()

我试图使用这两个按钮来控制它。

btn1 = Button(tk, text="Start Recording", width=16, height=5, command=combine)
btn1.grid(row=2,column=0)
btn2 = Button(tk, text="Stop Recording", width=16, height=5, command=stop)
btn2.grid(row=3,column=0)

我希望loop1和loop2可以停止button2。 显然, Thread没有terminate 所以我使用了另一种方法Process 这是代码:

from subprocess import call
from multiprocessing import Process
def loop1():
    while True:
        call (["raspivid -n -op 150 -w 640 -h 480 -b 2666666.67 -t 5000 -o test.mp4"],shell=True)
        call (["raspivid -n -op 150 -w 640 -h 480 -b 2666666.67 -t 5000 -o test1.mp4"],shell=True)
def loop2():
    while True:
        call (["arecord -D plughw:1 --duration=5 -f cd -vv rectest.wav"],shell=True)
        call (["arecord -D plughw:1 --duration=5 -f cd -vv rectest1.wav"],shell=True)
if __name__ == '__main__':
    Process(target = loop1).start()
    Process(target = loop2).start()

但是该程序在我运行后立即完成。 我知道在Processterminate函数。 但是我不知道如何使用它。

潜在的解决方案将使用Event 同样,制作GUI时的一个很好的经验法则是使用对象。

from threading import Thread,Event
from subprocess import call

class Controller(object):
    def __init__(self):
        self.thread1 = None
        self.thread2 = None
        self.stop_threads = Event()

    def loop1(self):
        while not self.stop_threads.is_set():
            call (["raspivid -n -op 150 -w 640 -h 480 -b 2666666.67 -t 5000 -o test.mp4"],shell=True)
            call (["raspivid -n -op 150 -w 640 -h 480 -b 2666666.67 -t 5000 -o test1.mp4"],shell=True)

    def loop2(self):
        while not self.stop_threads.is_set():
            call (["arecord -D plughw:1 --duration=5 -f cd -vv rectest.wav"],shell=True)
            call (["arecord -D plughw:1 --duration=5 -f cd -vv rectest1.wav"],shell=True)

    def combine(self):
        self.stop_threads.clear()
        self.thread1 = Thread(target = self.loop1)
        self.thread2 = Thread(target = self.loop2)
        self.thread1.start()
        self.thread2.start()

    def stop(self):
        self.stop_threads.set()
        self.thread1.join()
        self.thread2.join()
        self.thread1 = None
        self.thread2 = None

这样,您的按钮调用将变为:

control = Controller()
btn1 = Button(tk, text="Start Recording", width=16, height=5, command=control.combine)
btn1.grid(row=2,column=0)
btn2 = Button(tk, text="Stop Recording", width=16, height=5, command=control.stop)
btn2.grid(row=3,column=0)

在启动其他线程之前,请创建一个threading.Event对象。 让我们将其命名为stop

在线程函数的while循环中,测试是否为not stop.is_set()

在停止按钮的事件处理程序中,调用stop.set()


但是,由于您仅启动其他进程,因此您实际上并不需要线程。 您可以使用subprocess.Popen对象启动录制过程。

下面是一个如何执行此操作的示例,该脚本使用多个子流程并行转换多个视频。 在您的情况下,由于您使用的是GUI工具包,因此可以使用计时器定期调用manageprocs()函数。

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# $Date: 2014-02-15 14:44:31 +0100 $
#
# To the extent possible under law, Roland Smith has waived all copyright and
# related or neighboring rights to vid2mkv.py. This work is published from the
# Netherlands. See http://creativecommons.org/publicdomain/zero/1.0/

"""Convert all video files given on the command line to Theora/Vorbis streams
in a Matroska container."""

from __future__ import print_function, division

__version__ = '$Revision: a42ef58 $'[11:-2]

import os
import sys
import subprocess
from multiprocessing import cpu_count
from time import sleep


def warn(s):
    """Print a warning message.

    :param s: Message string
    """
    s = ' '.join(['Warning:', s])
    print(s, file=sys.stderr)


def checkfor(args, rv=0):
    """Make sure that a program necessary for using this script is
    available.

    :param args: String or list of strings of commands. A single string may
    not contain spaces.
    :param rv: Expected return value from evoking the command.
    """
    if isinstance(args, str):
        if ' ' in args:
            raise ValueError('no spaces in single command allowed')
        args = [args]
    try:
        with open(os.devnull, 'w') as bb:
            rc = subprocess.call(args, stdout=bb, stderr=bb)
        if rc != rv:
            raise OSError
    except OSError as oops:
        outs = "Required program '{}' not found: {}."
        print(outs.format(args[0], oops.strerror))
        sys.exit(1)


def startencoder(fname):
    """Use ffmpeg to convert a video file to Theora/Vorbis
    streams in a Matroska container.

    :param fname: Name of the file to convert.
    :returns: a 3-tuple of a Process, input path and output path
    """
    basename, ext = os.path.splitext(fname)
    known = ['.mp4', '.avi', '.wmv', '.flv', '.mpg', '.mpeg', '.mov', '.ogv']
    if ext.lower() not in known:
        warn("File {} has unknown extension, ignoring it.".format(fname))
        return (None, fname, None)
    ofn = basename + '.mkv'
    args = ['ffmpeg', '-i', fname, '-c:v', 'libtheora', '-q:v', '6', '-c:a',
            'libvorbis', '-q:a', '3', '-sn', ofn]
    with open(os.devnull, 'w') as bitbucket:
        try:
            p = subprocess.Popen(args, stdout=bitbucket, stderr=bitbucket)
            print("Conversion of {} to {} started.".format(fname, ofn))
        except:
            warn("Starting conversion of {} failed.".format(fname))
    return (p, fname, ofn)


def manageprocs(proclist):
    """Check a list of subprocesses tuples for processes that have ended and
    remove them from the list.

    :param proclist: a list of (process, input filename, output filename)
    tuples.
    """
    print('# of conversions running: {}\r'.format(len(proclist)), end='')
    sys.stdout.flush()
    for p in proclist:
        pr, ifn, ofn = p
        if pr is None:
            proclist.remove(p)
        elif pr.poll() is not None:
            print('Conversion of {} to {} finished.'.format(ifn, ofn))
            proclist.remove(p)
    sleep(0.5)


def main(argv):
    """Main program.

    :param argv: command line arguments
    """
    if len(argv) == 1:
        binary = os.path.basename(argv[0])
        print("{} version {}".format(binary, __version__), file=sys.stderr)
        print("Usage: {} [file ...]".format(binary), file=sys.stderr)
        sys.exit(0)
    checkfor(['ffmpeg', '-version'])
    avis = argv[1:]
    procs = []
    maxprocs = cpu_count()
    for ifile in avis:
        while len(procs) == maxprocs:
            manageprocs(procs)
        procs.append(startencoder(ifile))
    while len(procs) > 0:
        manageprocs(procs)


if __name__ == '__main__':
    main(sys.argv)
  相关解决方案