2026-07-02 16:47:37 +08:00
|
|
|
"""Main GUI window."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from .tabs.accounts import AccountsTab
|
|
|
|
|
from .tabs.apply import ApplyTab
|
|
|
|
|
from .tabs.collect import CollectTab
|
|
|
|
|
from .tabs.generate import GenerateTab
|
|
|
|
|
from .tabs.settings import SettingsTab
|
|
|
|
|
from .widgets import *
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
|
|
"""Main application window with the fixed five-tab workflow."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, db_path=None, config=None, config_path=None, ai_models_path=None):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.config = appconfig.load_config(config_path or appconfig.CONFIG_PATH) if config is None else config
|
|
|
|
|
self.config_path = (
|
|
|
|
|
config_path
|
|
|
|
|
or self.config.get("config_path")
|
|
|
|
|
or appconfig.CONFIG_PATH
|
|
|
|
|
)
|
|
|
|
|
self.db_path = _database_path(db_path, self.config)
|
|
|
|
|
self.ai_models_path = (
|
|
|
|
|
ai_models_path
|
|
|
|
|
or self.config.get("ai_models_path")
|
|
|
|
|
or appconfig.AI_MODELS_PATH
|
|
|
|
|
)
|
|
|
|
|
self.setWindowTitle("cmshopee")
|
|
|
|
|
self.resize(1180, 760)
|
2026-07-07 09:21:19 +08:00
|
|
|
self.setStyleSheet(BUTTON_BASE_STYLE)
|
2026-07-06 11:32:02 +08:00
|
|
|
self._settings_tab_index = TAB_TITLES.index("⑤ 设置")
|
|
|
|
|
self._last_tab_index = 0
|
|
|
|
|
self._reverting_tab_change = False
|
2026-07-02 16:47:37 +08:00
|
|
|
self.tabs = QTabWidget()
|
|
|
|
|
self.tabs.setObjectName("mainTabs")
|
|
|
|
|
self.tabs.setStyleSheet(TAB_STYLE)
|
|
|
|
|
self.tabs.currentChanged.connect(self._on_tab_changed)
|
|
|
|
|
for title in TAB_TITLES:
|
|
|
|
|
self.tabs.addTab(self._build_tab(title), title)
|
|
|
|
|
self.tabs.setTabIcon(TAB_TITLES.index("③ 更新shopee"), _warning_dot_icon())
|
|
|
|
|
self.setCentralWidget(self.tabs)
|
|
|
|
|
self.statusBar().showMessage("就绪")
|
|
|
|
|
|
|
|
|
|
def _build_tab(self, title):
|
|
|
|
|
if title == "① 导入采集":
|
|
|
|
|
return CollectTab(
|
|
|
|
|
db_path=self.db_path,
|
|
|
|
|
config=self.config,
|
|
|
|
|
status_callback=self.statusBar().showMessage,
|
|
|
|
|
open_accounts_callback=lambda: self.open_accounts_tab(),
|
|
|
|
|
refresh_workflow_callback=lambda: self.refresh_task_tabs(),
|
|
|
|
|
)
|
|
|
|
|
if title == "② AI生成":
|
|
|
|
|
return GenerateTab(
|
|
|
|
|
db_path=self.db_path,
|
|
|
|
|
config=self.config,
|
|
|
|
|
config_path=self.config_path,
|
|
|
|
|
status_callback=self.statusBar().showMessage,
|
|
|
|
|
open_accounts_callback=lambda: self.open_accounts_tab(),
|
|
|
|
|
)
|
|
|
|
|
if title == "③ 更新shopee":
|
|
|
|
|
return ApplyTab(
|
|
|
|
|
db_path=self.db_path,
|
|
|
|
|
config=self.config,
|
|
|
|
|
status_callback=self.statusBar().showMessage,
|
|
|
|
|
open_accounts_callback=lambda: self.open_accounts_tab(),
|
|
|
|
|
open_settings_callback=lambda: self.open_settings_tab(),
|
|
|
|
|
)
|
|
|
|
|
if title == "④ 账号管理":
|
|
|
|
|
return AccountsTab(
|
|
|
|
|
db_path=self.db_path,
|
|
|
|
|
config=self.config,
|
|
|
|
|
status_callback=self.statusBar().showMessage,
|
|
|
|
|
)
|
|
|
|
|
return SettingsTab(
|
|
|
|
|
config=self.config,
|
|
|
|
|
config_path=self.config_path,
|
|
|
|
|
ai_models_path=self.ai_models_path,
|
|
|
|
|
status_callback=self.statusBar().showMessage,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def refresh_task_tabs(self):
|
|
|
|
|
for index in range(self.tabs.count()):
|
|
|
|
|
widget = self.tabs.widget(index)
|
|
|
|
|
if hasattr(widget, "refresh_tasks"):
|
|
|
|
|
widget.refresh_tasks()
|
|
|
|
|
|
|
|
|
|
def _on_tab_changed(self, index):
|
2026-07-06 11:32:02 +08:00
|
|
|
if self._reverting_tab_change:
|
|
|
|
|
self._last_tab_index = index
|
|
|
|
|
self.statusBar().showMessage(f"当前:{self.tabs.tabText(index)}")
|
|
|
|
|
return
|
|
|
|
|
previous = self._last_tab_index
|
|
|
|
|
if previous == self._settings_tab_index and index != previous:
|
|
|
|
|
if not self._confirm_leave_settings_tab():
|
|
|
|
|
self._reverting_tab_change = True
|
|
|
|
|
try:
|
|
|
|
|
self.tabs.setCurrentIndex(previous)
|
|
|
|
|
finally:
|
|
|
|
|
self._reverting_tab_change = False
|
|
|
|
|
self._last_tab_index = previous
|
|
|
|
|
self.statusBar().showMessage(f"当前:{self.tabs.tabText(previous)}")
|
|
|
|
|
return
|
|
|
|
|
self._last_tab_index = index
|
2026-07-02 16:47:37 +08:00
|
|
|
self.statusBar().showMessage(f"当前:{self.tabs.tabText(index)}")
|
|
|
|
|
|
2026-07-06 11:32:02 +08:00
|
|
|
def _settings_tab(self):
|
|
|
|
|
return self.tabs.widget(self._settings_tab_index)
|
|
|
|
|
|
|
|
|
|
def _confirm_leave_settings_tab(self):
|
|
|
|
|
settings_tab = self._settings_tab()
|
|
|
|
|
if not hasattr(settings_tab, "is_dirty") or not settings_tab.is_dirty():
|
|
|
|
|
return True
|
|
|
|
|
box = QMessageBox(self)
|
|
|
|
|
box.setWindowTitle("未保存更改")
|
|
|
|
|
box.setText("⑤ 设置有未保存更改。要先保存再离开吗?")
|
|
|
|
|
save_button = box.addButton("保存", QMessageBox.AcceptRole)
|
|
|
|
|
discard_button = box.addButton("放弃", QMessageBox.DestructiveRole)
|
|
|
|
|
cancel_button = box.addButton("取消", QMessageBox.RejectRole)
|
|
|
|
|
box.setDefaultButton(save_button)
|
|
|
|
|
box.exec()
|
|
|
|
|
clicked = box.clickedButton()
|
|
|
|
|
if clicked is save_button:
|
|
|
|
|
return bool(settings_tab.save_app_settings())
|
|
|
|
|
if clicked is discard_button:
|
|
|
|
|
return bool(settings_tab.discard_unsaved_changes())
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def closeEvent(self, event):
|
|
|
|
|
if self._confirm_leave_settings_tab():
|
|
|
|
|
event.accept()
|
|
|
|
|
return
|
|
|
|
|
event.ignore()
|
|
|
|
|
|
2026-07-02 16:47:37 +08:00
|
|
|
def open_accounts_tab(self):
|
|
|
|
|
self.tabs.setCurrentIndex(TAB_TITLES.index("④ 账号管理"))
|
|
|
|
|
|
|
|
|
|
def open_settings_tab(self):
|
|
|
|
|
self.tabs.setCurrentIndex(TAB_TITLES.index("⑤ 设置"))
|