From c997dbdf6694664afda600f745e1e782cf34fb61 Mon Sep 17 00:00:00 2001 From: babysor00 Date: Fri, 29 Apr 2022 23:48:11 +0800 Subject: [PATCH] Make framework to support multiple pages --- .vscode/launch.json | 4 +- mkgui/app.py | 13 +++--- mkgui/base/_cli.py | 4 +- mkgui/base/api/fastapi_app.py | 69 --------------------------- mkgui/base/core.py | 18 +++---- mkgui/base/ui/streamlit_ui.py | 80 +++++++++++++++++++------------- mkgui/base/ui/streamlit_utils.py | 3 ++ 7 files changed, 70 insertions(+), 121 deletions(-) delete mode 100644 mkgui/base/api/fastapi_app.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 8b82132..85cf175 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,6 @@ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { "name": "Python: Web", "type": "python", @@ -68,8 +67,7 @@ "request": "launch", "program": "mkgui\\base\\_cli.py", "console": "integratedTerminal", - "args": [ "mkgui.app:mocking_bird" - ] + "args": [] }, ] } diff --git a/mkgui/app.py b/mkgui/app.py index 8387a60..775f695 100644 --- a/mkgui/app.py +++ b/mkgui/app.py @@ -38,6 +38,9 @@ if os.path.isdir(VOC_MODELS_DIRT): class Input(BaseModel): + message: str = Field( + ..., example="欢迎使用工具箱, 现已支持中文输入!", alias="文本内容" + ) local_audio_file: audio_input_selection = Field( ..., alias="输入语音(本地wav)", description="选择本地语音文件." @@ -56,9 +59,6 @@ class Input(BaseModel): ..., alias="语音编码模型", description="选择语音编码模型文件(目前只支持HifiGan类型)." ) - message: str = Field( - ..., example="欢迎使用工具箱, 现已支持中文输入!", alias="输出文本内容" - ) class AudioEntity(BaseModel): content: bytes @@ -72,7 +72,8 @@ class Output(BaseModel): If this method is implmeneted, it will be used instead of the default Output UI renderer. """ src, result = self.__root__ - streamlit_app.subheader("Result Audio") + + streamlit_app.subheader("Synthesized Audio") streamlit_app.audio(result.content, format="audio/wav") fig, ax = plt.subplots() @@ -85,8 +86,8 @@ class Output(BaseModel): streamlit_app.pyplot(fig) -def mocking_bird(input: Input) -> Output: - """欢迎使用MockingBird Web 2""" +def main(input: Input) -> Output: + """synthesize(合成)""" # load models encoder.load_model(Path(input.encoder.value)) current_synt = Synthesizer(Path(input.synthesizer.value)) diff --git a/mkgui/base/_cli.py b/mkgui/base/_cli.py index 937a839..036ee95 100644 --- a/mkgui/base/_cli.py +++ b/mkgui/base/_cli.py @@ -5,7 +5,7 @@ import typer cli = typer.Typer() @cli.command() -def launch_ui(opyrator: str, port: int = typer.Option(8051, "--port", "-p")) -> None: +def launch_ui(port: int = typer.Option(8051, "--port", "-p")) -> None: """Start a graphical UI server for the opyrator. The UI is auto-generated from the input- and output-schema of the given function. @@ -15,7 +15,7 @@ def launch_ui(opyrator: str, port: int = typer.Option(8051, "--port", "-p")) -> sys.path.append(os.getcwd()) from mkgui.base.ui.streamlit_ui import launch_ui - launch_ui(opyrator, port) + launch_ui(port) if __name__ == "__main__": cli() \ No newline at end of file diff --git a/mkgui/base/api/fastapi_app.py b/mkgui/base/api/fastapi_app.py deleted file mode 100644 index 234b6c5..0000000 --- a/mkgui/base/api/fastapi_app.py +++ /dev/null @@ -1,69 +0,0 @@ -from typing import Any, Dict - -from fastapi import FastAPI, status -from fastapi.middleware.cors import CORSMiddleware -from starlette.responses import RedirectResponse - -from mkgui.base import Opyrator -from mkgui.base.api.fastapi_utils import patch_fastapi - - -def launch_api(opyrator_path: str, port: int = 8501, host: str = "0.0.0.0") -> None: - import uvicorn - - from mkgui.base import Opyrator - from mkgui.base.api import create_api - - app = create_api(Opyrator(opyrator_path)) - uvicorn.run(app, host=host, port=port, log_level="info") - - -def create_api(opyrator: Opyrator) -> FastAPI: - - title = opyrator.name - if "opyrator" not in opyrator.name.lower(): - title += " - Opyrator" - - # TODO what about version? - app = FastAPI(title=title, description=opyrator.description) - - patch_fastapi(app) - - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - - @app.post( - "/call", - operation_id="call", - response_model=opyrator.output_type, - # response_model_exclude_unset=True, - summary="Execute the opyrator.", - status_code=status.HTTP_200_OK, - ) - def call(input: opyrator.input_type) -> Any: # type: ignore - """Executes this opyrator.""" - return opyrator(input) - - @app.get( - "/info", - operation_id="info", - response_model=Dict, - # response_model_exclude_unset=True, - summary="Get info metadata.", - status_code=status.HTTP_200_OK, - ) - def info() -> Any: # type: ignore - """Returns informational metadata about this Opyrator.""" - return {} - - # Redirect to docs - @app.get("/", include_in_schema=False) - def root() -> Any: - return RedirectResponse("./docs") - - return app diff --git a/mkgui/base/core.py b/mkgui/base/core.py index 069d352..fe7ed50 100644 --- a/mkgui/base/core.py +++ b/mkgui/base/core.py @@ -113,7 +113,7 @@ class Opyrator: self.function = func self._name = "Opyrator" - self._description = "" + self._action = "Execute" self._input_type = None self._output_type = None @@ -140,7 +140,7 @@ class Opyrator: # Get description from function doc_string = inspect.getdoc(self.function) if doc_string: - self._description = doc_string + self._action = doc_string except Exception: pass elif hasattr(self.function, "__call__"): @@ -155,19 +155,19 @@ class Opyrator: pass try: - # Get description from + # Get action from doc_string = inspect.getdoc(self.function.__call__) # type: ignore if doc_string: - self._description = doc_string + self._action = doc_string if ( - not self._description - or self._description == "Call self as a function." + not self._action + or self._action == "Call" ): # Get docstring from class instead of __call__ function doc_string = inspect.getdoc(self.function) if doc_string: - self._description = doc_string + self._action = doc_string except Exception: pass else: @@ -178,8 +178,8 @@ class Opyrator: return self._name @property - def description(self) -> str: - return self._description + def action(self) -> str: + return self._action @property def input_type(self) -> Any: diff --git a/mkgui/base/ui/streamlit_ui.py b/mkgui/base/ui/streamlit_ui.py index 52bdb4d..7401957 100644 --- a/mkgui/base/ui/streamlit_ui.py +++ b/mkgui/base/ui/streamlit_ui.py @@ -32,21 +32,20 @@ st.set_page_config( page_icon="🧊", layout="wide") -with st.spinner("Loading MockingBird GUI. Please wait..."): - opyrator = Opyrator("{opyrator_path}") - -render_streamlit_ui(opyrator, action="{action}") +render_streamlit_ui() """ +# with st.spinner("Loading MockingBird GUI. Please wait..."): +# opyrator = Opyrator("{opyrator_path}") + -def launch_ui(opyrator_path: str, port: int = 8501) -> None: +def launch_ui(port: int = 8501) -> None: with NamedTemporaryFile( suffix=".py", mode="w", encoding="utf-8", delete=False ) as f: - f.write(STREAMLIT_RUNNER_SNIPPET.format_map({'opyrator_path': opyrator_path, 'action': "Synthesize"})) + f.write(STREAMLIT_RUNNER_SNIPPET) f.seek(0) - # TODO: PYTHONPATH="$PYTHONPATH:/workspace/opyrator/src" import subprocess python_path = f'PYTHONPATH="$PYTHONPATH:{getcwd()}"' @@ -801,30 +800,42 @@ class OutputUI: streamlit.json(jsonable_encoder(output_data)) -def render_streamlit_ui(opyrator: Opyrator, action: str = "Execute") -> None: - title = opyrator.name +def getOpyrator(mode: str) -> Opyrator: + # if mode == None or mode.startswith('VC'): + # from mkgui.app_vc import vc + # return Opyrator(vc) + from mkgui.app import main + return Opyrator(main) + +def render_streamlit_ui() -> None: # init session_state = st.session_state session_state.input_data = {} + session_state.mode = None + + with st.spinner("Loading MockingBird GUI. Please wait..."): + session_state.mode = st.sidebar.selectbox( + '模式选择', + ("AI拟音", "VC拟音") + ) + opyrator = getOpyrator(session_state.mode) + title = opyrator.name col1, col2, _ = st.columns(3) col2.title(title) + col2.markdown("欢迎使用MockingBird Web 2") + image = Image.open('.\\mkgui\\static\\mb.png') col1.image(image) - # Add custom css settings - st.markdown(f"", unsafe_allow_html=True) - - if opyrator.description: - st.markdown(opyrator.description) - - left, right = st.columns([0.3, 0.8]) - InputUI(session_state=session_state, input_class=opyrator.input_type).render_ui(left) - + st.markdown("---") + left, right = st.columns([0.4, 0.6]) with left: - execute_selected = st.button(action) + st.header("Control 控制") + InputUI(session_state=session_state, input_class=opyrator.input_type).render_ui(st) + execute_selected = st.button(opyrator.action) if execute_selected: with st.spinner("Executing operation. Please wait..."): try: @@ -838,18 +849,23 @@ def render_streamlit_ui(opyrator: Opyrator, action: str = "Execute") -> None: else: # st.success("Operation executed successfully.") pass - if st.button("Clear"): - # Clear all state - for key in st.session_state.keys(): - del st.session_state[key] - session_state.input_data = {} - st.experimental_rerun() - - - if 'output_data' in session_state: - OutputUI( - session_state.output_data, session_state.latest_operation_input - ).render_ui(right) - # st.markdown("---") + with right: + st.header("Result 结果") + if 'output_data' in session_state: + OutputUI( + session_state.output_data, session_state.latest_operation_input + ).render_ui(st) + if st.button("Clear"): + # Clear all state + for key in st.session_state.keys(): + del st.session_state[key] + session_state.input_data = {} + st.experimental_rerun() + else: + # placeholder + st.caption("请使用左侧控制板进行输入并运行获得结果") + + # Add custom css settings + st.markdown(f"", unsafe_allow_html=True) diff --git a/mkgui/base/ui/streamlit_utils.py b/mkgui/base/ui/streamlit_utils.py index e8f15c6..beb6e65 100644 --- a/mkgui/base/ui/streamlit_utils.py +++ b/mkgui/base/ui/streamlit_utils.py @@ -4,6 +4,9 @@ div[data-testid="stBlock"] button { margin-bottom: 20px !important; border-color: #bfbfbf !important; } +section[data-testid="stSidebar"] div { + max-width: 10rem; +} pre code { white-space: pre-wrap; }