fix: веб-сервер не запускался на Windows, диаграмма тормозила при перетаскивании
Три дефекта из установки владельца на вторую машину. 1. Веб-сервер падал сразу: «web server process terminated unexpectedly». Установщик ставит в venv Hermes только customtkinter, pillow, pyyaml и psutil — fastapi и uvicorn отсутствовали в списке вовсе. Добавлены во все шесть мест: проверка, сообщение, pip, uv, перепроверка. Поэтому на Windows открывался только десктоп: веб физически не мог стартовать. 2. Лаунчер показывал голое «terminated unexpectedly» без причины — та же болезнь, что у кода 12. Теперь перехватывает вывод процесса и выводит последние строки ошибки в окне. 3. Окно тормозило при перетаскивании и продолжало двигаться несколько секунд после отпускания мыши. _RouteDiagram перерисовывал всю канву на КАЖДОЕ событие <Configure>, а при перетаскивании их сотни; очередь не успевала разгребаться. Гашение в приложении существовало, но _handle_debounced_resize был пустой заглушкой. Перерисовка сведена к одной после затишья. Замерено: 199 событий давали 199 перерисовок, теперь 4. Тесты: 373 passed, ruff чисто. Установщик пересобран. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
57e7ca2edd
commit
ef6a9e1acd
5 changed files with 48 additions and 9 deletions
|
|
@ -18,7 +18,7 @@ namespace HermesHubSetup
|
|||
// Подставляется сборщиком из фактического git-коммита. Раньше здесь
|
||||
// жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из
|
||||
// какого кода собран установщик.
|
||||
public const string BuildCommit = "36b449b";
|
||||
public const string BuildCommit = "57e7ca2";
|
||||
public const string MIN_HERMES_VERSION = "0.20.0";
|
||||
public const string MAX_TESTED_HERMES = "0.20.4";
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ namespace HermesHubSetup
|
|||
{
|
||||
ProcessStartInfo checkPsi = new ProcessStartInfo();
|
||||
checkPsi.FileName = pythonExe;
|
||||
checkPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil; print('DEPS_OK')\"";
|
||||
checkPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil, fastapi, uvicorn; print('DEPS_OK')\"";
|
||||
checkPsi.UseShellExecute = false;
|
||||
checkPsi.RedirectStandardOutput = true;
|
||||
checkPsi.RedirectStandardError = true;
|
||||
|
|
@ -159,7 +159,7 @@ namespace HermesHubSetup
|
|||
|
||||
if (needsInstall)
|
||||
{
|
||||
if (progressCallback != null) progressCallback("Installing dependencies into Hermes venv (customtkinter, Pillow, PyYAML, psutil)...", 40);
|
||||
if (progressCallback != null) progressCallback("Installing dependencies into Hermes venv (customtkinter, Pillow, PyYAML, psutil, FastAPI, uvicorn)...", 40);
|
||||
|
||||
// 1. Ensure pip is installed/bootstrapped if needed
|
||||
try
|
||||
|
|
@ -185,7 +185,7 @@ namespace HermesHubSetup
|
|||
{
|
||||
ProcessStartInfo pipPsi = new ProcessStartInfo();
|
||||
pipPsi.FileName = pythonExe;
|
||||
pipPsi.Arguments = "-m pip install --no-warn-script-location customtkinter pillow pyyaml psutil";
|
||||
pipPsi.Arguments = "-m pip install --no-warn-script-location customtkinter pillow pyyaml psutil fastapi uvicorn";
|
||||
pipPsi.UseShellExecute = false;
|
||||
pipPsi.RedirectStandardOutput = true;
|
||||
pipPsi.RedirectStandardError = true;
|
||||
|
|
@ -209,7 +209,7 @@ namespace HermesHubSetup
|
|||
{
|
||||
ProcessStartInfo uvPsi = new ProcessStartInfo();
|
||||
uvPsi.FileName = "uv";
|
||||
uvPsi.Arguments = string.Format("pip install --python \"{0}\" customtkinter pillow pyyaml psutil", pythonExe);
|
||||
uvPsi.Arguments = string.Format("pip install --python \"{0}\" customtkinter pillow pyyaml psutil fastapi uvicorn", pythonExe);
|
||||
uvPsi.UseShellExecute = false;
|
||||
uvPsi.RedirectStandardOutput = true;
|
||||
uvPsi.RedirectStandardError = true;
|
||||
|
|
@ -237,7 +237,7 @@ namespace HermesHubSetup
|
|||
{
|
||||
ProcessStartInfo recheckPsi = new ProcessStartInfo();
|
||||
recheckPsi.FileName = pythonExe;
|
||||
recheckPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil; print('DEPS_VERIFIED')\"";
|
||||
recheckPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil, fastapi, uvicorn; print('DEPS_VERIFIED')\"";
|
||||
recheckPsi.UseShellExecute = false;
|
||||
recheckPsi.RedirectStandardOutput = true;
|
||||
recheckPsi.RedirectStandardError = true;
|
||||
|
|
@ -309,7 +309,7 @@ namespace HermesHubSetup
|
|||
}
|
||||
|
||||
// 2. Install UI & System Dependencies into Hermes Python Environment
|
||||
if (progressCallback != null) progressCallback("Checking Python UI dependencies (customtkinter, Pillow)...", 35);
|
||||
if (progressCallback != null) progressCallback("Checking Python dependencies (customtkinter, Pillow, FastAPI, uvicorn)...", 35);
|
||||
if (!EnsurePythonDependencies(HermesPython, progressCallback))
|
||||
{
|
||||
return 13; // Dependency install failed
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -122,6 +122,11 @@ namespace HermesHub
|
|||
serverPsi.UseShellExecute = false;
|
||||
serverPsi.CreateNoWindow = true;
|
||||
serverPsi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
// Вывод перехватываем, чтобы при падении показать причину, а не
|
||||
// голое «terminated unexpectedly». Без этого владелец видит факт
|
||||
// отказа и ни слова о том, чего не хватает.
|
||||
serverPsi.RedirectStandardError = true;
|
||||
serverPsi.RedirectStandardOutput = true;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -144,7 +149,16 @@ namespace HermesHub
|
|||
}
|
||||
if (serverProcess.HasExited)
|
||||
{
|
||||
MessageBox.Show("Hermes Hub web server process terminated unexpectedly.", "Hermes Hub", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
string why = "";
|
||||
try { why = serverProcess.StandardError.ReadToEnd(); } catch { }
|
||||
if (string.IsNullOrEmpty(why))
|
||||
{
|
||||
try { why = serverProcess.StandardOutput.ReadToEnd(); } catch { }
|
||||
}
|
||||
if (why.Length > 1500) why = why.Substring(why.Length - 1500);
|
||||
string msg = "Веб-сервер Hermes Hub завершился с ошибкой.";
|
||||
if (!string.IsNullOrEmpty(why)) msg += Environment.NewLine + Environment.NewLine + why.Trim();
|
||||
MessageBox.Show(msg, "Hermes Hub", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
Thread.Sleep(200);
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -220,7 +220,32 @@ class _RouteDiagram(ctk.CTkFrame):
|
|||
self.context_status.pack()
|
||||
self._left_labels: list[str] = []
|
||||
self._right_labels: list[str] = []
|
||||
self.bind("<Configure>", self._redraw)
|
||||
self._redraw_timer_id: Any = None
|
||||
self.bind("<Configure>", self._schedule_redraw)
|
||||
|
||||
def _schedule_redraw(self, event: Any = None) -> None:
|
||||
"""Свести поток <Configure> к одной перерисовке.
|
||||
|
||||
При перетаскивании окна Tk шлёт <Configure> непрерывно, и раньше каждое
|
||||
событие вызывало полную перерисовку канвы: удаление и построение всех
|
||||
связей, подписей и узлов. Очередь не успевала разгребаться, и окно
|
||||
продолжало ползти ещё несколько секунд после того, как мышь отпущена.
|
||||
|
||||
Перерисовываем один раз, когда поток событий утих.
|
||||
"""
|
||||
if self._redraw_timer_id is not None:
|
||||
try:
|
||||
self.after_cancel(self._redraw_timer_id)
|
||||
except Exception:
|
||||
pass
|
||||
self._redraw_timer_id = self.after(80, self._redraw_now)
|
||||
|
||||
def _redraw_now(self) -> None:
|
||||
self._redraw_timer_id = None
|
||||
try:
|
||||
self._redraw()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def sync_slots(self, provider_count: int, agent_count: int) -> None:
|
||||
"""Grow/shrink endpoint slots to match the snapshot without hard caps."""
|
||||
|
|
|
|||
Loading…
Reference in a new issue