0

I am asking these questions together because I feel like they go together. The way to do one effects how I have to do the other. I am a computer science/programming/python novice.

I have a very long, complicated script that doesn't contain any user-defined functions. It takes the inputs and manipulates them to form an output. I have built a GUI with Tkinter for the user to input data. I want to run my script within my GUI mainloop and have the script print status updates and errors to the GUI.

Printing errors to GUI: I have searched and searched for a possible solution but I have not managed to crack this.

Using a subprocess looked the most promising, but I believe the subprocess must be a .exe file in order for that to work. I have only a Python script and do not know how to convert something into an executable (but I will need to learn soon anyway). As for the second response on that page with the sys module, I just don't understand what's going on in the "run_script" function to apply it to my own GUI.

There's this "show error" solution but I am not sure I really understand this, either. Is this saying that I need to put Try...Except... on all of the functions in my subscript and have it spit back a message box? Yick. I would really much prefer the exception get printed like it does in the command result/IDE and exit the command. Would the traceback solution on that page be a one-and-done sort of thing that I state once in my GUI and have it work for all functions in the GUI? Or would I have to call that show_error function every time I carry out a computation?

Incorporating the subscript: I realized that the manner in which I incorporate the subscript will impact how the messages are printed in the GUI. I have a bunch of print statements in my subscript (print "Loading modules...") to let the user know the program is running. Before moving on, here is an example of the code I am working with.

Here is a shortened version of my GUI code:

import Tkinter

###____________Widgets__________________###
class InputBox(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self, parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        # Frame
        self.OK = Tkinter.Frame(self, padx=3, pady=3)
        self.OK.grid(column=2, 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")

        # Approval
        self.OKbutton = Tkinter.Button(self.OK, text=u"OK", command=self.OKgo, anchor="e")
        self.OKbutton.pack(side="right")

        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):
        # Load Inputs
        DEMin = self.demEntryVar.get()
        WTin = self.wtEntryVar.get()
        soil = self.soilEntryVar.get()

Here's a very small piece of my "subscript":

# Convert soils shapefile to raster and assign integer values to HSG.
# A=1, B=2, C=3, 4=D and dual groups A/D=14, B/D=24, C/D=34
# "---" is treated as a D soil
import arcpy
print("Converting dual group soils to single groups...")
SoilUnclass = arcpy.PolygonToRaster_conversion(soil, "HSG", ScratchPath + r"\SoilUnclass", "MAXIMUM_COMBINED_AREA")
SoilClass = arcpy.sa.Reclassify(SoilUnclass, "HSG", arcpy.sa.RemapValue([["A", 1],
                       ["B", 2],
                       ["C", 3],
                       ["D", 4],
                       ["A/D", 14],
                       ["B/D", 24],
                       ["C/D", 34],
                       ["---", 4]]), "NODATA")
SoilClass.save(ScratchPath + r"\HSGras")

If I paste the subscript code directly into the OKgo function, I can get it to print to the Text widget by changing all the print statements to self.view.insert("end", "Converting soils... \n"). As I understand it, pasting my whole subscript into the OKgo function is undesirable because it freezes the GUI and generally is very long. The subscript can take a substantial amount of time to run so this isn't preferred.

I can also break my subscript into numerous user-defined functions and call them individually within the OKgo function with something like

import subscript as ss
ss.soilconvert(soil)

This would be much cleaner and easier for me to diagnose and do the Try...Except... thing with even thought it freezes the GUI. The outputs from one function are needed as inputs for the next function, however, and I don't know how to call those back. I also do not know how I would turn the print statements into something that could be read into the Text widget.

I saw some stuff about execfile too, but I feel like that's even harder to get the output into the Text widget with. :/ I don't know any programming conventions or know what would be easier to debug/update. I need some direction, please. Thanks.

EDIT:

I created a single-thread version using import statments and a multiprocessing version. I did something like this: Python multiprocessing redirect stdout of a child process to a Tkinter Text to trap stdout and print it to the GUI. I am still trying to get the exceptions to work. Redirecting sys.stderr in the same way I redirected sys.stdout is not working but I have yet to try to sys.excepthook thing.

Community
  • 1
  • 1
Rachael
  • 39
  • 3

1 Answers1

0

You can trap all exceptions and display them using sys.excepthook: just replace it with your own function that would open an message box with the error traceback and exit, if you want:

import sys, traceback
def my_except_hook(type, value, traceback):
    exception_string = "".join(traceback.format_exception(type, value, traceback)) 
    # do whatever you want from here
    print exception_string
sys.excepthook = my_except_hook

About starting a subscript from a Python script: the execfile solution can do, you can redirect stdout and stderr by replacing default sys.stdout and sys.stderr, however starting a subprocess using the subprocess module may be more appropriate to avoid corruption of the main Python script.

mguijarr
  • 7,641
  • 6
  • 45
  • 72
  • So if I use `execfile`, there's a chance for my subscript to become corrupted? With `subprocess`, my file needs to be an executable; I want to use it as a Python script. I hear I can use `multiprocess` to use it as a .py rather than a .exe ... I am still trying to understand the `multiprocess` module, though. It looks like I need or should pass variables from my main process to the subprocess using a queue/pipe? I am most concerned about corrupting my scripts and inputs; I want to minimize/zero the chance of corruption of any part of my scripts, even if the implementation is clunky. – Rachael Aug 05 '15 at 14:05
  • Go with the `subprocess` solution, the executable can be Python itself I don't see the problem ; you have the executable path in `sys.executable` so you can start a subprocess with a command line with `sys.executable` as first argument and your Python script as second argument... – mguijarr Aug 05 '15 at 20:37
  • Kind of. I solved them but then ran into a new problem so I'm back to square 1. I couldn't get the excepthook solution to work. :\ I am trying to implement routing sys.stdout/stderr in a threadsafe way so the GUI will update. – Rachael Aug 11 '15 at 14:18