feat: add Qt/Streamlit alternative frontends, fix hub output auto-scroll, update README

This commit is contained in:
Liang Jiaqing
2026-03-28 20:56:14 +08:00
parent a5dbce396a
commit 1d94384ee5
4 changed files with 2816 additions and 13 deletions

View File

@@ -101,6 +101,15 @@ tg_allowed_users = [YOUR_USER_ID]
python frontends/tgapp.py python frontends/tgapp.py
``` ```
### Alternative App Frontends
Besides the default Streamlit web UI, you can also try other frontend styles:
```bash
python frontends/qtapp.py # Qt-based desktop app
streamlit run frontends/stapp2.py # Alternative Streamlit UI
```
## 📊 Comparison with Similar Tools ## 📊 Comparison with Similar Tools
@@ -338,6 +347,15 @@ dingtalk_client_secret = "your_app_secret"
dingtalk_allowed_users = ["your_staff_id"] # 或 ['*'] dingtalk_allowed_users = ["your_staff_id"] # 或 ['*']
``` ```
### 其他 App 前端
除默认的 Streamlit Web UI 外,还可以尝试不同风格的前端:
```bash
python frontends/qtapp.py # 基于 Qt 的桌面应用
streamlit run frontends/stapp2.py # 另一种 Streamlit 风格 UI
```
## 📊 与同类产品对比 ## 📊 与同类产品对比

1746
frontends/qtapp.py Normal file

File diff suppressed because it is too large Load Diff

1045
frontends/stapp2.py Normal file

File diff suppressed because it is too large Load Diff

20
hub.pyw
View File

@@ -12,12 +12,8 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
def acquire_singleton(): def acquire_singleton():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: try:
s.bind(('127.0.0.1', LOCK_PORT)) s.bind(('127.0.0.1', LOCK_PORT)); s.listen(1);return s
s.listen(1) except OSError: return None
return s
except OSError:
return None
def discover_services(): def discover_services():
services = [] services = []
@@ -32,12 +28,9 @@ def discover_services():
frontends_dir = os.path.join(BASE_DIR, 'frontends') frontends_dir = os.path.join(BASE_DIR, 'frontends')
if os.path.isdir(frontends_dir): if os.path.isdir(frontends_dir):
for f in sorted(os.listdir(frontends_dir)): for f in sorted(os.listdir(frontends_dir)):
if f.endswith('app.py') and f != 'chatapp_common.py': if 'app' in f and f.endswith('.py') and f != 'chatapp_common.py':
if f == 'stapp.py': if 'stapp' in f: cmd = [sys.executable, '-m', 'streamlit', 'run', 'frontends/' + f, '--server.headless=true']
cmd = [sys.executable, '-m', 'streamlit', 'run', else: cmd = [sys.executable, 'frontends/' + f]
'frontends/' + f, '--server.headless=true']
else:
cmd = [sys.executable, 'frontends/' + f]
services.append({'name': 'frontends/' + f, 'cmd': cmd}) services.append({'name': 'frontends/' + f, 'cmd': cmd})
return services return services
@@ -213,11 +206,12 @@ class LauncherApp:
if not self.selected: if not self.selected:
return return
lines = self.mgr.get_output(self.selected) lines = self.mgr.get_output(self.selected)
at_bottom = self.output_text.yview()[1] >= 0.99
self.output_text.configure(state='normal') self.output_text.configure(state='normal')
self.output_text.delete('1.0', 'end') self.output_text.delete('1.0', 'end')
self.output_text.insert('end', ''.join(lines[-200:])) self.output_text.insert('end', ''.join(lines[-200:]))
self.output_text.configure(state='disabled') self.output_text.configure(state='disabled')
self.output_text.see('end') if at_bottom: self.output_text.see('end')
def _poll(self): def _poll(self):
for svc in self.services: for svc in self.services: