Skip to content

Commit

Permalink
Terminate subprocesses when the application is quit
Browse files Browse the repository at this point in the history
For #3
  • Loading branch information
peterstory committed Apr 11, 2024
1 parent 5a08a2b commit 655a6f6
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 22 deletions.
25 changes: 14 additions & 11 deletions src/pydiode/gui/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,30 @@ def stuck_running(returncodes):
return False


def check_subprocesses(widget, cancelled, *args):
def check_subprocesses(widget, cancelled, processes):
"""
Check whether all the subprocesses have exited. If so, display their error
messages and clean up after them.
:param widget: Used to schedule another check
:param cancelled: Boolean variable indicating cancellation request
:param args: An array of tuples, each containing a subprocess's name and
its popen object.
:param processes: An array of tuples, each containing a subprocess's name
and its popen object.
"""
# If requested, cancel subprocesses
if cancelled.get():
# Signal each process to exit
for name, popen in args:
for name, popen in processes:
popen.terminate()
# Mark this cancellation request as handled
cancelled.set(False)
# At the next check, hopefully the processes will have exited
widget.after(
SLEEP, lambda: check_subprocesses(widget, cancelled, *args)
SLEEP, lambda: check_subprocesses(widget, cancelled, processes)
)
else:
# Get returncodes for exited processes, None for running processes
returncodes = [popen.poll() for name, popen in args]
returncodes = [popen.poll() for name, popen in processes]
# Are any of the subprocesses still running?
still_running = any(code is None for code in returncodes)

Expand All @@ -109,10 +109,10 @@ def check_subprocesses(widget, cancelled, *args):
# Request termination of the processes
cancelled.set(True)
widget.after(
SLEEP, lambda: check_subprocesses(widget, cancelled, *args)
SLEEP, lambda: check_subprocesses(widget, cancelled, processes)
)
# Describe the issue
process_names = [name for name, popen in args]
process_names = [name for name, popen in processes]
error_msgs = get_premature_errors(
list(zip(process_names, returncodes))
)
Expand All @@ -121,15 +121,18 @@ def check_subprocesses(widget, cancelled, *args):
# If subprocesses are still running, keep waiting for them
elif still_running:
widget.after(
SLEEP, lambda: check_subprocesses(widget, cancelled, *args)
SLEEP, lambda: check_subprocesses(widget, cancelled, processes)
)
# Otherwise, all subprocesses have exited
else:
# If any subprocesses exited irregularly, describe the issue
error_msgs = get_process_errors(args)
error_msgs = get_process_errors(processes)
if error_msgs:
showerror(title="Error", message=error_msgs)
# Clean up
for name, popen in args:
for name, popen in processes:
popen.stdout.close()
popen.stderr.close()
# The array of subprocesses should be cleared, so it doesn't grow
# each time more subprocesses are started
processes.clear()
16 changes: 14 additions & 2 deletions src/pydiode/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
import sys
from tkinter import BooleanVar, IntVar, Listbox, StringVar, Tk, ttk


from pydiode.gui.receive import receive_files, set_target_directory
from pydiode.gui.receive import (
receive_files,
set_target_directory,
RECEIVE_PROCESSES,
)
from pydiode.gui.send import (
add_source_files,
remove_source_files,
send_files,
update_tx_btn,
SEND_PROCESSES,
)
import pydiode.pydiode
import pydiode.tar
Expand Down Expand Up @@ -166,9 +170,17 @@ def gui_main():
port.set(config["pydiode"].get("port", "1234"))
# TODO Add options for maximum bitrate and redundancy

# Override the default behavior of the Quit menu, so it doesn't cause the
# application to exit immediately
root.createcommand("tk::mac::Quit", root.quit)

# Start handling user input
root.mainloop()

# Cancel send and receive subprocesses
for name, popen in SEND_PROCESSES + RECEIVE_PROCESSES:
popen.terminate()

# Save settings
config["pydiode"] = {
"tab": active_tab.get(),
Expand Down
8 changes: 5 additions & 3 deletions src/pydiode/gui/receive.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

from pydiode.gui.common import check_subprocesses, SLEEP

# An array of tuples, each containing a subprocess's name and its popen object
RECEIVE_PROCESSES = []


def set_target_directory(target):
"""
Expand Down Expand Up @@ -45,11 +48,10 @@ def receive_files(
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
RECEIVE_PROCESSES.extend([("pydiode", pydiode), ("tar", tar)])
root.after(
SLEEP,
lambda: check_subprocesses(
root, cancelled, ("pydiode", pydiode), ("tar", tar)
),
lambda: check_subprocesses(root, cancelled, RECEIVE_PROCESSES),
)

def animate():
Expand Down
10 changes: 4 additions & 6 deletions src/pydiode/gui/send.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
OVERHEAD = 1.085
# Increment progress bars every 25 milliseconds, for smooth animation.
INCREMENT_INTERVAL = 25
# An array of tuples, each containing a subprocess's name and its popen object
SEND_PROCESSES = []


def add_source_files(sources_var, sources_list):
Expand Down Expand Up @@ -132,14 +134,10 @@ def send_files(
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
SEND_PROCESSES.extend([("tar", tar), ("pydiode", pydiode)])
root.after(
SLEEP,
lambda: check_subprocesses(
root,
cancelled,
("tar", tar),
("pydiode", pydiode),
),
lambda: check_subprocesses(root, cancelled, SEND_PROCESSES),
)

increment_size = get_increment_size(sources_list, progress_bar)
Expand Down

0 comments on commit 655a6f6

Please sign in to comment.