[英]Python Tkinter application causes fork() / exec() error on Mac OS X
我正在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问题,例如: Mac OS X上的Python多处理错误
但是我还无法弄清楚在我的特定情况下到底该解决什么问题。
关于如何使其在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()
我遇到了同样的问题,请检查以下一项:
https://stackoverflow.com/a/19082049/1956309
这使您可以跟踪此处的Tkinter错误:
http://bugs.python.org/issue5527#msg195480
对我有用的解决方案(带有Python 2.7的Mac OS 10.8)是重新排列代码,以便在调用任何类型的process.start()之后放置“ import Tkinter”。
看起来像:
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()
Pd:感谢JW Lim向我展示了良好的举止!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.