parent
9ec225726b
commit
f64713b75c
@ -0,0 +1,23 @@ |
||||
""" Miscellaneous utilities. """ |
||||
|
||||
import functools |
||||
import logging |
||||
import traceback |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def log_exceptions( caption="EXCEPTION" ): |
||||
"""Decorator that logs exceptions thrown by the wrapped function.""" |
||||
def decorator( func ): |
||||
"""The real decorator function.""" |
||||
@functools.wraps( func ) |
||||
def wrapper( *args, **kwargs ): |
||||
"""Wrapper around the function being decorated.""" |
||||
try: |
||||
return func( *args, **kwargs ) |
||||
except Exception as ex: |
||||
logging.critical( "%s: %s", caption, ex ) |
||||
logging.critical( traceback.format_exc() ) |
||||
raise |
||||
return wrapper |
||||
return decorator |
@ -0,0 +1,88 @@ |
||||
""" Web channel handler. """ |
||||
|
||||
import os |
||||
|
||||
from PyQt5.QtWidgets import QFileDialog |
||||
|
||||
from vasl_templates.webapp.config.constants import APP_NAME |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
class WebChannelHandler: |
||||
"""Handle web channel requests.""" |
||||
|
||||
_FILE_FILTERS = "Scenario files (*.json);;All files (*)" |
||||
|
||||
def __init__( self, window ): |
||||
|
||||
# initialize |
||||
self._window = window |
||||
|
||||
# NOTE: While loading/saving scenarios works fine when handled by the embedded browser, |
||||
# we can't get the full path of the file saved loaded (because of browser security). |
||||
# This means that we can't e.g. default saving a scenario to the same file it was loaded from. |
||||
# This is such a lousy UX, we handle load/save operations ourself, where we can manage this. |
||||
self._curr_scenario_fname = None |
||||
|
||||
def on_new_scenario( self ): |
||||
"""Called when the scenario is reset.""" |
||||
self._curr_scenario_fname = None |
||||
|
||||
def load_scenario( self ): |
||||
"""Called when the user wants to load a scenario.""" |
||||
|
||||
# ask the user which file to load |
||||
fname, _ = QFileDialog.getOpenFileName( |
||||
self._window, "Load scenario", |
||||
os.path.split(self._curr_scenario_fname)[0] if self._curr_scenario_fname else None, |
||||
WebChannelHandler._FILE_FILTERS |
||||
) |
||||
if not fname: |
||||
return None |
||||
|
||||
# load the scenario |
||||
try: |
||||
with open( fname, "r", encoding="utf-8" ) as fp: |
||||
data = fp.read() |
||||
except Exception as ex: #pylint: disable=broad-except |
||||
self._window.showErrorMsg( "Can't load the scenario:\n\n{}".format( ex ) ) |
||||
return None |
||||
self._curr_scenario_fname = fname |
||||
|
||||
return data |
||||
|
||||
def save_scenario( self, data ): |
||||
"""Called when the user wants to save a scenario.""" |
||||
|
||||
# ask the user where to save the scenario |
||||
fname, _ = QFileDialog.getSaveFileName( |
||||
self._window, "Save scenario", |
||||
self._curr_scenario_fname, |
||||
WebChannelHandler._FILE_FILTERS |
||||
) |
||||
if not fname: |
||||
return False |
||||
|
||||
# check the file extension |
||||
extn = os.path.splitext( fname )[1] |
||||
if not extn: |
||||
fname += ".json" |
||||
elif fname.endswith( "." ): |
||||
fname = fname[:-1] |
||||
|
||||
# save the file |
||||
try: |
||||
with open( fname, "w", encoding="utf-8" ) as fp: |
||||
fp.write( data ) |
||||
except Exception as ex: #pylint: disable=broad-except |
||||
self._window.showErrorMsg( "Can't save the scenario:\n\n{}".format( ex ) ) |
||||
return False |
||||
self._curr_scenario_fname = fname |
||||
|
||||
return True |
||||
|
||||
def on_scenario_name_change( self, val ): |
||||
"""Update the main window title to show the scenario name.""" |
||||
self._window.setWindowTitle( |
||||
"{} - {}".format( APP_NAME, val ) if val else APP_NAME |
||||
) |
Loading…
Reference in new issue