我的代码已成功将sys.stdout
打印到GUI Tkinter文本小部件。我从我的程序中得到一些奇怪的行为,我无法弄清楚为什么;我需要帮助排除故障。
最大的问题是sys.stderr
没有打印到我的GUI或我的IDE结果。这可能是我sys.stdout
如何进入我的GUI的结果,但我不足以诊断是否属于这种情况。我遵循了这段代码:Python multiprocessing redirect stdout of a child process to a Tkinter Text。它也可能与我的代码显然运行两次的原因有关。
我的主程序运行GUI并使用multiprocessing
从我制作的导入模块运行单独的脚本。这个脚本的第一件事就是检查输入中的错误。如果出现错误,我会给出一个自定义异常来结束进程而不关闭Python。该异常使用tkMessageBox
生成一个窗口,提醒用户程序需要注意。它用于打印回溯到GUI,但现在我不再重定向sys.stdout
。
无论如何,当我运行脚本并且它到达该异常时,它会打开两个窗口。一个看起来像一个Windows生成的操作系统消息框,里面有我编写的消息。另一个是一个空的白色窗口,标题是#34; Tk"当我点击" OK"在另一个消息框上。有时Python在碰到" OK"后崩溃了。 GUI不会崩溃,因此它必须是崩溃的子进程。 编辑:第二个窗口问题已解决,我的代码已更新以反映出来。但是,当我点击" OK"时,子进程仍然崩溃。在消息框上仍然加载模块两次(显然)。
我在导入的模块的开头也有一个print语句。在创建GUI之前导入模块并在我的IDE中显示。当我使用multiprocessing
在我的模块中启动该函数时,print语句再次显示在IDE中。该声明不是我正在运行的功能的一部分。
我没有计算机科学的背景,所以它可能是我看不到的完全明显的东西。
请看一下,看看会发生什么。我已经简化了我的代码,它应该适合你。我正在运行Windows 8.1和Python 2.7.9。
GUI /主循环:
#### _______________IMPORT MODULES_________________###
import Tkinter
from multiprocessing.queues import Queue
from multiprocessing import Process
import sys, traceback
from threading import Thread
import qBMPchugger
###_____Create Text Catcher ______###
def text_catcher(text_widget, queue):
while True:
text_widget.insert("end", queue.get())
app.update_idletasks()
class StdoutQueue(Queue):
def __init__(self, *args, **kwargs):
Queue.__init__(self, *args, **kwargs)
def write(self, msg):
self.put(msg)
def flush(self):
sys.__stdout__.flush()
def ErrorGrab(type, value, traceback):
exception_string = "".join(traceback.format_exception(type, value, traceback))
# do whatever you want from here
print exception_string
print "testing my excepthook"
###____________Widget__________________###
class InputBox(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
# Build Frame
self.OK = Tkinter.Frame(self, padx=3, pady=3)
self.OK.grid(column=0, row=2, columnspan=3, sticky="EW")
self.printFrame = Tkinter.LabelFrame(self, borderwidth=3, relief="ridge", padx=3, pady=3, text="Results")
self.printFrame.grid(column=0, row=3, columnspan=4, sticky="EW")
# Build "run" button
self.OKbutton = Tkinter.Button(self.OK, text=u"OK", command=self.OKgo, anchor="e")
self.OKbutton.pack(side="right")
# Build output viewer
self.view = Tkinter.Text(self.printFrame)
self.view.grid(column=0, row=0, columnspan=3, sticky="EW")
self.scroll = Tkinter.Scrollbar(self.printFrame, orient=Tkinter.VERTICAL)
self.scroll.config(command=self.view.yview)
self.view.config(yscrollcommand=self.scroll.set)
self.scroll.grid(column=4, row=0, sticky="SN")
def OKgo(self):
sys.stdout = q
sys.excepthook = ErrorGrab
monitor = Thread(target=text_catcher, args=(self.view, q))
monitor.daemon = True
monitor.start()
self.view.delete(1.0, "end")
self.update_idletasks()
print("Loading user-specified inputs...")
WTin = "D:/Python/Inputs/wtdepth1.aux"
print("LoadingModule")
self.update_idletasks()
import qBMPchugger
self.update_idletasks()
# Starts the child process, qBMPchugger.BMPcode
inarg = (q, WTin)
p = Process(target=qBMPchugger.BMPcode, args=inarg)
p.start()
p.join()
print("ended")
if __name__ == "__main__":
app = InputBox(None)
app.title("File Inputs and Program Settings")
# Start the text monitor
q = StdoutQueue()
monitor = Thread(target=text_catcher, args=(app.view, q))
monitor.daemon = True
monitor.start()
app.mainloop()
模块" qBMPchugger":
#### _______________INITIALIZE_________________###
print("Creating the program environment and importing modules...")
import os
import sys
import tkMessageBox
import Tkinter
# Create user-defined exceptions
class BadInput(Exception):
pass
def BMPcode(q, WTin):
### _________________VERIFY INPUTS________________###
boxRoot = Tkinter.Tk()
boxRoot.withdraw()
sys.stdout = q
sys.stderr = q
print("Checking out the Spatial Analyst extension from GIS...")
# Check out extension and overwrite outputs
print("Checking validity of specified inputs...")
# Check that the provided file paths are valid
inputs = [WTin]
for i in inputs:
if os.path.exists(i):
pass
else:
message = "\nInvalid file path: {}\nCorrect the path name and try again."
tkMessageBox.showerror("Invalid Path", message.format(i))
raise BadInput(message.format(i))
print("Success!")
编辑:我从模块中删除了一个使用arcpy的部分;它不应该是错误的来源。
更新1:我尝试将我的import qBMPchugger
语句从我的GUI代码的开头移动到我的函数OKgo
。结果是print("Creating the program environment and importing modules...")
在首次加载时在GUI中打印一次,然后在启动第二个过程时再次在IDE中打印。如果我在不关闭GUI的情况下再次运行OKgo
,它只会在IDE中打印。
然后我关闭了GUI并重新启动程序以检查打印语句的打印位置,然后出现错误;它在IDE中打印。我不认为我在运行之间更改了代码,这很有趣。我猜我现在正在调查这个追溯。我更新了我的代码,以反映import qBMPchugger
。
Exception in thread Thread-1:
Traceback (most recent call last):
File "D:\Python\Python27\lib\threading.py", line 810, in __bootstrap_inner
self.run()
File "D:\Python\Python27\lib\threading.py", line 763, in run
self.__target(*self.__args, **self.__kwargs)
File "D:/Python/Pycharm/Projects/BMP_Tool/GUI-thread.py", line 13, in text_catcher
app.update_idletasks()
File "D:\Python\Python27\lib\lib-tk\Tkinter.py", line 1020, in update_idletasks
self.tk.call('update', 'idletasks')
TclError: bad option ".242660736.242702216,relief": must be idletasks
更新2:我一直在忙着sys.stdout
和sys.stderr
。我在主程序中删除了stderr和stdout的任何重定向,并在子进程中执行了sys.stderr = q
。我收到的异常看起来像我编程的异常的一半;它缺少消息和异常的名称。我在没有退出GUI的情况下再次运行该功能,它给出了相同的异常,但是它说了"过程2",再次用"过程3"。关闭GUI并再次运行它给了我完整的例外,但再次运行它给了我缩短的版本。
Process Process-1:
Traceback (most recent call last):
File "D:\Python\Python27\lib\multiprocessing\process.py", line 258, in _bootstrap
self.run()
File "D:\Python\Python27\lib\multiprocessing\process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "D:\Python\Pycharm\Projects\BMP_Tool\qBMPchugger.py", line 44, in BMPcode
更新3:看起来孩子只能在GUI中打印stdout或stderr,而不是两者都打印,并且它优先选择stdout。