Python Tkinter应用程序在Mac OS X上导致fork()/ exec()错误

时间:2014-01-15 17:10:42

标签: python tkinter multiprocessing

我在Mac OS X(10.7和10.8)上运行python(2.7)Tkinter GUI应用程序。 UI处于一个单独的进程中,该进程使用多处理从主脚本分离出来。然而,当我跑步时,它失败了:

'The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec(). Break on THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY_YOU_MUST_EXEC__() to debug.'

这适用于Windows。

我发现了一个与python相关的错误:http://bugs.python.org/issue8713 但遗憾的是,看起来修复版仅在3.x版本中实现,但我也需要支持2.7。

我知道有关于同一错误的其他一些SO问题,例如:Python multiprocessing bug on Mac OS X

但是我无法弄清楚在我的特定情况下究竟会解决什么问题。

有关如何在Mac上使用它的任何想法吗?

代码如下。创建DriverVisualizer()(在另一个脚本中),初始化另一个进程的UI。

from util import *
from multiprocessing import Process, Pipe
from Tkinter import *
import threading
import Queue
from time import *

class VisualizerUI:
    def __init__(self, conn, count, pixelSize):
        self._conn = conn
        self._master = Tk()
        self._q = Queue.Queue()

        self._count = count
        self._values = []
        self._leds = []

        self._pixelSize = pixelSize
        self._pixelPad = int(pixelSize / 2)
        self._pixelSpace = 4

        #init colors to all black (off)
        for i in range(self._count):
            self._values.append("#000000")

        self.initUI()

        self._thread = threading.Thread(target=self.commThread).start()

    def mainloop(self):
        self._master.mainloop()
        try:
            self._conn.send({"status" : False})
        except:
            pass

    def updateUI(self):
        try:
            for i in range(self._count):
                    self._canvas.itemconfig(self._leds[i], fill=self._values[i])
        except TclError:
            #Looks like the UI closed!
            pass

    def commThread(self):
        data = None
        error = False

        while True:
            #bit of a hack, but need to check occasionaly for window size change
            if self._width != self._master.winfo_width() or self._height != self._master.winfo_height():
                self._width = self._master.winfo_width()
                self._height = self._master.winfo_height()
                self._master.after_idle(self.layoutPixels)

            try:
                data = self._conn.recv()
            except EOFError:
                error = True
                break

            if data["run"]:
                self._values = data["data"]
                self.updateUI()
                self._conn.send({"status" : True})
            else:
                break
        if not error:
            self._conn.send("Killing UI...")
            self._master.destroy()

    def layoutPixels(self):
        self._canvas.config(width=self._width, height=self._height)
        newRow = True
        x_off = self._pixelPad
        y_off = self._pixelPad
        for i in range(self._count):
            if (x_off + (self._pixelSize * 2) + self._pixelSpace + self._pixelPad) > self._width:
                newRow = True
                y_off = y_off + self._pixelPad + self._pixelSize
            if newRow:
                x_off = self._pixelPad
                newRow = False
            else:
                x_off = x_off + self._pixelSize + self._pixelSpace

            self._canvas.coords(self._leds[i], x_off, y_off, x_off + self._pixelSize, y_off + self._pixelSize)

        y = (y_off + self._pixelSize + self._pixelPad)
        if self._height != y:
            self._master.geometry("{0}x{1}".format(self._width, y))
            self._master.update()

    def __CancelCommand(event=None): 
        pass

    def initUI(self):
        m = self._master
        m.protocol('WM_DELETE_WINDOW', self.__CancelCommand)

        m.title("LED Strip Visualizer")
        m.geometry("1400x50")
        m.update()
        self._width = m.winfo_width()
        self._height = m.winfo_height()
        m.minsize(self._width, self._height)

        self._canvas = Canvas(self._master, background="#000000")
        c = self._canvas
        c.pack(side=TOP)

        for i in range(self._count):
            index = c.create_oval(0,0,self._pixelSize,self._pixelSize, fill=self._values[i])
            self._leds.append(index)

        #m.bind("<Configure>", self.resize)

        self.layoutPixels()

def toHexColor(r,g,b):
    return "#{0:02x}{1:02x}{2:02x}".format(r,g,b)

def startUI(conn, count, pixelSize):
        ui = VisualizerUI(conn, count, pixelSize)
        ui.mainloop()

class DriverVisualizer(object):
    """Main driver for Visualizer UI (for testing)"""

    def __init__(self, leds, pixelSize = 15, showCurrent = False):
        self.leds = leds
        self._showCurrent = showCurrent
        if self._showCurrent:
            self._peakCurrent = 0;
        else:
            self._peakCurrent = None

        self._parent_conn, self._child_conn = Pipe()
        p = Process(target=startUI, args=(self._child_conn, self.leds, pixelSize))

        p.start()
        sleep(0.5) # give the UI some time to spin up before throwing data at it

    def __del__(self):
        self._parent_conn.send({"data" : None, "run" : False})
        print self._parent_conn.recv()

    def calcCurrent(self, data):
        c = 0
        for r, g, b in data:
            c = c + int(((r/255.0) * 20.0) + ((g/255.0) * 20.0) + ((b/255.0) * 20.0))
            if c > self._peakCurrent: 
                self._peakCurrent = c

        return c

    #Push new data to strand
    def update(self, data):
        c = None
        if self._showCurrent:
            c = self.calcCurrent(data)
        self._parent_conn.send({"data" : [toHexColor(*(data[x])) for x in range(self.leds)], "run" : True, "c" : c, "peak" : self._peakCurrent})
        resp = self._parent_conn.recv()
        if not resp["status"]:
            error = True
            parent_conn.close()

1 个答案:

答案 0 :(得分:1)

我遇到同样的问题,请检查一下:
https://stackoverflow.com/a/19082049/1956309

这会让你跟踪一个Tkinter错误:
http://bugs.python.org/issue5527#msg195480

为我做的技巧(Mac OS 10.8 with Python 2.7)的解决方案是重新排列代码,以便&#34;导入Tkinter&#34;在您调用任何类型的process.start()

之后放置

看起来像:

import multiprocessing

def cam_loop(the_q):
    while True:
        the_q.put('foo in the queue')

def show_loop(the_q):
    while True:
        from_queue = the_q.get()
        print from_queue

if __name__ == '__main__':
    try:
        the_q = multiprocessing.Queue(1)

        cam_process = multiprocessing.Process(target=cam_loop,args=(the_q, ))
        cam_process.start()

        show_process = multiprocessing.Process(target=show_loop,args=(the_q, ))
        show_process.start()

        import Tkinter as tk  # << Here!
        cam_process.join()
        show_loop.join()

    except KeyboardInterrupt:
        cam_process.terminate()
        show_process.terminate()

P.d:感谢JW Lim向我展示礼貌!