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