gui_main.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. import sys
  2. import os
  3. import threading
  4. import subprocess
  5. try:
  6. from PyQt5.QtWidgets import (
  7. QApplication, QTabWidget, QWidget, QVBoxLayout, QHBoxLayout, QLabel,
  8. QRadioButton, QPushButton, QButtonGroup, QGroupBox, QMessageBox,
  9. QLineEdit, QScrollArea, QFileDialog, QFormLayout, QCheckBox, QTextEdit,
  10. QComboBox, QDialog, QDialogButtonBox, QGridLayout, QToolButton, QListWidget, QListWidgetItem
  11. )
  12. from PyQt5.QtCore import Qt, pyqtSignal, QObject
  13. HAS_QT = True
  14. except ImportError:
  15. HAS_QT = False
  16. print("PyQt5 not installed, GUI mode not available")
  17. from repo_sync import RepoSync, __version__
  18. from dotenv import load_dotenv, set_key, find_dotenv, dotenv_values
  19. import json
  20. import uuid
  21. # Explorer路径获取
  22. try:
  23. import win32com.client
  24. def get_active_explorer_path():
  25. shell = win32com.client.Dispatch("Shell.Application")
  26. for window in shell.Windows():
  27. if window.Name in ["文件资源管理器", "Windows Explorer"]:
  28. return window.Document.Folder.Self.Path
  29. return os.getcwd()
  30. except ImportError:
  31. def get_active_explorer_path():
  32. return os.getcwd()
  33. # 命令执行信号类
  34. class CommandSignals(QObject):
  35. output = pyqtSignal(str)
  36. finished = pyqtSignal(int)
  37. class MainTab(QWidget):
  38. def __init__(self, parent=None):
  39. super().__init__(parent)
  40. layout = QVBoxLayout()
  41. # 路径选择
  42. path_layout = QHBoxLayout()
  43. self.path_label = QLabel("Local Path: ")
  44. self.path_edit = QLineEdit()
  45. self.path_edit.setText(get_active_explorer_path())
  46. self.path_btn = QPushButton("Browse...")
  47. self.path_btn.clicked.connect(self.choose_path)
  48. path_layout.addWidget(self.path_label)
  49. path_layout.addWidget(self.path_edit)
  50. path_layout.addWidget(self.path_btn)
  51. layout.addLayout(path_layout)
  52. # 操作
  53. op_group = QGroupBox("Operation:")
  54. op_layout = QHBoxLayout()
  55. self.op_buttons = QButtonGroup(self)
  56. for i, op in enumerate(["create", "push", "pull", "clone", "delete"]):
  57. btn = QRadioButton(op.capitalize())
  58. if i == 0:
  59. btn.setChecked(True)
  60. self.op_buttons.addButton(btn, i)
  61. op_layout.addWidget(btn)
  62. op_group.setLayout(op_layout)
  63. layout.addWidget(op_group)
  64. # 平台
  65. pf_group = QGroupBox("Platform:")
  66. pf_layout = QHBoxLayout()
  67. self.pf_buttons = QButtonGroup(self)
  68. self.platforms = ["github", "gitlab", "gitee", "gitcode", "git.yoq.me", "coding", "aliyun", "cnb"]
  69. for i, pf in enumerate(self.platforms):
  70. btn = QRadioButton(pf.capitalize())
  71. if i == 0:
  72. btn.setChecked(True)
  73. self.pf_buttons.addButton(btn, i)
  74. pf_layout.addWidget(btn)
  75. pf_group.setLayout(pf_layout)
  76. layout.addWidget(pf_group)
  77. # 账户选择
  78. account_layout = QHBoxLayout()
  79. account_layout.addWidget(QLabel("Account:"))
  80. self.account_combo = QComboBox()
  81. self.account_combo.setMinimumWidth(200)
  82. account_layout.addWidget(self.account_combo)
  83. account_layout.addStretch()
  84. layout.addLayout(account_layout)
  85. # 按钮区域 (run和cancel)
  86. btn_layout = QHBoxLayout()
  87. self.run_btn = QPushButton("run it")
  88. self.run_btn.clicked.connect(self.run_repo_sync)
  89. self.cancel_btn = QPushButton("Cancel")
  90. self.cancel_btn.clicked.connect(self.cancel_operation)
  91. self.cancel_btn.setEnabled(False)
  92. btn_layout.addWidget(self.run_btn)
  93. btn_layout.addWidget(self.cancel_btn)
  94. layout.addLayout(btn_layout)
  95. # 命令执行结果
  96. result_group = QGroupBox("Execution Result:")
  97. result_layout = QVBoxLayout()
  98. self.result_text = QTextEdit()
  99. self.result_text.setReadOnly(True)
  100. result_layout.addWidget(self.result_text)
  101. result_group.setLayout(result_layout)
  102. layout.addWidget(result_group)
  103. self.setLayout(layout)
  104. # 命令执行相关
  105. self.process = None
  106. self.command_signals = CommandSignals()
  107. self.command_signals.output.connect(self.update_output)
  108. self.command_signals.finished.connect(self.process_finished)
  109. # 平台变更时更新账户列表
  110. self.pf_buttons.buttonClicked.connect(self.update_account_list)
  111. self.update_account_list()
  112. def choose_path(self):
  113. path = QFileDialog.getExistingDirectory(self, "Select Directory")
  114. if path:
  115. self.path_edit.setText(path)
  116. def update_account_list(self):
  117. self.account_combo.clear()
  118. pf_id = self.pf_buttons.checkedId()
  119. platform = self.platforms[pf_id]
  120. # 读取所有账户
  121. accounts = self.get_platform_accounts(platform)
  122. # 找出启用的账户
  123. enabled_account = "default"
  124. env_values = dotenv_values(find_dotenv())
  125. for account in accounts:
  126. if account != "default" and env_values.get(f"{platform}_{account}_enabled", "").lower() == "true":
  127. enabled_account = account
  128. break
  129. # 添加账户到下拉框,启用的账户放在最前面
  130. if enabled_account in accounts:
  131. accounts.remove(enabled_account)
  132. self.account_combo.addItem(f"{enabled_account} (启用中)")
  133. for account in accounts:
  134. self.account_combo.addItem(account)
  135. # 默认选择启用的账户
  136. self.account_combo.setCurrentIndex(0)
  137. def get_platform_accounts(self, platform):
  138. # 读取.env文件中的所有配置
  139. env_values = dotenv_values(find_dotenv())
  140. accounts = set()
  141. # 默认账户
  142. accounts.add("default")
  143. # 查找带有账户名的配置
  144. prefix = f"{platform}_"
  145. for key in env_values.keys():
  146. if key.startswith(prefix) and "_" in key[len(prefix):]:
  147. account_name = key[len(prefix):].split("_")[0]
  148. if account_name != "default" and account_name != "":
  149. accounts.add(account_name)
  150. return sorted(list(accounts))
  151. def run_repo_sync(self):
  152. repo_path = self.path_edit.text().strip()
  153. if not repo_path:
  154. QMessageBox.warning(self, "Warning", "Please select a local path.")
  155. return
  156. op_id = self.op_buttons.checkedId()
  157. pf_id = self.pf_buttons.checkedId()
  158. op = ["create", "push", "pull", "clone", "delete"][op_id]
  159. pf = self.platforms[pf_id]
  160. # 获取选择的账户名(去掉可能的"(启用中)"后缀)
  161. account_text = self.account_combo.currentText()
  162. account = account_text.split(" (")[0] if " (" in account_text else account_text
  163. # 清空结果区域
  164. self.result_text.clear()
  165. # 检查平台配置
  166. load_dotenv()
  167. token_key = f"{pf}_{account}_token" if account != "default" else f"{pf}_token"
  168. if not os.getenv(token_key):
  169. QMessageBox.warning(self, "Warning", f"Please configure {pf} token for account '{account}' in Settings tab first.")
  170. return
  171. # 构建命令
  172. cmd = [sys.executable, "-m", "repo_sync"]
  173. cmd.append(op)
  174. cmd.extend(["-p", pf])
  175. cmd.extend(["-repo_path", repo_path])
  176. # 如果不是默认账户,需要设置环境变量
  177. env = os.environ.copy()
  178. if account != "default":
  179. # 读取账户配置
  180. env_values = dotenv_values(find_dotenv())
  181. for key, value in env_values.items():
  182. if key.startswith(f"{pf}_{account}_"):
  183. field = key[len(f"{pf}_{account}_"):]
  184. env[f"{pf}_{field}"] = value
  185. # 执行命令
  186. self.run_btn.setEnabled(False)
  187. self.cancel_btn.setEnabled(True)
  188. self.result_text.append(f"Running: {' '.join(cmd)}\n")
  189. # 在新线程中执行命令
  190. self.process_thread = threading.Thread(
  191. target=self.run_process,
  192. args=(cmd, env)
  193. )
  194. self.process_thread.daemon = True
  195. self.process_thread.start()
  196. def run_process(self, cmd, env=None):
  197. try:
  198. self.process = subprocess.Popen(
  199. cmd,
  200. stdout=subprocess.PIPE,
  201. stderr=subprocess.STDOUT,
  202. text=True,
  203. bufsize=1,
  204. universal_newlines=True,
  205. env=env
  206. )
  207. # 读取输出
  208. for line in self.process.stdout:
  209. self.command_signals.output.emit(line)
  210. self.process.wait()
  211. self.command_signals.finished.emit(self.process.returncode)
  212. except Exception as e:
  213. self.command_signals.output.emit(f"Error: {str(e)}")
  214. self.command_signals.finished.emit(1)
  215. def update_output(self, text):
  216. self.result_text.append(text)
  217. # 自动滚动到底部
  218. self.result_text.verticalScrollBar().setValue(
  219. self.result_text.verticalScrollBar().maximum()
  220. )
  221. def process_finished(self, return_code):
  222. self.process = None
  223. self.run_btn.setEnabled(True)
  224. self.cancel_btn.setEnabled(False)
  225. if return_code == 0:
  226. self.result_text.append("\nOperation completed successfully.")
  227. else:
  228. self.result_text.append(f"\nOperation failed with return code {return_code}.")
  229. def cancel_operation(self):
  230. if self.process:
  231. self.process.terminate()
  232. self.result_text.append("\nOperation cancelled by user.")
  233. # 添加账户对话框
  234. class AddAccountDialog(QDialog):
  235. def __init__(self, platform, parent=None):
  236. super().__init__(parent)
  237. self.platform = platform
  238. self.setWindowTitle(f"Add {platform.capitalize()} Account")
  239. self.resize(400, 200)
  240. layout = QVBoxLayout()
  241. form_layout = QFormLayout()
  242. self.account_name = QLineEdit()
  243. form_layout.addRow("Account Name:", self.account_name)
  244. # 根据平台添加相应字段
  245. self.fields = {}
  246. platform_fields = {
  247. "github": ["username", "token", "private"],
  248. "gitlab": ["host", "username", "token", "private"],
  249. "gitee": ["username", "token", "private"],
  250. "gitcode": ["username", "token", "private"],
  251. "git.yoq.me": ["username", "token", "private"],
  252. "coding": ["username", "token", "project", "private"],
  253. "aliyun": ["compoanyid", "group_id", "username", "token", "private"],
  254. "cnb": ["username", "token", "private"]
  255. }
  256. for field in platform_fields.get(platform, ["username", "token"]):
  257. if field == "private":
  258. widget = QCheckBox()
  259. widget.setChecked(True)
  260. else:
  261. widget = QLineEdit()
  262. if field in ["token", "password"]:
  263. widget.setEchoMode(QLineEdit.Password)
  264. self.fields[field] = widget
  265. form_layout.addRow(f"{field.capitalize()}:", widget)
  266. layout.addLayout(form_layout)
  267. # 按钮
  268. buttons = QDialogButtonBox(
  269. QDialogButtonBox.Ok | QDialogButtonBox.Cancel
  270. )
  271. buttons.accepted.connect(self.accept)
  272. buttons.rejected.connect(self.reject)
  273. layout.addWidget(buttons)
  274. self.setLayout(layout)
  275. def get_account_data(self):
  276. data = {"name": self.account_name.text()}
  277. for field, widget in self.fields.items():
  278. if isinstance(widget, QCheckBox):
  279. data[field] = widget.isChecked()
  280. else:
  281. data[field] = widget.text()
  282. return data
  283. class SettingsTab(QWidget):
  284. def __init__(self, parent=None):
  285. super().__init__(parent)
  286. self.init_ui()
  287. def init_ui(self):
  288. layout = QVBoxLayout()
  289. # 创建平台选择标签页
  290. self.platform_tabs = QTabWidget()
  291. # 平台配置
  292. self.platform_configs = {
  293. "github": ["username", "token", "private"],
  294. "gitlab": ["host", "username", "token", "private"],
  295. "gitee": ["username", "token", "private"],
  296. "gitcode": ["username", "token", "private"],
  297. "git.yoq.me": ["username", "token", "private"],
  298. "coding": ["username", "token", "project", "private"],
  299. "aliyun": ["compoanyid", "group_id", "username", "token", "private"],
  300. "cnb": ["username", "token", "private"]
  301. }
  302. # 为每个平台创建标签页
  303. self.platform_pages = {}
  304. for platform in self.platform_configs.keys():
  305. page = QWidget()
  306. page_layout = QVBoxLayout()
  307. # 账户管理区域
  308. account_group = QGroupBox("账户管理")
  309. account_layout = QVBoxLayout()
  310. # 账户列表
  311. accounts_list_layout = QHBoxLayout()
  312. # 左侧:账户列表
  313. self.account_lists = {}
  314. account_list = QListWidget()
  315. account_list.setMinimumWidth(200)
  316. account_list.itemClicked.connect(lambda item, p=platform: self.select_account(p, item))
  317. account_list.setStyleSheet("""
  318. QListWidget::item:selected { background-color: #a6d8ff; }
  319. QListWidget::item[enabled="true"] { font-weight: bold; color: #0066cc; }
  320. """)
  321. accounts_list_layout.addWidget(account_list)
  322. self.account_lists[platform] = account_list
  323. # 右侧:账户详情
  324. account_details = QWidget()
  325. account_form = QFormLayout()
  326. account_details.setLayout(account_form)
  327. accounts_list_layout.addWidget(account_details, 1)
  328. account_layout.addLayout(accounts_list_layout)
  329. # 账户操作按钮
  330. buttons_layout = QHBoxLayout()
  331. add_btn = QPushButton("添加账户")
  332. add_btn.clicked.connect(lambda checked=False, p=platform: self.add_account(p))
  333. delete_btn = QPushButton("删除账户")
  334. delete_btn.clicked.connect(lambda checked=False, p=platform: self.delete_account(p))
  335. enable_btn = QPushButton("设为启用")
  336. enable_btn.clicked.connect(lambda checked=False, p=platform: self.enable_account(p))
  337. buttons_layout.addWidget(add_btn)
  338. buttons_layout.addWidget(delete_btn)
  339. buttons_layout.addWidget(enable_btn)
  340. buttons_layout.addStretch()
  341. account_layout.addLayout(buttons_layout)
  342. account_group.setLayout(account_layout)
  343. page_layout.addWidget(account_group)
  344. # 保存页面引用
  345. self.platform_pages[platform] = {
  346. "page": page,
  347. "form": account_form,
  348. "details": account_details
  349. }
  350. page.setLayout(page_layout)
  351. self.platform_tabs.addTab(page, platform.capitalize())
  352. layout.addWidget(self.platform_tabs)
  353. # 保存按钮
  354. self.save_btn = QPushButton("保存设置")
  355. self.save_btn.clicked.connect(self.save_settings)
  356. layout.addWidget(self.save_btn, alignment=Qt.AlignCenter)
  357. self.setLayout(layout)
  358. # 加载设置
  359. self.load_settings()
  360. # 连接标签页变更事件
  361. self.platform_tabs.currentChanged.connect(self.tab_changed)
  362. def tab_changed(self, index):
  363. try:
  364. # 使用有序字典确保顺序一致性
  365. platforms = list(self.platform_configs.keys())
  366. if index < 0 or index >= len(platforms):
  367. return
  368. platform = platforms[index]
  369. self.update_account_details(platform)
  370. except Exception as e:
  371. print(f"Error in tab_changed: {e}")
  372. def load_settings(self):
  373. # 读取.env文件
  374. env_values = dotenv_values(find_dotenv())
  375. # 为每个平台加载账户
  376. for platform in self.platform_configs.keys():
  377. account_list = self.account_lists[platform]
  378. account_list.clear()
  379. # 查找该平台的所有账户
  380. accounts = self.get_platform_accounts(platform, env_values)
  381. # 添加到列表
  382. for account in accounts:
  383. item = QListWidgetItem(account)
  384. # 标记默认账户
  385. if account == "default":
  386. item.setData(Qt.UserRole, "default")
  387. # 检查是否有其他账户被设为启用
  388. is_enabled = True
  389. for other_account in accounts:
  390. if other_account != "default" and self.is_account_enabled(platform, other_account, env_values):
  391. is_enabled = False
  392. break
  393. item.setData(Qt.UserRole + 1, is_enabled)
  394. else:
  395. item.setData(Qt.UserRole, account)
  396. item.setData(Qt.UserRole + 1, self.is_account_enabled(platform, account, env_values))
  397. # 设置启用状态的显示
  398. if item.data(Qt.UserRole + 1):
  399. item.setData(Qt.UserRole + 2, "true")
  400. item.setText(f"{account} (启用中)")
  401. account_list.addItem(item)
  402. # 选择第一个账户
  403. if account_list.count() > 0:
  404. account_list.setCurrentRow(0)
  405. self.select_account(platform, account_list.item(0))
  406. def is_account_enabled(self, platform, account, env_values=None):
  407. """检查账户是否被设为启用"""
  408. if env_values is None:
  409. env_values = dotenv_values(find_dotenv())
  410. if account == "default":
  411. # 默认账户,检查是否有其他账户被设为启用
  412. for key in env_values.keys():
  413. if key.startswith(f"{platform}_") and "_enabled" in key and env_values[key].lower() == "true":
  414. return False
  415. return True
  416. else:
  417. # 其他账户,检查是否有enabled标记
  418. return env_values.get(f"{platform}_{account}_enabled", "").lower() == "true"
  419. def get_platform_accounts(self, platform, env_values=None):
  420. if env_values is None:
  421. env_values = dotenv_values(find_dotenv())
  422. accounts = set()
  423. # 默认账户
  424. accounts.add("default")
  425. # 查找带有账户名的配置
  426. prefix = f"{platform}_"
  427. for key in env_values.keys():
  428. if key.startswith(prefix) and "_" in key[len(prefix):]:
  429. account_name = key[len(prefix):].split("_")[0]
  430. if account_name != "default" and account_name != "":
  431. accounts.add(account_name)
  432. return sorted(list(accounts))
  433. def select_account(self, platform, item):
  434. if not item:
  435. return
  436. account = item.data(Qt.UserRole)
  437. form = self.platform_pages[platform]["form"]
  438. # 清空表单
  439. while form.rowCount() > 0:
  440. form.removeRow(0)
  441. # 读取账户配置
  442. env_values = dotenv_values(find_dotenv())
  443. # 创建表单项
  444. self.field_widgets = {}
  445. for field in self.platform_configs[platform]:
  446. key = f"{platform}_{account}_{field}" if account != "default" else f"{platform}_{field}"
  447. value = env_values.get(key, "")
  448. if field == "private":
  449. widget = QCheckBox()
  450. widget.setChecked(value.lower() != "false")
  451. else:
  452. widget = QLineEdit()
  453. if field in ["token", "password"]:
  454. widget.setEchoMode(QLineEdit.Password)
  455. widget.setText(value)
  456. self.field_widgets[key] = widget
  457. form.addRow(f"{field.capitalize()}:", widget)
  458. def add_account(self, platform):
  459. dialog = AddAccountDialog(platform, self)
  460. if dialog.exec_() == QDialog.Accepted:
  461. account_data = dialog.get_account_data()
  462. account_name = account_data["name"]
  463. if not account_name:
  464. QMessageBox.warning(self, "Warning", "Account name cannot be empty.")
  465. return
  466. # 更新.env文件
  467. env_file = find_dotenv()
  468. if not env_file:
  469. env_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
  470. for field, value in account_data.items():
  471. if field == "name":
  472. continue
  473. key = f"{platform}_{account_name}_{field}"
  474. if isinstance(value, bool):
  475. value = str(value).lower()
  476. set_key(env_file, key, value)
  477. # 重新加载设置
  478. self.load_settings()
  479. # 选择新账户
  480. account_list = self.account_lists[platform]
  481. for i in range(account_list.count()):
  482. if account_list.item(i).data(Qt.UserRole) == account_name:
  483. account_list.setCurrentRow(i)
  484. self.select_account(platform, account_list.item(i))
  485. break
  486. def delete_account(self, platform):
  487. account_list = self.account_lists[platform]
  488. current_item = account_list.currentItem()
  489. if not current_item:
  490. return
  491. account = current_item.data(Qt.UserRole)
  492. if account == "default":
  493. QMessageBox.warning(self, "Warning", "Cannot delete the default account.")
  494. return
  495. reply = QMessageBox.question(
  496. self,
  497. "Confirm Deletion",
  498. f"Are you sure you want to delete the account '{account}' for {platform}?",
  499. QMessageBox.Yes | QMessageBox.No,
  500. QMessageBox.No
  501. )
  502. if reply == QMessageBox.Yes:
  503. # 删除.env中的相关配置
  504. env_file = find_dotenv()
  505. env_values = dotenv_values(env_file)
  506. prefix = f"{platform}_{account}_"
  507. keys_to_remove = [k for k in env_values.keys() if k.startswith(prefix)]
  508. # 重写.env文件,排除要删除的键
  509. with open(env_file, 'w') as f:
  510. for k, v in env_values.items():
  511. if k not in keys_to_remove:
  512. f.write(f"{k}={v}\n")
  513. # 重新加载设置
  514. self.load_settings()
  515. def enable_account(self, platform):
  516. account_list = self.account_lists[platform]
  517. current_item = account_list.currentItem()
  518. if not current_item:
  519. return
  520. account = current_item.data(Qt.UserRole)
  521. # 读取账户配置
  522. env_file = find_dotenv()
  523. env_values = dotenv_values(env_file)
  524. # 先清除所有账户的启用状态
  525. for key in list(env_values.keys()):
  526. if key.startswith(f"{platform}_") and key.endswith("_enabled"):
  527. del env_values[key]
  528. # 设置当前账户为启用
  529. if account != "default":
  530. set_key(env_file, f"{platform}_{account}_enabled", "true")
  531. # 将账户配置复制到默认配置
  532. account_config = {}
  533. prefix = f"{platform}_{account}_"
  534. for key, value in env_values.items():
  535. if key.startswith(prefix):
  536. field = key[len(prefix):]
  537. if field != "enabled": # 不复制enabled标记
  538. account_config[field] = value
  539. # 更新默认配置
  540. for field, value in account_config.items():
  541. default_key = f"{platform}_{field}"
  542. set_key(env_file, default_key, value)
  543. QMessageBox.information(
  544. self,
  545. "Success",
  546. f"Account '{account}' has been enabled for {platform}."
  547. )
  548. # 重新加载设置
  549. self.load_settings()
  550. def update_account_details(self, platform):
  551. account_list = self.account_lists[platform]
  552. current_item = account_list.currentItem()
  553. if current_item:
  554. self.select_account(platform, current_item)
  555. def save_settings(self):
  556. env_file = find_dotenv()
  557. if not env_file:
  558. env_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
  559. # 保存当前显示的账户配置
  560. platform = list(self.platform_configs.keys())[self.platform_tabs.currentIndex()]
  561. account_list = self.account_lists[platform]
  562. current_item = account_list.currentItem()
  563. if current_item and hasattr(self, 'field_widgets'):
  564. account = current_item.data(Qt.UserRole)
  565. for key, widget in self.field_widgets.items():
  566. if key.startswith(f"{platform}_{account}"):
  567. if isinstance(widget, QCheckBox):
  568. value = str(widget.isChecked()).lower()
  569. else:
  570. value = widget.text().strip()
  571. if value:
  572. set_key(env_file, key, value)
  573. QMessageBox.information(self, "Success", "Settings saved successfully!")
  574. # 如果修改的是启用的账户,更新默认配置
  575. if current_item and current_item.data(Qt.UserRole + 1):
  576. self.enable_account(platform)
  577. class AboutTab(QWidget):
  578. def __init__(self, parent=None):
  579. super().__init__(parent)
  580. layout = QVBoxLayout()
  581. layout.addWidget(QLabel(f"repo_sync tools v{__version__}"))
  582. layout.addWidget(QLabel("作者: liuyuqi.gov@msn.cn"))
  583. layout.addWidget(QLabel("GitHub: https://github.com/jianboy/repo_sync"))
  584. layout.addWidget(QLabel("\n功能说明:"))
  585. layout.addWidget(QLabel("- 支持多个代码托管平台"))
  586. layout.addWidget(QLabel("- 支持创建/推送/拉取/克隆/删除操作"))
  587. layout.addWidget(QLabel("- 自动获取资源管理器当前路径"))
  588. layout.addWidget(QLabel("- 配置信息保存在.env文件中"))
  589. layout.addWidget(QLabel("- 支持每个平台配置多个账户"))
  590. layout.addWidget(QLabel("- 命令行执行结果实时显示"))
  591. self.setLayout(layout)
  592. class RepoSyncMainWindow(QTabWidget):
  593. def __init__(self):
  594. super().__init__()
  595. self.setWindowTitle('repo_sync tools v1.12')
  596. self.resize(800, 700)
  597. self.main_tab = MainTab()
  598. self.addTab(self.main_tab, '主界面')
  599. self.settings_tab = SettingsTab()
  600. self.addTab(self.settings_tab, '设置')
  601. self.about_tab = AboutTab()
  602. self.addTab(self.about_tab, '关于')
  603. def main():
  604. """GUI主入口函数"""
  605. if not HAS_QT:
  606. print("PyQt5 not installed. Please install it with: pip install PyQt5")
  607. print("Running in fallback mode...")
  608. # 这里可以添加一个简单的命令行界面作为后备
  609. return
  610. try:
  611. app = QApplication(sys.argv)
  612. window = RepoSyncMainWindow()
  613. window.show()
  614. sys.exit(app.exec_())
  615. except Exception as e:
  616. print(f"Error starting GUI: {str(e)}")
  617. import traceback
  618. traceback.print_exc()
  619. if __name__ == '__main__':
  620. main()