本文介绍了Python Tkinter应用程序在Mac OS X上导致fork()/exec()错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Mac OS X(10.7和10.8)上运行python(2.7)Tkinter GUI应用程序. UI处于单独的过程中,可以使用多重处理从主脚本中分叉出来.但是,当我运行时,它失败并显示:

I'm running a python (2.7) Tkinter GUI application on Mac OS X (10.7 and 10.8). The UI is in a separate process that gets forked off from the main script using multiprocessing. When I run, however, it fails with:

'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上很好用.

This works on Windows just fine.

我发现与它有关的一个错误已用python记录: http://bugs.python.org/问题8713 但是可悲的是,该修复程序似乎仅在3.x版本中实现,但我也需要支持2.7.

I've found a bug related to it logged with python: http://bugs.python.org/issue8713But sadly, it looks like the fix was only implemented in 3.x versions but I need to support 2.7 as well.

我知道关于同一错误,还有其他一些SO问题,例如: Mac OS X上的Python多重处理错误

I know there's a few other SO questions regarding the same error, like this: Python multiprocessing bug on Mac OS X

但是我无法弄清楚在我的特殊情况下到底该解决什么问题.

But I've not been able to figure out what would actually fix the issue in my particular case.

关于如何在Mac上运行该软件的任何想法?

Any thoughts on how to get this to work on Mac?

代码在下面. (在另一个脚本中)DriverVisualizer()被创建,它将在另一个进程上初始化UI.

Code is below. DriverVisualizer() is created (in another script), which initializes the UI on another process.

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()

推荐答案

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

I run into same problem, check this one:
https://stackoverflow.com/a/19082049/1956309

您可以在这里跟踪Tkinter的错误:
http://bugs.python.org/issue5527#msg195480

Which puts you in track to a Tkinter bug here:
http://bugs.python.org/issue5527#msg195480

对我有用的解决方案(带有Python 2.7的Mac OS 10.8)是重新安排代码,以便在调用任何类型的process.start()之后都放置"import Tkinter"

The solution that did the trick for me (Mac OS 10.8 with Python 2.7) is to rearrange the code so the "import Tkinter" is placed after you have invoked any kind of 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 向我展示了良好的举止!

P.d: Thanks JW Lim for showing me good manners!

这篇关于Python Tkinter应用程序在Mac OS X上导致fork()/exec()错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 05:21