后台进程锁定GUI Python

Dea*_*ean 3 python multiprocessing python-2.7

我有一个背景Process(使用Processfrom multiprocessing)将对象推送到我的GUI,但是该后台进程不断锁定GUI,并且从不显示要推送的更改。对象被放入我的队列中,但是我的GUI中的更新方法没有被定期调用。我该如何做才能更定期地更新GUI?我的GUI用Tkinter编写。

我的后台进程中有一个无限循环,因为我总是需要不断读取USB端口以获取更多数据,因此基本上我的代码如下所示:

TracerAccess.py

import usb
from types import *
import sys
from multiprocessing import Process, Queue
import time


__idVendor__ = 0xFFFF
__idProduct__ = 0xFFFF

END_POINT = 0x82

def __printHEXList__(list):
    print ' '.join('%02x' % b for b in list)

def checkDeviceConnected():
    dev = usb.core.find(idVendor=__idVendor__, idProduct=__idProduct__)
    if dev is None:
        return False
    else:
        return True

class LowLevelAccess():
    def __init__(self):
        self.rawIn = []
        self.tracer = usb.core.find(idVendor=__idVendor__, idProduct=__idProduct__)
        if self.tracer is None:
            raise ValueError("Device not connected")
        self.tracer.set_configuration()

    def readUSB(self):
        """
        This method reads the USB data from the simtracer.
        """
        try:
            tmp = self.tracer.read(END_POINT, 10000,None, 100000).tolist()
            while(self.checkForEmptyData(tmp)):
                tmp = self.tracer.read(END_POINT, 10000,None, 100000).tolist()
            self.rawIn = tmp
        except:
            time.sleep(1)
            self.readUSB()

    def checkForEmptyData(self, raw):
        if(len(raw) == 10 or raw[10] is 0x60 or len(raw) == 11):
            return True
        else:
            return False

class DataAbstraction:
    def __init__(self, queue):
        self.queue = queue
        self.lowLevel = LowLevelAccess()
    def readInput(self):
        while True:
            self.lowLevel.readUSB()
            raw = self.lowLevel.rawIn
            self.queue.put(raw)
Run Code Online (Sandbox Code Playgroud)

ui.py

from Tkinter import *
import time
import TracerAccess as io
from multiprocessing import Process, Queue
from Queue import Empty
from math import ceil

def findNumberOfLines(message):
    lines = message.split("\n")
    return len(lines)


class Application(Frame):
    def addTextToRaw(self, text, changeColour=False, numberOfLines=0):
        self.rawText.config(state=NORMAL)
        if changeColour is True:
            self.rawText.insert(END,text, 'oddLine')
        else:
            self.rawText.insert(END,text)
        self.rawText.config(state=DISABLED)

    def updateRaw(self, text):
        if(self.numberOfData() % 2 is not 0):
            self.addTextToRaw(text, True)
        else:
            self.addTextToRaw(text)
    def startTrace(self):
        self.dataAbstraction = io.DataAbstraction(self.queue)
        self.splitProc = Process(target=self.dataAbstraction.readInput())
        self.stopButton.config(state="normal")
        self.startButton.config(state="disabled")
        self.splitProc.start()

    def pollQueue(self):
        try:
            data = self.queue.get(0)
            self.dataReturned.append(data)
            self.updateRaw(str(data).upper())
            self.rawText.tag_config("oddLine", background="#F3F6FA")
        except Empty:
            pass
        finally:
            try:
                if(self.splitProc.is_alive() is False):
                    self.stopButton.config(state="disabled")
                    self.startButton.config(state="normal")
            except AttributeError:
                pass
            self.master.after(10, self.pollQueue)

    def stopTrace(self):
        self.splitProc.join()
        self.stopButton.config(state="disabled")
        self.startButton.config(state="normal")

    def createWidgets(self):
        self.startButton = Button(self)
        self.startButton["text"] = "Start"
        self.startButton["command"] = self.startTrace
        self.startButton.grid(row = 0, column=0)

        self.stopButton = Button(self)
        self.stopButton["text"] = "Stop"
        self.stopButton["command"] = self.stopTrace
        self.stopButton.config(state="disabled")
        self.stopButton.grid(row = 0, column=1)

        self.rawText = Text(self, state=DISABLED, width=82)
        self.rawText.grid(row=1, columnspan=4)


    def __init__(self, master):
        Frame.__init__(self, master)
        self.queue = Queue()
        self.master.after(10, self.pollQueue)
        self.pack()
        self.dataReturned = []
        self.createWidgets()

    def numberOfData(self):
        return len(self.dataReturned)
Run Code Online (Sandbox Code Playgroud)

主程序

import ui as ui

if __name__ == "__main__":
    root = Tk()
    root.columnconfigure(0,weight=1)
    app = ui.Application(root)
    app.mainloop()
Run Code Online (Sandbox Code Playgroud)

因此,后台线程永远不会结束,但是当我结束该过程时,UI将在关闭之前开始显示。这个问题可能是由于我为TracerAccess.py模块设计而出现的,因为我是在直接使用Java格式并没有为python设计的经验之后开发此程序的。

Mar*_*oij 5

是什么multiprocess.Process呢,在内部,是一个真正的fork(),有效地复制你的过程。您也许可以将其可视化为:

                  / ["background" process] -------------\
[main process] --+                                       +-- [main process]
                  \ [main process continued] -----------/
Run Code Online (Sandbox Code Playgroud)

p.join()尝试将两个过程“结合”回一个。这实际上意味着:等待直到后台进程完成。这是该.join()函数的实际(完整)代码:

def join(self, timeout=None):
    '''
    Wait until child process terminates
    '''
    assert self._parent_pid == os.getpid(), 'can only join a child process'
    assert self._popen is not None, 'can only join a started process'
    res = self._popen.wait(timeout)
    if res is not None:
        _current_process._children.discard(self)
Run Code Online (Sandbox Code Playgroud)

注意如何self._popen.wait调用。

这显然不是您想要的。

在TKinter的上下文中,您可能想要的是使用tk事件循环,例如这样的代码(Python 3,但该概念也适用于Python 2)

from multiprocessing import Process, Queue
import time, tkinter, queue, random, sys

class Test:
    def __init__(self, root):
        self.root = root
        txt = tkinter.Text(root)
        txt.pack()

        self.q = Queue()

        p = Process(target=self.bg)
        p.start()

        self.checkqueue()
        print('__init__ done', end=' ')

    def bg(self):
        print('Starting bg loop', end=' ')
        n = 42
        while True:
            # Burn some CPU cycles
            (int(random.random() * 199999)) ** (int(random.random() * 1999999))
            n += 1
            self.q.put(n)
            print('bg task finished', end=' ')

    def checkqueue(self):
        try:
            print(self.q.get_nowait(), end=' ')
        except queue.Empty:
            print('Queue empty', end=' ')

        sys.stdout.flush()

        # Run myself again after 1 second
        self.root.after(1000, self.checkqueue)


root = tkinter.Tk()
Test(root)
root.mainloop()
Run Code Online (Sandbox Code Playgroud)

您无需调用.join(),而是使用.after()方法,该方法将函数调度为在n几微秒后运行(如果您曾经使用过Javascript,请考虑一下setTimeout())来读取队列。

根据您的实际内容bg()的功能,你可能不会需要事件multiprocesing在所有,只是调度函数.after() 可能是不够的。

另请参阅:http : //tkinter.unpythonic.net/wiki/UsingTheEventLoop