Python PyQT:如何从工作线程调用GUI函数? [英] Python PyQT: How to call a GUI function from a worker thread?

查看:856
本文介绍了Python PyQT:如何从工作线程调用GUI函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个pyqt gui,并调用一个漫长的进程(ffmpeg),我把一个单独的线程不阻止gui。然后,当一个更长命令列表的命令完成时,我想更新一个进度条。问题是,我不能在工作线程中调用gui线程中的一个函数。所以我可以在工作线程中运行一个代码,但是当我用while循环更新进度条并读取代码值时,gui将再次被阻止。我该如何解决呢我目前使用python线程,而不是Qthread。
Thx为任何帮助!

 导入线程,pexpect 

self.cmd_list = [ 'ffmpeg -i file outfile','等等]

self.stop_proc = False
self.executeCMD()

def spawn_ffmpeg_cmd(self)
for cmd in self.cmd_list:
if self.stop_proc == False:
thread = pexpect.spawn(cmd)
打印\\\
started:%s%cmd
cpl = thread.compile_pattern_list([pexpect.EOF,frame = * \d + fps = * \d +,'(。+)'])

while True:
i = thread.expect_list(cpl,timeout = None)
如果i == 0:#EOF
打印子进程退出
self.pgticker + = 1
break
elif i == 1:
frame_number_fps = thread.match.group(0)
print frame_number_fps
thread.close
elif i == 2:
pass
self.startButton.setEnabled(True)


def executeCMD(self):
self.startButton.setEnabled (False)
self.pgticker = 0
threading.Thread(target = self.spawn_ffmpeg_cmd,name =_ proc)。start()


def stopprocess (self):
self.stop_proc = True
self.cmd_list = []
os.system('pkill ffmpeg')
self.pgticker = len(self.cmd_list)
self.startButton.setEnabled(True)


def updateProgress(self):
pgfactor = 100 / len(self.cmd_list)
progress = 0.0
progress = pgfactor * int(self.pgticker)
self.progressBar.setProperty(value,progress)


解决方案

简而言之:移动到 QThread 并使用Qt的信号和插槽,在线程之间进行通信的方式。



这个答案提供了一些这样的例子:
https://stackoverflow.com/a/6789205/2319400



在你的情况下,使用上面的SomeObject版本可能如下所示:

  class Worker QtCore.QObject):

madeProgress = QtCore.pyqtSignal([int])
finished = QtCore.pyqtSignal()

def __init __(self,cmdlist)
self.cmdlist = cmdlist

def run(self):
for icmd,cmd in enumerate(self.cmdlist):
#执行你的工作
#processCommand(cmd)

#我们已经取得进展的信号
self.madeProgress.emit(icmd)

#发出完成的信号 - 重新完成
self.finished.emit()

然后将此工作人员移动到 QThread 您创建的实例。
按照链接答案的模式,您可以将 madeProgress 信号
连接到 setValue 进度条的插槽:

  workerThread = QThread()
workerObject = Worker(cmdlist)
workerObject.moveToThread(workerThread)
workerThread.started.connect(workerObject.run)
workerObject.finished.connect(workerThread.quit)

#创建一个具有最小/最大值根据
#你的cmdlist的长度
progressBar = QProgressBar()
progressBar.setRange(0,len(cmdlist))

#连接工作进度信号与进度条
workerObject.madeProgress.connect(progressBar.setValue)

#启动线程(同时启动您的工作)
workerThread.start()


I have a pyqt gui and calling a long process (ffmpeg) which I put on a separate thread to not block the gui. I then want to update a progress bar when one command of a longer list of commands finishes. The problem is, that I can't call a function in the gui thread out of the worker thread. So I let run a ticker in the worker thread, but when I update the progress bar with a while loop and reading the ticker value, the gui gets blocked again. How can I solve this. I used currently python threading and not Qthread. Thx for any help!

import threading, pexpect

self.cmd_list = ['ffmpeg -i file outfile','and so on']

self.stop_proc = False
self.executeCMD()

def spawn_ffmpeg_cmd(self):
    for cmd in self.cmd_list:
        if self.stop_proc == False:
            thread = pexpect.spawn(cmd)
            print "\nstarted: %s" % cmd
            cpl = thread.compile_pattern_list([pexpect.EOF,"frame= *\d+ fps=*\d+",'(.+)'])

            while True:
                i = thread.expect_list(cpl, timeout=None)
                if i == 0: # EOF
                    print "the sub process exited"
                    self.pgticker += 1
                    break
                elif i == 1:
                    frame_number_fps = thread.match.group(0)
                    print frame_number_fps
                    thread.close
                elif i == 2:
                    pass
    self.startButton.setEnabled(True)


def executeCMD(self):
    self.startButton.setEnabled(False)
    self.pgticker = 0
    threading.Thread(target=self.spawn_ffmpeg_cmd, name="_proc").start()


def stopprocess(self):
    self.stop_proc = True
    self.cmd_list = []
    os.system('pkill ffmpeg') 
    self.pgticker = len(self.cmd_list)
    self.startButton.setEnabled(True)


def updateProgress(self):  
    pgfactor = 100 / len(self.cmd_list)
    progress = 0.0
    progress = pgfactor*int(self.pgticker)
    self.progressBar.setProperty("value", progress)

解决方案

In short: Move to QThread and use Qt's signals and slots, they are the preferred way to communicate between threads.

This answer provides some examples how this could look like: https://stackoverflow.com/a/6789205/2319400

In your case, using the "SomeObject" version from the above could look like this:

class Worker(QtCore.QObject):

    madeProgress = QtCore.pyqtSignal([int])
    finished = QtCore.pyqtSignal()

    def __init__(self, cmdlist):
        self.cmdlist = cmdlist

    def run(self):
        for icmd, cmd in enumerate(self.cmdlist):
            # execute your work
            # processCommand(cmd)

            # signal that we've made progress
            self.madeProgress.emit(icmd)

        # emit the finished signal - we're done
        self.finished.emit()

Then move this worker to a QThread instance you create. Following the pattern from the linked answer, you can then connect the madeProgress signal to the setValue slot of a progressbar:

workerThread = QThread()
workerObject = Worker(cmdlist)
workerObject.moveToThread(workerThread)
workerThread.started.connect(workerObject.run)
workerObject.finished.connect(workerThread.quit)

# create a progressbar with min/max according to
# the length of your cmdlist
progressBar = QProgressBar()
progressBar.setRange(0, len(cmdlist))

# connect the worker's progress signal with the progressbar
workerObject.madeProgress.connect(progressBar.setValue)

# start the thread (starting your worker at the same time)
workerThread.start()

这篇关于Python PyQT:如何从工作线程调用GUI函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆