Skip to content

Commit

Permalink
GUI.pyw: allow terminating the running script
Browse files Browse the repository at this point in the history
Fixes #3485
  • Loading branch information
Elvish-Hunter committed Oct 25, 2018
1 parent af29e2d commit 03f24f8
Showing 1 changed file with 101 additions and 43 deletions.
144 changes: 101 additions & 43 deletions data/tools/GUI.pyw
Expand Up @@ -47,37 +47,57 @@ def wrap_elem(line):
return "\"{0}\"".format(line)
return line

def run_tool(tool,queue,command):
"""Runs a maintenance tool with the desired arguments and pushes the output in the supplied queue"""
# set the encoding for the subprocess
# otherwise, with the new Unicode literals used by the wml tools,
# we may get UnicodeDecodeErros
env=os.environ
env['PYTHONIOENCODING']="utf8"
if sys.platform=="win32":
# Windows wants a string, Linux wants a list and Polly wants a cracker
# Windows wants also strings flavoured with double quotes
# since maps return iterators, we must cast them as lists, otherwise join won't work
# not doing this causes an OSError: [WinError 87]
# this doesn't happen on Python 2.7, because here map() returns a list
wrapped_line=list(map(wrap_elem,command))
queue.put_nowait(' '.join(wrapped_line)+"\n")
si=subprocess.STARTUPINFO()
si.dwFlags=subprocess.STARTF_USESHOWWINDOW|subprocess.SW_HIDE # to avoid showing a DOS prompt
try:
output=subprocess.check_output(' '.join(wrapped_line),stderr=subprocess.STDOUT,startupinfo=si,env=env)
queue.put_nowait(str(output, "utf8"))
except subprocess.CalledProcessError as error:
# post the precise message and the remaining output as a tuple
queue.put_nowait((tool,error.returncode,str(error.output, "utf8")))
else: # STARTUPINFO is not available, nor needed, outside of Windows
queue.put_nowait(' '.join(command)+"\n")
try:
output=subprocess.check_output(command,stderr=subprocess.STDOUT,env=env)
queue.put_nowait(str(output, "utf8"))
except subprocess.CalledProcessError as error:
# post the precise message and the remaining output as a tuple
queue.put_nowait((tool,error.returncode, str(error.output, "utf8")))
class ToolThread(threading.Thread):
def __init__(self,tool,queue,command):
super().__init__()
self.tool=tool
self.command=command
self.queue=queue
self.subproc=None
def run(self):
# we can't use check_output, because it doesn't support
# performing operations on the subprocess
# so we'll have to use subprocess.Popen() instead

# set the encoding for the subprocess
# otherwise, with the new Unicode literals used by the wml tools,
# we may get UnicodeDecodeErros
env=os.environ
env['PYTHONIOENCODING']="utf8"
if sys.platform=="win32":
# Windows wants a string, Linux wants a list and Polly wants a cracker
# Windows wants also strings flavoured with double quotes
# since maps return iterators, we must cast them as lists, otherwise join won't work
# not doing this causes an OSError: [WinError 87]
# this doesn't happen on Python 2.7, because here map() returns a list
wrapped_line=list(map(wrap_elem,self.command))
self.queue.put_nowait(' '.join(wrapped_line)+"\n")
si=subprocess.STARTUPINFO()
si.dwFlags=subprocess.STARTF_USESHOWWINDOW|subprocess.SW_HIDE # to avoid showing a DOS prompt
self.subproc=subprocess.Popen(' '.join(wrapped_line),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
startupinfo=si,
env=env)
else: # STARTUPINFO is not available, nor needed, outside of Windows
self.queue.put_nowait(' '.join(self.command)+"\n")
self.subproc=subprocess.Popen(self.command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env)
out,err=self.subproc.communicate() # waits for the subprocess to finish and gets the output
self.queue.put_nowait(str(out, "utf8"))
self.queue.put_nowait(str(err, "utf8"))
# unlike check_output, Popen never raises if a subprocess terminates abnormally
# the documentation writes about the chance of OSError being raised
# but I tested by deleting wmllint and it doesn't happen
# perhaps because it's a Python script and the Python interpreter
# certainly exists
if self.subproc.returncode!=0:
# post a tuple into the queue to allow showing the error
self.queue.put_nowait((self.tool,self.subproc.returncode))
def terminate(self):
self.subproc.kill()

def is_wesnoth_tools_path(path):
"""Checks if the supplied path may be a wesnoth/data/tools directory"""
Expand Down Expand Up @@ -259,13 +279,22 @@ Self destroys when the tool thread is over"""
sticky=E+W,
padx=5,
pady=5)
terminate_button=Button(frame,
text="Terminate script",
image=ICONS["process-stop"],
compound=LEFT,
command=self.terminate)
terminate_button.grid(row=2,
column=0,
padx=5,
pady=5)
frame.columnconfigure(0,weight=1)
# place the popup in the middle of the main window
# get the main window position and dimension
self.geometry("{0}x{1}+{2}+{3}".format(400,
80,
140,
int(root.winfo_rootx()+(root.winfo_width()-400)/2),
int(root.winfo_rooty()+(root.winfo_height()-80)/2)))
int(root.winfo_rooty()+(root.winfo_height()-140)/2)))
wait_progress.start(10)
self.check_thread_alive()
def check_thread_alive(self):
Expand All @@ -277,6 +306,8 @@ Self destroys when the tool thread is over"""
self.after(100,self.check_thread_alive)
else:
self.after(1,self.destroy)
def terminate(self):
self.thread.terminate()

class ContextMenu(Menu):
def __init__(self,x,y,widget):
Expand Down Expand Up @@ -1184,7 +1215,7 @@ wmllint will be run only on the Wesnoth core directory""")
showerror("Error","""The selected directory does not exists""")
return # stop here
# start thread and wmllint subprocess
wmllint_thread=threading.Thread(target=run_tool,args=("wmllint",self.queue,wmllint_command_string))
wmllint_thread=ToolThread("wmllint",self.queue,wmllint_command_string)
wmllint_thread.start()
# build popup
dialog=Popup(self.parent,"wmllint",wmllint_thread)
Expand Down Expand Up @@ -1250,7 +1281,7 @@ wmlscope will be run only on the Wesnoth core directory""")
showerror("Error","""The selected directory does not exists""")
return # stop here
# start thread and wmlscope subprocess
wmlscope_thread=threading.Thread(target=run_tool,args=("wmlscope",self.queue,wmlscope_command_string))
wmlscope_thread=ToolThread("wmlscope",self.queue,wmlscope_command_string)
wmlscope_thread.start()
# build popup
dialog=Popup(self.parent,"wmlscope",wmlscope_thread)
Expand Down Expand Up @@ -1285,7 +1316,7 @@ wmlindent will be run on the Wesnoth core directory""")
showerror("Error","""The selected directory does not exists""")
return # stop here
# start thread and wmllint subprocess
wmlindent_thread=threading.Thread(target=run_tool,args=("wmlindent",self.queue,wmlindent_command_string))
wmlindent_thread=ToolThread("wmlindent",self.queue,wmlindent_command_string)
wmlindent_thread.start()
# build popup
dialog=Popup(self.parent,"wmlindent",wmlindent_thread)
Expand Down Expand Up @@ -1338,7 +1369,7 @@ wmlxgettext won't be run""")
if initialdomain:
wmlxgettext_command_string.extend(["--initialdomain",initialdomain])
# start thread and wmlxgettext subprocess
wmlxgettext_thread=threading.Thread(target=run_tool,args=("wmlxgettext",self.queue,wmlxgettext_command_string))
wmlxgettext_thread=ToolThread("wmlxgettext",self.queue,wmlxgettext_command_string)
wmlxgettext_thread.start()
# build popup
dialog=Popup(self.parent,"wmlxgettext",wmlxgettext_thread)
Expand All @@ -1349,15 +1380,13 @@ If it contains a string, pushes it into the Text widget.
If it contains an error in form of a tuple, displays a message and pushes the remaining output in the Text widget"""
if not self.queue.empty():
queue_item=self.queue.get_nowait()
# I tried posting directly the error in the queue, but for some reason
# isinstance(queue_item,subprocess.CalledProcessError) is ignored
# if there's a tuple in the queue, it's because a tool exited with
# non-zero status
if isinstance(queue_item,tuple):
showerror("Error","""There was an error while executing {0}.
Error code: {1}""".format(queue_item[0],queue_item[1]))
self.text.configure(state=NORMAL)
self.text.insert(END,queue_item[2])
self.text.configure(state=DISABLED)
# otherwise it's just the output
elif isinstance(queue_item,str):
self.text.configure(state=NORMAL)
self.text.insert(END,queue_item)
Expand Down Expand Up @@ -1625,7 +1654,36 @@ turIeO7MeevMhu3Nfe7SjevWn+zZq+3aou7aofjdj/bfmfLgo/zim/fkmfzurP//////////////
/////////////////////////////////////////////////////////////////////////yH5
BAEKAH8ALAAAAAAQABAAAAeOgH+Cg4SFgx+GhB6EIVqDQzCDWiCEVypHNSkcDggZPidYhUYWL0xV
TygGDDKGSxtTaWJjZz8EO4ZCGlBmYWBlPAI0hkAXRWhfXWQkDTmGNh0sXF5bWRATMYklGEhRTkEK
Iol/LRImVFYjAyviTRUBOj0AFEqJUn9JEQcFD0R/9uI4EixwIa7QjRkFExIKBAA7=''')
Iol/LRImVFYjAyviTRUBOj0AFEqJUn9JEQcFD0R/9uI4EixwIa7QjRkFExIKBAA7='''),
"process-stop":PhotoImage(data=b'''
R0lGODlhIAAgAOf/AIgAAZAID7gAALkAAL8AAMEAAMIAALsCAZwKD8MAAMQAAMMABsYBAMwAArwF
AsYCCMcEAI4UD6AREsgHAL4LBKIUFKEUGZEZGJoXFsoLAZsYHLURF8kMFJUdIJ0bHrgVGaYaHcIT
EJ8dGp4dH5AiJM0TDqAfILoZGpMkIM0UFqkeJIwnKcQYGc4WHr4eHcccG9EaGacmJZIsLckfHdMe
G9UgHMsjJdcjJMcqKtglK9knJsotLNspJ80wLdUuLN4tKc0xNNYwMs8yNdgxM9A0NtE1N9kzNNI2
N9o0NdQ3ONU5OdY6OuY2Nd04Pdc7O9g8POA6Oeg4N+k5N9o9PZlPT9s/Pto/Q+s7PtxAP9xARN1B
QJhUUd1BRd9CQN5CRqBVVdlGRelDQOFFSNtHRtxIR+tGR+VIRd5KSPVFRuZJS+5ISa1aXKhcW+FN
S+lLSOBNUP5GSv9HRapeXeJPUqtfXuRQU+tPUK1hYPtNUfBSU/dRUKtmaPlSUa5oau1ZWudbXvdZ
WfZZXuteW+RgXrBwcOZiYPpdYe5hY7JyculkYuhkaLR0dPdiYvJlZu1nZbd2dvpkZO9pZ/BqaO9q
bbV7d/5nZ/dpa7V9f/lrbPJtb+5wb7iAgvZxc/5vcLWDg/Fzcvlyb/Z3du95evl5ePN8ffZ/gPOD
gPqCg/OEh/WFgvyEhfaGg/iIi/KKiqCin/+Iiaahn/qKjfWMjPCOjPaNjaKkofiPj/GRlP+OjPmQ
kKWnpKaopfaUkamrqPmWk6qsqfyZlrKtq/uZnK6wrfednbCyrrK0sbO1ssqzsO6qqLe5trm7uLy+
u8i7u72/vO+ytMu+v8DCvsHDwO23t/O2uM/Cw/C6uve6vMbIxfK9vMfKxvW/v8vNyvLDwM3PzPLF
yM/RzvbHxNLU0dPV0vnMz/XQ0PfR0ufa2urd3ebh3/vc2+3g4ePl4eTm4/Dj5Ovl5OXn5Obo5e3n
5ufq5u/p6Ors6Ovu6u3v7PPu7PXv7u/x7vDy7/Hz8PL08fP18tbW1iH5BAEKAP8ALAAAAAAgACAA
AAj+AP8JHDjQE4CDCBMeZEOwoUOBmzC04kWRYi5btmixWhXjzsOPlzSYSlSlZJUnKFE6mUNKxZ6P
DSl5MKXIik0rJlM+WSkKRB+YAmWiSmSTC5ebJXWu/ASCEMxHI4YaBUMVzFGcJ1U6eaNJxKKHi6IW
4lK17NWsKpe8mSTiUUNEJlAVskIWTBo8Z6raLBkGzZMlSo68cWTh0kBCcQcVtcsomR4yVPeWwTQL
DeAjR84kwuBJIABaf0rSNWPoHLtnerxcnZyO3S00gY8QOeMIgOdcKKvYhJOMXbx4qLVYUcPp3e9z
cZocKUIEiCTb/wDgfiJai55p87JTA2Qn1Lvs6CD+aVlOpHkk6NJTmuQCyFr2etZGycu+DpMYzMyB
ADl/Wyf1Klgwso099xRYoDugnIFfc/o1gl4uTjjh3xNYYLLNPfpkKM8oCpK3ww444CAIerYsEeGE
fqiyDz8smtPJETZwsMACCSRQgANXkIiEiRGe6Ecu+LDYTz/8bKMHBDXWeOMAUqBHCxJQLiGlE4L4
kg8//uCjDpH7bBMIjTYOIGYU6MlihBFQQimIMEH6k88plYTjDz/7dNPIAgWIOYAATKDHShBnnnkI
MW3mU8oNE2BCzpz6fMMJBWIKwCd6qbQRxKVBnJLNiviccoMCCSwQijn97LPOKyHsKcAAfQrERgz0
oliKKSnX0BNLDqDWuEAp5rhjywuqDqBDC9D9s0YMmUABQwkTTJAJLj7kmuQCsgADrKQD8DBDAMgQ
xIYFedDQAATkpiCtkg5QkCq2PLwQQTMO0VGBHTRAwAAD59rogJ7ssnABNB/dIQG9+CaJLr8COMCD
Cx1UA5QcErhBg8H6IuzADyegADBM3ETzhQRlwJDkkpEm/MMHJATDDDbjPDSONs4cQwUCaqhRRhlh
hHHFFVJEwQQTG6wASzHLsPyRONgwY8wWCjUNgAxDF90yUOJwI40yxfyySy2udO2KLr0Mo4w03EwN
VEPjiOPN2tys7Y04Zj8UEAA7''')
}
ROOT_W,ROOT_H=800,480
# the following string may be confusing, so here there's an explanation
Expand Down

0 comments on commit 03f24f8

Please sign in to comment.