为什么我的代码运行两次,崩溃,而不是打印stderr?多输出,输出重定向到GUI

时间:2015-08-07 18:12:07

标签: python error-handling tkinter multiprocessing python-multiprocessing

我的代码已成功将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.stdoutsys.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。

0 个答案:

没有答案