Python 在不阻塞主循环的线程中运行bash脚本

Python 在不阻塞主循环的线程中运行bash脚本,python,multithreading,bash,Python,Multithreading,Bash,我有一个python程序,当我在线程中运行bash脚本并显示一些消息时,我希望progressbar被激活 当我单击一个按钮时,线程开始启动脚本,但是消息和progressbar只有在线程完成时才会响应,只显示最后一条消息 在阅读中,我明白我正在阻塞主循环,但我不知道如何解决这个问题 我的程序的简化代码,问题是当调用“on_fixme_button_pressed”时: 如果使用subprocess.Popen则使用.wait()等待返回的处理程序。您可以使用.poll()调查流程是否已完成[以

我有一个python程序,当我在线程中运行bash脚本并显示一些消息时,我希望progressbar被激活

当我单击一个按钮时,线程开始启动脚本,但是消息和progressbar只有在线程完成时才会响应,只显示最后一条消息

在阅读中,我明白我正在阻塞主循环,但我不知道如何解决这个问题

我的程序的简化代码,问题是当调用“on_fixme_button_pressed”时:


如果使用
subprocess.Popen
则使用
.wait()
等待返回的处理程序。您可以使用
.poll()
调查流程是否已完成[以及退出代码],或者使用
.communicate()
与流程进行交互(标准输入/输出)

文档可在此处找到:


你试过什么

如果使用
subprocess.Popen
则使用
.wait()
等待返回的处理程序。您可以使用
.poll()
调查流程是否已完成[以及退出代码],或者使用
.communicate()
与流程进行交互(标准输入/输出)

文档可在此处找到:


你试过什么

我真的不确定它是否有帮助,但我建议您看看Pexpect模块:


据我所知,它是一个更为复杂的系统调用“启动器”。

我真的不确定它是否会有帮助,但我建议您看看Pexpect模块:


据我所知,它是一个更复杂的系统调用“启动器”。

最终解决了使用GObject.timeout\u add来更新消息状态的问题,使用线程类中的一个变量来知道进程何时完成,并使用
.communicate()
由@johan lundberg建议的选项

最终解决了使用GObject.timeout\u add来更新消息的状态,使用线程类中的一个变量来知道进程何时完成,还使用@johan lundberg建议的
.communicate()
选项至少有两个问题:

  • target=self.run\u script()
    不正确:它会立即运行函数。删除
    ()
    ,将方法作为参数传递,而不是调用它
  • 您可以调用GUI方法(在
    self.event\u info.set\u label
    上),而不受后台线程的保护。最简单的修复方法是使用
    idle\u add()
    调用
    set\u label

    使用
    idle\u add
    是安全的,因为模块顶部有
    Gobject.init\u threads()

另见


(gtk2)

至少有两个问题:

  • target=self.run\u script()
    不正确:它会立即运行函数。删除
    ()
    ,将方法作为参数传递,而不是调用它
  • 您可以调用GUI方法(在
    self.event\u info.set\u label
    上),而不受后台线程的保护。最简单的修复方法是使用
    idle\u add()
    调用
    set\u label

    使用
    idle\u add
    是安全的,因为模块顶部有
    Gobject.init\u threads()

另见


(gtk2)

我已经编辑了这个问题,并将我的程序的简化代码放在您可以看到单击的“修复”按钮上,我将运行脚本称为线程。我还得看看你所指的.wait()东西我已经编辑了这个问题,并把我的程序的简化代码放在你可以看到点击的“修复”按钮上,我把运行脚本称为线程。我还要看看你指出的.wait()不是一个常规函数,而是一个构造函数。如果要对打开的进程执行任何操作,则需要将其返回值存储在一个变量中,您可以与该变量交互并调用其方法(例如
wait
)。是的,使用
.communicate()
似乎只需编写
p=Popen(command)
,然后编写
p.communicate()
,但由于某些原因,直到命令结束,弹出窗口才会显示“我的工作完成了”消息。我建议发布更新的代码。以下是更新的代码()。我删除了弹出窗口,只在主窗口上工作。当一个线程运行时,该代码应该对progressbar进行脉冲处理,但它会一直冻结,直到线程完成。我想我理解这是因为主循环被阻塞了,但我不知道如何解决它。主要问题在于点击的on_fixme_按钮调用中,如果您不希望主进程阻塞,则不应该调用
communicate
方法,因为根据定义,它会阻塞,直到进程完成(或者,如文档所述:“等待进程终止”)。您需要做的是阅读文档。或者,请阅读您已经回答的答案:Johan Lundberg已经告诉您,要调查流程是否已完成,请使用
poll
方法。
Popen
不是常规函数,而是构造函数。如果要对打开的进程执行任何操作,则需要将其返回值存储在一个变量中,您可以与该变量交互并调用其方法(例如
wait
)。是的,使用
.communicate()
似乎只需编写
p=Popen(command)
,然后编写
p.communicate()
,但由于某些原因,直到命令结束,弹出窗口才会显示“我的工作完成了”消息。我建议发布更新的代码。以下是更新的代码()。我删除了弹出窗口,只在主窗口上工作。当一个线程运行时,该代码应该对progressbar进行脉冲处理,但它会一直冻结,直到线程完成。我想我理解这是因为主循环被阻塞了,但我不知道如何解决它。主要问题是在“打开修复”按钮中单击
from gi.repository import Gtk, Gdk, Pango, GObject, GLib
import os, sys
import xml.etree.ElementTree as etree
from urllib.request import urlopen
from subprocess import Popen
import threading


UI_FILE = "remendo_gtk.ui"
#UI_FILE = "/usr/local/share/remendo_gtk/ui/remendo_gtk.ui"

GObject.threads_init()

class GUI:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file(UI_FILE)
        self.builder.connect_signals(self)

        self.window = self.builder.get_object('remendo')
        self.event_treeview = self.builder.get_object('event_treeview')
        self.event_info = self.builder.get_object('event_info')
        self.progressbar = self.builder.get_object('progressbar')
        self.progressbar_lock = threading.Lock()

        self.selected_event = ''
        self.url_script = ''
        self.local_script = ''
        self.window.connect("destroy", lambda _: Gtk.main_quit())
        self.set_event_list()

        self.window.show_all()

    def pulse_progressbar(self):
        if threading.active_count() > 1:
            self.progressbar.pulse()
            return True

        self.progressbar.set_fraction(0.0)
        self.progressbar_lock.release()
        return False

    def on_fixme_button_clicked(self, button):
        self.event_info.set_label("Fixing now...")
        script = threading.Thread(target=self.run_script(), args=(self,))
        script.start()

        if self.progressbar_lock.acquire(False):
            GLib.timeout_add(250, self.pulse_progressbar)

    def run_script(self):
        try:
            self.local_script = '/tmp/%s.sh' % self.selected_event.replace(" ", "_")
            script = urlopen(self.url_script)
            localscript = open(self.local_script, 'wb')
            localscript.write(script.read())
            localscript.close()

            command = ['bash', self.local_script]
            p = Popen(command)
            p.communicate()

            #self.event_solved()
            self.event_info.set_label("My work is done. Good day!")
        except:
            self.event_info.set_label("Script failed: %s" % self.local_script)

        return False


def main():
    app = GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())
Gobject.idle_add(self.event_info.set_label, msg) # in a background thread