feat: tune cmhub image timeouts
This commit is contained in:
@@ -42,7 +42,7 @@ class CMHubError(AIError):
|
|||||||
|
|
||||||
CMHUB_IMAGE_MAX_BYTES = 20 * 1024 * 1024
|
CMHUB_IMAGE_MAX_BYTES = 20 * 1024 * 1024
|
||||||
CMHUB_TITLE_READ_TIMEOUT_SECONDS = 600
|
CMHUB_TITLE_READ_TIMEOUT_SECONDS = 600
|
||||||
CMHUB_IMAGE_READ_TIMEOUT_SECONDS = 650
|
CMHUB_IMAGE_READ_TIMEOUT_SECONDS = 900
|
||||||
CMHUB_IMAGE_CONCURRENCY_LIMIT = 5
|
CMHUB_IMAGE_CONCURRENCY_LIMIT = 5
|
||||||
CMHUB_IMAGE_DOWNLOAD_ATTEMPTS = 3
|
CMHUB_IMAGE_DOWNLOAD_ATTEMPTS = 3
|
||||||
CMHUB_IMAGE_SLOW_DOWNLOAD_SECONDS = 20.0
|
CMHUB_IMAGE_SLOW_DOWNLOAD_SECONDS = 20.0
|
||||||
|
|||||||
+25
-4
@@ -44,6 +44,8 @@ AI_CONCURRENCY_MIN = 1
|
|||||||
AI_CONCURRENCY_MAX = 5
|
AI_CONCURRENCY_MAX = 5
|
||||||
AI_RETRY_MIN = 0
|
AI_RETRY_MIN = 0
|
||||||
AI_RETRY_MAX = 10
|
AI_RETRY_MAX = 10
|
||||||
|
CMHUB_CONNECT_TIMEOUT_DEFAULT = 66
|
||||||
|
CMHUB_CONNECT_TIMEOUT_OLD_DEFAULT = 10
|
||||||
RUNTIME_CONFIG_KEYS = {
|
RUNTIME_CONFIG_KEYS = {
|
||||||
"config_path",
|
"config_path",
|
||||||
"ai_models_path",
|
"ai_models_path",
|
||||||
@@ -81,7 +83,7 @@ DEFAULT_CONFIG = {
|
|||||||
"base_url": "",
|
"base_url": "",
|
||||||
"title_alias": "",
|
"title_alias": "",
|
||||||
"image_alias": "",
|
"image_alias": "",
|
||||||
"connect_timeout": 10,
|
"connect_timeout": CMHUB_CONNECT_TIMEOUT_DEFAULT,
|
||||||
"use_system_proxy": False,
|
"use_system_proxy": False,
|
||||||
"download_with_curl": "auto",
|
"download_with_curl": "auto",
|
||||||
"check_balance_before_batch": False,
|
"check_balance_before_batch": False,
|
||||||
@@ -350,7 +352,7 @@ def _deep_merge(defaults, loaded):
|
|||||||
return merged
|
return merged
|
||||||
|
|
||||||
|
|
||||||
def _normalize_config_values(config):
|
def _normalize_config_values(config, migrate_old_cmhub_connect_timeout=False):
|
||||||
if not isinstance(config, dict):
|
if not isinstance(config, dict):
|
||||||
return config
|
return config
|
||||||
ai = config.get("ai")
|
ai = config.get("ai")
|
||||||
@@ -376,6 +378,10 @@ def _normalize_config_values(config):
|
|||||||
cmhub = ai.get("cmhub")
|
cmhub = ai.get("cmhub")
|
||||||
if isinstance(cmhub, dict):
|
if isinstance(cmhub, dict):
|
||||||
cmhub["base_url"] = normalize_cmhub_base_url(cmhub.get("base_url", ""))
|
cmhub["base_url"] = normalize_cmhub_base_url(cmhub.get("base_url", ""))
|
||||||
|
cmhub["connect_timeout"] = _normalize_cmhub_connect_timeout(
|
||||||
|
cmhub.get("connect_timeout"),
|
||||||
|
migrate_old_default=migrate_old_cmhub_connect_timeout,
|
||||||
|
)
|
||||||
cmhub["download_with_curl"] = _normalize_cmhub_download_with_curl(
|
cmhub["download_with_curl"] = _normalize_cmhub_download_with_curl(
|
||||||
cmhub.get("download_with_curl", "auto")
|
cmhub.get("download_with_curl", "auto")
|
||||||
)
|
)
|
||||||
@@ -390,6 +396,18 @@ def _clamp_int(value, minimum, maximum, default):
|
|||||||
return min(int(maximum), max(int(minimum), number))
|
return min(int(maximum), max(int(minimum), number))
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_cmhub_connect_timeout(value, migrate_old_default=False):
|
||||||
|
try:
|
||||||
|
number = int(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
number = CMHUB_CONNECT_TIMEOUT_DEFAULT
|
||||||
|
if number <= 0:
|
||||||
|
number = CMHUB_CONNECT_TIMEOUT_DEFAULT
|
||||||
|
if migrate_old_default and number == CMHUB_CONNECT_TIMEOUT_OLD_DEFAULT:
|
||||||
|
return CMHUB_CONNECT_TIMEOUT_DEFAULT
|
||||||
|
return number
|
||||||
|
|
||||||
|
|
||||||
def _normalize_cmhub_download_with_curl(value):
|
def _normalize_cmhub_download_with_curl(value):
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return "true" if value else "false"
|
return "true" if value else "false"
|
||||||
@@ -560,7 +578,7 @@ def load_config(path=CONFIG_PATH) -> dict:
|
|||||||
except json.JSONDecodeError as exc:
|
except json.JSONDecodeError as exc:
|
||||||
raise ConfigError(f"配置文件不是有效 JSON: {path}") from exc
|
raise ConfigError(f"配置文件不是有效 JSON: {path}") from exc
|
||||||
normalized = _deep_merge(DEFAULT_CONFIG, loaded)
|
normalized = _deep_merge(DEFAULT_CONFIG, loaded)
|
||||||
_normalize_config_values(normalized)
|
_normalize_config_values(normalized, migrate_old_cmhub_connect_timeout=True)
|
||||||
_assert_no_secrets(normalized)
|
_assert_no_secrets(normalized)
|
||||||
return _with_runtime_paths(normalized, path)
|
return _with_runtime_paths(normalized, path)
|
||||||
|
|
||||||
@@ -633,7 +651,10 @@ def cmhub_config(config=None) -> dict:
|
|||||||
merged["base_url"] = normalize_cmhub_base_url(merged.get("base_url", ""))
|
merged["base_url"] = normalize_cmhub_base_url(merged.get("base_url", ""))
|
||||||
merged["title_alias"] = str(merged.get("title_alias", "") or "").strip()
|
merged["title_alias"] = str(merged.get("title_alias", "") or "").strip()
|
||||||
merged["image_alias"] = str(merged.get("image_alias", "") or "").strip()
|
merged["image_alias"] = str(merged.get("image_alias", "") or "").strip()
|
||||||
merged["connect_timeout"] = int(merged.get("connect_timeout", 10) or 10)
|
merged["connect_timeout"] = _normalize_cmhub_connect_timeout(
|
||||||
|
merged.get("connect_timeout"),
|
||||||
|
migrate_old_default=False,
|
||||||
|
)
|
||||||
merged["download_with_curl"] = _normalize_cmhub_download_with_curl(
|
merged["download_with_curl"] = _normalize_cmhub_download_with_curl(
|
||||||
merged.get("download_with_curl", "auto")
|
merged.get("download_with_curl", "auto")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class SettingsTab(QWidget):
|
|||||||
self.cmhub_connect_timeout_spin = QSpinBox()
|
self.cmhub_connect_timeout_spin = QSpinBox()
|
||||||
self.cmhub_connect_timeout_spin.setObjectName("cmhubConnectTimeoutSpin")
|
self.cmhub_connect_timeout_spin.setObjectName("cmhubConnectTimeoutSpin")
|
||||||
self.cmhub_connect_timeout_spin.setRange(1, 3600)
|
self.cmhub_connect_timeout_spin.setRange(1, 3600)
|
||||||
self.cmhub_connect_timeout_spin.setValue(10)
|
self.cmhub_connect_timeout_spin.setValue(appconfig.CMHUB_CONNECT_TIMEOUT_DEFAULT)
|
||||||
self.cmhub_check_balance_checkbox = QCheckBox("批量生成前检查余额")
|
self.cmhub_check_balance_checkbox = QCheckBox("批量生成前检查余额")
|
||||||
self.cmhub_check_balance_checkbox.setObjectName("cmhubCheckBalanceCheckbox")
|
self.cmhub_check_balance_checkbox.setObjectName("cmhubCheckBalanceCheckbox")
|
||||||
self.cmhub_refresh_button = QPushButton("刷新别名")
|
self.cmhub_refresh_button = QPushButton("刷新别名")
|
||||||
@@ -825,7 +825,16 @@ class SettingsTab(QWidget):
|
|||||||
self._loaded_cmhub_api_key = appconfig.get_cmhub_api_key(path=self.cmhub_config_path)
|
self._loaded_cmhub_api_key = appconfig.get_cmhub_api_key(path=self.cmhub_config_path)
|
||||||
self.cmhub_api_key_edit.setText(self._loaded_cmhub_api_key)
|
self.cmhub_api_key_edit.setText(self._loaded_cmhub_api_key)
|
||||||
self.cmhub_connect_timeout_spin.setValue(
|
self.cmhub_connect_timeout_spin.setValue(
|
||||||
max(1, int(cmhub_cfg.get("connect_timeout", 10) or 10))
|
max(
|
||||||
|
1,
|
||||||
|
int(
|
||||||
|
cmhub_cfg.get(
|
||||||
|
"connect_timeout",
|
||||||
|
appconfig.CMHUB_CONNECT_TIMEOUT_DEFAULT,
|
||||||
|
)
|
||||||
|
or appconfig.CMHUB_CONNECT_TIMEOUT_DEFAULT
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.cmhub_check_balance_checkbox.setChecked(
|
self.cmhub_check_balance_checkbox.setChecked(
|
||||||
bool(cmhub_cfg.get("check_balance_before_batch", False))
|
bool(cmhub_cfg.get("check_balance_before_batch", False))
|
||||||
|
|||||||
+10
-1
@@ -3,7 +3,7 @@ id: T-553
|
|||||||
title: cmhub 生图超时口径调优(连接 66 秒,读取 900 秒)
|
title: cmhub 生图超时口径调优(连接 66 秒,读取 900 秒)
|
||||||
phase: 7
|
phase: 7
|
||||||
deps: [T-535, T-545, T-547, T-548]
|
deps: [T-535, T-545, T-547, T-548]
|
||||||
status: TODO
|
status: DONE
|
||||||
created: 2026-07-08
|
created: 2026-07-08
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -50,3 +50,12 @@ cmhub 项目侧建议:桌面端连接超时提高到 60/120 秒量级;生图
|
|||||||
## 边界(不改什么)
|
## 边界(不改什么)
|
||||||
|
|
||||||
只改 cmhub 连接超时默认值、生图读取等待常量、⑤设置页显示文案、相关单测和文档;不改 cmhub API 协议、Base URL/Key/别名配置结构、标题读取等待 600 秒、图片并发上限 5、下载线程池语义、DB、Excel、Shopee/CDP 更新流程,也不增加生图读超时重试。
|
只改 cmhub 连接超时默认值、生图读取等待常量、⑤设置页显示文案、相关单测和文档;不改 cmhub API 协议、Base URL/Key/别名配置结构、标题读取等待 600 秒、图片并发上限 5、下载线程池语义、DB、Excel、Shopee/CDP 更新流程,也不增加生图读超时重试。
|
||||||
|
|
||||||
|
## 执行记录
|
||||||
|
|
||||||
|
- 2026-07-08:完成 T-553。
|
||||||
|
- 代码:`app/appconfig.py` 新增 `CMHUB_CONNECT_TIMEOUT_DEFAULT=66` 和旧默认 `10` 的读取迁移逻辑;读取旧 `data/config.json` 时缺失或旧默认 `10` 会归一为 `66`,但用户后续显式保存 `10` 仍会保留,避免堵死高级调试。
|
||||||
|
- 代码:`app/ai.py` 将 `CMHUB_IMAGE_READ_TIMEOUT_SECONDS` 从 `650` 改为 `900`,生图请求、requests 下载与 curl `--max-time` 继续共用该常量;生图读超时仍不自动重发。
|
||||||
|
- 代码:`app/gui/tabs/settings.py` 的 cmhub 连接超时默认值改为 `66`,⑤设置页「返回超时」通过常量展示为「标题 600 秒 / 图片 900 秒」。
|
||||||
|
- 测试:`tests/test_appconfig.py` 覆盖新默认 66、旧默认 10 读取迁移、显式保存 10 保留;`tests/test_ai.py` 覆盖生图请求/下载/curl/read timeout 的 900 秒口径;`tests/test_gui.py` 更新设置页展示断言。
|
||||||
|
- 验证:`python -m ruff check app tests main.py` 通过;`py -3.10 -m compileall app main.py` 通过;`git diff --check` 通过;`py -3.10 -m unittest tests.test_appconfig tests.test_ai tests.test_gui` 通过(160 tests);`py -3.10 -m unittest discover -s tests` 通过(257 tests)。
|
||||||
|
|||||||
+13
-10
@@ -490,9 +490,9 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
self.assertEqual("1K", payload["resolution"])
|
self.assertEqual("1K", payload["resolution"])
|
||||||
self.assertEqual("1:1", payload["aspect_ratio"])
|
self.assertEqual("1:1", payload["aspect_ratio"])
|
||||||
self.assertTrue(payload["image_base64"].startswith("data:image/jpeg;base64,"))
|
self.assertTrue(payload["image_base64"].startswith("data:image/jpeg;base64,"))
|
||||||
self.assertEqual((3, 650), calls[0][2]["timeout"])
|
self.assertEqual((3, ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS), calls[0][2]["timeout"])
|
||||||
self.assertEqual("https://cdn.example.com/generated.png", downloads[0][0])
|
self.assertEqual("https://cdn.example.com/generated.png", downloads[0][0])
|
||||||
self.assertEqual((3, 650), downloads[0][1]["timeout"])
|
self.assertEqual((3, ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS), downloads[0][1]["timeout"])
|
||||||
self.assertEqual(91, events[0]["metadata"]["points_balance"])
|
self.assertEqual(91, events[0]["metadata"]["points_balance"])
|
||||||
timed_steps = [
|
timed_steps = [
|
||||||
event for event in steps
|
event for event in steps
|
||||||
@@ -668,7 +668,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
image_bytes, elapsed = ai._download_cmhub_image_with_retry(
|
image_bytes, elapsed = ai._download_cmhub_image_with_retry(
|
||||||
"https://cdn.example.com/generated.png",
|
"https://cdn.example.com/generated.png",
|
||||||
connect_timeout=3,
|
connect_timeout=3,
|
||||||
read_timeout=650,
|
read_timeout=ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS,
|
||||||
on_step=steps.append,
|
on_step=steps.append,
|
||||||
slow_threshold=0,
|
slow_threshold=0,
|
||||||
)
|
)
|
||||||
@@ -716,7 +716,10 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
self.assertIn("--connect-timeout", args)
|
self.assertIn("--connect-timeout", args)
|
||||||
self.assertEqual("3", args[args.index("--connect-timeout") + 1])
|
self.assertEqual("3", args[args.index("--connect-timeout") + 1])
|
||||||
self.assertIn("--max-time", args)
|
self.assertIn("--max-time", args)
|
||||||
self.assertEqual("650", args[args.index("--max-time") + 1])
|
self.assertEqual(
|
||||||
|
str(ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS),
|
||||||
|
args[args.index("--max-time") + 1],
|
||||||
|
)
|
||||||
self.assertIn("--max-filesize", args)
|
self.assertIn("--max-filesize", args)
|
||||||
self.assertEqual(str(ai.CMHUB_IMAGE_MAX_BYTES), args[args.index("--max-filesize") + 1])
|
self.assertEqual(str(ai.CMHUB_IMAGE_MAX_BYTES), args[args.index("--max-filesize") + 1])
|
||||||
self.assertIn("--noproxy", args)
|
self.assertIn("--noproxy", args)
|
||||||
@@ -733,7 +736,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
image_bytes = ai._download_cmhub_image(
|
image_bytes = ai._download_cmhub_image(
|
||||||
url,
|
url,
|
||||||
connect_timeout=3,
|
connect_timeout=3,
|
||||||
read_timeout=650,
|
read_timeout=ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS,
|
||||||
use_system_proxy=False,
|
use_system_proxy=False,
|
||||||
download_with_curl="true",
|
download_with_curl="true",
|
||||||
)
|
)
|
||||||
@@ -748,7 +751,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
ai._download_cmhub_image(
|
ai._download_cmhub_image(
|
||||||
"http://127.0.0.1/a.png",
|
"http://127.0.0.1/a.png",
|
||||||
connect_timeout=3,
|
connect_timeout=3,
|
||||||
read_timeout=650,
|
read_timeout=ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS,
|
||||||
download_with_curl="true",
|
download_with_curl="true",
|
||||||
)
|
)
|
||||||
run.assert_not_called()
|
run.assert_not_called()
|
||||||
@@ -772,7 +775,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
image_bytes = ai._download_cmhub_image(
|
image_bytes = ai._download_cmhub_image(
|
||||||
"https://cdn.example.com/generated.png",
|
"https://cdn.example.com/generated.png",
|
||||||
connect_timeout=3,
|
connect_timeout=3,
|
||||||
read_timeout=650,
|
read_timeout=ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS,
|
||||||
download_with_curl="true",
|
download_with_curl="true",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -797,7 +800,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
image_bytes = ai._download_cmhub_image(
|
image_bytes = ai._download_cmhub_image(
|
||||||
"https://cdn.example.com/generated.png",
|
"https://cdn.example.com/generated.png",
|
||||||
connect_timeout=3,
|
connect_timeout=3,
|
||||||
read_timeout=650,
|
read_timeout=ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS,
|
||||||
download_with_curl="auto",
|
download_with_curl="auto",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -821,7 +824,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
image_bytes = ai._download_cmhub_image(
|
image_bytes = ai._download_cmhub_image(
|
||||||
"https://cdn.example.com/generated.png",
|
"https://cdn.example.com/generated.png",
|
||||||
connect_timeout=3,
|
connect_timeout=3,
|
||||||
read_timeout=650,
|
read_timeout=ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS,
|
||||||
download_with_curl="auto",
|
download_with_curl="auto",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -886,7 +889,7 @@ class AITests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual("read_timeout", raised.exception.code)
|
self.assertEqual("read_timeout", raised.exception.code)
|
||||||
self.assertEqual(1, len(calls))
|
self.assertEqual(1, len(calls))
|
||||||
self.assertEqual((3, 650), calls[0][2]["timeout"])
|
self.assertEqual((3, ai.CMHUB_IMAGE_READ_TIMEOUT_SECONDS), calls[0][2]["timeout"])
|
||||||
|
|
||||||
self.assert_removed(temp_dir)
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,10 @@ class AppConfigTests(TempDirMixin, unittest.TestCase):
|
|||||||
self.assertEqual("cmhub", ai["backend"])
|
self.assertEqual("cmhub", ai["backend"])
|
||||||
self.assertEqual("cmhub", appconfig.ai_backend(config))
|
self.assertEqual("cmhub", appconfig.ai_backend(config))
|
||||||
self.assertEqual("", appconfig.cmhub_config(config)["base_url"])
|
self.assertEqual("", appconfig.cmhub_config(config)["base_url"])
|
||||||
|
self.assertEqual(
|
||||||
|
appconfig.CMHUB_CONNECT_TIMEOUT_DEFAULT,
|
||||||
|
appconfig.cmhub_config(config)["connect_timeout"],
|
||||||
|
)
|
||||||
self.assertFalse(os.path.exists(cmhub_path))
|
self.assertFalse(os.path.exists(cmhub_path))
|
||||||
self.assertEqual({"api_key": ""}, appconfig.load_cmhub_config(cmhub_path))
|
self.assertEqual({"api_key": ""}, appconfig.load_cmhub_config(cmhub_path))
|
||||||
|
|
||||||
@@ -244,6 +248,20 @@ class AppConfigTests(TempDirMixin, unittest.TestCase):
|
|||||||
self.assertEqual("cmhub", appconfig.ai_config(migrated)["backend"])
|
self.assertEqual("cmhub", appconfig.ai_config(migrated)["backend"])
|
||||||
self.assertEqual(180, appconfig.response_timeout(migrated))
|
self.assertEqual(180, appconfig.response_timeout(migrated))
|
||||||
|
|
||||||
|
with open(config_path, "w", encoding="utf-8") as fh:
|
||||||
|
json.dump({"ai": {"cmhub": {"connect_timeout": 10}}}, fh)
|
||||||
|
migrated = appconfig.load_config(config_path)
|
||||||
|
self.assertEqual(
|
||||||
|
appconfig.CMHUB_CONNECT_TIMEOUT_DEFAULT,
|
||||||
|
appconfig.cmhub_config(migrated)["connect_timeout"],
|
||||||
|
)
|
||||||
|
|
||||||
|
saved = appconfig.save_config(
|
||||||
|
{"ai": {"cmhub": {"connect_timeout": 10}}},
|
||||||
|
path=config_path,
|
||||||
|
)
|
||||||
|
self.assertEqual(10, appconfig.cmhub_config(saved)["connect_timeout"])
|
||||||
|
|
||||||
direct_cfg = appconfig.default_config()
|
direct_cfg = appconfig.default_config()
|
||||||
direct_cfg["ai"]["backend"] = "direct"
|
direct_cfg["ai"]["backend"] = "direct"
|
||||||
self.assertEqual("direct", appconfig.ai_backend(direct_cfg))
|
self.assertEqual("direct", appconfig.ai_backend(direct_cfg))
|
||||||
|
|||||||
+1
-1
@@ -930,7 +930,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
tab.image_concurrency_spin.setValue(2)
|
tab.image_concurrency_spin.setValue(2)
|
||||||
tab.retry_spin.setValue(1)
|
tab.retry_spin.setValue(1)
|
||||||
tab.resolution_combo.setCurrentIndex(tab.resolution_combo.findData("2k"))
|
tab.resolution_combo.setCurrentIndex(tab.resolution_combo.findData("2k"))
|
||||||
self.assertEqual("标题 600 秒 / 图片 650 秒", tab.response_timeout_label.text())
|
self.assertEqual("标题 600 秒 / 图片 900 秒", tab.response_timeout_label.text())
|
||||||
tab.jpg_quality_spin.setValue(86)
|
tab.jpg_quality_spin.setValue(86)
|
||||||
tab.chrome_path_edit.setText("D:\\Chrome\\chrome.exe")
|
tab.chrome_path_edit.setText("D:\\Chrome\\chrome.exe")
|
||||||
tab.default_debug_port_spin.setValue(9300)
|
tab.default_debug_port_spin.setValue(9300)
|
||||||
|
|||||||
Reference in New Issue
Block a user