From ee8213d1d2c2008d2d996929500c9e87dac325a3 Mon Sep 17 00:00:00 2001 From: yum Date: Sat, 17 Dec 2022 17:51:12 -0800 Subject: Finish python virtual env GUI can now download all TaSTT dependencies and install them into a virtual environment. * Add buttons to check embedded python version & install dependencies * Add class to wrap interacting with embedded Python * Put all TaSTT python scripts into a folder --- GUI/GUI/GUI/Frame.cpp | 116 +++++++++++++++++++++++++++++++++++----- GUI/GUI/GUI/Frame.h | 11 +++- GUI/GUI/GUI/GUI.vcxproj | 2 + GUI/GUI/GUI/GUI.vcxproj.filters | 14 +++-- GUI/GUI/GUI/PythonWrapper.cpp | 54 +++++++++++++++++++ GUI/GUI/GUI/PythonWrapper.h | 28 ++++++++++ GUI/package.ps1 | 1 + 7 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 GUI/GUI/GUI/PythonWrapper.cpp create mode 100644 GUI/GUI/GUI/PythonWrapper.h (limited to 'GUI') diff --git a/GUI/GUI/GUI/Frame.cpp b/GUI/GUI/GUI/Frame.cpp index 841bfb9..74683ca 100644 --- a/GUI/GUI/GUI/Frame.cpp +++ b/GUI/GUI/GUI/Frame.cpp @@ -1,23 +1,44 @@ #include "Frame.h" +#include "PythonWrapper.h" #include +#include +#include + +namespace { + enum FrameIds { + ID_PY_PANEL, + ID_PY_VERSION_BUTTON, + ID_PY_SETUP_BUTTON, + ID_PY_OUT, + }; +}; Frame::Frame() - : wxFrame(nullptr, wxID_ANY, "TaSTT") + : wxFrame(nullptr, wxID_ANY, "TaSTT"), + py_panel_(this, ID_PY_PANEL), + py_panel_sizer_(wxVERTICAL), + py_version_button_(&py_panel_, ID_PY_VERSION_BUTTON, "Check embedded Python version"), + py_setup_button_(&py_panel_, ID_PY_SETUP_BUTTON, "Set up Python virtual environment"), + py_out_(&py_panel_, ID_PY_OUT, wxEmptyString, wxDefaultPosition, + wxSize(/*x_px=*/480, /*y_px=*/160), wxTE_MULTILINE) { - Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT); + Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT); + Bind(wxEVT_BUTTON, &Frame::OnGetPythonVersion, this, ID_PY_VERSION_BUTTON); + Bind(wxEVT_BUTTON, &Frame::OnSetupPython, this, ID_PY_SETUP_BUTTON); - // wx needs this to be able to load PNGs. - wxImage::AddHandler(&png_handler_); + // wx needs this to be able to load PNGs. + wxImage::AddHandler(&png_handler_); + const std::string icon_path = "Resources/logo.png"; + LoadAndSetIcon(icon_path); - const std::string logo_path = "Resources/logo.png"; - if (!std::filesystem::exists(logo_path)) { - wxLogFatalError("Logo is missing from %s", logo_path.c_str()); - } - wxBitmap icon_img("Resources/logo.png", wxBITMAP_TYPE_PNG); - wxIcon icon; - icon.CopyFromBitmap(icon_img); - SetIcon(icon); + wxSize py_out_size(/*x=*/80, /*y=*/20); + py_out_.SetSize(py_out_size); + + py_panel_.SetSizer(&py_panel_sizer_); + py_panel_sizer_.Add(&py_version_button_); + py_panel_sizer_.Add(&py_setup_button_); + py_panel_sizer_.Add(&py_out_); } void Frame::OnExit(wxCommandEvent& event) @@ -25,3 +46,74 @@ void Frame::OnExit(wxCommandEvent& event) Close(true); } +void Frame::OnGetPythonVersion(wxCommandEvent& event) +{ + PythonWrapper py; + std::string py_version = py.GetVersion(); + py_out_.AppendText(py_version + "\n"); +} + +void Frame::OnSetupPython(wxCommandEvent& event) +{ + PythonWrapper py; + + py_out_.AppendText("Setting up Python virtual environment\n"); + py_out_.AppendText("This could take several minutes, please be patient!\n"); + py_out_.AppendText("This will download ~5GB of dependencies.\n"); + py_out_.AppendText("Dependencies are installed in the executable folder, " + "so deleting the folder is all that's needed to undo this."); + + { + std::string py_out; + std::ostringstream py_out_oss; + py_out_oss << "Installing pip" << std::endl; + py_out_.AppendText(py_out_oss.str()); + if (!py.InstallPip(&py_out)) { + std::ostringstream py_out_oss; + py_out_oss << "Failed to install pip: " << py_out; + py_out_.AppendText(py_out_oss.str()); + } + } + + const std::vector pip_deps{ + "pillow", + "pydub", + "pyaudio", + "playsound==1.2.2", + "torch --extra-index-url https://download.pytorch.org/whl/cu116", + "git+https://github.com/openai/whisper.git", + "openvr", + "editdistance", + "pydub", + "python-osc", + }; + + for (const auto& pip_dep : pip_deps) { + { + std::ostringstream py_out_oss; + py_out_oss << "Installing " << pip_dep << std::endl; + py_out_.AppendText(py_out_oss.str()); + } + std::string py_out; + bool res = py.InvokeWithArgs({ "-m", "pip", "install", pip_dep }, &py_out); + if (!res) { + std::ostringstream py_out_oss; + py_out_oss << "Failed to install " << pip_dep << ": " << py_out << std::endl; + py_out_.AppendText(py_out_oss.str()); + return; + } + } + + py_out_.AppendText("Python virtual environment successfully set up!\n"); +} + +void Frame::LoadAndSetIcon(const std::string& icon_path) { + if (!std::filesystem::exists(icon_path)) { + wxLogFatalError("Logo is missing from %s", icon_path.c_str()); + } + wxBitmap icon_img(icon_path, wxBITMAP_TYPE_PNG); + wxIcon icon; + icon.CopyFromBitmap(icon_img); + SetIcon(icon); +} + diff --git a/GUI/GUI/GUI/Frame.h b/GUI/GUI/GUI/Frame.h index 4dcfd4a..62e9169 100644 --- a/GUI/GUI/GUI/Frame.h +++ b/GUI/GUI/GUI/Frame.h @@ -13,8 +13,15 @@ public: private: wxPNGHandler png_handler_; + wxPanel py_panel_; + wxBoxSizer py_panel_sizer_; + wxButton py_version_button_; + wxButton py_setup_button_; + wxTextCtrl py_out_; - void OnHello(wxCommandEvent& event); void OnExit(wxCommandEvent& event); - void OnAbout(wxCommandEvent& event); + void OnGetPythonVersion(wxCommandEvent& event); + void OnSetupPython(wxCommandEvent& event); + + void LoadAndSetIcon(const std::string& icon_path); }; diff --git a/GUI/GUI/GUI/GUI.vcxproj b/GUI/GUI/GUI/GUI.vcxproj index 79bb220..223f47e 100644 --- a/GUI/GUI/GUI/GUI.vcxproj +++ b/GUI/GUI/GUI/GUI.vcxproj @@ -138,10 +138,12 @@ + + diff --git a/GUI/GUI/GUI/GUI.vcxproj.filters b/GUI/GUI/GUI/GUI.vcxproj.filters index c332693..74e4659 100644 --- a/GUI/GUI/GUI/GUI.vcxproj.filters +++ b/GUI/GUI/GUI/GUI.vcxproj.filters @@ -24,16 +24,22 @@ Source Files + + Source Files + - - Source Files + + Header Files - Source Files + Header Files - Source Files + Header Files + + + Header Files \ No newline at end of file diff --git a/GUI/GUI/GUI/PythonWrapper.cpp b/GUI/GUI/GUI/PythonWrapper.cpp new file mode 100644 index 0000000..27d12fd --- /dev/null +++ b/GUI/GUI/GUI/PythonWrapper.cpp @@ -0,0 +1,54 @@ +#include "PythonWrapper.h" + +#include + +#include + +bool PythonWrapper::InvokeWithArgs(std::vector&& args, std::string* out) { + std::ostringstream cmd_oss; + cmd_oss << "Resources/Python/python.exe"; + for (const auto& arg : args) { + cmd_oss << " " << arg; + } + + wxArrayString cmd_output_ary; + long result = wxExecute(cmd_oss.str(), cmd_output_ary); + std::ostringstream cmd_out_oss; + for (const auto& line : cmd_output_ary) { + if (!cmd_out_oss.str().empty()) { + cmd_out_oss << std::endl; + } + cmd_out_oss << line; + } + if (result == -1) { + std::ostringstream err_oss; + err_oss << "Error while executing python command \"" << cmd_oss.str() << "\": Failed to launch process"; + *out = err_oss.str(); + return false; + } else if (result) { + std::ostringstream err_oss; + err_oss << "Error while executing python command \"" << cmd_oss.str() << "\": Process returned " << result << ": " << cmd_out_oss.str(); + *out = err_oss.str(); + return false; + } + + *out = cmd_out_oss.str(); + return true; +} + + +std::string PythonWrapper::GetVersion() { + std::string result; + bool ok = InvokeWithArgs({ "--version" }, &result); + if (!ok) { + wxLogFatalError("Failed to get python version: %s", result.c_str()); + } + return result; +} + +bool PythonWrapper::InstallPip(std::string* out) { + std::string result; + + std::string pip_path = "Resources/Python/get-pip.py"; + return InvokeWithArgs({ pip_path }, out); +} diff --git a/GUI/GUI/GUI/PythonWrapper.h b/GUI/GUI/GUI/PythonWrapper.h new file mode 100644 index 0000000..607507d --- /dev/null +++ b/GUI/GUI/GUI/PythonWrapper.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#ifndef WX_PRECOMP +#include +#endif + +#include +#include + +/* + * This class wraps interactions with the embedded Python interpreter. +*/ +class PythonWrapper +{ +public: + // Invoke the interpreter with arguments. + // On error, sets `out` to an error message and returns false. + bool InvokeWithArgs(std::vector&& args, std::string* out); + + // Execute python --version. + std::string GetVersion(); + + // Execute get-pip.py. + bool InstallPip(std::string* out); +}; + diff --git a/GUI/package.ps1 b/GUI/package.ps1 index 4b25f3f..0c2cec0 100644 --- a/GUI/package.ps1 +++ b/GUI/package.ps1 @@ -8,5 +8,6 @@ mkdir $install_dir > $null mkdir $install_dir/Resources > $null cp ../Images/logo.png TaSTT/Resources cp -Recurse ../Python TaSTT/Resources/Python +cp -Recurse ../Scripts TaSTT/Resources/Scripts cp GUI/x64/Release/GUI.exe TaSTT/TaSTT.exe -- cgit v1.2.3