diff --git a/CloudflareSpeedTest-GUI.py b/CloudflareSpeedTest-GUI.py deleted file mode 100644 index c8cbb6b..0000000 --- a/CloudflareSpeedTest-GUI.py +++ /dev/null @@ -1,375 +0,0 @@ -import sys -import os -import json -import subprocess -import ctypes -from functools import partial -from PyQt5 import QtWidgets, QtGui, QtCore - -def resource_path(relative_path): - """获取 PyInstaller 打包后资源文件路径""" - if hasattr(sys, "_MEIPASS"): - return os.path.join(sys._MEIPASS, relative_path) - return os.path.join(os.path.abspath("."), relative_path) - -APP_ICON = resource_path("app.ico") -CFST_EXE = "cfst.exe" -IP_FILE_NAME = "ip.txt" -SAVED_SETTINGS_FILE = "saved_settings.json" -APP_USER_MODEL_ID = "com.example.cloudflarespeedtest" - -def _set_windows_appid(appid): - try: - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) - except Exception: - pass - - -if sys.platform.startswith("win"): - _set_windows_appid(APP_USER_MODEL_ID) - -class MainWin(QtWidgets.QWidget): - def __init__(self): - super().__init__() - - self.setWindowTitle("CloudflareSpeedTest_GUI -- 小琳解说") - - # 图标(修复:PyInstaller 下也能找到) - if os.path.exists(APP_ICON): - self.setWindowIcon(QtGui.QIcon(APP_ICON)) - else: - self.setWindowIcon(QtGui.QIcon(resource_path("app.ico"))) - - self.setFont(QtGui.QFont("Microsoft YaHei", 10)) - self.resize(500, 520) - - self._build_ui() - self._load_saved_settings_list() - - def _build_ui(self): - main_layout = QtWidgets.QVBoxLayout(self) - - params = [ - ("-n", "200", "延迟线程 1-1000"), - ("-t", "4", "延迟次数"), - ("-dn", "10", "下载数量"), - ("-dt", "10", "下载时间(秒)"), - ("-tp", "443", "端口"), - ("-url", "https://cf.xiu2.xyz/url", "测速地址"), - ("-httping", "", "HTTPing 模式 (勾选启用)"), - ("-httping-code", "200", "HTTP 有效状态码"), - ("-cfcolo", "HKG,KHH,NRT,LAX", "地区码, HTTPing 模式可用"), - ("-tl", "9999", "平均延迟上限(ms)"), - ("-tll", "0", "平均延迟下限(ms)"), - ("-tlr", "1.00", "丢包上限 0.00-1.00"), - ("-sl", "0", "下载速度下限 MB/s"), - ("-p", "10", "显示结果数量"), - ("-f", "ip.txt", "IP 段文件"), - ("-ip", "", "指定 IP 段"), - ("-o", "result.csv", "输出文件"), - ("-dd", "", "禁用下载测速 (勾选启用)"), - ("-allip", "", "测速全部 IP (勾选启用)") - ] - - grid = QtWidgets.QGridLayout() - grid.setColumnStretch(1, 1) - - self.controls = {} - row = 0 - - for key, default, hint in params: - cb = QtWidgets.QCheckBox(key) - cb.setChecked(False) - - if key == "-n": - widget = QtWidgets.QSpinBox() - widget.setRange(1, 1000) - try: - widget.setValue(int(default)) - except Exception: - widget.setValue(200) - widget.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) - widget.setEnabled(False) - else: - widget = QtWidgets.QLineEdit(default) - if default == "": - widget.setPlaceholderText(hint) - widget.setEnabled(False) - widget.setStyleSheet("QLineEdit:disabled { color: gray; }") - - lbl = QtWidgets.QLabel(hint) - cb.stateChanged.connect(partial(self._on_checkbox_toggled, key)) - - grid.addWidget(cb, row, 0) - grid.addWidget(widget, row, 1) - grid.addWidget(lbl, row, 2) - - self.controls[key] = (cb, widget) - row += 1 - - main_layout.addLayout(grid) - - save_load_layout = QtWidgets.QGridLayout() - save_load_layout.setColumnStretch(1, 1) - - save_label = QtWidgets.QLabel("保存设置名称") - self.save_name_edit = QtWidgets.QLineEdit() - self.save_name_edit.setPlaceholderText("填写保存设置名称") - self.save_btn = QtWidgets.QPushButton("保存设置") - - load_label = QtWidgets.QLabel("已保存设置") - self.load_combo = QtWidgets.QComboBox() - self.load_combo.setEditable(False) - - sp = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - self.save_name_edit.setSizePolicy(sp) - self.load_combo.setSizePolicy(sp) - - self.load_btn = QtWidgets.QPushButton("加载设置") - self.delete_btn = QtWidgets.QPushButton("删除已保存") - - load_btns_layout = QtWidgets.QHBoxLayout() - load_btns_layout.addWidget(self.load_btn) - load_btns_layout.addWidget(self.delete_btn) - load_btns_layout.addStretch() - - save_load_layout.addWidget(save_label, 0, 0) - save_load_layout.addWidget(self.save_name_edit, 0, 1) - save_load_layout.addWidget(self.save_btn, 0, 2) - - save_load_layout.addWidget(load_label, 1, 0) - save_load_layout.addWidget(self.load_combo, 1, 1) - save_load_layout.addLayout(load_btns_layout, 1, 2) - - # 运行按钮 - self.run_btn = QtWidgets.QPushButton("运行\n测速") - btn_size = 88 - self.run_btn.setFixedSize(btn_size, btn_size) - - font = QtGui.QFont("Microsoft YaHei", 14) - font.setBold(True) - self.run_btn.setFont(font) - self.run_btn.setStyleSheet(""" - QPushButton { - background-color: #0078D7; - color: white; - border: none; - font-weight: bold; - border-radius: 8px; - } - QPushButton:pressed { - background-color: #005A9E; - } - """) - - save_load_layout.addWidget( - self.run_btn, 0, 3, 2, 1, alignment=QtCore.Qt.AlignCenter - ) - - main_layout.addLayout(save_load_layout) - - self.save_btn.clicked.connect(self._on_save_clicked) - self.load_btn.clicked.connect(self._on_load_clicked) - self.delete_btn.clicked.connect(self._on_delete_clicked) - self.run_btn.clicked.connect(self._on_run_clicked) - - def _on_checkbox_toggled(self, key, state): - cb, widget = self.controls[key] - enabled = (state == 2) - widget.setEnabled(enabled) - if isinstance(widget, QtWidgets.QLineEdit): - if enabled: - widget.setStyleSheet("QLineEdit { color: black; }") - else: - widget.setStyleSheet("QLineEdit:disabled { color: gray; }") - - def _load_saved_settings_list(self): - self.load_combo.clear() - if not os.path.exists(SAVED_SETTINGS_FILE): - return - try: - with open(SAVED_SETTINGS_FILE, "r", encoding="utf-8") as f: - data = json.load(f) - names = sorted(data.keys()) - self.load_combo.addItems(names) - except Exception: - pass - - def _read_saved_settings(self): - if not os.path.exists(SAVED_SETTINGS_FILE): - return {} - try: - with open(SAVED_SETTINGS_FILE, "r", encoding="utf-8") as f: - return json.load(f) - except Exception: - return {} - - def _write_saved_settings(self, data): - try: - with open(SAVED_SETTINGS_FILE, "w", encoding="utf-8") as f: - json.dump(data, f, ensure_ascii=False, indent=2) - return True - except Exception: - return False - - def _on_save_clicked(self): - name = self.save_name_edit.text().strip() - if not name: - QtWidgets.QMessageBox.warning(self, "保存失败", "请填写保存设置的名称。") - return - - settings = {} - for k, (cb, widget) in self.controls.items(): - if isinstance(widget, QtWidgets.QSpinBox): - val = widget.value() - else: - val = widget.text() - settings[k] = [cb.isChecked(), val] - - all_saved = self._read_saved_settings() - all_saved[name] = settings - - ok = self._write_saved_settings(all_saved) - if ok: - QtWidgets.QMessageBox.information(self, "保存成功", f"设置已保存为: {name}") - self._load_saved_settings_list() - idx = self.load_combo.findText(name) - if idx >= 0: - self.load_combo.setCurrentIndex(idx) - else: - QtWidgets.QMessageBox.warning(self, "保存失败", "写入保存文件失败。") - - def _on_load_clicked(self): - name = self.load_combo.currentText().strip() - if not name: - QtWidgets.QMessageBox.warning(self, "加载失败", "请先选择一个已保存的设置名称。") - return - - all_saved = self._read_saved_settings() - if name not in all_saved: - QtWidgets.QMessageBox.warning(self, "加载失败", "所选设置不存在或已被删除。") - self._load_saved_settings_list() - return - - settings = all_saved[name] - - for k, (cb, widget) in self.controls.items(): - if k in settings: - checked, val = settings[k] - cb.setChecked(bool(checked)) - if isinstance(widget, QtWidgets.QSpinBox): - try: - widget.setValue(int(val)) - except Exception: - pass - widget.setEnabled(bool(checked)) - else: - widget.setText(str(val)) - widget.setEnabled(bool(checked)) - widget.setStyleSheet( - "QLineEdit { color: black; }" if widget.isEnabled() else "QLineEdit:disabled { color: gray; }" - ) - - QtWidgets.QMessageBox.information(self, "加载成功", f"已加载设置: {name}") - - def _on_delete_clicked(self): - name = self.load_combo.currentText().strip() - if not name: - QtWidgets.QMessageBox.warning(self, "删除失败", "请先选择一个已保存的设置名称。") - return - - all_saved = self._read_saved_settings() - if name not in all_saved: - QtWidgets.QMessageBox.warning(self, "删除失败", "所选设置不存在。") - self._load_saved_settings_list() - return - - reply = QtWidgets.QMessageBox.question( - self, "确认删除", - f"确定要删除已保存设置: {name} ?", - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No - ) - - if reply != QtWidgets.QMessageBox.Yes: - return - - del all_saved[name] - ok = self._write_saved_settings(all_saved) - if ok: - QtWidgets.QMessageBox.information(self, "删除成功", f"已删除: {name}") - self._load_saved_settings_list() - else: - QtWidgets.QMessageBox.warning(self, "删除失败", "删除时写入文件失败。") - - def _find_file_case_insensitive(self, target_name): - target_lower = target_name.lower() - for entry in os.listdir("."): - if entry.lower() == target_lower: - return entry - return None - - def _build_cmd_list(self, exe_name): - cmd_list = [exe_name] - - for k, (cb, widget) in self.controls.items(): - if not cb.isChecked(): - continue - - if k == "-n": - cmd_list.append(k) - cmd_list.append(str(widget.value())) - continue - - if k in ("-httping", "-dd", "-allip"): - cmd_list.append(k) - continue - - val = widget.text().strip() - if val == "": - continue - - cmd_list.append(k) - cmd_list.append(val) - - return cmd_list - - def _on_run_clicked(self): - cfst_actual = self._find_file_case_insensitive(CFST_EXE) - ip_actual = self._find_file_case_insensitive(IP_FILE_NAME) - - missing = [] - if not cfst_actual: - missing.append(CFST_EXE) - if not ip_actual: - missing.append(IP_FILE_NAME) - - if missing: - missing_str = ",".join(missing) - QtWidgets.QMessageBox.warning( - self, "文件缺失", - f"未找到必要文件: {missing_str}\n请将缺失文件放在程序同目录后重试。" - ) - return - - cmd_list = self._build_cmd_list(cfst_actual) - if not cmd_list: - cmd_list = [cfst_actual] - - try: - if os.name == "nt": - CREATE_NEW_CONSOLE = 0x00000010 - subprocess.Popen(cmd_list, creationflags=CREATE_NEW_CONSOLE) - else: - subprocess.Popen(cmd_list) - except Exception: - return - -if __name__ == "__main__": - app = QtWidgets.QApplication(sys.argv) - - # 全局图标 - app.setWindowIcon(QtGui.QIcon(APP_ICON)) - - w = MainWin() - w.show() - sys.exit(app.exec_())