Skip to content

Instantly share code, notes, and snippets.

@jdalbey
Created December 22, 2024 19:36
Show Gist options
  • Save jdalbey/b9e92044f28f6ec2d3dbbeef9ccba7a0 to your computer and use it in GitHub Desktop.
Save jdalbey/b9e92044f28f6ec2d3dbbeef9ccba7a0 to your computer and use it in GitHub Desktop.
SYLK spreadsheet renderer proof of concept

OK, I got a realtime GUI "proof of concept" working on my Linux workstation.
It uses Python and Libre Office Calc and a web browser.

The spreadsheet is a text document in SYLK format. It is attached as input_file.slk.

The Python script is attached as SylkRenderer.py

Save these files and open input_file.slk in a text editor.

In a terminal window launch a headless instance of LibreOffice Calc with this command:

libreoffice --headless --invisible --accept="socket,host=localhost,port=2002;urp;"

In a separate terminal window run the python script:

python SylkRenderer.py

Wait a few seconds and a browser window will open that renders the spreadsheet like this:

A B C D
1 2 3 6
4 5 6
7 8 9
18

In the text editor make a change to the .slk file. For example, change K9 to K99. Save the file. Wait a few seconds.

The browser window will automatically refresh showing the recalculated spreadsheet.

A B C D
1 2 3 6
4 5 6
7 8 99
108

Admittedly it's very clunky but I think this is the kind of workflow you wanted, where updating a serialized form of spreadsheet will be rendered in "realtime" in a graphical viewer.

I think what you ultimately want is to integrate the text editor and the viewer into a single application with two display panels.

input_file.slk:

ID;PCALCOOO32
C;X1;Y1;K1
C;X2;Y1;K2
C;X3;Y1;K3
C;X4;Y1;K6;EA1+B1+C1
C;X1;Y2;K4
C;X2;Y2;K5
C;X3;Y2;K6
C;X1;Y3;K7
C;X2;Y3;K8
C;X3;Y3;K99
C;X3;Y4;K18;ESUM(C1:C3)
E

SylkRenderer.py:

import os
import time
import uno
import webbrowser
from selenium import webdriver

# Render a SYLK file in a web browser with realtime updating.

# Function to get the last modified timestamp of the SYLK file
def get_file_mod_time(file_path):
    """Returns the last modified timestamp of the file."""
    return os.path.getmtime(file_path)

# Function to open SYLK file in LibreOffice Calc and export it to HTML
def open_and_recalculate_in_calc(slk_file_path, output_html_path):
    """Send the SYLK file to LibreOffice Calc and export to HTML."""
    # Connect to LibreOffice in headless mode
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext(
        "com.sun.star.bridge.UnoUrlResolver", localContext
    )
    context = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")

    # Get the LibreOffice Desktop (to manage open files)
    desktop = context.ServiceManager.createInstanceWithContext(
        "com.sun.star.frame.Desktop", context
    )

    # Open the .slk file
    file_url = uno.systemPathToFileUrl(os.path.abspath(slk_file_path))
    document = desktop.loadComponentFromURL(file_url, "_blank", 0, ())

    # Wait for LibreOffice to finish recalculating
    time.sleep(2)

    # Save the document as HTML
    output_url = uno.systemPathToFileUrl(os.path.abspath(output_html_path))

    # Filters used when saving document.
    # https://github.com/LibreOffice/core/tree/330df37c7e2af0564bcd2de1f171bed4befcc074/filter/source/config/fragments/filters
    filter = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
    filter.Name = 'FilterName'
    filter.Value = 'HTML (StarCalc)'
    filters = (filter,)
    # Store the HTML document
    document.storeToURL(output_url, filters)
    # Close the document
    document.close(True)

    print(f"Document saved as HTML: {output_html_path}")

# Function to launch Selenium and open the HTML file in the browser
def open_browser_with_html(html_file_path):
    """Open the HTML file in the browser using Selenium."""
    # Launch Chrome browser (or another browser if needed)
    options = webdriver.ChromeOptions()
    
    driver = webdriver.Chrome(options=options)
    driver.get(f"file:///{os.path.abspath(html_file_path)}")

    return driver

# Function to watch the SYLK file for changes and refresh the browser if needed
def watch_and_update(slk_file_path, output_html_path, browser_driver):
    """Watch the SYLK file for changes and update the browser if necessary."""
    # Store the initial last modified timestamp of the SYLK file
    last_mod_time = get_file_mod_time(slk_file_path)

    while True:
        # Wait for a short period before checking for changes
        time.sleep(2)

        # Check if the file last modified time has changed
        current_mod_time = get_file_mod_time(slk_file_path)
        if current_mod_time != last_mod_time:
            print(f"File modified, updating...")

            # Update the last modified time
            last_mod_time = current_mod_time

            # Send the updated SYLK file to LibreOffice Calc and export it
            open_and_recalculate_in_calc(slk_file_path, output_html_path)

            # Refresh the browser with the new content
            browser_driver.refresh()

            print("Browser refreshed with updated HTML content.")

# Main function to orchestrate the process
def main(slk_file_path, output_html_path):
    # Step 1: Open the SYLK file in LibreOffice Calc and export to HTML
    open_and_recalculate_in_calc(slk_file_path, output_html_path)

    # Step 2: Open the resulting HTML file in a browser using Selenium
    browser_driver = open_browser_with_html(output_html_path)

    # Step 3: Monitor the SYLK file for changes and refresh the browser if the file is modified
    watch_and_update(slk_file_path, output_html_path, browser_driver)

if __name__ == "__main__":
    # Define the paths for the input and output files
    slk_file = "input_file.slk"  # Path to your SYLK file
    html_file = "output_file.html"  # Desired output HTML file

    # Run the main process
    main(slk_file, html_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment