diff options
| author | yum <yum.food.vr@gmail.com> | 2022-12-17 20:19:32 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2022-12-17 20:19:32 -0800 |
| commit | 03fbf0e8ca409fe4c26e246286a975724ad0994b (patch) | |
| tree | 298ac903b8706acc51ebd7fbf726848d2cb052c8 | |
| parent | ee8213d1d2c2008d2d996929500c9e87dac325a3 (diff) | |
GUI: Add ability to start & stop transcription engine
| -rw-r--r-- | GUI/GUI/GUI/Frame.cpp | 83 | ||||
| -rw-r--r-- | GUI/GUI/GUI/Frame.h | 8 | ||||
| -rw-r--r-- | GUI/GUI/GUI/PythonWrapper.cpp | 38 | ||||
| -rw-r--r-- | GUI/GUI/GUI/PythonWrapper.h | 11 |
4 files changed, 137 insertions, 3 deletions
diff --git a/GUI/GUI/GUI/Frame.cpp b/GUI/GUI/GUI/Frame.cpp index 74683ca..db2bdd7 100644 --- a/GUI/GUI/GUI/Frame.cpp +++ b/GUI/GUI/GUI/Frame.cpp @@ -10,6 +10,8 @@ namespace { ID_PY_PANEL,
ID_PY_VERSION_BUTTON,
ID_PY_SETUP_BUTTON,
+ ID_PY_APP_START_BUTTON,
+ ID_PY_APP_STOP_BUTTON,
ID_PY_OUT,
};
};
@@ -20,11 +22,16 @@ Frame::Frame() 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_app_start_button_(&py_panel_, ID_PY_APP_START_BUTTON, "Begin transcribing"),
+ py_app_stop_button_(&py_panel_, ID_PY_APP_STOP_BUTTON, "Stop transcribing"),
py_out_(&py_panel_, ID_PY_OUT, wxEmptyString, wxDefaultPosition,
- wxSize(/*x_px=*/480, /*y_px=*/160), wxTE_MULTILINE)
+ wxSize(/*x_px=*/480, /*y_px=*/160), wxTE_MULTILINE),
+ py_app_(nullptr)
{
Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT);
Bind(wxEVT_BUTTON, &Frame::OnGetPythonVersion, this, ID_PY_VERSION_BUTTON);
+ Bind(wxEVT_BUTTON, &Frame::OnAppStart, this, ID_PY_APP_START_BUTTON);
+ Bind(wxEVT_BUTTON, &Frame::OnAppStop, this, ID_PY_APP_STOP_BUTTON);
Bind(wxEVT_BUTTON, &Frame::OnSetupPython, this, ID_PY_SETUP_BUTTON);
// wx needs this to be able to load PNGs.
@@ -38,6 +45,8 @@ Frame::Frame() 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_app_start_button_);
+ py_panel_sizer_.Add(&py_app_stop_button_);
py_panel_sizer_.Add(&py_out_);
}
@@ -60,8 +69,8 @@ void Frame::OnSetupPython(wxCommandEvent& event) 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.");
+ py_out_.AppendText("Dependencies are installed in the GUI's folder, "
+ "so deleting the folder is all that's needed to uninstall.\n");
{
std::string py_out;
@@ -107,6 +116,74 @@ void Frame::OnSetupPython(wxCommandEvent& event) py_out_.AppendText("Python virtual environment successfully set up!\n");
}
+void Frame::OnAppStart(wxCommandEvent& event) {
+ if (py_app_) {
+ if (wxProcess::Exists(py_app_->GetPid())) {
+ py_out_.AppendText("Transcription engine already running\n");
+ return;
+ }
+ delete py_app_;
+ py_app_ = nullptr;
+ }
+
+ py_out_.AppendText("Launching transcription engine\n");
+
+ PythonWrapper py;
+ auto cb = [&](wxProcess* proc, int ret) -> void {
+ std::ostringstream py_out_oss;
+ py_out_oss << "Transcription engine exited with code " << ret << std::endl;
+
+ py_out_.AppendText(py_out_oss.str());
+ return;
+ };
+
+ wxProcess* p = py.StartApp(std::move(cb));
+ if (!p) {
+ py_out_.AppendText("Failed to launch transcription engine\n");
+ return;
+ }
+
+ py_app_ = p;
+}
+
+void Frame::OnAppStop(wxCommandEvent& event) {
+ if (py_app_) {
+ const long pid = py_app_->GetPid();
+
+ // Try to kill it politely.
+ wxProcess::Kill(pid);
+ for (int i = 0; i < 10; i++) {
+ if (!wxProcess::Exists(pid)) {
+ break;
+ }
+ wxMilliSleep(10);
+ }
+
+ // If it doesn't accept its fate, murder it with an axe.
+ bool first = true;
+ int loop_cnt = 0;
+ while (wxProcess::Exists(pid)) {
+ if (first) {
+ first = false;
+ py_out_.AppendText("Timed out trying to stop transcription engine "
+ "cleanly, sending SIGKILL\n");
+ }
+ else if (++loop_cnt % 100 == 0) {
+ py_out_.AppendText("Waiting for transcription engine to exit");
+ }
+ wxProcess::Kill(pid, wxSIGKILL);
+ wxMilliSleep(10);
+ }
+
+ // Since we don't process the termination event, py_app_ deletes itself!
+ py_app_ = nullptr;
+ py_out_.AppendText("Stopped transcription engine\n");
+ }
+ else {
+ py_out_.AppendText("Transcription engine already stopped\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());
diff --git a/GUI/GUI/GUI/Frame.h b/GUI/GUI/GUI/Frame.h index 62e9169..414d2b3 100644 --- a/GUI/GUI/GUI/Frame.h +++ b/GUI/GUI/GUI/Frame.h @@ -6,6 +6,8 @@ #include <wx/wx.h>
#endif
+#include <memory>
+
class Frame : public wxFrame
{
public:
@@ -17,11 +19,17 @@ private: wxBoxSizer py_panel_sizer_;
wxButton py_version_button_;
wxButton py_setup_button_;
+ wxButton py_app_start_button_;
+ wxButton py_app_stop_button_;
wxTextCtrl py_out_;
+ wxProcess* py_app_;
+
void OnExit(wxCommandEvent& event);
void OnGetPythonVersion(wxCommandEvent& event);
void OnSetupPython(wxCommandEvent& event);
+ void OnAppStart(wxCommandEvent& event);
+ void OnAppStop(wxCommandEvent& event);
void LoadAndSetIcon(const std::string& icon_path);
};
diff --git a/GUI/GUI/GUI/PythonWrapper.cpp b/GUI/GUI/GUI/PythonWrapper.cpp index 27d12fd..7270ab5 100644 --- a/GUI/GUI/GUI/PythonWrapper.cpp +++ b/GUI/GUI/GUI/PythonWrapper.cpp @@ -4,6 +4,38 @@ #include <sstream> +class PythonProcess : public wxProcess { +public: + PythonProcess(std::function<void(wxProcess* proc, int ret)>&& exit_callback) : exit_cb_(exit_callback) {} + + virtual void OnTerminate(int pid, int status) wxOVERRIDE { + exit_cb_(this, status); + } + +private: + const std::function<void(wxProcess* proc, int ret)> exit_cb_; +}; + +wxProcess* PythonWrapper::InvokeAsyncWithArgs(std::vector<std::string>&& args, + std::function<void(wxProcess* proc, int ret)>&& exit_callback) { + std::ostringstream cmd_oss; + cmd_oss << "Resources/Python/python.exe"; + for (const auto& arg : args) { + cmd_oss << " " << arg; + } + + auto *p = new PythonProcess(std::move(exit_callback)); + // TODO(yum) we should hide the console & stream output to a friendlier interface + int pid = wxExecute(cmd_oss.str(), wxEXEC_ASYNC, p); + + if (!pid) { + delete p; + p = nullptr; + } + + return p; +} + bool PythonWrapper::InvokeWithArgs(std::vector<std::string>&& args, std::string* out) { std::ostringstream cmd_oss; cmd_oss << "Resources/Python/python.exe"; @@ -52,3 +84,9 @@ bool PythonWrapper::InstallPip(std::string* out) { std::string pip_path = "Resources/Python/get-pip.py"; return InvokeWithArgs({ pip_path }, out); } + +wxProcess* PythonWrapper::StartApp(std::function<void(wxProcess* proc, int ret)>&& exit_callback) { + return InvokeAsyncWithArgs({ "Resources/Scripts/transcribe.py" }, + std::move(exit_callback)); +} + diff --git a/GUI/GUI/GUI/PythonWrapper.h b/GUI/GUI/GUI/PythonWrapper.h index 607507d..4407b5e 100644 --- a/GUI/GUI/GUI/PythonWrapper.h +++ b/GUI/GUI/GUI/PythonWrapper.h @@ -6,6 +6,8 @@ #include <wx/wx.h> #endif +#include <wx/process.h> + #include <string> #include <vector> @@ -15,6 +17,13 @@ class PythonWrapper { public: + + // Invoke the interpreter asynchronously with the given arguments. + // When the process exits, `exit_callback` runs. + // The caller is responsible for deleting wxProcess. + wxProcess* InvokeAsyncWithArgs(std::vector<std::string>&& args, + std::function<void(wxProcess* proc, int ret)>&& exit_callback); + // Invoke the interpreter with arguments. // On error, sets `out` to an error message and returns false. bool InvokeWithArgs(std::vector<std::string>&& args, std::string* out); @@ -24,5 +33,7 @@ public: // Execute get-pip.py. bool InstallPip(std::string* out); + + wxProcess* StartApp(std::function<void(wxProcess* proc, int ret)>&& exit_callback); }; |
