mirror of
https://github.com/ublue-os/forge.git
synced 2025-07-14 13:45:46 +03:00
wip - nicegui
This commit is contained in:
parent
c3ed45a21b
commit
85b0b3fdc5
39 changed files with 2748 additions and 896 deletions
59
anvil/nicegui/_tests/main.py
Normal file
59
anvil/nicegui/_tests/main.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import os.path
|
||||
import platform
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
async def run_command(command: str) -> None:
|
||||
"""Run a command in the background and display the output in the pre-created dialog."""
|
||||
dialog.open()
|
||||
result.content = ''
|
||||
command = command.replace('python3', sys.executable) # NOTE replace with machine-independent Python path (#1240)
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*shlex.split(command, posix='win' not in sys.platform.lower()),
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT,
|
||||
cwd=os.path.dirname(os.path.abspath(__file__))
|
||||
)
|
||||
# NOTE we need to read the output in chunks, otherwise the process will block
|
||||
output = ''
|
||||
while True:
|
||||
new = await process.stdout.read(4096)
|
||||
if not new:
|
||||
break
|
||||
output += new.decode()
|
||||
# NOTE the content of the markdown element is replaced every time we have new output
|
||||
result.content = f'```\n{output}\n```'
|
||||
|
||||
with ui.header().classes(replace='row items-center') as header:
|
||||
ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
|
||||
with ui.tabs() as tabs:
|
||||
ui.tab('Home')
|
||||
ui.tab('Ansible')
|
||||
ui.tab('Registry')
|
||||
|
||||
with ui.footer(value=False) as footer:
|
||||
ui.label('Footer')
|
||||
|
||||
with ui.left_drawer().classes('bg-blue-100') as left_drawer:
|
||||
ui.label('Side menu')
|
||||
|
||||
ui.button('Build Project', on_click=lambda: run_command('python3 runner.py')).props('no-caps')
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
result = ui.markdown()
|
||||
|
||||
|
||||
with ui.page_sticky(position='bottom-right', x_offset=20, y_offset=20):
|
||||
ui.button(on_click=footer.toggle, icon='contact_support').props('fab')
|
||||
|
||||
with ui.tab_panels(tabs, value='A').classes('w-full'):
|
||||
with ui.tab_panel('A'):
|
||||
ui.label('Content of A')
|
||||
with ui.tab_panel('B'):
|
||||
ui.label('Content of B')
|
||||
with ui.tab_panel('C'):
|
||||
ui.label('Content of C')
|
||||
|
||||
ui.run()
|
14
anvil/nicegui/_tests/runner.py
Normal file
14
anvil/nicegui/_tests/runner.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import sys
|
||||
import ansible_runner
|
||||
|
||||
# run ansible/generic commands in interactive mode locally
|
||||
out, err, rc = ansible_runner.run_command(
|
||||
executable_cmd='ansible-playbook',
|
||||
cmdline_args=['../playbooks/project_clone.yml', '-i', '../inventory.yml'],
|
||||
input_fd=sys.stdin,
|
||||
output_fd=sys.stdout,
|
||||
error_fd=sys.stderr,
|
||||
)
|
||||
print("rc: {}".format(rc))
|
||||
print("out: {}".format(out))
|
||||
print("err: {}".format(err))
|
88
anvil/nicegui/_tests/test.py
Normal file
88
anvil/nicegui/_tests/test.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# from nicegui import ui
|
||||
|
||||
# @ui.page('/')
|
||||
# def page_layout():
|
||||
# ui.label('CONTENT')
|
||||
# [ui.label(f'Line {i}') for i in range(100)]
|
||||
# with ui.header(elevated=True).style('background-color: #3874c8').classes('items-center justify-between'):
|
||||
# ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
|
||||
# ui.label('HEADER')
|
||||
# with ui.left_drawer(fixed=False).style('background-color: #ebf1fa').props('bordered') as left_drawer:
|
||||
# ui.label('LEFT DRAWER')
|
||||
# with ui.footer().style('background-color: #3874c8'):
|
||||
# ui.label('FOOTER')
|
||||
|
||||
# # ui.link('show page with fancy layout', page_layout)
|
||||
|
||||
# ui.run()
|
||||
|
||||
# from nicegui import ui
|
||||
|
||||
# with ui.header().classes(replace='row items-center') as header:
|
||||
# ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
|
||||
# with ui.tabs() as tabs:
|
||||
# ui.tab('Home')
|
||||
# ui.tab('Ansible')
|
||||
# ui.tab('Registry')
|
||||
|
||||
# with ui.footer(value=False) as footer:
|
||||
# ui.label('Footer')
|
||||
|
||||
# with ui.left_drawer().classes('bg-blue-100') as left_drawer:
|
||||
# ui.label('Side menu')
|
||||
|
||||
# with ui.page_sticky(position='bottom-right', x_offset=20, y_offset=20):
|
||||
# ui.button(on_click=footer.toggle, icon='contact_support').props('fab')
|
||||
|
||||
# with ui.tab_panels(tabs, value='A').classes('w-full'):
|
||||
# with ui.tab_panel('A'):
|
||||
# ui.label('Content of A')
|
||||
# with ui.tab_panel('B'):
|
||||
# ui.label('Content of B')
|
||||
# with ui.tab_panel('C'):
|
||||
# ui.label('Content of C')
|
||||
|
||||
# ui.run()
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import os.path
|
||||
import platform
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
async def run_command(command: str) -> None:
|
||||
"""Run a command in the background and display the output in the pre-created dialog."""
|
||||
dialog.open()
|
||||
result.content = ''
|
||||
command = command.replace('python3', sys.executable) # NOTE replace with machine-independent Python path (#1240)
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*shlex.split(command, posix='win' not in sys.platform.lower()),
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT,
|
||||
cwd=os.path.dirname(os.path.abspath(__file__))
|
||||
)
|
||||
# NOTE we need to read the output in chunks, otherwise the process will block
|
||||
output = ''
|
||||
while True:
|
||||
new = await process.stdout.read(4096)
|
||||
if not new:
|
||||
break
|
||||
output += new.decode()
|
||||
# NOTE the content of the markdown element is replaced every time we have new output
|
||||
result.content = f'```\n{output}\n```'
|
||||
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
result = ui.markdown()
|
||||
|
||||
ui.button('python3 runner.py', on_click=lambda: run_command('python3 runner.py')).props('no-caps')
|
||||
ui.button('python3 slow.py', on_click=lambda: run_command('python3 slow.py')).props('no-caps')
|
||||
with ui.row().classes('items-center'):
|
||||
ui.button('python3 hello.py "<message>"', on_click=lambda: run_command(f'python3 hello.py "{message.value}"')) \
|
||||
.props('no-caps')
|
||||
message = ui.input('message', value='NiceGUI')
|
||||
|
||||
# NOTE: On Windows reload must be disabled to make asyncio.create_subprocess_exec work (see https://github.com/zauberzeug/nicegui/issues/486)
|
||||
ui.run(reload=platform.system() != 'Windows')
|
35
anvil/nicegui/main.py
Executable file
35
anvil/nicegui/main.py
Executable file
|
@ -0,0 +1,35 @@
|
|||
import pages
|
||||
import pages.about
|
||||
import pages.ansible
|
||||
import pages.home
|
||||
import pages.registry
|
||||
import theme
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
@ui.page("/")
|
||||
def index_page() -> None:
|
||||
with theme.frame("Home"):
|
||||
pages.home.content()
|
||||
|
||||
|
||||
@ui.page("/ansible")
|
||||
def ansible_page() -> None:
|
||||
with theme.frame("Ansible"):
|
||||
pages.ansible.content()
|
||||
|
||||
|
||||
@ui.page("/registry")
|
||||
def registry_page() -> None:
|
||||
with theme.frame("Registry"):
|
||||
pages.registry.content()
|
||||
|
||||
|
||||
@ui.page("/about")
|
||||
def about_page() -> None:
|
||||
with theme.frame("About"):
|
||||
pages.about.content()
|
||||
|
||||
|
||||
ui.run(title="uBlue Forge", port=3000)
|
8
anvil/nicegui/menu.py
Normal file
8
anvil/nicegui/menu.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from nicegui import ui
|
||||
|
||||
|
||||
def menu() -> None:
|
||||
ui.link("Home", "/").classes(replace="text-white")
|
||||
ui.link("Ansible", "/ansible").classes(replace="text-white")
|
||||
ui.link("Registry", "/registry").classes(replace="text-white")
|
||||
ui.link("About", "/about").classes(replace="text-white")
|
1
anvil/nicegui/pages/__init__.py
Normal file
1
anvil/nicegui/pages/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from nicegui import ui
|
10
anvil/nicegui/pages/about.py
Normal file
10
anvil/nicegui/pages/about.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
def content() -> None:
|
||||
project_root = str(get_project_root())
|
||||
ui.label("Work in progress...").classes("text-h6")
|
||||
ui.image(project_root + "/nicegui/pages/assets/work-in-progress.png").classes(
|
||||
"w-[200%]"
|
||||
)
|
54
anvil/nicegui/pages/ansible.py
Normal file
54
anvil/nicegui/pages/ansible.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import ansible_runner
|
||||
import re
|
||||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
# Ansible integration
|
||||
def run_ansible_playbook(playbook_name: str, ngui_log: ui.log):
|
||||
project_root = str(get_project_root())
|
||||
playbook_path = project_root + "/ansible/playbooks/"
|
||||
inventory_path = project_root + "/ansible/inventory.yml"
|
||||
response, error, return_code = ansible_runner.interface.run_command(
|
||||
executable_cmd="ansible-playbook",
|
||||
cmdline_args=[playbook_path + playbook_name, "-i", inventory_path],
|
||||
)
|
||||
# remove color characters from response until clear how to display them in a log
|
||||
ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-9;#]+[mGK]?)")
|
||||
ansible_log = format(ansi_escape.sub("", response))
|
||||
ngui_log.clear()
|
||||
ngui_log.push(ansible_log)
|
||||
|
||||
|
||||
# Page content
|
||||
def content() -> None:
|
||||
with ui.row().classes("w-full"):
|
||||
with ui.row().classes("w-full"):
|
||||
# First Row
|
||||
with ui.card().classes("h-full"):
|
||||
ui.label("Build").classes("text-h6")
|
||||
ui.button(
|
||||
text="Clone project",
|
||||
on_click=lambda: run_ansible_playbook(
|
||||
"project_clone.yml", ngui_log=log
|
||||
),
|
||||
)
|
||||
ui.button(
|
||||
text="Build project",
|
||||
on_click=lambda: run_ansible_playbook(
|
||||
"project_build.yml", ngui_log=log
|
||||
),
|
||||
)
|
||||
# Second Row
|
||||
with ui.card().classes("h-full"):
|
||||
ui.label("Deploy").classes("text-h6")
|
||||
ui.button(
|
||||
"Deploy VM",
|
||||
on_click=lambda: ui.notify("This playbook is not implemented yet"),
|
||||
)
|
||||
|
||||
with ui.row().classes("w-full"):
|
||||
with ui.card().classes("w-full"):
|
||||
ui.label("Playbook Log").classes("text-h6")
|
||||
ui.button("Clear Log", on_click=lambda: log.clear())
|
||||
log = ui.log().classes("w-full h-full")
|
BIN
anvil/nicegui/pages/assets/work-in-progress.png
Normal file
BIN
anvil/nicegui/pages/assets/work-in-progress.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 524 KiB |
10
anvil/nicegui/pages/home.py
Normal file
10
anvil/nicegui/pages/home.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
def content() -> None:
|
||||
project_root = str(get_project_root())
|
||||
ui.label("Work in progress...").classes("text-h6")
|
||||
ui.image(project_root + "/nicegui/pages/assets/work-in-progress.png").classes(
|
||||
"w-[200%]"
|
||||
)
|
10
anvil/nicegui/pages/registry.py
Normal file
10
anvil/nicegui/pages/registry.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
def content() -> None:
|
||||
project_root = str(get_project_root())
|
||||
ui.label("Work in progress...").classes("text-h6")
|
||||
ui.image(project_root + "/nicegui/pages/assets/work-in-progress.png").classes(
|
||||
"w-[200%]"
|
||||
)
|
26
anvil/nicegui/theme.py
Normal file
26
anvil/nicegui/theme.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from contextlib import contextmanager
|
||||
|
||||
from menu import menu
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
@contextmanager
|
||||
def frame(navigation_title: str, enable_right_drawer: bool = False):
|
||||
"""Custom page frame to share the same styling and behavior across all pages"""
|
||||
ui.colors(primary="#4051b5", secondary="#dddbff", accent="#171d9a")
|
||||
with ui.header():
|
||||
with ui.row():
|
||||
menu()
|
||||
ui.space()
|
||||
with ui.link(target="https://github.com/ublue-os/forge", new_tab=True):
|
||||
ui.icon("eva-github").classes("text-2xl")
|
||||
|
||||
with ui.column().classes():
|
||||
ui.label(navigation_title).classes("text-h4")
|
||||
yield
|
||||
|
||||
with ui.footer(value=False):
|
||||
ui.add_head_html(
|
||||
'<link href="https://unpkg.com/eva-icons@1.1.3/style/eva-icons.css" rel="stylesheet" />'
|
||||
)
|
5
anvil/nicegui/utils.py
Normal file
5
anvil/nicegui/utils.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from pathlib import Path
|
||||
|
||||
|
||||
def get_project_root() -> Path:
|
||||
return Path(__file__).parent.parent
|
Loading…
Add table
Add a link
Reference in a new issue