Skip to content

Commit

Permalink
Handle ctrl-C from terminal and close the viewer (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
abey79 authored Oct 18, 2021
1 parent 000e328 commit ea7dadc
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ New features and improvements:

* Relative coordinates are now used by default to reduce file size. If absolute coordinates are needed, they a new `--absolute` option for the `write` command.
* A homing command (as defined by the `final_pu_params` configuration parameter) is no longer emitted between layers.
* The viewer (`show` command) now catches interruptions from the terminal (ctrl-C) and closes itself (#321)

Bug fixes:
* ...
Expand Down
40 changes: 40 additions & 0 deletions vpype_viewer/qtviewer/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import os
import signal
import socket
from contextlib import contextmanager
from typing import Callable

from PySide2 import QtNetwork
from PySide2.QtCore import QCoreApplication
from PySide2.QtGui import QIcon, QPalette
from PySide2.QtWidgets import QAction, QActionGroup
Expand Down Expand Up @@ -73,3 +78,38 @@ def __init__(self, current: float = 0.8, parent=None):
act.setCheckable(True)
act.setChecked(w == current)
act.setData(w)


class SignalWatchdog(QtNetwork.QAbstractSocket):
"""This object notify PySide2's event loop of an incoming signal and makes it process it.
The python interpreter flags incoming signals and triggers the handler only upon the next
bytecode is processed. Since PySide2's C++ event loop function never/rarely returns when
the UX is in the background, the Python interpreter doesn't have a chance to run and call
the handler.
From: https://stackoverflow.com/a/65802260/229511 and
https://stackoverflow.com/a/37229299/229511
"""

def __init__(self):
# noinspection PyTypeChecker
super().__init__(QtNetwork.QAbstractSocket.SctpSocket, None) # type: ignore
self.writer, self.reader = socket.socketpair()
self.writer.setblocking(False)
signal.set_wakeup_fd(self.writer.fileno())
self.setSocketDescriptor(self.reader.fileno())
self.readyRead.connect(lambda: None)


@contextmanager
def set_sigint_handler(handler: Callable):
original_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, handler)
# noinspection PyUnusedLocal
watchdog = SignalWatchdog()
try:
yield
finally:
signal.signal(signal.SIGINT, original_handler)
del watchdog
11 changes: 9 additions & 2 deletions vpype_viewer/qtviewer/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from .._scales import UnitType
from ..engine import Engine, ViewMode
from .utils import PenOpacityActionGroup, PenWidthActionGroup, load_icon
from .utils import PenOpacityActionGroup, PenWidthActionGroup, load_icon, set_sigint_handler

__all__ = ["QtViewerWidget", "QtViewer", "show"]

Expand Down Expand Up @@ -480,4 +480,11 @@ def show(

widget.show()

return app.exec_()
# noinspection PyUnusedLocal
def sigint_handler(signum, frame):
QApplication.quit()

with set_sigint_handler(sigint_handler):
res = app.exec_()

return res

0 comments on commit ea7dadc

Please sign in to comment.