From ae22cb82b2db661e386ab94adad88b70a83921d9 Mon Sep 17 00:00:00 2001 From: Andrew Lorimer Date: Sat, 6 Nov 2021 12:07:36 +1100 Subject: [PATCH] add config file comments, minor JS refactoring --- README.md | 6 +++ ppt-control.ini | 70 ++++++++++++++++++++++++++++++- ppt_control/config.py | 66 +---------------------------- ppt_control/ppt_control.py | 10 ++--- ppt_control/static/index.html | 15 ++++--- ppt_control/static/ppt-control.js | 21 ++++++---- ppt_control/static/settings.js | 1 - ppt_control/static/style.css | 11 ++++- setup.py | 3 +- 9 files changed, 116 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 64a4d19..5e3ba5f 100755 --- a/README.md +++ b/README.md @@ -68,3 +68,9 @@ Various settings can be changed in `%AppData%\ppt-control\ppt-control.ini`. This - ✔️ JS client show status on last slide - ✔️ Shortcut to edit config in systray menu - Export all slides on presentation init +- Abstract pres name in class & underscore `presentation` instance +- Check assertions +- Use `__debug__` flag +- Make constant for static dir +- Comment sys.coinitflags +- Die if error in run_ws or run_http \ No newline at end of file diff --git a/ppt-control.ini b/ppt-control.ini index a9b9bdd..83b822e 100644 --- a/ppt-control.ini +++ b/ppt-control.ini @@ -1,14 +1,82 @@ [Main] -logging = debug + +# Logging level +# Options: debug, info, warning, error, critical +# (see https://docs.python.org/3/howto/logging.html#when-to-use-logging) +logging = info + +# Cache location +# Directory to store slide preview images for each presentation cache = C:\Windows\Temp\ppt-cache + +# Cache format +# Can be set to any image filter set in the registry, but most commonly JPG or PNG +# (see https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slide.export) cache_format = JPG + +# Cache timeout +# Time after which slides should be exported again to reflect any changes made in the +# PowerPoint editor. Set to 0 to never consider the cache stale (not recommended). Value +# is in seconds. cache_timeout = 300 + +# Cache initialisation +# Whether to export all slides when a slideshow is started. Can be set to any falsy or +# truthy value. +cache_init = True + +# Black-white behaviour +# Sets the behaviour when returning from black or white mode. Options: "both", "literal" +# In PowerPoint's presenter view, if the slideshow is in black or white screen mode, +# attempting to switch to the other mode (white/black) will result in the slideshow +# returning to a normal (visible) state, rather than white/black. This behaviour is +# also the default for ppt-control, and is invoked by setting "blackwhite" to "both" +# (when the slideshow is in black or white mode, requesting either black or white mode +# will return the slideshow to normal). The more intuitive setting is "literal", whereby +# when the slideshow is in black or white mode, pressing the button corresponding to the +# mode that the slideshow is already in will return the slideshow to normal mode, and +# pressing the other button will put the slideshow into that mode explicitly. blackwhite = both +# Refresh interval +# Describes the number of seconds between backend updates, which consist of: +# - synchronisation of PowerPoint's events with the internal state. +# - updating the interface (systray icon) status +# - check for PowerPoint files that have been opened in protected view +# You could set a shorter value here, but these updates don't have to be instant so +# the default value of 2 seconds should work fine. +refresh = 2 + +# Disable protecte view +# If set to a truthy value, ppt-control will attempt to disable protected view on files +# which have been opened in protected view. Obviously this introduces some security risk, +# so if you are uncomfortable with this, leave it off and you will have to disable +# protected view for each file manually to be able to control it will ppt-control. +disable_protected = True + [HTTP] + +# HTTP interface +# The interface to listen for HTTP requests on. Change this if you want to restrict the +# HTTP frontend to a specific network. A blank value means all interfaces. interface = + +# HTTP port +# The port to listen for HTTP requests on. The default value of 80 is the standard +# port for HTTP, so when set to this value you don't need to include the port number +# in the URL when navigating to the frontend. For all other values you will need to +# specify the port number in the web browser (except 443, but that's a bad idea). port = 80 [WebSocket] +# WebSocket interface +# The interface to listen for WebSocket requests on. Change this if you want to restrict +# the WebSocket interface to a specific network. A value of 0.0.0.0 means all interfaces. interface = 0.0.0.0 + + +# WebSocket port +# The port to listen for WebSocket requests on. This needs to match the port specified in +# whatever frontend you are using (e.g. in the JS code for the HTTP frontend, or in the +# script settings for the OBS frontend). port = 5678 diff --git a/ppt_control/config.py b/ppt_control/config.py index a1e90ab..61ff70f 100755 --- a/ppt_control/config.py +++ b/ppt_control/config.py @@ -4,83 +4,21 @@ prefs = None defaults = { 'Main': { - # Logging level - # Options: debug, info, warning, error, critical - # (see https://docs.python.org/3/howto/logging.html#when-to-use-logging) - 'logging': 'debug', - - # Cache location - # Directory to store slide preview images for each presentation + 'logging': 'info', 'cache': r'''C:\Windows\Temp\ppt-cache''', - - # Cache format - # Can be set to any image filter set in the registry, but most commonly JPG or PNG - # (see https://docs.microsoft.com/en-us/office/vba/api/powerpoint.slide.export) 'cache_format': 'JPG', - - # Cache timeout - # Time after which slides should be exported again to reflect any changes made in the - # PowerPoint editor. Value is in seconds. 'cache_timeout': 5*60, - - # Cache initialisation - # Whether to export all slides when a slideshow is started. Can be set to any falsy or - # truthy value. 'cache_init': True, - - # Black-white behaviour - # Sets the behaviour when returning from black or white mode. Options: "both", "literal" - # In PowerPoint's presenter view, if the slideshow is in black or white screen mode, - # attempting to switch to the other mode (white/black) will result in the slideshow - # returning to a normal (visible) state, rather than white/black. This behaviour is - # also the default for ppt-control, and is set by setting the value of "blackwhite" - # to "both" (since when the slideshow is in black or white mode, requesting either - # of black or white mode will return the slideshow to normal). The more intuitive - # settings is "literal", whereby when the slideshow is in black or white mode, - # pressing the button corresponding to the mode that the slideshow is already in will - # return the slideshow to normal mode, and pressing the other button will put the - # slideshow into that mode. 'blackwhite': 'both', - - # Refresh interval - # Describes the number of seconds between backend updates, which consist of: - # - synchronisation of PowerPoint's events with the internal state. - # - updating the interface (systray icon) status - # - check for PowerPoint files that have been opened in protected view - # You could set a shorter value here, but these updates don't have to be instant so - # the default value of 2 seconds should work fine. 'refresh': 2, - - # Disable protecte view - # If set to a truthy value, ppt-control will attempt to disable protected view on files - # which have been opened in protected view. Obviously this introduces some security risk, - # so if you are uncomfortable with this, leave it off and you will have to disable - # protected view for each file manually to be able to control it will ppt-control. 'disable_protected': True }, 'HTTP': { - # HTTP interface - # The interface to listen for HTTP requests on. Change this if you want to restrict the - # HTTP frontend to a specific network. A blank value means all interfaces. 'interface': '', - - # HTTP port - # The port to listen for HTTP requests on. The default value of 80 is the standard - # port for HTTP, so when set to this value you don't need to include the port number - # in the URL when navigating to the frontend. For all other values you will need to - # specify the port number in the web browser (except 443, but that's a bad idea). 'port': 80 }, 'WebSocket': { - # WebSocket interface - # The interface to listen for WebSocket requests on. Change this if you want to restrict - # the WebSocket interface to a specific network. A value of 0.0.0.0 means all interfaces. 'interface': '0.0.0.0', - - # WebSocket port - # The port to listen for WebSocket requests on. This needs to match the port specified in - # whatever frontend you are using (e.g. in the JS code for the HTTP frontend, or in the - # script settings for the OBS frontend). 'port': 5678 } } @@ -111,4 +49,4 @@ def export_defaults(file): after installation. """ prefs = loadconf([]) - prefs.write(file, space_around_delimiters=True) \ No newline at end of file + prefs.write(file, space_around_delimiters=True) diff --git a/ppt_control/ppt_control.py b/ppt_control/ppt_control.py index e5a38c1..45e0611 100755 --- a/ppt_control/ppt_control.py +++ b/ppt_control/ppt_control.py @@ -514,12 +514,14 @@ class Presentation: def export(self, slide): """ Export a relatively low-resolution image of a slide using PowerPoint's built-in export - function. The cache destination is set in the config. + function. The cache destination is set in the config. The slide is not exported if it has + a non-stale cached file. """ destination = config.prefs["Main"]["cache"] + "\\" + self.presentation.Name + "\\" + str(slide) + "." + config.prefs["Main"]["cache_format"].lower() logger.debug("Exporting slide {} of {}".format(slide, self.presentation.Name)) os.makedirs(os.path.dirname(destination), exist_ok=True) - if not os.path.exists(destination) or time.time() - os.path.getmtime(destination) > config.prefs.getint("Main", "cache_timeout"): + if not os.path.exists(destination) or (config.prefs.getint("Main", "cache_timeout") > 0 and + time.time() - os.path.getmtime(destination) > config.prefs.getint("Main", "cache_timeout")): if slide <= self.slide_total(): attempts = 0 while attempts < 3: @@ -731,10 +733,9 @@ def start_interface(): if config.prefs["Main"]["logging"] == "debug": log_level = logging.DEBUG elif config.prefs["Main"]["logging"] == "info": - log_level = logging.CRITICAL + log_level = logging.INFO else: log_level = logging.WARNING - log_level = logging.DEBUG log_formatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-7.7s] %(message)s") logger = logging.getLogger("ppt-control") @@ -774,7 +775,6 @@ def start_interface(): icon = MyIcon(PKG_NAME) icon.icon = Image.open(os.path.dirname(os.path.realpath(__file__)) + r'''\static\icons\ppt.ico''') icon.title = "{} {}".format(PKG_NAME, PKG_VERSION) - #refresh_menu(icon) icon.visible = True icon.run(setup=start_server) diff --git a/ppt_control/static/index.html b/ppt_control/static/index.html index 0c04bc8..13c241b 100755 --- a/ppt_control/static/index.html +++ b/ppt_control/static/index.html @@ -34,15 +34,18 @@

- Show current slide - Show next slide - Keyboard shortcuts +

Presentation: - Disconnected + + Disconnected + + +

- - + Show current slide + Show next slide + Keyboard shortcuts diff --git a/ppt_control/static/ppt-control.js b/ppt_control/static/ppt-control.js index c00ca68..39714f6 100644 --- a/ppt_control/static/ppt-control.js +++ b/ppt_control/static/ppt-control.js @@ -1,4 +1,9 @@ -var DEFAULT_TITLE = "ppt-control" +const DEFAULT_TITLE = "ppt-control"; +const LABEL_STOPPED = "Slideshow stopped"; +const LABEL_RUNNING = "Slideshow running"; +const LABEL_DISCONNECTED = "Disconnected"; +const LABEL_FINAL_PREFIX = "Final slide ‐ "; +const LABEL_END_PREFIX = "End of slideshow ‐ "; var preloaded = false; var preload = []; @@ -126,6 +131,8 @@ next_img.onclick = function (event) { next.click() } +window.addEventListener('resize', function(event) {set_control_width()}, true); + function sync_current() { if (show_current.checked) { @@ -211,9 +218,8 @@ function refreshInterface() { var d = new Date; if (Object.keys(presentationData).length > 0) { currentPresentationData = presentationData[getPresentationName()]; - console.log("Current presentation is " + getPresentationName()); presentation_text.style.display = "block"; - status_text.textContent = "Slideshow running"; + status_text.innerHTML = LABEL_RUNNING; startBtn.style.display = "none"; stopBtn.style.display = "block"; if (show_current.checked) { @@ -230,10 +236,10 @@ function refreshInterface() { } } if (currentPresentationData.slide_current == currentPresentationData.slide_total) { - status_text.textContent = "Slideshow running (last slide)"; + status_text.innerHTML = LABEL_FINAL_PREFIX + LABEL_RUNNING; } if (currentPresentationData.slide_current == currentPresentationData.slide_total + 1) { - status_text.textContent = "End of slideshow"; + status_text.innerHTML = LABEL_END_PREFIX + LABEL_RUNNING; next_img.src = "/black.jpg"; } else { next_img.src = "/cache/" + currentPresentationData.name + "/" + (currentPresentationData.slide_current + 1) + ".jpg?t=" + d.getTime(); @@ -241,7 +247,7 @@ function refreshInterface() { if (currentPresentationData.slide_current == 0) { current_img.src = "/black.jpg"; next_img.src = "/black.jpg"; - status_text.textContent = "Slideshow not running"; + status_text.innerHTML = LABEL_STOPPED; startBtn.style.display = "block"; stopBtn.style.display = "none"; } @@ -261,7 +267,7 @@ function disconnect() { document.title = DEFAULT_TITLE; current_img.src = "/black.jpg"; next_img.src = "/black.jpg"; - status_text.textContent = "Disconnected"; + status_text.innerHTML = LABEL_DISCONNECTED; total.textContent = "?"; current.value = ""; presentation_text.style.display = "none"; @@ -271,7 +277,6 @@ function disconnect() { function receive_message(event) { data = JSON.parse(event.data); - console.log("Received data: " + data); if (Object.keys(presentationData).includes(data.name)) { // Existing presentation presentationData[data.name] = {"name": data.name, "pres_open": data.pres_open, "slideshow": data.slideshow, "visible": data.visible, "slide_current": data.slide_current, "slide_total": data.slide_total, "option": presentationData[data.name].option}; diff --git a/ppt_control/static/settings.js b/ppt_control/static/settings.js index 901af0b..54dc1f0 100644 --- a/ppt_control/static/settings.js +++ b/ppt_control/static/settings.js @@ -25,7 +25,6 @@ function getCookie(cname) { } function saveSettings() { - console.log("Saving settings") settingsString = JSON.stringify({showcurrent: show_current.checked, shownext: show_next.checked, enable_shortcuts: shortcuts.checked}); setCookie(COOKIENAME, settingsString, COOKIEEXP); } diff --git a/ppt_control/static/style.css b/ppt_control/static/style.css index 56a3d57..fb1fec7 100644 --- a/ppt_control/static/style.css +++ b/ppt_control/static/style.css @@ -83,6 +83,15 @@ input[type='checkbox'] { display: none; } -.status_text { +.start, .stop { + float: right; + margin: 0 0 0 10px; +} + +.status_container { float: right; +} + +.status_text { + vertical-align: middle; } \ No newline at end of file diff --git a/setup.py b/setup.py index d7660da..b8f20d9 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,8 @@ setuptools.setup( packages=setuptools.find_packages(), python_requires='>=3.6', # as of v0.0.1, OBS only supports use of Python 3.6 for scripts. Otherwise the package works fine on > 3.6. install_requires=['pywin32', 'websockets', 'pystray'], # https://packaging.python.org/en/latest/requirements.html - data_files=[(ppt_control.__configdir__, ['ppt-control.ini'])], + data_files=[(ppt_control.CONFIG_DIR, [ppt_control.CONFIG_FILE])], + entry_points={'gui_scripts': ['ppt-control = ppt_control.ppt_control:start_interface']}, package_data={"": ["static/*", "static/icons/*"]} #include_package_data=True ) -- 2.43.2