Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
d35031b
:construction: start with bundleing cli tool
enryH Feb 24, 2025
41cdd53
:sparkles: with pyvis added the basic example works using the cli
enryH Feb 24, 2025
47b924b
:construction::bug: streamlit not easy to run from within vuegen script
enryH Feb 24, 2025
9bc0177
Merge branch 'main' into os_installers
enryH Feb 25, 2025
d874d39
:sparkles: start streamlit without subprocess - runs from self-contai…
enryH Feb 26, 2025
363e8bf
:bug: ensure that networking is properly enabled
enryH Feb 26, 2025
0c1d279
:art: make Streamlit report aware of where it runs
enryH Feb 26, 2025
1d23016
:bug: add st_aggrid as it is imported, not installed
enryH Feb 26, 2025
c8eac52
:construction: start testing of bundeling for quarto
enryH Feb 26, 2025
2387f23
:memo: add current spec file
enryH Feb 26, 2025
1fa37c8
:sparkles:: First simple working example of a GUI (streamlit local)
enryH Feb 28, 2025
3ee9a55
:sparkles: add basic GUI command
enryH Feb 28, 2025
c4925d6
:art: set default to running streamlit app directly
enryH Feb 28, 2025
ea08a8a
:art: format app script further
enryH Feb 28, 2025
c65ce37
⏪ undo deletion of dependencies
enryH Feb 28, 2025
6fe5165
Merge branch 'main' into os_installers
enryH Feb 28, 2025
f13f383
:sparkles::art: Try to build executable on ubuntu (+ formating)
enryH Feb 28, 2025
71cc175
:bug: cli different than on MAC, ⚡ speed up testing for now
enryH Feb 28, 2025
8a77e2f
:bug: install vuegen itself with gui deps
enryH Feb 28, 2025
8712c2a
:art: introduce labels for runner images
enryH Feb 28, 2025
e9c0047
:sparkles: add other runner OS platforms with labels
enryH Feb 28, 2025
6a0fc78
:bug: poetry optional dependencies need to be explicit, fix name of job
enryH Feb 28, 2025
ef61a03
:bug: colon not allowed in name...
enryH Feb 28, 2025
c55208a
:rewind: undo command change
enryH Feb 28, 2025
b9a66ff
:sparkles: define basic example as default, ship it with bundle
enryH Feb 28, 2025
a53e49d
:art: make process label somehow fit UI limitations
enryH Mar 3, 2025
2dd4e74
Merge branch 'main' into os_installers
enryH Mar 3, 2025
0009bb8
:bug: acutally get variable value, not test instance exist
enryH Mar 3, 2025
29d1d41
:rewind: restore main branch commands (render, not convert)
enryH Mar 3, 2025
d5e344d
:truck::fire: remove cli exectuable for GUI bundle only
enryH Mar 3, 2025
6444353
🔧 Only show streamlit for now (quarto not yet bundled)
enryH Mar 3, 2025
1e236f8
:construction: add quarto execution for bundle
enryH Mar 3, 2025
a13e784
:sparkles: get quarto html output from bundle
enryH Mar 5, 2025
fb6d368
Merge branch 'main' of https://github.com/Multiomics-Analytics-Group/…
enryH Mar 5, 2025
d46bc02
:bug: add bundle dependencies, RadioButton toggle
enryH Mar 5, 2025
fb1f4a2
:bug: fix command om cdci
enryH Mar 5, 2025
d090c80
:arg: reduce console output (global logging level is DEBUG?)
enryH Mar 5, 2025
e1150c0
:bug: use macos-15 explicitly
enryH Mar 5, 2025
86c5660
:art: format and rename callback factory
enryH Mar 5, 2025
f9b7979
:memo: add hint to execution procedure
enryH Mar 5, 2025
672bb71
:bug: try to fix python executable path
enryH Mar 5, 2025
a3b4688
:art: format helper script
enryH Mar 5, 2025
068239a
:construction: move python file to main folder
enryH Mar 6, 2025
4e93792
🚧 try to use miniconda action for python exe
enryH Mar 6, 2025
9f90814
🚧 inspect conda on GitHub Actions runners
enryH Mar 6, 2025
40244ea
🚧 find the documented test environment
enryH Mar 6, 2025
d89adf2
:bug: test environment not auto-activated
enryH Mar 6, 2025
6736e37
🚧 try to activate test env...
enryH Mar 6, 2025
866616c
🚧 get environment to be activated
enryH Mar 6, 2025
03af479
🚧 modify default shell params
enryH Mar 6, 2025
7874992
🚧 use quarto native functionality
enryH Mar 6, 2025
5a36a0e
🚧 try to add tinytex
enryH Mar 6, 2025
743221c
:bug: check for dependencies
enryH Mar 6, 2025
668c245
:bug: try to add both tools on the fly (chromnium and tinyte) needed …
enryH Mar 7, 2025
9ca1e11
Merge branch 'main' into os_installers
enryH Mar 7, 2025
ac13935
:bug: close parantheses (merge error)
Mar 7, 2025
012526f
:bug: make Windows Path for bundled quarto executable
Mar 7, 2025
f52e8c6
🚧 test further options
Mar 7, 2025
0196926
:art: add file directory selection - allow to specify a directory
enryH Mar 8, 2025
62a6c69
🚧 test now with onefile
enryH Mar 8, 2025
830b557
🚧 test also non-windowed app
enryH Mar 8, 2025
9ae1074
:bug: set default static dir only on main methods, pass parameter on …
enryH Mar 8, 2025
a361d99
:bug: allow to specify log directory
enryH Mar 9, 2025
83b1b66
:sparkles: allow to set output directory, pass on to get_report
enryH Mar 9, 2025
283e09a
:art: separate by row, cont. increments
enryH Mar 10, 2025
0c83c9f
:bug: fix #89 - check if output was actually created
enryH Mar 10, 2025
aaeadd4
🚧 allow to set Python Path manually
enryH Mar 10, 2025
9682be6
:bug: set generic kernel name
enryH Mar 10, 2025
1cefe8c
:art: fix typo in function name
enryH Mar 10, 2025
f6cbac9
:art: use isostandard for log file format
enryH Mar 11, 2025
2d585f2
:sparkles: this let's the interpreter look for the packages in the sh…
enryH Mar 11, 2025
51cc2f0
:bug: only add folder as import, not the zip file
enryH Mar 11, 2025
038909d
:bug: fix cdci
enryH Mar 11, 2025
550c78a
🚧 build for python 3.12 with jupyter installed
enryH Mar 11, 2025
6bea275
🚧 add makefile to document local build on mac
enryH Mar 12, 2025
b2b86aa
:bug: example data not in normal writable file path for bundled app, …
enryH Mar 12, 2025
41363b1
:bug: svg from readme cannot be rendered
enryH Mar 12, 2025
c2d1fbf
:bug: reset logger (relevant for GUI)
enryH Mar 12, 2025
f300e1a
:boom: Write config file based on directory to output directory of re…
enryH Mar 13, 2025
f3eef5c
:sparkles: improve folder dialog for PythonPath, set str explicitly f…
enryH Mar 13, 2025
dbe96ed
:art: set default logger_id (so not all loggers are set to debug)
Mar 14, 2025
8222d2b
:bug: do not overwrite _PATH
Mar 14, 2025
65465b9
:art: add logo to app and clean-up app.py
enryH Mar 14, 2025
0c85974
:art: do not reset directories when nothing is set, adj. app size
enryH Mar 17, 2025
2f86c1e
:sparkles: cache python exectutable set previously
enryH Mar 17, 2025
fafc95c
:art: format, escape pagemark
enryH Mar 17, 2025
91f6878
Merge branch 'main' into os_installers
enryH Mar 17, 2025
576a36e
:sparkles: copy example data to vuegen_gui dir in home directory, lib…
enryH Mar 17, 2025
189bcff
:art: remove comment and sort
Mar 18, 2025
ecf8d4e
:bug: use selected logger, not always root to reset handlers
Mar 18, 2025
b8df80a
:sparkles: document and restore all actions, use windowed build
enryH Mar 18, 2025
da8bc30
:bug: add 'save' tag to ensure that altair plots can be exported
enryH Mar 18, 2025
e88a93b
:art: format docs as they were adapted
enryH Mar 18, 2025
e5b5985
:memo: link README of GUI
enryH Mar 18, 2025
4a191d8
🚧 gh release upload for adding executables to release?
enryH Mar 19, 2025
fd46857
:art: no time zone in log file name, use onefile for windows
enryH Mar 19, 2025
907f083
:art: remove some comments and fix docstring
enryH Mar 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions executables/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Pyinstaller one folder executable

- pyvis templates were not copied, so make these explicit (see [this](https://stackoverflow.com/a/72687433/9684872))

```bash
# from this README folder
pyinstaller -D --collect-all pyvis --collect-all streamlit --collect-all st_aggrid -n vuegen ../src/vuegen/__main__.py
```

```bash
# other pyinstaller options
--noconfirm # is used to avoid the prompt for overwriting the existing dist folder
```

## Pyinstaller options

```bash
What to generate:
-D, --onedir Create a one-folder bundle containing an executable (default)
-F, --onefile Create a one-file bundled executable.
--specpath DIR Folder to store the generated spec file (default: current directory)
-n NAME, --name NAME Name to assign to the bundled app and spec file (default: first script's basename)
Windows and macOS specific options:
-c, --console, --nowindowed
Open a console window for standard i/o (default). On Windows this option has no effect if the first script is a
'.pyw' file.
-w, --windowed, --noconsole
Windows and macOS: do not provide a console window for standard i/o. On macOS this also triggers building a
macOS .app bundle. On Windows this option is automatically set if the first script is a '.pyw' file. This option
is ignored on *NIX systems.
```

## Using bundled executable

try using basic example

```bash
./dist/vuegen/vuegen -d ../docs/example_data/Basic_example_vuegen_demo_notebook -st_autorun
```
55 changes: 55 additions & 0 deletions executables/vuegen.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_all

datas = []
binaries = []
hiddenimports = []
tmp_ret = collect_all('pyvis')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('streamlit')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('st_aggrid')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]


a = Analysis(
['../src/vuegen/__main__.py'],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='vuegen',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='vuegen',
)
39 changes: 39 additions & 0 deletions gui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# VueGen GUI

## Local execution of the GUI

Install required dependencies from package

```bash
pip install 'vuegen[gui]'
# or with local repo
pip install '.[gui]'
# or for local editable install
pip install -e '.[gui]'
```

Can be started locally with

```bash
# from within gui directory
python app.py
```

## Build executable GUI

For now do not add the `--windowed` option, as it will not show the console output,
which is useful for debugging and especially terminating any running processes, e.g.
as the streamlit server and the GUI itself.

```bash
# from this README folder
pyinstaller \
-n vuegen_gui \
--no-confirm \
--onedir \
--collect-all pyvis \
--collect-all streamlit \
--collect-all st_aggrid \
--collect-all customtkinter \
app.py
```
162 changes: 162 additions & 0 deletions gui/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""GUI for vuegen command-line tool.

usage: VueGen [-h] [-c CONFIG] [-dir DIRECTORY] [-rt REPORT_TYPE]
[-st_autorun]

optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Path to the YAML configuration file.
-dir DIRECTORY, --directory DIRECTORY
Path to the directory from which the YAML
config will be inferred.
-rt REPORT_TYPE, --report_type REPORT_TYPE
Type of the report to generate (streamlit,
html, pdf, docx, odt, revealjs, pptx, or
jupyter).
-st_autorun, --streamlit_autorun
Automatically run the Streamlit app after
report generation.
"""

import sys
import tkinter as tk

import customtkinter

from vuegen.__main__ import main
from vuegen.report import ReportType

customtkinter.set_appearance_mode("system")
customtkinter.set_default_color_theme("dark-blue")


##########################################################################################
# callbacks
def create_run_vuegen(is_dir, config_path, report_type, run_streamlit):
def inner():
args = ["vuegen"]
if is_dir:
args.append("--directory")
else:
args.append("--config")
args.append(config_path.get())
args.append("--report_type")
args.append(report_type.get())
if run_streamlit:
args.append("--streamlit_autorun")
print("args:", args)
sys.argv = args
main() # Call the main function from vuegen

return inner


def optionmenu_callback(choice):
"""Good for logging changes?"""
print("optionmenu dropdown clicked:", choice)


def radiobutton_event(value):
def radio_button_callback():
print("radiobutton toggled, current value:", value.get())

return radio_button_callback


##########################################################################################
# APP
app = customtkinter.CTk()
app.geometry("460x400")
app.title("VueGen GUI")

##########################################################################################
# Config or directory input
ctk_label_config = customtkinter.CTkLabel(
app,
text="Add path to config file or directory. Select radio button accordingly",
)
ctk_label_config.grid(row=0, column=0, columnspan=2, padx=20, pady=20)
is_dir = tk.IntVar(value=1)
callback_radio_config = radiobutton_event(is_dir)
ctk_radio_config_0 = customtkinter.CTkRadioButton(
app,
text="Use config",
command=callback_radio_config,
variable=is_dir,
value=0,
)
ctk_radio_config_0.grid(row=1, column=0, padx=20, pady=2)
ctk_radio_config_1 = customtkinter.CTkRadioButton(
app,
text="Use dir",
command=callback_radio_config,
variable=is_dir,
value=1,
)
ctk_radio_config_1.grid(row=1, column=1, padx=20, pady=2)

config_path = tk.StringVar()
config_path_entry = customtkinter.CTkEntry(
app,
width=400,
textvariable=config_path,
)
config_path_entry.grid(row=2, column=0, columnspan=2, padx=20, pady=10)

##########################################################################################
# Report type dropdown
# - get list of report types from Enum
report_types = [report_type.value.lower() for report_type in ReportType]

ctk_label_report = customtkinter.CTkLabel(
app,
text="Select type of report to generate (use streamlit for now)",
)
ctk_label_report.grid(row=3, column=0, columnspan=2, padx=20, pady=20)
report_type = tk.StringVar(value=report_types[0])
report_dropdown = customtkinter.CTkOptionMenu(
app,
values=report_types,
variable=report_type,
command=optionmenu_callback,
)
report_dropdown.grid(row=4, column=0, columnspan=2, padx=20, pady=20)

_report_type = report_dropdown.get()
print("report_type value:", _report_type)

##########################################################################################
# Run Streamlit radio button
run_streamlit = tk.IntVar(value=1)
callback_radio_st_run = radiobutton_event(run_streamlit)
ctk_radio_st_autorun_1 = customtkinter.CTkRadioButton(
app,
text="autorun streamlit",
value=1,
variable=run_streamlit,
command=callback_radio_st_run,
)
ctk_radio_st_autorun_1.grid(row=5, column=0, padx=20, pady=20)
ctk_radio_st_autorun_0 = customtkinter.CTkRadioButton(
app,
text="skip starting streamlit",
value=0,
variable=run_streamlit,
command=callback_radio_st_run,
)
ctk_radio_st_autorun_0.grid(row=5, column=1, padx=20, pady=20)

##########################################################################################
# Run VueGen button
run_vuegen = create_run_vuegen(is_dir, config_path, report_type, run_streamlit)
run_button = customtkinter.CTkButton(
app,
text="Run VueGen",
command=run_vuegen,
)
run_button.grid(row=6, column=0, columnspan=2, padx=20, pady=20)

##########################################################################################
# Run the app in the mainloop
app.mainloop()
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ build-backend = "poetry_dynamic_versioning.backend"
# https://stackoverflow.com/a/60990574/9684872
[tool.poetry.extras]
docs = ["sphinx", "sphinx-book-theme", "myst-nb", "ipywidgets", "sphinx-new-tab-link", "jupytext"]
gui = ["customtkinter"]

[tool.poetry.scripts]
# https://python-poetry.org/docs/pyproject/#scripts
Expand Down
35 changes: 26 additions & 9 deletions src/vuegen/quarto_reportview.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import subprocess
import sys
from pathlib import Path
from typing import List

Expand All @@ -20,6 +21,12 @@ class QuartoReportView(r.ReportView):

def __init__(self, report: r.Report, report_type: r.ReportType):
super().__init__(report=report, report_type=report_type)
self.BUNDLED_EXECUTION = False
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
self.report.logger.info("running in a PyInstaller bundle")
self.BUNDLED_EXECUTION = True
else:
self.report.logger.info("running in a normal Python process")

def generate_report(
self, output_dir: str = BASE_DIR, static_dir: str = STATIC_FILES_DIR
Expand Down Expand Up @@ -155,22 +162,32 @@ def run_report(self, output_dir: str = BASE_DIR) -> None:
The folder where the report was generated (default is 'sections').
"""
try:
subprocess.run(
["quarto", "render", os.path.join(output_dir, f"{self.BASE_DIR}.qmd")],
check=True,
)
if self.report_type == r.ReportType.JUPYTER:
if not self.BUNDLED_EXECUTION:
subprocess.run(
[
"quarto",
"convert",
"render",
os.path.join(output_dir, f"{self.BASE_DIR}.qmd"),
],
check=True,
)
self.report.logger.info(
f"'{self.report.title}' '{self.report_type}' report rendered"
)
if self.report_type == r.ReportType.JUPYTER:
subprocess.run(
[
"quarto",
"convert",
os.path.join(output_dir, f"{self.BASE_DIR}.qmd"),
],
check=True,
)
self.report.logger.info(
f"'{self.report.title}' '{self.report_type}' report rendered"
)
else:
self.report.logger.info(
f"Quarto report '{self.report.title}' '{self.report_type}' cannot "
"be run in a PyInstaller bundle"
)
except subprocess.CalledProcessError as e:
self.report.logger.error(
f"Error running '{self.report.title}' {self.report_type} report: {str(e)}"
Expand Down
Loading
Loading