commit 7b4b2ae86c08f43ab7a5f5d121d887a138ae76df Author: KenanZhu <3471685733@qq.com> Date: Tue Nov 4 00:14:45 2025 +0800 chore(*): first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccfe4db --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +**/build +**/dist +**/models/*.onnx +**/driver/*.exe +**/.git +**/.vscode +**/.stfolder +**/.stignore +**/gui/AutoLibraryResources.py +**/__pycache__ +**/gui/AutoLibraryResource.py +**/gui/Ui_ALMainWindow.py +**/gui/Ui_ALConfigWidget.py +**/gui/translators/qtbase_zh_CN.qm diff --git a/AutoLib.py b/AutoLib.py new file mode 100644 index 0000000..e103583 --- /dev/null +++ b/AutoLib.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import os +import queue + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.edge.service import Service + +from MsgBase import MsgBase +from LibLogin import LibLogin +from LibLogout import LibLogout +from LibReserve import LibReserve + +from ConfigReader import ConfigReader + + +class AutoLib(MsgBase): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + ): + super().__init__(input_queue, output_queue) + + self.__system_config_reader = None + self.__users_config_reader = None + self.__driver = None + + + def __initBrowserDriver( + self + ) -> bool: + + self._showTrace("正在初始化浏览器驱动......") + edge_options = webdriver.EdgeOptions() + + if self.__system_config_reader.get("web_driver/headless"): + edge_options.add_argument("--headless") + edge_options.add_argument("--disable-gpu") + edge_options.add_argument("--no-sandbox") + edge_options.add_argument("--disable-dev-shm-usage") + + edge_options.add_argument("--window-size=1280,720") + edge_options.add_argument("--remote-allow-origins=*") + + # omit ssl errors and verbose log level + edge_options.add_argument("--ignore-certificate-errors") + edge_options.add_argument("--ignore-ssl-errors") + edge_options.add_argument("--log-level=OFF") + edge_options.add_argument("--silent") + + edge_options.add_experimental_option("excludeSwitches", ["enable-automation"]) + edge_options.add_experimental_option("useAutomationExtension", False) + edge_options.add_argument("--disable-blink-features=AutomationControlled") + edge_options.add_argument( + "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "\ + "AppleWebKit/537.36 (KHTML, like Gecko) "\ + "Chrome/120.0.0.0 "\ + "Safari/537.36 "\ + "Edg/120.0.0.0" + ) + + # init browser driver + self.__driver_path = self.__system_config_reader.get("web_driver/driver_path") + self.__driver_type = self.__system_config_reader.get("web_driver/driver_type") + self.__driver_path = os.path.abspath(self.__driver_path) + try: + service = None + if self.__driver_path: + service = Service(executable_path=self.__driver_path) + match self.__driver_type.lower(): + case "edge": + self.__driver = webdriver.Edge(service=service, options=edge_options) + case "chrome": + self.__driver = webdriver.Chrome(service=service, options=edge_options) + case "firefox": + self.__driver = webdriver.Firefox(service=service, options=edge_options) + case _: + raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type}") + self.__driver.implicitly_wait(10) + self.__driver.execute_script( + "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})" + ) + except Exception as e: + self._showTrace(f"浏览器驱动初始化失败: {e}") + return False + # init library operators + self.__lib_login = LibLogin(self._input_queue, self._output_queue, self.__driver) + self.__lib_logout = LibLogout(self._input_queue, self._output_queue, self.__driver) + self.__lib_reserve = LibReserve(self._input_queue, self._output_queue, self.__driver) + self._showTrace(f"浏览器驱动已初始化, 类型: {self.__driver_type}, 路径: {self.__driver_path}") + return True + + + def __waitResponseLoad( + self, + ) -> bool: + + # wait for page load + try: + WebDriverWait(self.__driver, 5).until( # title contains "首页" + EC.title_contains("首页") + ) + WebDriverWait(self.__driver, 2).until( # username field presence + EC.presence_of_element_located((By.NAME, "username")) + ) + WebDriverWait(self.__driver, 2).until( # password field presence + EC.presence_of_element_located((By.NAME, "password")) + ) + WebDriverWait(self.__driver, 2).until( # captcha field presence + EC.presence_of_element_located((By.NAME, "answer")) + ) + WebDriverWait(self.__driver, 2).until( # captcha image presence + EC.presence_of_element_located((By.ID, "loadImgId")) + ) + return True + except: + self._showTrace(f"登录页面加载失败 !") + return False + + + def __initDriverUrl( + self, + ) -> bool: + + self.__driver.get(self.__system_config_reader.get("library/host_url")) + if not self.__waitResponseLoad(): + return False + return True + + + def __run( + self, + username: str, + password: str, + reserve_info: dict, + ) -> bool: + + success = False + + # login + if not self.__lib_login.login( + username, + password, + self.__system_config_reader.get("login/max_attempt", 5), + self.__system_config_reader.get("login/auto_captcha", True), + ): + return False + run_mode = self.__system_config_reader.get("run/mode", 1) + run_mode = { + "auto_reserve": run_mode&0x1, + "auto_checkin": run_mode&0x2, + "auto_renewal": run_mode&0x4, + } + # reserve or checkin or renewal + """ + Here, we collect the run mode from the config file. + """ + if self.__lib_reserve.canReserve(reserve_info.get("date")) and run_mode["auto_reserve"]: + if self.__lib_reserve.reserve(reserve_info): + self._showTrace(f"用户 {username} 预约成功 !") + success = True + else: + self._showTrace(f"用户 {username} 预约失败 !") + success = False + # logout + if not self.__lib_logout.logout( + username, + ): + self.__driver.get(self.__system_config_reader.get("library/host_url")) + return False + return success + + + def run( + self, + system_config_reader: ConfigReader, + users_config_reader: ConfigReader, + ): + + self.__system_config_reader = system_config_reader + self.__users_config_reader = users_config_reader + if not self.__initBrowserDriver(): + return + else: + if not self.__initDriverUrl(): + return + + user_counter = {"current": 0, "success": 0, "failed": 0} + users = self.__users_config_reader.get("users") + self._showTrace(f"共发现 {len(users)} 个用户, "\ + f"用户配置文件路径: {self.__users_config_reader.configPath()}") + + for user in users: + self._showTrace(f"正在处理第 {user_counter["current"]}/{len(users)} 个用户: {user['username']}......") + if self.__run( + username=user["username"], + password=user["password"], + reserve_info=user["reserve_info"], + ): + user_counter["success"] += 1 + else: + user_counter["failed"] += 1 + self._showTrace(f"处理完成, 共计 {user_counter["current"]} 个用户, "\ + f"成功 {user_counter["success"]} 个用户, "\ + f"失败 {user_counter["failed"]} 个用户") + return + + + def close( + self, + ) -> bool: + + if self.__driver: + self.__driver.quit() + self.__driver = None + self._showTrace(f"浏览器驱动已关闭") + return True + else: + self._showTrace(f"浏览器驱动未初始化,无需关闭") + return False \ No newline at end of file diff --git a/ConfigReader.py b/ConfigReader.py new file mode 100644 index 0000000..194a1a5 --- /dev/null +++ b/ConfigReader.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import json + + +class ConfigReader: + + def __init__( + self, + config_path: str + ): + + self._config_path = config_path + self._config_data = {} + if not self.__readConfig(): + return None + + + def __readConfig( + self + ) -> bool: + + try: + with open(self._config_path, 'r', encoding='utf-8') as file: + self._config_data = json.load(file) + return True + except Exception as e: + print(f"Error reading config file: {e}") + return False + + + def getConfigs( + self + ) -> dict: + + return self._config_data.copy() + + + def getConfig( + self, + key: str + ) -> dict: + + return self._config_data.get(key, {}) + + + def get( + self, + key: str, + default: any = None + ) -> any: + + keys = key.split('/') + current = self._config_data + for k in keys: + if isinstance(current, dict) and k in current: + current = current[k] + else: + return default + return current + + + def hasConfig( + self, + key: str + ) -> bool: + + return self.getConfig(key) != {} + + + def reReadConfig( + self + ) -> bool: + + return self.__readConfig() + + + def configPath( + self + ) -> str: + + return self._config_path diff --git a/ConfigWriter.py b/ConfigWriter.py new file mode 100644 index 0000000..bbddb48 --- /dev/null +++ b/ConfigWriter.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import json + + +class ConfigWriter: + + def __init__( + self, + config_path: str, + config_data: dict + ): + + self.__config_path = config_path + self.__config_data = config_data if config_data is not None else {} + if config_data is None: + return None + if not self.__writeConfig(): + return None + + + def __writeConfig( + self + ) -> bool: + + try: + with open(self.__config_path, "w") as f: + json.dump(self.__config_data, f, indent=4, sort_keys=False) + return True + except: + return False + + + def setConfigs( + self, + configs: dict + ) -> bool: + + self.__config_data = configs + return self.__writeConfig() + + + def setConfig( + self, + key: str, + value: dict + ) -> bool: + + self.__config_data[key] = value + return self.__writeConfig() + + + def set( + self, + key: str, + value: dict + ) -> bool: + + keys = key.replace("\\", "/").split("/") + current = self.__config_data + for k in keys[:-1]: + if k not in current or not isinstance(current[k], dict): + current[k] = {} + current = current[k] + current[keys[-1]] = value + return self.__writeConfig() + + + def reWriteConfig( + self + ) -> bool: + + return self.__writeConfig() + + + def configPath( + self + ) -> str: + + return self.__config_path \ No newline at end of file diff --git a/LibCheckin.py b/LibCheckin.py new file mode 100644 index 0000000..57544ef --- /dev/null +++ b/LibCheckin.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import re +import time +import queue + +from datetime import datetime, timedelta +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from LibOperator import LibOperator + + +class LibCheckin(LibOperator): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + driver + ): + + super().__init__(input_queue, output_queue) + + self.__driver = driver + + + def _waitResponseLoad( + self, + ) -> bool: + + pass \ No newline at end of file diff --git a/LibCheckout.py b/LibCheckout.py new file mode 100644 index 0000000..be4ba7a --- /dev/null +++ b/LibCheckout.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import re +import time +import queue + +from datetime import datetime, timedelta +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from LibOperator import LibOperator + + +class LibCheckout(LibOperator): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + driver + ): + + super().__init__(input_queue, output_queue) + + self.__driver = driver + + + def _waitResponseLoad( + self, + ) -> bool: + + pass \ No newline at end of file diff --git a/LibLogin.py b/LibLogin.py new file mode 100644 index 0000000..8cbedb7 --- /dev/null +++ b/LibLogin.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import time +import queue +import base64 + +import ddddocr + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from LibOperator import LibOperator + + +class LibLogin(LibOperator): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + driver + ): + + super().__init__(input_queue, output_queue) + + self.__driver = driver + self.__ddddocr = ddddocr.DdddOcr() + + + def _waitResponseLoad( + self, + ) -> bool: + + # wait to verify login success + try: + WebDriverWait(self.__driver, 5).until( # title contains "自选座位 :: 座位预约系统" + EC.title_contains("自选座位 :: 座位预约系统") + ) + WebDriverWait(self.__driver, 3).until( # search button presence + EC.presence_of_element_located((By.ID, "search")) + ) + WebDriverWait(self.__driver, 3).until( # select content presence + EC.presence_of_element_located((By.CLASS_NAME, "selectContent")) + ) + return True + except Exception as e: + self._showTrace(f"登录页面加载失败 ! : {e}") + return False + + + def __fillLogInElements( + self, + username: str, + password: str, + ) -> bool: + + # ensure elements presence and fill them + try: + username_element = self.__driver.find_element(By.NAME, "username") + username_element.clear() + username_element.send_keys(username) + password_element = self.__driver.find_element(By.NAME, "password") + password_element.clear() + password_element.send_keys(password) + except Exception as e: + self._showTrace(f"用户名或密码填写失败 ! : {e}") + return False + return True + + + def __autoRecognizeCaptcha( + self, + ) -> str: + + # auto recognize captcha + try: + captcha_img = self.__driver.find_element(By.ID, "loadImgId") + img_src = captcha_img.get_attribute("src") + base64_str = img_src.split(',', 1)[1] + captcha_img = base64.b64decode(base64_str) + captcha_text = self.__ddddocr.classification(captcha_img) + captcha_text = ''.join(filter(str.isalnum, captcha_text)).lower() + self._showTrace(f"识别到验证码为 : '{captcha_text}'.") + if len(captcha_text) != 4: + raise Exception("识别到的验证码长度不等于 4 个字符 !") + return captcha_text + except Exception as e: + self._showTrace(f"验证码识别失败 ! : {e}") + self.__refreshCaptcha() + return "" + + + def __manualRecognizeCaptcha( + self, + ) -> str: + + # manual recognize captcha + try: + self._show_msg("请输入验证码:") + captcha_text = self._wait_msg(timeout=15) + self._showTrace(f"输入的验证码为 : '{captcha_text}'.") + if len(captcha_text) != 4: + raise Exception("输入的验证码长度不等于 4 个字符 !") + return captcha_text + except Exception as e: + self._showTrace(f"输入验证码失败 ! : {e}") + self.__refreshCaptcha() + return "" + + + def __refreshCaptcha( + self, + ): + + # refresh captcha + try: + self._showTrace("刷新验证码......") + self.__driver.find_element( + By.ID, "loadImgId" + ).click() + time.sleep(1) + return True + except Exception as e: + self._showTrace(f"刷新验证码失败 ! : {e}") + self.__refreshCaptcha() + return False + + + def login( + self, + username: str, + password: str, + max_attempts: int = 5, + auto_captcha: bool = True, + ) -> bool: + + if self.__driver is None: + self._showTrace("未提供有效 WebDriver 实例 !") + return False + # begin login process + for attempt in range(max_attempts): + self._showTrace(f"用户 {username} 第 {attempt + 1} 次尝试登录......") + if not self.__fillLogInElements( + username, + password, + ): + continue + while True: + if auto_captcha: + captcha_text = self.__autoRecognizeCaptcha() + else: + self._showTrace(f"用户未配置自动识别验证码, 请手动输入验证码 !") + captcha_text = self.__manualRecognizeCaptcha() + if captcha_text: + break + captcha_element = self.__driver.find_element(By.NAME, "answer") + captcha_element.clear() + captcha_element.send_keys(captcha_text) + self._showTrace("尝试登录...") + try: + self.__driver.find_element( + By.XPATH, + "//input[@type='button' and @value='登录']" + ).click() + except Exception as e: + self._showTrace(f"登录失败 ! : {e}") + continue + if self._waitResponseLoad(): + self._showTrace(f"用户 {username} 第 {attempt + 1} 次登录成功 !") + return True + else: + self._showTrace(f"用户 {username} 第 {attempt + 1} 次登录失败 !") + return False diff --git a/LibLogout.py b/LibLogout.py new file mode 100644 index 0000000..b894fa2 --- /dev/null +++ b/LibLogout.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import queue + +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from LibOperator import LibOperator + + +class LibLogout(LibOperator): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + driver, + ): + + super().__init__(input_queue, output_queue) + + self.__driver = driver + + + def _waitResponseLoad( + self, + ) -> bool: + + return True + + + def logout( + self, + username: str, + ) -> bool: + + if self.__driver is None: + self._showTrace("未提供有效 WebDriver 实例 !") + + return False + + try: + self.__driver.find_element( + By.XPATH, "//a[@href='/logout']" + ).click() + + self._showTrace(f"用户 {username} 注销成功 !") + + return True + + except Exception as e: + self._showTrace(f"用户 {username} 注销失败 ! : {e}") + + return False diff --git a/LibOperator.py b/LibOperator.py new file mode 100644 index 0000000..81851c3 --- /dev/null +++ b/LibOperator.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import queue + +from MsgBase import MsgBase + + +class LibOperator(MsgBase): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + ): + + super().__init__(input_queue, output_queue) + + + def _waitResponseLoad( + self, + ) -> bool: + + pass diff --git a/LibRenew.py b/LibRenew.py new file mode 100644 index 0000000..5df4450 --- /dev/null +++ b/LibRenew.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import os +import queue + +from LibOperator import LibOperator + + +class LibRenew(LibOperator): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + driver + ): + + super().__init__(input_queue, output_queue) + + self.__driver = driver + + + def _waitResponseLoad( + self, + ) -> bool: + + pass \ No newline at end of file diff --git a/LibReserve.py b/LibReserve.py new file mode 100644 index 0000000..cedc134 --- /dev/null +++ b/LibReserve.py @@ -0,0 +1,652 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import re +import time +import queue + +from datetime import datetime, timedelta +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from LibOperator import LibOperator + + +class LibReserve(LibOperator): + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + driver + ): + + super().__init__(input_queue, output_queue) + + self.__driver = driver + # library floor and room mapping in website + self.__floor_map = { + "2": "二层", + "3": "三层", + "4": "四层", + "5": "五层" + } + self.__room_map = { + "1": "二层内环", + "2": "二层外环", + "3": "三层内环", + "4": "三层外环", + "5": "四层内环", + "6": "四层外环", + "7": "四层期刊区", + "8": "五层考研" + } + + + def _waitResponseLoad( + self, + ) -> bool: + + try: + WebDriverWait(self.__driver, 5).until( + EC.presence_of_element_located((By.CLASS_NAME, "layoutSeat")) + ) + title_elements = [] + # reserve failed without title elements, so we need to try + try: + WebDriverWait(self.__driver, 1).until( + EC.presence_of_element_located((By.CSS_SELECTOR, ".layoutSeat dt")) + ) + title_elements = self.__driver.find_elements( + By.CSS_SELECTOR, ".layoutSeat dt" + ) + except: + pass + content_elements = self.__driver.find_elements( + By.CSS_SELECTOR, ".layoutSeat dd" + ) + if not content_elements: + self._showTrace("未找到预约结果") + raise + title = title_elements[0].text if title_elements else "" + contents = [element.text for element in content_elements if element.text.strip()] + for message in contents: + if "预约失败" in message or "已有1个有效预约" in message: + self._showTrace(f"预约失败 - {"".join(contents)}") + raise + if "预定好了" in title or "预约成功" in title or "操作成功" in title: + if len(contents) >= 6: + date_val = contents[1].split(" : ")[1].strip() if " : " in contents[1] else contents[1].strip() + time_val = contents[2].split(" : ")[1].strip() if " : " in contents[2] else contents[2].strip() + seat_val = contents[3].split(" : ")[1].strip() if " : " in contents[3] else contents[3].strip() + checkin_val = contents[5].strip() + self._showTrace(f"\n"\ + f" 预约成功 !\n"\ + f" 预约日期: {date_val}, \n"\ + f" 预约时间: {time_val}, \n"\ + f" 预约座位: {seat_val}, \n"\ + f" 签到时间: {checkin_val}") + else: + self._showTrace(f"\n"\ + f" 预约成功 !\n"\ + f" 未找获取到详细信息") + return True + except: + self._showTrace(f"预约结果加载失败 !") + return False + + @staticmethod + def __timeToMins( + time_str: str + ) -> int: + + hour, minute = map(int, time_str.split(":")) + return hour*60 + minute + + @staticmethod + def __minsToTime( + mins: int + ) -> str: + + hour, minute = divmod(mins, 60) + return f"{hour:02d}:{minute:02d}" + + + def __checkReserveInfo( + self, + reserve_info: dict + ) -> bool: + + try: + # check the required information + # reserve_info["place"] + if reserve_info.get("floor") is None: + raise ValueError("未指定楼层") + if reserve_info["floor"] not in self.__floor_map: + raise ValueError(f"楼层 '{reserve_info['floor']}' 不存在") + if reserve_info.get("room") is None: + raise ValueError("未指定房间") + if reserve_info["room"] not in self.__room_map: + raise ValueError(f"房间 '{reserve_info['room']}' 不存在") + if reserve_info.get("seat_id") is None: + raise ValueError("未指定座位") + except ValueError as e: + self._showTrace( + f"预约信息错误 ! : {e}, "\ + f"由于缺少必要的预约信息, 无法开始预约流程, 请检查预约信息是否完整" + ) + return False + + # check and try to fix the time errors + cur_time_str = time.strftime("%Y-%m-%d %H:%M", time.localtime()) + cur_date, curr_time = cur_time_str.split() + if not reserve_info.get("date"): + reserve_info["date"] = cur_date + self._showTrace(f"预约日期未指定, 自动设置为当前日期: {cur_date}") + else: + if reserve_info["date"] < cur_date: + self._showTrace( + f"预约日期错误 ! :"\ + f"{reserve_info['date']} 早于当前日期 {cur_date}, 自动设置为当前日期" + ) + reserve_info["date"] = cur_date + # check the begin time + begin_time = reserve_info.get("begin_time") + if not begin_time: + reserve_info["begin_time"] = { + "time": curr_time, + "max_diff": 30, + "prefer_early": True + } + self._showTrace(f"开始时间未指定, 自动设置为当前时间: {curr_time}, 最大时间差为 30 分钟, 优先选择更早预约时间") + else: + begin_time = reserve_info["begin_time"] + if "time" not in begin_time: + begin_time["time"] = curr_time + self._showTrace(f"开始时间未指定, 自动设置为当前时间: {curr_time}") + if "max_diff" not in begin_time: + begin_time["max_diff"] = 30 + self._showTrace(f"最大时间差未指定, 自动设置为 30 分钟") + if "prefer_early" not in begin_time: + begin_time["prefer_early"] = True + self._showTrace(f"是否优先选择更早预约时间未指定, 自动设置为 True") + expect_duration = reserve_info.get("expect_duration") + if not expect_duration: + reserve_info["expect_duration"] = 4 + expect_duration = 4 + self._showTrace("预约持续时间未指定, 使用默认时长为 4 小时") + if not reserve_info.get("satisfy_duration"): + reserve_info["satisfy_duration"] = True + self._showTrace("预约满足时长要求未指定, 默认满足") + # check the end time + if not reserve_info.get("end_time"): + begin_mins = self.__timeToMins(reserve_info["begin_time"]["time"]) + end_mins = begin_mins + reserve_info["expect_duration"] * 60 + end_time_str = self.__minsToTime(end_mins) + reserve_info["end_time"] = { + "time": end_time_str, + "max_diff": 30, + "prefer_early": False + } + self._showTrace(f"结束时间未指定, 自动设置为开始时间加上期望时长: {end_time_str}, 最大时间差为 30 分钟, 优先选择较晚预约时间") + else: + end_time = reserve_info["end_time"] + if "time" not in end_time: + begin_mins = self.__timeToMins(reserve_info["begin_time"]["time"]) + end_mins = begin_mins + reserve_info["expect_duration"] * 60 + end_time["time"] = self.__minsToTime(end_mins) + self._showTrace(f"结束时间未指定, 自动设置为开始时间加上期望时长: {end_time['time']}") + if "max_diff" not in end_time: + end_time["max_diff"] = 30 + self._showTrace(f"最大时间差未指定, 自动设置为 30 分钟") + if "prefer_early" not in end_time: + end_time["prefer_early"] = False + self._showTrace(f"是否优先选择较早预约时间未指定, 自动设置为 False") + # check the reserve time boundary and fix the errors + # + # get time string for message show + begin_time_str = reserve_info["begin_time"]["time"] + end_time_str = reserve_info["end_time"]["time"] + + # minute time for check and fix them + begin_mins = self.__timeToMins(begin_time_str) + end_mins = self.__timeToMins(end_time_str) + + # ensure begin time is not later than end time + if begin_mins > end_mins: + reserve_info["begin_time"]["time"], reserve_info["end_time"]["time"] = end_time_str, begin_time_str + reserve_info["begin_time"]["prefer_early"], reserve_info["end_time"]["prefer_early"] = \ + reserve_info["end_time"]["prefer_early"], reserve_info["begin_time"]["prefer_early"] + self._showTrace("预约开始时间晚于预约结束时间,自动调换开始时间和结束时间") + + # update the begin_mins and end_mins after swap + begin_time_str, end_time_str = end_time_str, begin_time_str + begin_mins, end_mins = end_mins, begin_mins + + # ensure end time is not later than 22:30 + max_end_mins = self.__timeToMins("22:30") + if end_mins > max_end_mins: + reserve_info["end_time"]["time"] = "22:30" + end_time_str = "22:30" + end_mins = max_end_mins + self._showTrace("预约结束时间超过 22:30, 自动设置为 22:30") + + # ensure expect duration is shorter than 8 hours + max_duration_mins = 8 * 60 + duration_mins = end_mins - begin_mins + if duration_mins > max_duration_mins: + new_end_mins = begin_mins + max_duration_mins + reserve_info["end_time"]["time"] = self.__minsToTime(new_end_mins) + self._showTrace("预约持续时间超过8小时, 自动设置为 8 小时") + self._showTrace( + f"预约信息检查完成,准备预约 " + f"{reserve_info['date']} " + f"{reserve_info['begin_time']["time"]} - " + f"{reserve_info['end_time']["time"]} " + f"图书馆 " + f"{self.__floor_map[reserve_info['floor']]} " + f"{self.__room_map[reserve_info['room']]} " + f"的座位 {reserve_info['seat_id']}" + ) + return True + + + def __clickElement( + self, + trigger_locator: tuple, + fail_msg: str, + success_msg: str, + option_locator: tuple = None, + ) -> bool: + + try: + # click the trigger element + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable(trigger_locator) + ).click() + if option_locator: + # select the option element if specified + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable(option_locator) + ).click() + self._showTrace(success_msg) + return True + except: + self._showTrace(fail_msg) + return False + + + def __selectDate( + self, + date_str: str + ) -> bool: + + return self.__clickElement( + trigger_locator=(By.ID, "onDate_select"), + option_locator=(By.XPATH, f"//p[@id='options_onDate']/a[@value='{date_str}']"), + success_msg=f"日期 {date_str} 选择成功 !", + fail_msg=f"选择日期失败 ! : {date_str} 不可用" + ) + + + def __selectPlace( + self, + place: str + ) -> bool: + + actual_place = "1" if place == "图书馆" else "1" + return self.__clickElement( + trigger_locator=(By.ID, "display_building"), + option_locator=(By.XPATH, f"//p[@id='options_building']/a[@value='{actual_place}']"), + success_msg=f"预约场所 {place} 选择成功 !", + fail_msg=f"选择预约场所失败 ! : {place} 不可用" + ) + + + def __selectFloor( + self, + floor: str + ) -> bool: + + display_floor = self.__floor_map.get(floor) + return self.__clickElement( + trigger_locator=(By.ID, "floor_select"), + option_locator=(By.XPATH, f"//p[@id='options_floor']/a[@value='{floor}']"), + success_msg=f"楼层 {display_floor} 选择成功 !", + fail_msg=f"选择楼层失败 ! : {display_floor} 不可用" + ) + + + def __selectRoom( + self, + room: str + ) -> bool: + + display_room = self.__room_map.get(room) + return self.__clickElement( + trigger_locator=(By.ID, f"room_{room}"), + option_locator=None, + success_msg=f"房间 {display_room} 选择成功 !", + fail_msg=f"选择房间失败 ! : {display_room} 不可用" + ) + + + def __selectSeat( + self, + seat_id: str + ) -> bool: + + try: + # wait fot seat layout element to load + WebDriverWait(self.__driver, 5).until( + EC.presence_of_element_located((By.ID, "seatLayout")) + ) + all_seats = self.__driver.find_elements( + By.CSS_SELECTOR, "li[id^='seat_']" + ) + seat_id_upper = seat_id.lstrip('0').upper() + for seat in all_seats: + if not seat_id_upper == seat.text.lstrip('0'): + continue + seat_link = seat.find_element(By.TAG_NAME, "a") + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable(seat_link) + ) + seat_link.click() + seat_status = seat_link.get_attribute("title") + self._showTrace(f"座位 {seat_id} 选择成功 ! : 当前状态 - '{seat_status}'") + return True + self._showTrace(f"座位 {seat_id} 在该楼层区域中不存在, 请检查座位号是否正确") + except: + self._showTrace(f"座位选择失败 !") + return False + + + def __selectNearestTime( + self, + time_id: str, + time_type: str, + target_time: int, + max_time_diff: int = 30, + prefer_earlier: bool = True + ) -> int: + + try: + all_time_opts = self.__driver.find_elements( + By.CSS_SELECTOR, + f"#{time_id} ul li a" + ) + free_times = [] + best_time_diff = max_time_diff + best_actual_diff = None + best_time_opt = None + + for time_opt in all_time_opts: + time_attr = time_opt.get_attribute("time") + if time_attr == "now": + now = datetime.now() + time_val = int(now.hour*60 + now.minute) + elif time_attr and time_attr.isdigit(): + time_val = int(time_attr) + else: + continue + free_times.append(self.__minsToTime(time_val)) + actual_diff = time_val - target_time + abs_diff = abs(actual_diff) + if abs_diff < best_time_diff or ( + abs_diff == best_time_diff and ( + # prefer earlier time + (prefer_earlier and actual_diff < 0) or + # prefer later time + (not prefer_earlier and actual_diff > 0) + ) + ): + best_time_diff = abs_diff + best_actual_diff = actual_diff + best_time_opt = time_opt + + if best_time_opt is not None: + best_time_opt.click() + abs_time_diff = abs(best_actual_diff) + if best_actual_diff < 0: + time_relation = f"早了 {abs_time_diff} 分钟" + elif best_actual_diff > 0: + time_relation = f"晚了 {abs_time_diff} 分钟" + else: + time_relation = f"正好等于 {time_type}" + target_time += best_actual_diff + self._showTrace( + f"选择距离期望 {time_type} 最近的 {best_time_opt.text}, "\ + f"与期望 {time_type} 相比 {time_relation}" + ) + return target_time + self._showTrace( + f"无法选择最近的 {time_type} {self.__minsToTime(target_time)}, "\ + f"所有可选时间与目标时间相差都超过 {max_time_diff} 分钟" + ) + self._showTrace(f"当前可供预约的 {time_type} 有: {free_times}") + return -1 + except: + self._showTrace(f"{time_type} {self.__minsToTime(target_time)} 选择失败 !") + return -1 + + + def __selectSeatTime( + self, + begin_time: dict, + end_time: dict, + expct_duration: int = 4, + satisfy_duration: bool = True + ) -> bool: + + expect_begin_time = actual_begin_time = begin_time["time"] + expect_end_time = actual_end_time = end_time["time"] + expect_begin_mins = self.__timeToMins(expect_begin_time) + expect_end_mins = self.__timeToMins(expect_end_time) + + # select the begin time + if self.__selectNearestTime( + time_id="startTime", # dont change into begin, this is the element in the page + time_type="开始时间", + target_time=expect_begin_mins, + max_time_diff=begin_time["max_diff"], + prefer_earlier=begin_time["prefer_early"] + ) == -1: + return False + else: + actual_begin_time = self.__minsToTime(expect_begin_mins) + # if 'satisfy_duration' is True. + # select the end time based on the begin time + # (because it may be changed under the 'max time diff' strategy) and expect duration. + if satisfy_duration: + expect_end_mins = int(expect_begin_mins + expct_duration*60) + self._showTrace( + f"需要满足期望预约持续时间: {expct_duration} 小时, "\ + f"根据开始时间 {actual_begin_time} 计算结束时间: {self.__minsToTime(expect_end_mins)}" + ) + # select the end time + if self.__selectNearestTime( + time_id="endTime", + time_type="结束时间", + target_time=expect_end_mins, + max_time_diff=end_time["max_diff"], + prefer_earlier=end_time["prefer_early"] + ) == -1: + return False + else: + actual_end_time = self.__minsToTime(expect_end_mins) + self._showTrace( + f"期望预约时间段: {expect_begin_time} - {expect_end_time}, " + f"实际预约时间段: {actual_begin_time} - {actual_end_time}" + ) + return True + + + def canReserve( + self, + date: str + ) -> bool: + + if date is None: + self._showTrace("日期未指定, 无法检查预约状态") + return True + else: + self._showTrace(f"正在检查用户在日期 {date} 是否可预约......") + date_obj = datetime.strptime(date, "%Y-%m-%d").date() + try: + # we need to navigate to the history page to check if we can reserve + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable((By.XPATH, "//a[@href='/history?type=SEAT']")) + ).click() + WebDriverWait(self.__driver, 2).until( + EC.presence_of_element_located((By.CLASS_NAME, "myReserveList")) + ) + WebDriverWait(self.__driver, 2).until( + EC.presence_of_element_located((By.CSS_SELECTOR, ".myReserveList dl")) + ) + except: + self._showTrace("加载预约记录页面失败 !") + return False + checked_count = 0 + max_attemots = 3 # we only check (3*4=)12 reservations + + for _ in range(max_attemots): + try: + # check if there's any reservation on the date + reservations = self.__driver.find_elements( + By.CSS_SELECTOR, ".myReserveList dl" + ) + except: + self._showTrace("加载预约记录失败 !") + return False + for i in range(checked_count, len(reservations) - 1): # the last one is load button + reservation = reservations[i] + try: + time_element = reservation.find_element( + By.CSS_SELECTOR, "dt" + ) + status_elements = reservation.find_elements( + By.CSS_SELECTOR, "a" + ) + is_reserved = any("已预约" in status.text for status in status_elements) + # process time element to get the date string + time_str = time_element.text.strip() + today = datetime.now().date() + if "明天" in time_str: + target_date = today + timedelta(days=1) + date_str = target_date.strftime("%Y-%m-%d") + elif "今天" in time_str: + target_date = today + date_str = target_date.strftime("%Y-%m-%d") + elif "昨天" in time_str: + target_date = today - timedelta(days=1) + date_str = target_date.strftime("%Y-%m-%d") + else: + date_match = re.search(r'(\d{4}-\d{1,2}-\d{1,2})', time_str) + if date_match: + date_str = date_match.group(1) + else: + continue + # reservation is earlier than the given date, can reserve + if datetime.strptime(date_str, "%Y-%m-%d").date() < date_obj: + self._showTrace(f"用户在 {date} 可预约") + return True + # reservation is later than the given date, check the next one + elif datetime.strptime(date_str, "%Y-%m-%d").date() > date_obj: + continue + # compare with the given date + if date_str == date and is_reserved: + self._showTrace(f"用户在 {date} 已存在有效预约, 无法预约") + return False + except: + self._showTrace(f"解析第 {i + 1} 条预约记录时发生未知错误 !") + continue + checked_count = len(reservations) - 1 + # load new reservations if still not sure + try: + more_btn = self.__driver.find_element(By.ID, "moreBtn") + if more_btn.is_displayed() and more_btn.is_enabled(): + self.__driver.execute_script("arguments[0].scrollIntoView(true);", more_btn) + self.__driver.execute_script("arguments[0].click();", more_btn) + else: + break + except: + break + self._showTrace(f"用户在 {date} 可预约") + return True + + + def reserve( + self, + reserve_info: dict + ) -> bool: + + submit_reserve = False + reserve_success = False + have_hover_on_page = False + + # reserve info + if not self.__checkReserveInfo(reserve_info): + return False + # map page + try: + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable((By.XPATH, "//a[@href='/map']")) + ).click() + WebDriverWait(self.__driver, 5).until( + EC.presence_of_element_located((By.ID, "seatLayout")) + ) + except: + self._showTrace(f"加载预约选座页面失败 !") + return False + # date, place, floor + if not self.__selectDate(reserve_info["date"]): + return False + if not self.__selectPlace(reserve_info["place"]): + return False + if not self.__selectFloor(reserve_info["floor"]): + return False + # room find + try: + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable((By.ID, "findRoom")) + ).click() + except: + self._showTrace("加载房间/区域失败 !") + return False + # room + if not self.__selectRoom(reserve_info["room"]): + return False + else: + have_hover_on_page = True + # seat selections + if not self.__selectSeat(reserve_info["seat_id"]): + pass + elif not self.__selectSeatTime( + begin_time=reserve_info["begin_time"], + end_time=reserve_info["end_time"], + expct_duration=reserve_info["expect_duration"], + satisfy_duration=reserve_info["satisfy_duration"] + ): + pass + else: + try: + WebDriverWait(self.__driver, 5).until( + EC.element_to_be_clickable((By.ID, "reserveBtn")) + ).click() + submit_reserve = True + if not self._waitResponseLoad(): + raise + reserve_success = True + except: + self._showTrace(f"预约提交失败 !") + if not submit_reserve and have_hover_on_page: + self.__driver.refresh() + return reserve_success \ No newline at end of file diff --git a/Main.py b/Main.py new file mode 100644 index 0000000..cca8e0c --- /dev/null +++ b/Main.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import sys + +from PySide6.QtCore import QTranslator +from PySide6.QtWidgets import QApplication + +from gui.ALMainWindow import ALMainWindow +from gui import AutoLibraryResource + + +def main(): + + app = QApplication(sys.argv) + translator = QTranslator() + if translator.load(":/res/trans/translators/qtbase_zh_CN.ts"): + app.installTranslator(translator) + app.setStyle('Fusion') + window = ALMainWindow() + window.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": + + main() \ No newline at end of file diff --git a/MsgBase.py b/MsgBase.py new file mode 100644 index 0000000..6a8ca56 --- /dev/null +++ b/MsgBase.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import time +import queue + + +class MsgBase: + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + ): + + self._class_name = self.__class__.__name__ + self._input_queue = input_queue + self._output_queue = output_queue + + + def _showMsg( + self, + msg: str + ): + + self._output_queue.put(f"[{self._class_name:<12}] >>> : {msg}") + + + def _showTrace( + self, + msg: str + ): + + timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + self._output_queue.put(f"{timestamp}-[{self._class_name:<12}] : {msg}") + + + def _waitMsg( + self, + timeout: float = 1.0, + ) -> str: + + try: + msg = self._input_queue.get(timeout=timeout) + return msg + except queue.Empty: + return None + + + def _inputMsg( + self, + timeout: float = 1.0, + ) -> bool: + + try: + self._input_queue.get(timeout=timeout) + return True + except queue.Empty: + return False diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..8b55232 --- /dev/null +++ b/Pipfile @@ -0,0 +1,15 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +ddddocr = "*" +selenium = "*" +pyinstaller = "*" +pyside6 = "*" + +[dev-packages] + +[requires] +python_version = "3.13" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..0bb9f2f --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,630 @@ +{ + "_meta": { + "hash": { + "sha256": "26dffc26812d5328611959b95713a7ed65e20c08c60089b54283b0f406dd08e4" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.13" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "altgraph": { + "hashes": [ + "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", + "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff" + ], + "version": "==0.17.4" + }, + "attrs": { + "hashes": [ + "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", + "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" + ], + "markers": "python_version >= '3.9'", + "version": "==25.4.0" + }, + "certifi": { + "hashes": [ + "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", + "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43" + ], + "markers": "python_version >= '3.7'", + "version": "==2025.10.5" + }, + "cffi": { + "hashes": [ + "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", + "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", + "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", + "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", + "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", + "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", + "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", + "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", + "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", + "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", + "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", + "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", + "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", + "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", + "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", + "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", + "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", + "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", + "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", + "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", + "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", + "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", + "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", + "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", + "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", + "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", + "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", + "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", + "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", + "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", + "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", + "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", + "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", + "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", + "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", + "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", + "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", + "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", + "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", + "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", + "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", + "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", + "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", + "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", + "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", + "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", + "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", + "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", + "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", + "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", + "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", + "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", + "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", + "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", + "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", + "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", + "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", + "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", + "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", + "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", + "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", + "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", + "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", + "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", + "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", + "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", + "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", + "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", + "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", + "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", + "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", + "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", + "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", + "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", + "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", + "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", + "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", + "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", + "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", + "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", + "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", + "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", + "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", + "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf" + ], + "markers": "python_version >= '3.9'", + "version": "==2.0.0" + }, + "coloredlogs": { + "hashes": [ + "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", + "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==15.0.1" + }, + "ddddocr": { + "hashes": [ + "sha256:5991594d481d33ba0b136022e910f578d6d5b0ca536b44886591359622ab0c70", + "sha256:7c44b58ba7d7566d785c65b8526ec5b78efacd121e993dea4fda5f7966897428" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.0.6" + }, + "flatbuffers": { + "hashes": [ + "sha256:255538574d6cb6d0a79a17ec8bc0d30985913b87513a01cce8bcdb6b4c44d0e2", + "sha256:676f9fa62750bb50cf531b42a0a2a118ad8f7f797a511eda12881c016f093b12" + ], + "version": "==25.9.23" + }, + "h11": { + "hashes": [ + "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", + "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" + ], + "markers": "python_version >= '3.8'", + "version": "==0.16.0" + }, + "humanfriendly": { + "hashes": [ + "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", + "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==10.0" + }, + "idna": { + "hashes": [ + "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", + "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" + ], + "markers": "python_version >= '3.8'", + "version": "==3.11" + }, + "mpmath": { + "hashes": [ + "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", + "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c" + ], + "version": "==1.3.0" + }, + "numpy": { + "hashes": [ + "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", + "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", + "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", + "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", + "sha256:13b9062e4f5c7ee5c7e5be96f29ba71bc5a37fed3d1d77c37390ae00724d296d", + "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", + "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", + "sha256:1b219560ae2c1de48ead517d085bc2d05b9433f8e49d0955c82e8cd37bd7bf36", + "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", + "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", + "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", + "sha256:36dc13af226aeab72b7abad501d370d606326a0029b9f435eacb3b8c94b8a8b7", + "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", + "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", + "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", + "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", + "sha256:4c01835e718bcebe80394fd0ac66c07cbb90147ebbdad3dcecd3f25de2ae7e2c", + "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", + "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", + "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", + "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", + "sha256:64c5825affc76942973a70acf438a8ab618dbd692b84cd5ec40a0a0509edc09a", + "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", + "sha256:6d34ed9db9e6395bb6cd33286035f73a59b058169733a9db9f85e650b88df37e", + "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", + "sha256:6e274603039f924c0fe5cb73438fa9246699c78a6df1bd3decef9ae592ae1c05", + "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", + "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", + "sha256:7fd09cc5d65bda1e79432859c40978010622112e9194e581e3415a3eccc7f43f", + "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", + "sha256:81b3a59793523e552c4a96109dde028aa4448ae06ccac5a76ff6532a85558a7f", + "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", + "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", + "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", + "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", + "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", + "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", + "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", + "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", + "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", + "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", + "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", + "sha256:957e92defe6c08211eb77902253b14fe5b480ebc5112bc741fd5e9cd0608f847", + "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", + "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", + "sha256:9984bd645a8db6ca15d850ff996856d8762c51a2239225288f08f9050ca240a0", + "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", + "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", + "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", + "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", + "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", + "sha256:a7b2f9a18b5ff9824a6af80de4f37f4ec3c2aab05ef08f51c77a093f5b89adda", + "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", + "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", + "sha256:bafa7d87d4c99752d07815ed7a2c0964f8ab311eb8168f41b910bd01d15b6032", + "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", + "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", + "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", + "sha256:d149aee5c72176d9ddbc6803aef9c0f6d2ceeea7626574fc68518da5476fa346", + "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", + "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", + "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", + "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", + "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", + "sha256:e78aecd2800b32e8347ce49316d3eaf04aed849cd5b38e0af39f829a4e59f5eb", + "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", + "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", + "sha256:ed759bf7a70342f7817d88376eb7142fab9fef8320d6019ef87fae05a99874e1", + "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", + "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", + "sha256:faba246fb30ea2a526c2e9645f61612341de1a83fb1e0c5edf4ddda5a9c10996", + "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", + "sha256:fdebe771ca06bb8d6abce84e51dca9f7921fe6ad34a0c914541b063e9a68928b", + "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb" + ], + "markers": "python_version >= '3.11'", + "version": "==2.3.4" + }, + "onnxruntime": { + "hashes": [ + "sha256:0be6a37a45e6719db5120e9986fcd30ea205ac8103fd1fb74b6c33348327a0cc", + "sha256:0f9b4ae77f8e3c9bee50c27bc1beede83f786fe1d52e99ac85aa8d65a01e9b77", + "sha256:162f4ca894ec3de1a6fd53589e511e06ecdc3ff646849b62a9da7489dee9ce95", + "sha256:1f9cc0a55349c584f083c1c076e611a7c35d5b867d5d6e6d6c823bf821978088", + "sha256:218295a8acae83905f6f1aed8cacb8e3eb3bd7513a13fe4ba3b2664a19fc4a6b", + "sha256:25de5214923ce941a3523739d34a520aac30f21e631de53bba9174dc9c004435", + "sha256:2ff531ad8496281b4297f32b83b01cdd719617e2351ffe0dba5684fb283afa1f", + "sha256:45d127d6e1e9b99d1ebeae9bcd8f98617a812f53f46699eafeb976275744826b", + "sha256:4ca88747e708e5c67337b0f65eed4b7d0dd70d22ac332038c9fc4635760018f7", + "sha256:6f91d2c9b0965e86827a5ba01531d5b669770b01775b23199565d6c1f136616c", + "sha256:76ff670550dc23e58ea9bc53b5149b99a44e63b34b524f7b8547469aaa0dcb8c", + "sha256:87d8b6eaf0fbeb6835a60a4265fde7a3b60157cf1b2764773ac47237b4d48612", + "sha256:8bace4e0d46480fbeeb7bbe1ffe1f080e6663a42d1086ff95c1551f2d39e7872", + "sha256:8f7d1fe034090a1e371b7f3ca9d3ccae2fabae8c1d8844fb7371d1ea38e8e8d2", + "sha256:902c756d8b633ce0dedd889b7c08459433fbcf35e9c38d1c03ddc020f0648c6e", + "sha256:9d2385e774f46ac38f02b3a91a91e30263d41b2f1f4f26ae34805b2a9ddef466", + "sha256:a7730122afe186a784660f6ec5807138bf9d792fa1df76556b27307ea9ebcbe3", + "sha256:b28740f4ecef1738ea8f807461dd541b8287d5650b5be33bca7b474e3cbd1f36", + "sha256:b8f029a6b98d3cf5be564d52802bb50a8489ab73409fa9db0bf583eabb7c2321", + "sha256:bbfd2fca76c855317568c1b36a885ddea2272c13cb0e395002c402f2360429a6", + "sha256:da44b99206e77734c5819aa2142c69e64f3b46edc3bd314f6a45a932defc0b3e", + "sha256:e2b9233c4947907fd1818d0e581c049c41ccc39b2856cc942ff6d26317cee145" + ], + "markers": "python_version >= '3.10'", + "version": "==1.23.2" + }, + "outcome": { + "hashes": [ + "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8", + "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.0.post0" + }, + "packaging": { + "hashes": [ + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" + ], + "markers": "python_version >= '3.8'", + "version": "==25.0" + }, + "pefile": { + "hashes": [ + "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", + "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6" + ], + "markers": "python_full_version >= '3.6.0'", + "version": "==2023.2.7" + }, + "pillow": { + "hashes": [ + "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", + "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e", + "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", + "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc", + "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642", + "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", + "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1", + "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", + "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", + "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba", + "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", + "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", + "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739", + "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", + "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f", + "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10", + "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", + "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", + "sha256:3adfb466bbc544b926d50fe8f4a4e6abd8c6bffd28a26177594e6e9b2b76572b", + "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", + "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", + "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d", + "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", + "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", + "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", + "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3", + "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371", + "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953", + "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", + "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", + "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", + "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7", + "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", + "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082", + "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", + "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d", + "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", + "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", + "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad", + "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", + "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a", + "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d", + "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", + "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", + "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", + "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b", + "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", + "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", + "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", + "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", + "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a", + "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257", + "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", + "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", + "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c", + "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c", + "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", + "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", + "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8", + "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5", + "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", + "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e", + "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8", + "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e", + "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275", + "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", + "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76", + "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227", + "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", + "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", + "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79", + "sha256:c7b2a63fd6d5246349f3d3f37b14430d73ee7e8173154461785e43036ffa96ca", + "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa", + "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", + "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", + "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197", + "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", + "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", + "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", + "sha256:d49e2314c373f4c2b39446fb1a45ed333c850e09d0c59ac79b72eb3b95397363", + "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", + "sha256:d64317d2587c70324b79861babb9c09f71fbb780bad212018874b2c013d8600e", + "sha256:d77153e14b709fd8b8af6f66a3afbb9ed6e9fc5ccf0b6b7e1ced7b036a228782", + "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", + "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0", + "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", + "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", + "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", + "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", + "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", + "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1" + ], + "markers": "python_version >= '3.10'", + "version": "==12.0.0" + }, + "protobuf": { + "hashes": [ + "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", + "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", + "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", + "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", + "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", + "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9", + "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3", + "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", + "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", + "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298" + ], + "markers": "python_version >= '3.9'", + "version": "==6.33.0" + }, + "pycparser": { + "hashes": [ + "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", + "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23" + }, + "pyinstaller": { + "hashes": [ + "sha256:0a48f55b85ff60f83169e10050f2759019cf1d06773ad1c4da3a411cd8751058", + "sha256:53559fe1e041a234f2b4dcc3288ea8bdd57f7cad8a6644e422c27bb407f3edef", + "sha256:6d5f8617f3650ff9ef893e2ab4ddbf3c0d23d0c602ef74b5df8fbef4607840c8", + "sha256:73ba72e04fcece92e32518bbb1e1fb5ac2892677943dfdff38e01a06e8742851", + "sha256:7fd1c785219a87ca747c21fa92f561b0d2926a7edc06d0a0fe37f3736e00bd7a", + "sha256:b1752488248f7899281b17ca3238eefb5410521291371a686a4f5830f29f52b3", + "sha256:b756ddb9007b8141c5476b553351f9d97559b8af5d07f9460869bfae02be26b0", + "sha256:ba618a61627ee674d6d68e5de084ba17c707b59a4f2a856084b3999bdffbd3f0", + "sha256:bc10eb1a787f99fea613509f55b902fbd2d8b73ff5f51ff245ea29a481d97d41", + "sha256:c8b7ef536711617e12fef4673806198872033fa06fa92326ad7fd1d84a9fa454", + "sha256:d0af8a401de792c233c32c44b16d065ca9ab8262ee0c906835c12bdebc992a64", + "sha256:d1ebf84d02c51fed19b82a8abb4df536923abd55bb684d694e1356e4ae2a0ce5" + ], + "index": "pypi", + "markers": "python_version < '3.15' and python_version >= '3.8'", + "version": "==6.16.0" + }, + "pyinstaller-hooks-contrib": { + "hashes": [ + "sha256:56e972bdaad4e9af767ed47d132362d162112260cbe488c9da7fee01f228a5a6", + "sha256:ccbfaa49399ef6b18486a165810155e5a8d4c59b41f20dc5da81af7482aaf038" + ], + "markers": "python_version >= '3.8'", + "version": "==2025.9" + }, + "pyreadline3": { + "hashes": [ + "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", + "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6" + ], + "markers": "python_version >= '3.8'", + "version": "==3.5.4" + }, + "pyside6": { + "hashes": [ + "sha256:4b709bdeeb89d386059343a5a706fc185cee37b517bda44c7d6b64d5fdaf3339", + "sha256:70a8bcc73ea8d6baab70bba311eac77b9a1d31f658d0b418e15eb6ea36c97e6f", + "sha256:9f402f883e640048fab246d36e298a5e16df9b18ba2e8c519877e472d3602820", + "sha256:ae8c3c8339cd7c3c9faa7cc5c52670dcc8662ccf4b63a6fed61c6345b90c4c01", + "sha256:c2cbc5dc2a164e3c7c51b3435e24203e90e5edd518c865466afccbd2e5872bb0" + ], + "index": "pypi", + "markers": "python_version < '3.14' and python_version >= '3.9'", + "version": "==6.10.0" + }, + "pyside6-addons": { + "hashes": [ + "sha256:08d4ed46c4c9a353a9eb84134678f8fdd4ce17fb8cce2b3686172a7575025464", + "sha256:15d32229d681be0bba1b936c4a300da43d01e1917ada5b57f9e03a387c245ab0", + "sha256:88e61e21ee4643cdd9efb39ec52f4dc1ac74c0b45c5b7fa453d03c094f0a8a5c", + "sha256:92536427413f3b6557cf53f1a515cd766725ee46a170aff57ad2ff1dfce0ffb1", + "sha256:99d93a32c17c5f6d797c3b90dd58f2a8bae13abde81e85802c34ceafaee11859" + ], + "markers": "python_version < '3.14' and python_version >= '3.9'", + "version": "==6.10.0" + }, + "pyside6-essentials": { + "hashes": [ + "sha256:003e871effe1f3e5b876bde715c15a780d876682005a6e989d89f48b8b93e93a", + "sha256:1d5e013a8698e37ab8ef360e6960794eb5ef20832a8d562e649b8c5a0574b2d8", + "sha256:6dd0936394cb14da2fd8e869899f5e0925a738b1c8d74c2f22503720ea363fb1", + "sha256:b1dd0864f0577a448fb44426b91cafff7ee7cccd1782ba66491e1c668033f998", + "sha256:fc167eb211dd1580e20ba90d299e74898e7a5a1306d832421e879641fc03b6fe" + ], + "markers": "python_version < '3.14' and python_version >= '3.9'", + "version": "==6.10.0" + }, + "pysocks": { + "hashes": [ + "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299", + "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", + "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.7.1" + }, + "pywin32-ctypes": { + "hashes": [ + "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", + "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755" + ], + "markers": "python_version >= '3.6'", + "version": "==0.2.3" + }, + "selenium": { + "hashes": [ + "sha256:c117af6727859d50f622d6d0785b945c5db3e28a45ec12ad85cee2e7cc84fc4c", + "sha256:ed47563f188130a6fd486b327ca7ba48c5b11fb900e07d6457befdde320e35fd" + ], + "index": "pypi", + "markers": "python_version >= '3.10'", + "version": "==4.38.0" + }, + "setuptools": { + "hashes": [ + "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", + "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" + ], + "markers": "python_version >= '3.9'", + "version": "==80.9.0" + }, + "shiboken6": { + "hashes": [ + "sha256:0bc5631c1bf150cbef768a17f5f289aae1cb4db6c6b0c19b2421394e27783717", + "sha256:7a5f5f400ebfb3a13616030815708289c2154e701a60b9db7833b843e0bee543", + "sha256:b01377e68d14132360efb0f4b7233006d26aa8ae9bb50edf00960c2a5f52d148", + "sha256:dfc4beab5fec7dbbebbb418f3bf99af865d6953aa0795435563d4cbb82093b61", + "sha256:e612734da515d683696980107cdc0396a3ae0f07b059f0f422ec8a2333810234" + ], + "markers": "python_version < '3.14' and python_version >= '3.9'", + "version": "==6.10.0" + }, + "sniffio": { + "hashes": [ + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, + "sympy": { + "hashes": [ + "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", + "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5" + ], + "markers": "python_version >= '3.9'", + "version": "==1.14.0" + }, + "trio": { + "hashes": [ + "sha256:150f29ec923bcd51231e1d4c71c7006e65247d68759dd1c19af4ea815a25806b", + "sha256:4ab65984ef8370b79a76659ec87aa3a30c5c7c83ff250b4de88c29a8ab6123c5" + ], + "markers": "python_version >= '3.10'", + "version": "==0.32.0" + }, + "trio-websocket": { + "hashes": [ + "sha256:22c72c436f3d1e264d0910a3951934798dcc5b00ae56fc4ee079d46c7cf20fae", + "sha256:df605665f1db533f4a386c94525870851096a223adcb97f72a07e8b4beba45b6" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.2" + }, + "typing-extensions": { + "hashes": [ + "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", + "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" + ], + "markers": "python_version >= '3.9'", + "version": "==4.15.0" + }, + "urllib3": { + "extras": [ + "socks" + ], + "hashes": [ + "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", + "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" + ], + "markers": "python_version >= '3.9'", + "version": "==2.5.0" + }, + "websocket-client": { + "hashes": [ + "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", + "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef" + ], + "markers": "python_version >= '3.9'", + "version": "==1.9.0" + }, + "wsproto": { + "hashes": [ + "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", + "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==1.2.0" + } + }, + "develop": {} +} diff --git a/document/manual.html b/document/manual.html new file mode 100644 index 0000000..02bdbb1 --- /dev/null +++ b/document/manual.html @@ -0,0 +1,847 @@ + + + + + + AutoLibrary 操作手册 + + + +
+ + +
+
+

AutoLibrary

+
+ +
+

工具简介

+
+
+
+

AutoLibrary 是一款专为北京建筑大学图书馆设计的自动化工具,旨在帮助学生简化图书馆座位操作流程,节省宝贵时间。

+
+

本工具模拟人工操作,通过简单的界面交互使用。

+ +

工具特点

+
    +

    模拟人工操作,不干扰图书馆系统正常运行

    +

    支持多种预约模式,满足不同使用场景

    +

    支持多账号批量预约

    +

    自动处理验证码,减少人工干预

    +
+
+
+
+ +
+

准备工作

+
+
+
+
+

下载浏览器驱动

+

工具需要通过浏览器驱动来控制浏览器,请根据您使用的浏览器下载对应版本的驱动:

+ +
+
+ +

Microsoft Edge

+

适用于Windows 10/11系统

+ 下载驱动 +
+ +
+ +

Google Chrome

+

最常用的浏览器

+ 下载驱动 +
+ +
+ +

Mozilla Firefox

+

开源浏览器

+ 下载驱动 +
+
+ +
+ 注意: 浏览器驱动版本必须与您的浏览器版本兼容,否则工具无法正常工作。 +
+
+ 浏览器驱动下载页面截图区域 +
+
+
+ +
+
+
+

确认驱动路径

+

下载驱动后,将浏览器驱动程序的路径(如'C:\Users\Administrator\Downloads\msedgedriver.exe')输入到AutoLibrary配置界面中。

+
+ 驱动文件放置位置截图区域 +
+
+
+
+
+ +
+

使用步骤

+
+
+
+
+

编辑配置文件

+

使用文本编辑器(如记事本、Visual Studio Code等)打开config.json和users.json文件,按照您的需求修改参数。

+
+ 重要: 请勿使用Microsoft Word等富文本编辑器,这可能导致文件格式错误。 +
+
+ 配置文件编辑截图区域 +
+
+
+ +
+
+
+

运行工具

+

双击运行main.exe文件,工具将自动开始预约流程。

+
+ 工具运行界面截图区域 +
+
+
+ +
+
+
+

监控运行状态

+

如果headless模式设置为false,您将看到浏览器窗口自动操作。请勿手动干预浏览器窗口。

+
+ 浏览器自动操作截图区域 +
+
+
+ +
+
+
+

查看结果

+

工具运行完成后,查看生成的日志文件确认预约结果。

+
+ 运行日志截图区域 +
+
+
+
+
+ +
+

配置说明

+

AutoLibrary通过两个配置文件来控制工具行为:config.json(工具设置)和users.json(用户信息)。

+ +
+ + +
+ +
+
+

工具配置文件

+

config.json文件控制工具的基本运行参数:

+
+{ + "library": { + "lib_host_url": "http://10.1.20.7", + "lib_login_url": "/login" + }, + "mode": { + "run_mode": 1 + }, + "login": { + "auto_captcha": true, + "login_attempt": 3 + }, + "web_driver": { + "driver_type": "edge", + "driver_path": "msedgedriver.exe", + "headless": false + } +} +
+ +

参数说明

+
    +
  • run_mode: 运行模式,可组合使用(1+4+8=13)
  • +
  • auto_captcha: 自动验证码识别,建议保持true
  • +
  • login_attempt: 登录尝试次数,默认3次
  • +
  • driver_type: 浏览器类型(edge/chrome/firefox)
  • +
  • driver_path: 驱动文件路径
  • +
  • headless: 无头模式,false会显示浏览器窗口
  • +
+
+ +
+

用户配置文件

+

users.json文件包含用户账号和预约信息:

+
+{ + "users": [ + { + "username": "您的学号", + "password": "您的密码", + "reserve_info": { + "date": "2025-10-30", + "start_time": "09:30", + "end_time": "17:00", + "place": "1", + "floor": "4", + "room": "5", + "seat_id": "31A", + "expect_duration": 6 + } + } + ] +} +
+ +

参数说明

+
    +
  • username: 学号
  • +
  • password: 密码
  • +
  • date: 预约日期(格式:YYYY-MM-DD)
  • +
  • start_time/end_time: 预约时间段
  • +
  • place/floor/room: 图书馆位置信息
  • +
  • seat_id: 座位编号(重要)
  • +
  • expect_duration: 期望使用时长(小时)
  • +
+
+ 提示: 可以添加多个用户,工具会按顺序处理每个用户的预约请求。 +
+
+
+
+ +
+

功能详解

+
+
+
+

自动预约(模式 +1)

+

当您当前没有有效预约时,工具会自动为您预约指定座位。

+
+ 适用场景: 提前预约第二天的座位 +
+
+ +
+
+

自动签到(模式 +4)

+

如果您已有预约,且在可签到时间范围内,工具会自动完成签到。

+
+ 适用场景: 避免因忘记签到而失去座位 +
+
+ +
+
🔄
+

自动续约(模式 +8)

+

当您正在使用座位且到达可续约时间时,工具会自动延长使用时间。

+
+ 适用场景: 需要长时间使用座位的情况 +
+
+
+ +

模式组合使用

+

运行模式可以组合使用,只需将对应模式的数值相加:

+
    +
  • 自动预约 + 自动签到 + 自动续约 = 13(推荐)
  • +
  • 自动预约 = 1
  • +
  • 自动预约 + 自动签到 = 5
  • +
+
+ +
+

故障排除

+

常见问题及解决方法

+ +
+
工具启动时报错"无法找到驱动"
+
+

这是因为浏览器驱动未正确安装或版本不匹配。

+
    +
  • 检查驱动文件是否放置在正确位置
  • +
  • 确认驱动版本与浏览器版本完全匹配
  • +
  • 尝试重新下载并安装驱动
  • +
+
+
+ +
+
登录失败,提示账号密码错误
+
+

请检查users.json文件中的账号密码是否正确。

+
    +
  • 确认学号和密码无误
  • +
  • 检查是否有特殊字符需要转义
  • +
  • 尝试手动登录图书馆系统确认账号可用
  • +
+
+
+ +
+
预约失败,提示座位不可用
+
+

目标座位可能已被他人预约或不在可预约时间。

+
    +
  • 确认座位编号是否正确
  • +
  • 检查预约时间是否符合图书馆规定
  • +
  • 尝试预约其他座位或调整预约时间
  • +
+
+
+
+ +
+

常见问题

+ +
+
使用AutoLibrary是否安全?
+
+

AutoLibrary完全模拟人工操作,不干扰图书馆系统正常运行。工具不会收集或上传您的个人信息,所有数据仅保存在本地配置文件中。

+
+
+ +
+
可以同时预约多个座位吗?
+
+

根据图书馆规定,每个账号同一时间段只能预约一个座位。但您可以在users.json中添加多个账号,工具会依次处理每个账号的预约请求。

+
+
+ +
+
工具运行期间可以操作电脑吗?
+
+

可以正常使用电脑,但请勿操作工具自动打开的浏览器窗口,否则可能会干扰工具的正常运行。

+
+
+
+ +
+

下载安装

+
+

获取AutoLibrary

+

点击下方按钮下载最新版本的AutoLibrary工具包:

+ 下载 AutoLibrary v0.01 +
+

文件大小:约15MB

+

系统要求:Windows 10/11,支持Edge/Chrome/Firefox浏览器

+
+
+ +

安装步骤

+
    +
  1. 下载压缩包并解压到任意文件夹
  2. +
  3. 根据您使用的浏览器下载对应版本的驱动
  4. +
  5. 将驱动文件放置到工具文件夹中
  6. +
  7. 按照本手册说明编辑配置文件
  8. +
  9. 双击main.exe运行工具
  10. +
+
+
+
+ + + + \ No newline at end of file diff --git a/driver/readme.md b/driver/readme.md new file mode 100644 index 0000000..c077df4 --- /dev/null +++ b/driver/readme.md @@ -0,0 +1 @@ +This folder is used to store the browser driver by selenium. \ No newline at end of file diff --git a/gui/ALConfigWidget.py b/gui/ALConfigWidget.py new file mode 100644 index 0000000..f03e875 --- /dev/null +++ b/gui/ALConfigWidget.py @@ -0,0 +1,800 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import os +import sys + +from PySide6.QtCore import ( + Qt, Signal, Slot, QTime, QDate, QDir, QFileInfo +) +from PySide6.QtWidgets import ( + QWidget, QLineEdit, QMessageBox, QFileDialog, QListWidgetItem +) +from PySide6.QtGui import QCloseEvent + +from .Ui_ALConfigWidget import Ui_ALConfigWidget + +from ConfigReader import ConfigReader +from ConfigWriter import ConfigWriter + + +class ALConfigWidget(QWidget, Ui_ALConfigWidget): + + configWidgetCloseSingal = Signal(dict) + + def __init__( + self, + parent = None, + config_paths = { + "system": + f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("system.json"))}", + "users": + f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("users.json"))}", + } + ): + + super().__init__(parent) + + self.setupUi(self) + self.connectSignals() + self.modifyUi() + self.__config_paths = config_paths + self.__system_config_data = self.loadSystemConfig(self.__config_paths["system"]) + self.__users_config_data = self.loadUsersConfig(self.__config_paths["users"]) + if not self.__system_config_data: + self.initlizeDefaultConfig("system") + if not self.__users_config_data: + self.initlizeDefaultConfig("users") + self.initlizeConfigToWidget("system", self.__system_config_data) + self.initlizeConfigToWidget("users", self.__users_config_data) + + + def modifyUi( + self + ): + + self.initlizeFloorRoomMap() + self.initilizeUserInfoWidget() + + + def connectSignals( + self + ): + + self.ShowPasswordCheckBox.clicked.connect(self.onShowPasswordCheckBoxChecked) + self.FloorComboBox.currentIndexChanged.connect(self.onFloorComboBoxCurrentIndexChanged) + self.UserListWidget.currentItemChanged.connect(self.onUserListWidgetCurrentItemChanged) + self.AddUserButton.clicked.connect(self.onAddUserButtonClicked) + self.DelUserButton.clicked.connect(self.onDelUserButtonClicked) + self.BrowseBrowserDriverButton.clicked.connect(self.onBrowseBrowserDriverButtonClicked) + self.BrowseCurrentSystemConfigButton.clicked.connect(self.onBrowseCurrentSystemConfigButtonClicked) + self.BrowseCurrentUserConfigButton.clicked.connect(self.onBrowseCurrentUserConfigButtonClicked) + self.BrowseExportSystemConfigButton.clicked.connect(self.onBrowseExportSystemConfigButtonClicked) + self.BrowseExportUserConfigButton.clicked.connect(self.onBrowseExportUserConfigButtonClicked) + self.ExportConfigButton.clicked.connect(self.onExportConfigButtonClicked) + self.NewConfigButton.clicked.connect(self.onNewConfigButtonClicked) + self.LoadConfigButton.clicked.connect(self.onLoadConfigButtonClicked) + self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked) + self.CancelButton.clicked.connect(self.onCancelButtonClicked) + + + def closeEvent( + self, + event: QCloseEvent + ): + + self.configWidgetCloseSingal.emit(self.__config_paths) + super().closeEvent(event) + + + def initlizeFloorRoomMap( + self + ): + + self.__floor_map = { + "2": "二层", + "3": "三层", + "4": "四层", + "5": "五层" + } + self.__room_map = { + "1": "二层内环", + "2": "二层外环", + "3": "三层内环", + "4": "三层外环", + "5": "四层内环", + "6": "四层外环", + "7": "四层期刊区", + "8": "五层考研" + } + self.__floor_rmap = { + v: k for k, v in self.__floor_map.items() + } + self.__room_rmap = { + v: k for k, v in self.__room_map.items() + } + self.__floor_room_map = { + "二层": ["二层内环", "二层外环"], + "三层": ["三层内环", "三层外环"], + "四层": ["四层内环", "四层外环", "四层期刊区"], + "五层": ["五层考研"] + } + + + def initlizeDefaultConfigPaths( + self + ) -> dict: + + script_path = sys.executable + script_dir = QFileInfo(script_path).absoluteDir() + return { + "users": QDir.toNativeSeparators(script_dir.absoluteFilePath("users.json")), + "system": QDir.toNativeSeparators(script_dir.absoluteFilePath("system.json")) + } + + + def initlizeDefaultConfig( + self, + which: str + ): + + default_config_paths = self.initlizeDefaultConfigPaths() + if which == "system": + self.__system_config_data = self.defaultSystemConfig() + self.__config_paths["system"] = default_config_paths["system"] + self.saveSystemConfig(self.__config_paths["system"], self.__system_config_data) + elif which == "users": + self.__users_config_data = self.defaultUsersConfig() + self.__config_paths["users"] = default_config_paths["users"] + self.saveUsersConfig(self.__config_paths["users"], self.__users_config_data) + if which == "system": + file_type = "系统配置文件" + elif which == "users": + file_type = "用户配置文件" + QMessageBox.information( + self, + "提示 - AutoLibrary", + f"{file_type}已初始化, \n"\ + f" 文件路径: {self.__config_paths[which]}" + ) + + + def initlizeConfigToWidget( + self, + which: str, + config_data: dict + ): + + if which == "system": + self.setSystemConfigToWidget(config_data) + self.CurrentSystemConfigEdit.setText(self.__config_paths["system"]) + elif which == "users": + self.initilizeUserInfoWidget() + self.fillUsersList(config_data) + self.CurrentUserConfigEdit.setText(self.__config_paths["users"]) + + + def defaultSystemConfig( + self + ) -> dict: + + return { + "library": { + "host_url": "http://10.1.20.7", + "login_url": "/login" + }, + "login": { + "auto_captcha": True, + "max_attempt": 3 + }, + "web_driver": { + "driver_type": "edge", + "driver_path": "msedgedriver.exe", + "headless": False + }, + "mode": { + "run_mode": 1 + } + } + + + def defaultUsersConfig( + self + ) -> dict: + + return { + "users": [] + } + + + def collectSystemConfigFromWidget( + self + ) -> dict: + + system_config = self.defaultSystemConfig() + # library config is never changed + system_config["login"]["auto_captcha"] = self.AutoCaptchaCheckBox.isChecked() + system_config["login"]["max_attempt"] = self.LoginAttemptSpinBox.value() + system_config["web_driver"]["driver_type"] = self.BrowserTypeComboBox.currentText() + system_config["web_driver"]["driver_path"] = self.BrowseBrowserDriverEdit.text() + system_config["web_driver"]["headless"] = self.HeadlessCheckBox.isChecked() + run_mode = 0 + if self.AutoReserveCheckBox.isChecked(): + run_mode |= 0x01 + if self.AutoCheckinCheckBox.isChecked(): + run_mode |= 0x02 + if self.AutoRenewalCheckBox.isChecked(): + run_mode |= 0x04 + system_config["mode"]["run_mode"] = run_mode + return system_config + + + def setSystemConfigToWidget( + self, + system_config: dict + ): + + self.HostUrlEdit.setText(system_config["library"]["host_url"]) + self.LoginUrlEdit.setText(system_config["library"]["login_url"]) + self.AutoCaptchaCheckBox.setChecked(system_config["login"]["auto_captcha"]) + self.LoginAttemptSpinBox.setValue(system_config["login"]["max_attempt"]) + self.BrowserTypeComboBox.setCurrentText(system_config["web_driver"]["driver_type"]) + driver_path = os.path.abspath(system_config["web_driver"]["driver_path"]) + self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(driver_path)) + self.HeadlessCheckBox.setChecked(system_config["web_driver"]["headless"]) + run_mode = system_config["mode"]["run_mode"] + self.AutoReserveCheckBox.setChecked(run_mode&0x01) + self.AutoCheckinCheckBox.setChecked(run_mode&0x02) + self.AutoRenewalCheckBox.setChecked(run_mode&0x04) + + + def initilizeUserInfoWidget( + self + ): + + self.UsernameEdit.setText("") + self.PasswordEdit.setText("") + self.UserListWidget.setSortingEnabled(True) + self.PasswordEdit.setEchoMode(QLineEdit.Password) + self.ShowPasswordCheckBox.setChecked(False) + self.FloorComboBox.setCurrentIndex(1) # use for the '__init__' to effect the signal + self.FloorComboBox.setCurrentIndex(0) + self.DateEdit.setDate(QDate.currentDate()) + self.DateEdit.setMinimumDate(QDate.currentDate()) + self.DateEdit.setMaximumDate(QDate.currentDate()) + if QTime.currentTime() > QTime(19, 0, 0) and QTime.currentTime() < QTime(23, 0, 0): + self.DateEdit.setMaximumDate(QDate.currentDate().addDays(1)) + self.BeginTimeEdit.setTime(QTime.currentTime()) + self.PreferEarlyBeginTimeCheckBox.setChecked(False) + self.MaxBeginTimeDiffSpinBox.setValue(10) + self.EndTimeEdit.setTime(QTime.currentTime().addSecs(120*60)) + self.PreferLateEndTimeCheckBox.setChecked(False) + self.MaxEndTimeDiffSpinBox.setValue(10) + self.ExpectDurationSpinBox.setValue(self.BeginTimeEdit.time().secsTo(self.EndTimeEdit.time())/3600) + self.SatisfyDurationCheckBox.setChecked(False) + + + def collectUserConfigFromUserInfoWidget( + self + ) -> dict: + + user_config = { + "username": self.UsernameEdit.text(), + "password": self.PasswordEdit.text(), + "reserve_info": { + "begin_time":{}, + "end_time": {} + } + } + user_config["reserve_info"]["date"] = self.DateEdit.dateTime().toString("yyyy-MM-dd") + user_config["reserve_info"]["place"] = self.PlaceComboBox.currentText() + user_config["reserve_info"]["floor"] = self.__floor_rmap[self.FloorComboBox.currentText()] + user_config["reserve_info"]["room"] = self.__room_rmap[self.RoomComboBox.currentText()] + user_config["reserve_info"]["seat_id"] = self.SeatIDEdit.text() + user_config["reserve_info"]["begin_time"]["time"] = self.BeginTimeEdit.time().toString("HH:mm") + user_config["reserve_info"]["begin_time"]["max_diff"] = self.MaxBeginTimeDiffSpinBox.value() + user_config["reserve_info"]["begin_time"]["prefer_early"] = self.PreferEarlyBeginTimeCheckBox.isChecked() + user_config["reserve_info"]["end_time"]["time"] = self.EndTimeEdit.time().toString("HH:mm") + user_config["reserve_info"]["end_time"]["max_diff"] = self.MaxEndTimeDiffSpinBox.value() + user_config["reserve_info"]["end_time"]["prefer_early"] = not self.PreferLateEndTimeCheckBox.isChecked() + user_config["reserve_info"]["expect_duration"] = self.ExpectDurationSpinBox.value() + user_config["reserve_info"]["satisfy_duration"] = self.SatisfyDurationCheckBox.isChecked() + return user_config + + + def collectUserConfigFromUserListWidget( + self, + index: int + ) -> dict: + + user_config = self.defaultUsersConfig() + if index < 0 or index >= self.UserListWidget.count(): + return user_config + user_item = self.UserListWidget.item(index) + if user_item: + user_config = user_item.data(Qt.UserRole) + return user_config + + + def setUserConfigToWidget( + self, + user_config: dict + ) -> None: + + try: + self.UsernameEdit.setText(user_config["username"]) + self.PasswordEdit.setText(user_config["password"]) + self.DateEdit.setDate(QDate.fromString(user_config["reserve_info"]["date"], "yyyy-MM-dd")) + self.PlaceComboBox.setCurrentText(user_config["reserve_info"]["place"]) + self.FloorComboBox.setCurrentText(self.__floor_map[user_config["reserve_info"]["floor"]]) + self.RoomComboBox.setCurrentText(self.__room_map[user_config["reserve_info"]["room"]]) + self.SeatIDEdit.setText(user_config["reserve_info"]["seat_id"]) + self.BeginTimeEdit.setTime(QTime.fromString(user_config["reserve_info"]["begin_time"]["time"], "H:mm")) + self.MaxBeginTimeDiffSpinBox.setValue(user_config["reserve_info"]["begin_time"]["max_diff"]) + self.PreferEarlyBeginTimeCheckBox.setChecked(user_config["reserve_info"]["begin_time"]["prefer_early"]) + self.EndTimeEdit.setTime(QTime.fromString(user_config["reserve_info"]["end_time"]["time"], "H:mm")) + self.MaxEndTimeDiffSpinBox.setValue(user_config["reserve_info"]["end_time"]["max_diff"]) + self.PreferLateEndTimeCheckBox.setChecked(not user_config["reserve_info"]["end_time"]["prefer_early"]) + self.ExpectDurationSpinBox.setValue(user_config["reserve_info"]["expect_duration"]) + self.SatisfyDurationCheckBox.setChecked(user_config["reserve_info"]["satisfy_duration"]) + except: + QMessageBox.warning( + self, + "警告 - AutoLibrary", + "用户配置文件读取发生错误 !\n"\ + f"用户: {user_config['username']} 配置文件可能已损坏" + ) + + + def loadSystemConfig( + self, + system_config_path: str + ) -> dict: + + try: + if not system_config_path or not os.path.exists(system_config_path): + raise Exception("文件路径不存在") + system_config = ConfigReader(system_config_path).getConfigs() + if system_config and "library" in system_config\ + and "web_driver" in system_config\ + and "login" in system_config: + return system_config + return None + except Exception as e: + QMessageBox.warning( + self, + "警告 - AutoLibrary", + f"系统配置文件读取发生错误 ! : {e}\n"\ + f"文件路径: {system_config_path}" + ) + return None + + + def saveSystemConfig( + self, + system_config_path: str, + system_config_data: dict + ) -> bool: + + try: + if not system_config_path: + raise Exception("文件路径为空") + if not system_config_data or not isinstance(system_config_data, dict): + raise Exception("系统配置数据为空或类型错误") + ConfigWriter(system_config_path, system_config_data) + return True + except Exception as e: + QMessageBox.warning( + self, + "警告 - AutoLibrary", + f"配置文件写入发生错误 ! : {e}\n"\ + f"文件路径: {system_config_path}" + ) + return False + + + def loadUsersConfig( + self, + users_config_path: str + ) -> dict: + + try: + if not users_config_path or not os.path.exists(users_config_path): + raise Exception("文件路径不存在") + users_config = ConfigReader(users_config_path).getConfigs() + if users_config and "users" in users_config: + return users_config + return None + except Exception as e: + QMessageBox.warning( + self, + "警告 - AutoLibrary", + f"用户配置文件读取发生错误 ! : {e}\n"\ + f"文件路径: {users_config_path}" + ) + return None + + + def saveUsersConfig( + self, + users_config_path: str, + users_config_data: dict + ) -> bool: + + try: + if not users_config_path: + raise Exception("文件路径为空") + if not users_config_data or not isinstance(users_config_data, dict): + raise Exception("用户配置数据为空或类型错误") + ConfigWriter(users_config_path, users_config_data) + return True + except Exception as e: + QMessageBox.warning( + self, + "警告 - AutoLibrary", + f"用户配置文件写入发生错误 ! : {e}\n"\ + f"文件路径: \n{users_config_path}" + ) + return False + + + def saveConfigs( + self, + system_config_path: str, + users_config_path: str + ) -> bool: + + if users_config_path: + self.__users_config_data = self.defaultUsersConfig() + for index in range(self.UserListWidget.count()): + user_config = self.collectUserConfigFromUserListWidget(index) + if user_config: + self.__users_config_data["users"].append(user_config) + if not self.saveUsersConfig( + users_config_path, + self.__users_config_data + ): + return False + if system_config_path: + self.__system_config_data = self.collectSystemConfigFromWidget() + if not self.saveSystemConfig( + system_config_path, + self.__system_config_data + ): + return False + return True + + + def loadConfig( + self, + config_path: str + ) -> bool: + + if not config_path: + config_path = QFileDialog.getOpenFileName( + self, + "选择配置文件 - AutoLibrary", + f"{QDir.toNativeSeparators(QDir.currentPath())}", + "JSON 文件 (*.json);;所有文件 (*)" + )[0] + if not config_path: + return False + try: + system_config = self.loadSystemConfig(config_path) + users_config = self.loadUsersConfig(config_path) + if system_config is not None: + self.__config_paths["system"] = config_path + self.__system_config_data.update(system_config) + self.setSystemConfigToWidget(self.__system_config_data) + self.CurrentSystemConfigEdit.setText(config_path) + return True + if users_config is not None: + self.__config_paths["users"] = config_path + self.__users_config_data.update(users_config) + self.fillUsersList(self.__users_config_data) + self.CurrentUserConfigEdit.setText(config_path) + return True + except: + return False + + + def fillUsersList( + self, + users_config_data: list[dict] + ): + + self.UserListWidget.clear() + if "users" in users_config_data: + for user in users_config_data["users"]: + user_item = QListWidgetItem(user["username"]) + user_item.setData(Qt.UserRole, user) + self.UserListWidget.addItem(user_item) + + + def addUser( + self + ): + + new_user = { + "username": f"新用户-{self.UserListWidget.count()}", + "password": "000000", + "reserve_info": { + "date": f"{QDate.currentDate().toString("yyyy-MM-dd")}", + "place": "\u56fe\u4e66\u9986", + "floor": "2", + "room": "1", + "seat_id": "", + "begin_time": { + "time": f"{QTime.currentTime().toString("hh:mm")}", + "max_diff": 0, + "prefer_early": False + }, + "end_time": { + "time": f"{QTime.currentTime().addSecs(2*3600).toString("hh:mm")}", + "max_diff": 0, + "prefer_early": True + }, + "expect_duration": 2.0, + "satisfy_duration": False + } + } + user_item = QListWidgetItem(new_user["username"]) + user_item.setData(Qt.UserRole, new_user) + self.UserListWidget.addItem(user_item) + self.UserListWidget.setCurrentItem(user_item) + self.setUserConfigToWidget(new_user) + + + def delUser( + self + ): + + current_item = self.UserListWidget.currentItem() + if current_item: + self.UserListWidget.takeItem(self.UserListWidget.row(current_item)) + self.UserListWidget.setCurrentItem(None) + + @Slot() + def onShowPasswordCheckBoxChecked( + self, + checked: bool + ): + + if checked: + self.PasswordEdit.setEchoMode(QLineEdit.Normal) + else: + self.PasswordEdit.setEchoMode(QLineEdit.Password) + + @Slot() + def onFloorComboBoxCurrentIndexChanged( + self, + ): + + floor = self.FloorComboBox.currentText() + self.RoomComboBox.clear() + self.RoomComboBox.addItems(self.__floor_room_map[floor]) + self.RoomComboBox.setCurrentIndex(0) + + @Slot() + def onUserListWidgetCurrentItemChanged( + self, + current: QListWidgetItem, + previous: QListWidgetItem + ): + # dont care about the 'self.__users_config_data', we already + # cant effectively update the data of each user, due to the + # possiblity of frequency edit. we just let the QListWidget + # help us. + if not current: + self.initilizeUserInfoWidget() + return + if previous: + user = self.collectUserConfigFromUserInfoWidget() + if user: + previous.setText(user["username"]) + previous.setData(Qt.UserRole, user) + user = current.data(Qt.UserRole) + if user: + self.setUserConfigToWidget(user) + + @Slot() + def onAddUserButtonClicked( + self + ): + + self.addUser() + + @Slot() + def onDelUserButtonClicked( + self + ): + + self.delUser() + + @Slot() + def onBrowseBrowserDriverButtonClicked( + self + ): + + browser_driver_path = QFileDialog.getOpenFileName( + self, + "选择浏览器驱动 - AutoLibrary", + self.CurrentSystemConfigEdit.text(), + "可执行文件 (*.exe);;所有文件 (*)" + )[0] + if browser_driver_path: + self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(browser_driver_path)) + + @Slot() + def onBrowseCurrentSystemConfigButtonClicked( + self + ): + + system_config_path = QFileDialog.getOpenFileName( + self, + "选择其它的系统配置 - AutoLibrary", + self.CurrentSystemConfigEdit.text(), + "JSON 文件 (*.json);;所有文件 (*)" + )[0] + if system_config_path: + system_config_path = QDir.toNativeSeparators(system_config_path) + self.loadConfig(system_config_path) + + @Slot() + def onBrowseCurrentUserConfigButtonClicked( + self + ): + + users_config_path = QFileDialog.getOpenFileName( + self, + "选择其它的用户配置 - AutoLibrary", + self.CurrentUserConfigEdit.text(), + "JSON 文件 (*.json);;所有文件 (*)" + )[0] + if users_config_path: + users_config_path = QDir.toNativeSeparators(users_config_path) + self.loadConfig(users_config_path) + + @Slot() + def onBrowseExportSystemConfigButtonClicked( + self + ): + + system_config_path = QFileDialog.getSaveFileName( + self, + "导出系统配置 - AutoLibrary", + self.CurrentSystemConfigEdit.text(), + "JSON 文件 (*.json);;所有文件 (*)" + )[0] + if system_config_path: + self.ExportSystemConfigEdit.setText(QDir.toNativeSeparators(system_config_path)) + + @Slot() + def onBrowseExportUserConfigButtonClicked( + self + ): + + users_config_path = QFileDialog.getSaveFileName( + self, + "导出用户配置 - AutoLibrary", + self.CurrentUserConfigEdit.text(), + "JSON 文件 (*.json);;所有文件 (*)" + )[0] + if users_config_path: + self.ExportUserConfigEdit.setText(QDir.toNativeSeparators(users_config_path)) + + @Slot() + def onExportConfigButtonClicked( + self + ): + + msg = "" + + system_config_path = self.ExportSystemConfigEdit.text() + users_config_path = self.ExportUserConfigEdit.text() + if system_config_path: + if self.saveConfigs( + system_config_path, + users_config_path="" + ): + msg += f"系统配置文件已导出到: \n'{system_config_path}'\n" + if users_config_path: + if self.saveConfigs( + "", users_config_path + ): + msg += f"用户配置文件已导出到: \n'{users_config_path}'\n" + if msg: + QMessageBox.information( + self, + "信息 - AutoLibrary", + msg + ) + + @Slot() + def onLoadConfigButtonClicked( + self + ): + + self.loadConfig("") + + @Slot() + def onNewConfigButtonClicked( + self + ): + + file_path = self.CurrentSystemConfigEdit.text() + folder_dir = QFileDialog.getExistingDirectory( + self, + "选择新建配置的文件夹 - AutoLibrary", + QDir.toNativeSeparators(QFileInfo(os.path.abspath(file_path)).absoluteDir().path()) + ) + if not folder_dir: + return + system_config_path = QDir.toNativeSeparators(os.path.join(folder_dir, "system.json")) + users_config_path = QDir.toNativeSeparators(os.path.join(folder_dir, "users.json")) + system_exists = os.path.isfile(system_config_path) + users_exists = os.path.isfile(users_config_path) + if system_exists or users_exists: + exist_files = [] + if system_exists: + exist_files.append(system_config_path) + if users_exists: + exist_files.append(users_config_path) + reply = QMessageBox.information( + self, + "信息 - AutoLibrary", + f"文件夹中已存在以下文件, 是否覆盖 ?\n{chr(10).join(exist_files)}", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + if reply == QMessageBox.No: + return + self.__system_config_data = self.defaultSystemConfig() + self.__users_config_data = self.defaultUsersConfig() + self.__config_paths = { + "system": system_config_path, + "users": users_config_path + } + self.initlizeConfigToWidget("system", self.__system_config_data) + self.initlizeConfigToWidget("users", self.__users_config_data) + + @Slot() + def onConfirmButtonClicked( + self + ): + + if self.UserListWidget.currentItem() is not None: + user = self.collectUserConfigFromUserInfoWidget() + if user: + self.UserListWidget.currentItem().setData(Qt.UserRole, user) + if self.saveConfigs( + self.__config_paths["system"], + self.__config_paths["users"] + ): + QMessageBox.information( + self, + "信息 - AutoLibrary", + "配置文件保存成功 !\n" + f"系统配置文件路径: \n{self.__config_paths['system']}\n"\ + f"用户配置文件路径: \n{self.__config_paths['users']}" + ) + else: + QMessageBox.warning( + self, + "警告 - AutoLibrary", + "配置文件保存失败, 请检查文件路径权限" + ) + self.close() + + @Slot() + def onCancelButtonClicked( + self + ): + + self.close() diff --git a/gui/ALConfigWidget.ui b/gui/ALConfigWidget.ui new file mode 100644 index 0000000..3f738d0 --- /dev/null +++ b/gui/ALConfigWidget.ui @@ -0,0 +1,1790 @@ + + + ALConfigWidget + + + + 0 + 0 + 520 + 700 + + + + + 520 + 700 + + + + + 520 + 700 + + + + 编辑配置 - AutoLibrary + + + + 5 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 35 + 110 + + + + + 1313 + 16777215 + + + + 0 + + + true + + + + 用户设置 + + + + 5 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 260 + 16777215 + + + + 用户列表 + + + + + + + 100 + 0 + + + + + 250 + 16777215 + + + + false + + + QListView::ViewMode::ListMode + + + -1 + + + false + + + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 删除用户 + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 添加用户 + + + + + + + + + + + + + 16777215 + 16777215 + + + + 用户信息 + + + + 5 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 100 + + + + + 16777215 + 100 + + + + 登录信息 + + + false + + + false + + + + 3 + + + 3 + + + 3 + + + 3 + + + 5 + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 用户名: + + + + + + + 5 + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + 10 + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 显示 + + + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 密码: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + 25 + + + + + + + + + + + 0 + 470 + + + + + 16777215 + 480 + + + + 预约信息 + + + + 3 + + + 3 + + + 3 + + + 3 + + + 5 + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + + + 100 + 30 + + + + + 100 + 30 + + + + 结束时间: + + + + + + + false + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + 图书馆 + + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 期望时长: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + + + + H:mm + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 日期: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + 2000 + 1 + 1 + + + + + + + + + + H:mm + + + + + + + + 100 + 25 + + + + + 16777215 + 25 + + + + 优先选择最早 + + + true + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 最大时长偏差: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 120 + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 区域: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + 二层 + + + + + 三层 + + + + + 四层 + + + + + 五层 + + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + + + 100 + 25 + + + + + 16777215 + 25 + + + + 120 + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 优先选择最晚 + + + true + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 开始时间: + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 楼层: + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 座位号: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 8.000000000000000 + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + 2025 + 11 + 1 + + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 最大时长偏差: + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 地点: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 优先满足时长要求 + + + true + + + + + + + + + + + + + + 系统设置 + + + + 5 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + 登录设置 + + + + 3 + + + 3 + + + 3 + + + 3 + + + 5 + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 自动识别验证码 + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 登录尝试次数: + + + + + + + + 50 + 25 + + + + + 80 + 25 + + + + 1 + + + 10 + + + 5 + + + + + + + + + + 浏览器设置 + + + + 5 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 浏览器类型: + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + edge + + + 0 + + + 3 + + + 3 + + + + edge + + + + + chrome + + + + + firefox + + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 驱动路径: + + + + + + + 5 + + + + + + 265 + 25 + + + + + 260 + 25 + + + + + + + + + 35 + 25 + + + + + 35 + 25 + + + + ... + + + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 无头模式 + + + + + + + + + + 图书馆设置 + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + 5 + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 登录URL: + + + + + + + false + + + + 0 + 25 + + + + + 16777215 + 25 + + + + http://10.1.20.7 + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 主机URL: + + + + + + + false + + + + 0 + 25 + + + + + 16777215 + 25 + + + + /login + + + + + + + + + + 运行模式 + + + + 5 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 自动预约 + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 自动签到 + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 自动续约 + + + + + + + + + + + 0 + 270 + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + 0 + + + + + + + + 其它 + + + + 5 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + 当前配置 + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + true + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + true + + + true + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 当前系统配置路径: + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 当前用户配置路径: + + + + + + + + 35 + 25 + + + + + 35 + 25 + + + + ;;; + + + + + + + + 35 + 25 + + + + + 35 + 25 + + + + ... + + + + + + + + + + 导出路径 + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 系统配置导出路径: + + + + + + + + 35 + 25 + + + + + 35 + 25 + + + + ... + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + 用户配置导出路径: + + + + + + + + 35 + 25 + + + + + 35 + 25 + + + + ... + + + + + + + + 100 + 25 + + + + + 100 + 25 + + + + 导出配置文件 + + + + + ExportUserConfigEdit + ExportSystemConfigLabel + BrowseExportUserConfigButton + ExportUserConfigLabel + BrowseExportSystemConfigButton + ExportConfigButton + ExportSystemConfigEdit + + + + + + + 0 + 200 + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + 0 + + + + + + + + + + + 5 + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 取消 + + + + + + + + 1280 + 16777215 + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + 0 + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 新建配置 + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 载入配置 + + + + + + + + 80 + 25 + + + + + 80 + 25 + + + + 确认 + + + + + + + + + + diff --git a/gui/ALMainWindow.py b/gui/ALMainWindow.py new file mode 100644 index 0000000..eb9ead9 --- /dev/null +++ b/gui/ALMainWindow.py @@ -0,0 +1,310 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 KenanZhu. +All rights reserved. + +This software is provided "as is", without any warranty of any kind. +You may use, modify, and distribute this file under the terms of the MIT License. +See the LICENSE file for details. +""" +import os +import sys +import time +import queue + +from PySide6.QtCore import ( + Qt, Signal, Slot, QDir, QFileInfo, QTimer, QThread +) +from PySide6.QtWidgets import ( + QMainWindow, QMenu +) +from PySide6.QtGui import ( + QTextCursor, QCloseEvent, QFont, QIcon +) + +from .Ui_ALMainWindow import Ui_ALMainWindow +from .ALConfigWidget import ALConfigWidget + +from . import AutoLibraryResource + +from AutoLib import AutoLib +from ConfigReader import ConfigReader + + +class AutoLibWorker(QThread): + + finishedSignal = Signal() + showTraceSignal = Signal(str) + showMsgSignal = Signal(str) + + def __init__( + self, + input_queue: queue.Queue, + output_queue: queue.Queue, + config_paths: dict + ): + + super().__init__() + + self.__input_queue = input_queue + self.__output_queue = output_queue + self.__config_paths = config_paths + self.__stopped = False + + + def checkTimeAvailable( + self, + ) -> bool: + + current_time = time.strftime("%H:%M", time.localtime()) + if current_time >= "23:30" or current_time <= "07:30": + return False + return True + + + def checkConfigPaths( + self, + ) -> bool: + + if not all( + os.path.exists(path) for path in self.__config_paths.values() + ): + self.showTraceSignal.emit( + "配置文件路径不存在,请检查配置文件路径是否正确。" + ) + return False + return True + + + def run( + self + ): + + try: + if not self.checkTimeAvailable(): + self.showTraceSignal.emit( + "当前时间不在图书馆开放时间内。\n"\ + " 请在 07:30 - 23:30 之间尝试" + ) + return + if not self.checkConfigPaths(): + return + self.showTraceSignal.emit("AutoLibrary 开始运行") + auto_lib = AutoLib( + self.__input_queue, + self.__output_queue, + ) + auto_lib.run( + ConfigReader(self.__config_paths["system"]), + ConfigReader(self.__config_paths["users"]), + ) + auto_lib.close() + self.showTraceSignal.emit("AutoLibrary 运行结束") + except Exception as e: + self.showTraceSignal.emit( + f"AutoLibrary 运行时发生异常:{e}" + ) + finally: + self.finishedSignal.emit() + + + def stop( + self + ): + + self.__stopped = True + + +class ALMainWindow(QMainWindow, Ui_ALMainWindow): + + def __init__( + self + ): + + super().__init__() + self.__class_name = self.__class__.__name__ + + self.setupUi(self) + self.__input_queue = queue.Queue() + self.__output_queue = queue.Queue() + self.__auto_lib = AutoLib( + self.__input_queue, + self.__output_queue, + ) + self.__config_paths = { + "system": + f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("system.json"))}", + "users": + f"{QDir.toNativeSeparators(QFileInfo(sys.executable).absoluteDir().absoluteFilePath("users.json"))}", + } + self.__alConfigWidget = None + self.__auto_lib_thread = None + + self.modifyUi() + self.connectSignals() + self.startMsgPolling() + + + def modifyUi( + self + ): + + icon = QIcon(":/res/icon/icons/AutoLibrary.ico") + self.setWindowIcon(icon) + self.MessageIOTextEdit.setFont(QFont("Courier New", 10)) + + + def connectSignals( + self + ): + + self.ConfigButton.clicked.connect(self.onConfigButtonClicked) + self.StartButton.clicked.connect(self.onStartButtonClicked) + self.StopButton.clicked.connect(self.onStopButtonClicked) + self.SendButton.clicked.connect(self.onSendButtonClicked) + self.MessageEdit.returnPressed.connect(self.onSendButtonClicked) + + + def closeEvent( + self, + event: QCloseEvent, + ): + + if self.__timer and self.__timer.isActive(): + self.__timer.stop() + if self.__auto_lib: + self.__auto_lib.close() + if self.__alConfigWidget: + self.__alConfigWidget.close() + super().closeEvent(event) + + + def appendToTextEdit( + self, + text: str, + ): + + cursor = self.MessageIOTextEdit.textCursor() + cursor.movePosition(QTextCursor.End) + cursor.insertText(text + "\n") + self.MessageIOTextEdit.setTextCursor(cursor) + self.MessageIOTextEdit.ensureCursorVisible() + scrollbar = self.MessageIOTextEdit.verticalScrollBar() + scrollbar.setValue(scrollbar.maximum()) + + + def startMsgPolling( + self + ): + + self.__timer = QTimer() + self.__timer.timeout.connect(self.pollMsgQueue) + self.__timer.start(100) + + + def setControlButtons( + self, + config_button_enabled: bool, + start_button_enabled: bool, + stop_button_enabled: bool, + ): + + self.ConfigButton.setEnabled(config_button_enabled) + self.StartButton.setEnabled(start_button_enabled) + self.StopButton.setEnabled(stop_button_enabled) + + @Slot() + def showMsg( + self, + msg: str, + ): + + self.appendToTextEdit(f"[{self.__class_name:<12}] >>> : {msg}") + + @Slot() + def showTrace( + self, + msg: str, + ): + + timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + self.appendToTextEdit(f"{timestamp}-[{self.__class_name:<12}] : {msg}") + + @Slot() + def pollMsgQueue( + self, + ): + + try: + while True: + msg = self.__output_queue.get_nowait() + self.appendToTextEdit(msg) + except queue.Empty: + pass + + @Slot(dict) + def onConfigWidgetClosed( + self, + config_paths: dict, + ): + + self.__alConfigWidget = None + self.ConfigButton.setEnabled(True) + self.StartButton.setEnabled(True) + self.StopButton.setEnabled(False) + self.__config_paths = config_paths + + @Slot() + def onConfigButtonClicked( + self, + ): + + if self.__alConfigWidget is None: + self.__alConfigWidget = ALConfigWidget( + self, + self.__config_paths + ) + self.__alConfigWidget.configWidgetCloseSingal.connect(self.onConfigWidgetClosed) + self.__alConfigWidget.setWindowFlags(Qt.Window) + self.__alConfigWidget.setWindowModality(Qt.ApplicationModal) + self.__alConfigWidget.show() + self.__alConfigWidget.raise_() + self.__alConfigWidget.activateWindow() + self.ConfigButton.setEnabled(False) + + @Slot() + def onStartButtonClicked( + self, + ): + + self.setControlButtons(False, False, True) + self.__auto_lib_thread = AutoLibWorker( + self.__input_queue, + self.__output_queue, + self.__config_paths, + ) + self.__auto_lib_thread.finishedSignal.connect(self.onStopButtonClicked) + self.__auto_lib_thread.showMsgSignal.connect(self.showMsg) + self.__auto_lib_thread.showTraceSignal.connect(self.showTrace) + self.__auto_lib_thread.start() + + @Slot() + def onStopButtonClicked( + self + ): + + if self.__auto_lib_thread and self.__auto_lib_thread.isRunning(): + self.__auto_lib_thread.stop() + self.showTrace("正在停止操作......") + self.setControlButtons(True, True, False) + + @Slot() + def onSendButtonClicked( + self + ): + + msg = self.MessageEdit.text().strip() + if not msg: + return + self.showMsg(msg) + self.MessageEdit.clear() \ No newline at end of file diff --git a/gui/ALMainWindow.ui b/gui/ALMainWindow.ui new file mode 100644 index 0000000..56f3526 --- /dev/null +++ b/gui/ALMainWindow.ui @@ -0,0 +1,264 @@ + + + ALMainWindow + + + + 0 + 0 + 540 + 300 + + + + + 540 + 300 + + + + + 1280 + 720 + + + + AutoLibrary + + + true + + + + + 5 + + + 3 + + + 0 + + + 3 + + + 0 + + + + + 5 + + + + + + 1280 + 0 + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + 0 + + + + + + + + 80 + 30 + + + + + 80 + 30 + + + + 配置 + + + + + + + false + + + + 80 + 30 + + + + + 80 + 30 + + + + 停止脚本 + + + + + + + true + + + + 80 + 30 + + + + + 80 + 30 + + + + background-color: rgb(10, 170, 10); +font: 12pt "Microsoft YaHei UI"; +color: rgb(255, 255, 255); +font: 9pt "Segoe UI"; +font: 700 9pt "Microsoft YaHei UI"; + + + 启动脚本 + + + + + + + + + + 0 + 175 + + + + QMenu::icon { + width: 0px; + height: 0px; +} + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Plain + + + 0 + + + 0 + + + false + + + false + + + QPlainTextEdit::LineWrapMode::NoWrap + + + false + + + Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextBrowserInteraction|Qt::TextInteractionFlag::TextEditable|Qt::TextInteractionFlag::TextEditorInteraction|Qt::TextInteractionFlag::TextSelectableByKeyboard|Qt::TextInteractionFlag::TextSelectableByMouse + + + false + + + false + + + + + + + 5 + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + true + + + + + + + + 80 + 30 + + + + + 80 + 30 + + + + 发送 + + + + + + + + + + false + + + + 0 + 0 + 540 + 33 + + + + true + + + + + false + + + + + + diff --git a/gui/AutoLibraryResource.qrc b/gui/AutoLibraryResource.qrc new file mode 100644 index 0000000..ba03dfd --- /dev/null +++ b/gui/AutoLibraryResource.qrc @@ -0,0 +1,8 @@ + + + icons/AutoLibrary.ico + + + translators/qtbase_zh_CN.qm + + diff --git a/gui/icons/AutoLibrary.ico b/gui/icons/AutoLibrary.ico new file mode 100644 index 0000000..e48e78f Binary files /dev/null and b/gui/icons/AutoLibrary.ico differ diff --git a/gui/translators/qtbase_zh_CN.ts b/gui/translators/qtbase_zh_CN.ts new file mode 100644 index 0000000..8fd8117 --- /dev/null +++ b/gui/translators/qtbase_zh_CN.ts @@ -0,0 +1,7898 @@ + + + + + CloseButton + + Close Tab + 关闭标签页 + + + + MAC_APPLICATION_MENU + + About %1 + 关于 %1 + + + Preferences... + 首选项... + + + Services + 服务 + + + Hide %1 + 隐藏 %1 + + + Hide Others + 隐藏其他 + + + Show All + 全部显示 + + + Quit %1 + 退出 %1 + + + + Print Device Input Slot + + Automatic + 自动 + + + + Print Device Output Bin + + Automatic + 自动 + + + + QAbstractSocket + + Socket operation timed out + 套接字操作超时 + + + Operation on socket is not supported + 套接字操作不被支持 + + + Host not found + 主机未找到 + + + Connection refused + 连接被拒绝 + + + Connection timed out + 连接超时 + + + Trying to connect while connection is in progress + 在连接正在进行时尝试连接 + + + Socket is not connected + 套接字未连接 + + + Network unreachable + 网络无法访问 + + + + QAbstractSpinBox + + &Select All + 全选(&S) + + + &Step up + 这是数值框的加减按钮 + 升高(&S) + + + Step &down + 这是数值框的加减按钮 + 降低(&D) + + + + QAccessibleActionInterface + + Press + 按下 + + + Increase + 变大 + + + Decrease + 变小 + + + ShowMenu + 显示菜单 + + + SetFocus + 设置焦点 + + + Toggle + 切换 + + + Scroll Left + 向左滚动 + + + Scroll Right + 向右滚动 + + + Scroll Up + 向上滚动 + + + Scroll Down + 向下滚动 + + + Previous Page + 上一页 + + + Next Page + 下一页 + + + Triggers the action + 触发操作 + + + Increase the value + 提高数值 + + + Decrease the value + 降低数值 + + + Shows the menu + 显示菜单 + + + Sets the focus + 设置焦点 + + + Toggles the state + 切换状态 + + + Scrolls to the left + 向左滚动 + + + Scrolls to the right + 向右滚动 + + + Scrolls up + 向上滚动 + + + Scrolls down + 向下滚动 + + + Goes back a page + 转到上一页 + + + Goes to the next page + 转到下一页 + + + + QAndroidPlatformTheme + + Yes + + + + Yes to All + 全是 + + + No + + + + No to All + 全否 + + + + QApplication + + Executable '%1' requires Qt %2, found Qt %3. + 可执行程序“%1”要求 Qt %2,但找到的是 Qt %3。 + + + Incompatible Qt Library Error + Qt 程序库不兼容错误 + + + + QCocoaMenuItem + + About Qt + 关于 Qt + + + About + 关于 + + + Config + 配置 + + + Preference + 首选项 + + + Options + 选项 + + + Setting + 设置 + + + Setup + 设置 + + + Quit + 退出 + + + Exit + 退出 + + + Cut + 剪切 + + + Copy + 复制 + + + Paste + 粘贴 + + + Select All + 全选 + + + + QCocoaTheme + + Don't Save + 不保存 + + + + QColorDialog + + Hu&e: + HSV 模型中的 Hue 一般按照 PS 习惯翻译为“色相”。虽然 Hue 也有翻译成色调的,但那更倾向于传统绘画,不应该用在 HSV 这种显然是数字色彩的语境中。“色相”是数码美术界对 Hue 的通用翻译,此翻译已经过色彩管理的多本大学教材核对。 + 色相(&E): + + + &Sat: + 饱和度(&S): + + + &Val: + HSV 模型中的 Value 一般按照 PS 习惯翻译为明度 + 明度(&V): + + + &Red: + 通道名称 + 红(&R): + + + &Green: + 通道名称 + 绿(&G): + + + Bl&ue: + 通道名称 + 蓝(&U): + + + A&lpha channel: + 透明度通道(&A): + + + &HTML: + HTML(&H): + + + Cursor at %1, %2 +Press ESC to cancel + 光标位于 %1, %2 +按 ESC 键取消 + + + Select Color + 选择颜色 + + + &Basic colors + 基本颜色(&B) + + + &Custom colors + 自定义颜色(&C) + + + &Add to Custom Colors + 添加到自定义颜色(&A) + + + &Pick Screen Color + 拾取屏幕颜色(&P) + + + + QComboBox + + Open the combo box selection popup + 打开组合框弹出选单 + + + False + + + + True + + + + + QCommandLineParser + + Displays version information. + 显示版本信息。 + + + Displays this help. + 显示此帮助。 + + + Displays help on commandline options. + 在命令行选项上显示帮助。 + + + Displays help including Qt specific options. + 显示包括 Qt 特有选项的帮助。 + + + Unknown option '%1'. + 未知选项“%1”。 + + + Unknown options: %1. + 未知选项:%1。 + + + Missing value after '%1'. + “%1”之后缺少值。 + + + Unexpected value after '%1'. + “%1”之后的异常值。 + + + [options] + [选项] + + + Usage: %1 + 用法:%1 + + + Options: + 选项: + + + Arguments: + 参数: + + + + QCoreApplication + + %1: key is empty + QSystemSemaphore + %1:键为空 + + + %1: unable to make key + QSystemSemaphore + %1:无法创建键 + + + %1: ftok failed + QSystemSemaphore + %1:ftok 失败 + + + + QCupsJobWidget + + Job + 任务 + + + Job Control + 任务控制 + + + Scheduled printing: + 计划打印: + + + Billing information: + 账单信息: + + + Job priority: + 任务优先级: + + + Banner Pages + 横幅页面 + + + End: + Banner page at end + 结束: + + + Start: + Banner page at start + 开始: + + + Print Immediately + 立即打印 + + + Hold Indefinitely + 无限期待机 + + + Day (06:00 to 17:59) + 白天 (06:00 到 17:59) + + + Night (18:00 to 05:59) + 夜间 (18:00 到 05:59) + + + Second Shift (16:00 to 23:59) + 第二班 (16:00 到 23:59) + + + Third Shift (00:00 to 07:59) + 第三班 (00:00 到 07:59) + + + Weekend (Saturday to Sunday) + 周末 (周六到周日) + + + Specific Time + 指定时间 + + + None + CUPS Banner page + + + + Standard + CUPS Banner page + 标准 + + + Unclassified + CUPS Banner page + 无密级 + + + Confidential + CUPS Banner page + 机密 + + + Classified + CUPS Banner page + 有密级 + + + Secret + CUPS Banner page + 秘密 + + + Top Secret + CUPS Banner page + 绝密 + + + + QCupsPrinterSupport + + Authentication Needed + 需要身份验证 + + + Authentication needed to use %1. + 需要身份验证才能使用 %1。 + + + Authentication needed to use %1 on %2. + 需要身份验证才能在 %2 上使用 %1。 + + + Username: + 用户名: + + + Password: + 密码: + + + + QDB2Driver + + Unable to connect + 无法连接 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + Unable to set autocommit + 无法设置自动提交 + + + + QDB2Result + + Unable to execute statement + 无法执行语句 + + + Unable to prepare statement + 无法准备语句 + + + Unable to bind variable + 无法绑定变量 + + + Unable to fetch record %1 + 无法获取记录 %1 + + + Unable to fetch next + 无法获取下一个 + + + Unable to fetch first + 无法获取第一个 + + + + QDBusTrayIcon + + OK + 确定 + + + + QDateTimeParser + + AM + AM + + + am + am + + + PM + PM + + + pm + pm + + + + QDialog + + What's This? + 这是什么? + + + + QDialogButtonBox + + OK + 确定 + + + + QDirModel + + Name + 名称 + + + Size + 体积 + + + Kind + Match OS X Finder + 类型 + + + Type + All other platforms + 类型 + + + Date Modified + 修改日期 + + + + QDnsLookup + + Operation cancelled + 操作已取消 + + + + QDnsLookupRunnable + + IPv6 addresses for nameservers are currently not supported + 目前不支持名称服务器的 IPv6 地址 + + + Invalid domain name + 无效的域名 + + + Not yet supported on Android + Android 尚不支持 + + + Resolver functions not found + 找不到解析器函数 + + + Resolver initialization failed + 解析器初始化失败 + + + Server could not process query + 服务器无法处理查询 + + + Server failure + 服务器故障 + + + Non existent domain + 不存在的域 + + + Server refused to answer + 服务器拒绝回答 + + + Invalid reply received + 收到的回复无效 + + + Could not expand domain name + 无法扩展域名 + + + Invalid IPv4 address record + 无效的 IPv4 地址记录 + + + Invalid IPv6 address record + 无效的 IPv6 地址记录 + + + Invalid canonical name record + 无效的规范名称记录 + + + Invalid name server record + 无效的名称服务器记录 + + + Invalid pointer record + 无效的指针记录 + + + Invalid mail exchange record + 无效的邮件交换记录 + + + Invalid service record + 无效的服务记录 + + + Invalid text record + 无效的文本记录 + + + Resolver library can't be loaded: No runtime library loading support + 无法加载解析器库:不支持运行时库加载 + + + No hostname given + 未指定主机名 + + + Invalid hostname + 无效的主机名 + + + Host %1 could not be found. + 找不到主机 %1。 + + + Unknown error + 未知错误 + + + + QDockWidget + + Float + Accessible name for button undocking a dock widget (floating state) + 悬浮 + + + Undocks and re-attaches the dock widget + 悬浮和停靠可停靠部件 + + + Close + Accessible name for button closing a dock widget + 关闭 + + + Closes the dock widget + 关闭可停靠部件 + + + + QDomParser + + Error occurred while processing XML declaration + 处理 XML 声明时出错 + + + Multiple DTD sections are not allowed + 不允许多个 DTD 节 + + + Error occurred while processing document type declaration + 处理文档类型声明时出错 + + + Error occurred while processing comment + 处理注释时出错 + + + Error occurred while processing a processing instruction + 处理一个处理指令时出错 + + + Error occurred while processing a start element + 处理一个开始元素时出错 + + + Unexpected end element '%1' + 异常结束元素“%1” + + + Error occurred while processing an end element + 处理一个结束元素时出错 + + + Error occurred while processing the element content + 处理元素内容时出错 + + + Error occurred while processing comments + 处理注释时出错 + + + Error occurred while processing an entity reference + 处理一个实体引用时出错 + + + Unexpected token + 异常令牌 + + + Tag mismatch + 标签不匹配 + + + Error occurred while processing entity declaration + 处理实体声明时出错 + + + Error occurred while processing notation declaration + 处理记号声明时出错 + + + + QDtls + + Invalid (empty) secret + 无效 (空) 的加密 + + + Multicast and broadcast addresses are not supported + 不支持多播和广播地址 + + + Cannot set peer after handshake started + 握手开始后无法设置对等机 + + + Invalid address + 无效地址 + + + Cannot set verification name after handshake started + 握手开始后无法设置验证名称 + + + Cannot set configuration after handshake started + 握手开始后无法设置配置 + + + Cannot start/continue handshake, invalid handshake state + 无法 启动/继续 握手,握手状态无效 + + + Invalid (nullptr) socket + 无效 (nullptr) 套接字 + + + To start a handshake you must set peer's address and port first + 要开始握手,必须先设置对等机的地址和端口 + + + To start a handshake, DTLS server requires non-empty datagram (client hello) + 要开始握手,DTLS 服务器需要非空数据报 (客户端 hello) + + + Cannot start handshake, already done/in progress + 无法启动握手,已经完成/正在进行中 + + + A valid QUdpSocket and non-empty datagram are needed to continue the handshake + 需要有效的 QUdpSocket 和非空数据报来继续握手 + + + Cannot continue handshake, not in InProgress state + 无法继续握手,未处于 InProgress 状态 + + + Cannot resume, not in VerificationError state + 无法恢复,不在 VerificationError 状态 + + + No handshake in progress, nothing to abort + 没有握手正在进行中,没有什么可以中止的 + + + Cannot send shutdown alert, not encrypted + 无法发送关机警报,未加密 + + + Cannot write a datagram, not in encrypted state + 无法写入数据报,未处于加密状态 + + + Cannot read a datagram, not in encrypted state + 无法读取数据报,未处于加密状态 + + + %1 failed + %1: Some function + %1 失败 + + + Invalid SslMode, SslServerMode or SslClientMode expected + 无效的 SslMode,应为 SslServerMode 或 SslClientMode + + + Invalid protocol version, DTLS protocol expected + 协议版本无效,应为 DTLS 协议 + + + BIO_ADD_new failed, cannot start handshake + BIO_ADD_new 失败,无法启动握手 + + + Cannot start the handshake, verified client hello expected + 无法启动握手,需要验证客户端 hello + + + Peer verification failed + 对等验证失败 + + + The DTLS connection has been closed + DTLS 连接已关闭 + + + Error while writing: %1 + 写入时出错:%1 + + + The DTLS connection has been shutdown + DTLS 连接已断开 + + + Error while reading: %1 + 读取时出错:%1 + + + + QDtlsClientVerifier + + A valid UDP socket, non-empty datagram, valid address/port were expected + 需要有效的 UDP 套接字、非空数据报和有效的地址/端口 + + + BIO_ADDR_new failed, ignoring client hello + BIO_ADDR_new 失败,忽略客户端 hello + + + + QErrorMessage + + Debug Message: + 调试消息: + + + Warning: + 警告: + + + Critical Error: + 严重错误: + + + Fatal Error: + 致命错误: + + + Information: + 信息: + + + &Show this message again + 再次显示此消息(&S) + + + &OK + 确定(&O) + + + + QFile + + Destination file is the same file. + 目标文件是同一个文件。 + + + Source file does not exist. + 源文件不存在。 + + + Destination file exists + 目标文件已存在 + + + Error while renaming: %1 + 重命名时出错:%1 + + + Unable to restore from %1: %2 + 无法从 %1 恢复:%2 + + + Will not rename sequential file using block copy + 将不使用块复制重命名序列文件 + + + Cannot remove source file + 无法删除源文件 + + + Cannot open destination file: %1 + 无法打开目标文件:%1 + + + Cannot open %1 for input + 无法打开 %1 进行输入 + + + Cannot open for output: %1 + 无法打开进行输出:%1 + + + Cannot open for output + 无法打开以进行输出 + + + Failure to write block + 写入块失败 + + + Cannot create %1 for output + 无法创建 %1 进行输出 + + + + QFileDevice + + No file engine available or engine does not support UnMapExtension + 无可用文件引擎或引擎不支持 UnMapExtension + + + No file engine available + 无可用文件引擎 + + + + QFileDialog + + All Files (*) + 所有文件 (*) + + + Look in: + 查找范围: + + + Back + 后退 + + + Go back + 后退 + + + Alt+Left + Alt+左方向键 + + + Forward + 前进 + + + Go forward + 前进 + + + Alt+Right + Alt+右方向键 + + + Parent Directory + 父目录 + + + Go to the parent directory + 转到父目录 + + + Alt+Up + Alt+上方向键 + + + Create New Folder + 新建文件夹 + + + Create a New Folder + 新建一个文件夹 + + + List View + 列表视图 + + + Change to list view mode + 更改为列表视图模式 + + + Detail View + 详情视图 + + + Change to detail view mode + 更改为详情视图模式 + + + Sidebar + 侧边栏 + + + List of places and bookmarks + 位置和书签列表 + + + Files + 文件 + + + Files of type: + 文件类型: + + + Find Directory + 查找目录 + + + Open + 打开 + + + Save As + 另存为 + + + Directory: + 目录: + + + File &name: + 文件名称(&N): + + + &Open + 打开(&O) + + + &Choose + 选择(&C) + + + &Save + 保存(&S) + + + Show + 显示 + + + &Rename + 重命名(&R) + + + &Delete + 删除(&D) + + + Show &hidden files + 显示隐藏文件(&H) + + + &New Folder + 新建文件夹(&N) + + + All files (*) + 所有文件 (*) + + + Directories + 目录 + + + %1 +Directory not found. +Please verify the correct directory name was given. + %1 +目录未找到。 +请检查指定的目录名是否正确。 + + + %1 already exists. +Do you want to replace it? + %1 已存在。 +您想要替换它吗? + + + %1 +File not found. +Please verify the correct file name was given. + %1 +文件未找到。 +请检查指定的文件名是否正确。 + + + New Folder + 新建文件夹 + + + Delete + 删除 + + + '%1' is write protected. +Do you want to delete it anyway? + “%1”已被写入保护。 +您仍然想要删除它吗? + + + Are you sure you want to delete '%1'? + 您确定要删除“%1”吗? + + + Could not delete directory. + 无法删除目录。 + + + Recent Places + 最近位置 + + + Remove + 移除 + + + My Computer + 我的电脑 + + + Drive + 驱动器 + + + %1 File + %1 is a file name suffix, for example txt + %1 文件 + + + File + 文件 + + + File Folder + Match Windows Explorer + 文件夹 + + + Folder + All other platforms + 文件夹 + + + Alias + OS X Finder + 别名 + + + Shortcut + All other platforms + 快捷方式 + + + Unknown + 未知 + + + + QFileSystemModel + + <b>The name "%1" cannot be used.</b><p>Try using another name, with fewer characters or no punctuation marks. + <b>文件名“%1”不可使用。</b><p>请尝试其他文件名,减少字数或避免使用标点符号。 + + + Invalid filename + 无效的文件名 + + + Name + 名称 + + + Size + 体积 + + + Kind + Match OS X Finder + 类型 + + + Type + All other platforms + 类型 + + + Date Modified + 修改日期 + + + My Computer + 我的电脑 + + + Computer + 计算机 + + + + QFontDatabase + + Normal + The Normal or Regular font weight + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 标准 (Normal) + + + Bold + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 粗体 (Bold) + + + Demi Bold + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 次粗 (Demi Bold) + + + Medium + The Medium font weight + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 适中 (Medium) + + + Black + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 浓体 (Black) + + + Light + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 细体 (Light) + + + Thin + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 淡体 (Thin) + + + Extra Light + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 特细 (Extra Light) + + + Extra Bold + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 特粗 (Extra Bold) + + + Extra + The word for "Extra" as in "Extra Bold, Extra Thin" used as a pattern for string searches + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 特 (Extra) + + + Demi + The word for "Demi" as in "Demi Bold" used as a pattern for string searches + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 次 (Demi) + + + Italic + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 斜体 (Italic) + + + Oblique + 按照字体教材翻译修订,保留英文是因为西文字体自带的风格只有英文命名,如果只有中文,核对时将异常困难。 + 伪斜体 (Oblique) + + + Any + 任意 + + + Latin + 按照 unicode 码表标准名称统一译名 + 拉丁文 + + + Greek + 按照 unicode 码表标准名称统一译名 + 希腊文 + + + Cyrillic + 按照 unicode 码表标准名称统一译名 + 西里尔字母 + + + Armenian + 按照 unicode 码表标准名称统一译名 + 亚美尼亚字母 + + + Hebrew + 按照 unicode 码表标准名称统一译名 + 希伯来文字母 + + + Arabic + 按照 unicode 码表标准名称统一译名 + 阿拉伯文字母 + + + Syriac + 按照 unicode 码表标准名称统一译名 + 叙利亚字母 + + + Thaana + 按照 unicode 码表标准名称统一译名 + 它拿字母 + + + Devanagari + 按照 unicode 码表标准名称统一译名 + 天城文 + + + Bengali + 按照 unicode 码表标准名称统一译名 + 孟加拉文 + + + Gurmukhi + 按照 unicode 码表标准名称统一译名 + 古木基文 + + + Gujarati + 按照 unicode 码表标准名称统一译名 + 古吉拉特文 + + + Oriya + 按照 unicode 码表标准名称统一译名 + 奥里亚文 + + + Tamil + 按照 unicode 码表标准名称统一译名 + 泰米尔文 + + + Telugu + 按照 unicode 码表标准名称统一译名 + 泰卢固文 + + + Kannada + 按照 unicode 码表标准名称统一译名 + 卡纳达文 + + + Malayalam + 马拉亚拉姆文 + + + Sinhala + 僧伽罗文 + + + Thai + 泰文 + + + Lao + 寮文 + + + Tibetan + 藏文 + + + Myanmar + 缅甸文 + + + Georgian + 格鲁吉亚文 + + + Khmer + 高棉文 + + + Simplified Chinese + 简体中文 + + + Traditional Chinese + 繁体中文 + + + Japanese + 日文 + + + Korean + 韩文 + + + Vietnamese + 越南文 + + + Symbol + 符号 + + + Ogham + 欧甘字母 + + + Runic + 卢恩字母 + + + N'Ko + 西非书面语 + + + + QFontDialog + + Select Font + 选择字体 + + + &Font + 字体(&F) + + + Font st&yle + 字体风格(&Y) + + + &Size + 大小(&S) + + + Effects + 效果 + + + Stri&keout + 删除线(&K) + + + &Underline + 下划线(&U) + + + Sample + 示例 + + + Wr&iting System + 按照 unicode 码表标准名称统一译名 + 文字系统(&I) + + + + QFtp + + Not connected + 未连接 + + + Host %1 not found + 主机 %1 没有找到 + + + Connection refused to host %1 + 连接被主机 %1 拒绝 + + + Connection timed out to host %1 + 主机 %1 连接超时 + + + Connected to host %1 + 连接到主机 %1 + + + Data Connection refused + 数据连接被拒绝 + + + Unknown error + 未知错误 + + + Connecting to host failed: +%1 + 连接主机失败: +%1 + + + Login failed: +%1 + 登录失败: +%1 + + + Listing directory failed: +%1 + 列出目录失败: +%1 + + + Changing directory failed: +%1 + 改变目录失败: +%1 + + + Downloading file failed: +%1 + 下载文件失败: +%1 + + + Uploading file failed: +%1 + 上传文件失败: +%1 + + + Removing file failed: +%1 + 移除文件失败: +%1 + + + Creating directory failed: +%1 + 创建目录失败: +%1 + + + Removing directory failed: +%1 + 移除目录失败: +%1 + + + Connection closed + 连接已关闭 + + + + QGnomeTheme + + &OK + 确定(&O) + + + &Save + 保存(&S) + + + &Cancel + 取消(&C) + + + &Close + 关闭(&C) + + + Close without Saving + 关闭且不保存 + + + + QGuiApplication + + QT_LAYOUT_DIRECTION + Translate this string to the string 'LTR' in left-to-right languages or to 'RTL' in right-to-left languages (such as Hebrew and Arabic) to get proper widget layout. + LTR + + + QPA plugin. See QGuiApplication documentation for available options for each plugin. + QPA 插件。查看 QGuiApplication 文档获取每个插件的可用选项。 + + + Path to the platform plugins. + 平台插件的路径。 + + + Platform theme. + 平台主题。 + + + Additional plugins to load, can be specified multiple times. + 要加载的额外插件,可被多次指定。 + + + Window geometry for the main window, using the X11-syntax, like 100x100+50+50. + 主窗口的窗口尺寸,使用 X11 语法,例如 100x100+50+50。 + + + Default window icon. + 默认窗口图标。 + + + Title of the first window. + 第一个窗口的标题。 + + + Sets the application's layout direction to Qt::RightToLeft (debugging helper). + 设置应用程序的布局方向到 Qt::RightToLeft (调试器辅助工具)。 + + + Restores the application from an earlier session. + 从先前会话恢复应用程序。 + + + Display name, overrides $DISPLAY. + 显示名称,覆盖$DISPLAY。 + + + Instance name according to ICCCM 4.1.2.5. + 依照 ICCCM 4.1.2.5 规范的示例名称。 + + + Disable mouse grabbing (useful in debuggers). + 禁用鼠标抓取 (在调试器中有用)。 + + + Force mouse grabbing (even when running in a debugger). + 强制鼠标抓取 (即使运行在调试器中)。 + + + ID of the X11 Visual to use. + 要使用的 X11 视觉 ID。 + + + Alias for --windowgeometry. + --windowgeometry 的别名。 + + + Alias for --windowicon. + --windowicon 的别名。 + + + Alias for --windowtitle. + --windowtitle 的别名。 + + + + QHostInfo + + No host name given + 未指定主机名 + + + Unknown error + 未知错误 + + + + QHostInfoAgent + + No host name given + 未指定主机名 + + + Invalid hostname + 无效的主机名 + + + Unknown address type + 未知的地址类型 + + + Host not found + 主机未找到 + + + Unknown error (%1) + 未知错误(%1) + + + + QHttp + + Connection closed + 连接已关闭 + + + Host %1 not found + 主机 %1 没有找到 + + + Connection refused + 连接被拒绝 + + + Proxy requires authentication + 代理需要验证 + + + Host requires authentication + 主机需要验证 + + + Data corrupted + 数据错误 + + + Unknown protocol specified + 所指定的协议是未知的 + + + SSL handshake failed + SSL 握手失败 + + + Too many redirects + 重定向太多 + + + Insecure redirect + 不安全的重定向 + + + + QHttpSocketEngine + + Did not receive HTTP response from proxy + 未收到代理的 HTTP 响应 + + + Error parsing authentication request from proxy + 解析来自代理的身份验证请求时出错 + + + Authentication required + 需要身份验证 + + + Proxy denied connection + 代理拒绝连接 + + + Error communicating with HTTP proxy + 和 HTTP 代理通讯时发生错误 + + + Proxy server not found + 未找到代理服务器 + + + Proxy connection refused + 代理连接被拒绝 + + + Proxy server connection timed out + 代理服务器连接超时 + + + Proxy connection closed prematurely + 代理连接过早关闭 + + + + QIBaseDriver + + Error opening database + 打开数据库出错 + + + Could not start transaction + 无法开始事务 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + + QIBaseResult + + Unable to create BLOB + 无法创建 BLOB + + + Unable to write BLOB + 无法写入 BLOB + + + Unable to open BLOB + 无法打开 BLOB + + + Unable to read BLOB + 无法读取 BLOB + + + Could not find array + 无法找到数组 + + + Could not get array data + 无法获取数组数据 + + + Could not get query info + 无法获取查询信息 + + + Could not start transaction + 无法开始事务 + + + Unable to commit transaction + 无法提交事务 + + + Could not allocate statement + 无法分配语句 + + + Could not prepare statement + 无法准备语句 + + + Could not describe input statement + 无法描述输入语句 + + + Could not describe statement + 无法描述语句 + + + Unable to close statement + 无法关闭语句 + + + Unable to execute query + 无法执行查询 + + + Could not fetch next item + 无法获取下一项 + + + Could not get statement info + 无法得到语句信息 + + + + QIODevice + + file to open is a directory + 要打开的文件是一个目录 + + + Unknown error + 未知错误 + + + Permission denied + 权限被拒绝 + + + Too many open files + 打开的文件太多 + + + No such file or directory + 此文件或目录不存在 + + + No space left on device + 设备中已没有剩余空间 + + + + QImageReader + + Invalid device + 无效的设备 + + + File not found + 文件未找到 + + + Unsupported image format + 不支持的图像格式 + + + Unable to read image data + 无法读取图像数据 + + + Unknown error + 未知错误 + + + + QImageWriter + + Unknown error + 未知错误 + + + Device is not set + 设备未设置 + + + Cannot open device for writing: %1 + 无法打开设备进行写入:%1 + + + Device not writable + 设备不可写入 + + + Unsupported image format + 不支持的图像格式 + + + Image is empty + 图像为空 + + + + QInputDialog + + Enter a value: + 输入一个值: + + + + QJsonParseError + + no error occurred + 没有错误发生 + + + unterminated object + 未终止的对象 + + + missing name separator + 缺少名称分隔符 + + + unterminated array + 未终止的数组 + + + missing value separator + 缺失值分隔符 + + + illegal value + 非法值 + + + invalid termination by number + 无效的编号终止 + + + illegal number + 非法数字 + + + invalid escape sequence + 无效的转义序列 + + + invalid UTF8 string + 无效的 UTF8 字符串 + + + unterminated string + 未终止的字符串 + + + object is missing after a comma + 逗号后缺少对象 + + + too deeply nested document + 文件嵌套太深 + + + too large document + 文件太大 + + + garbage at the end of the document + 文件末尾的垃圾 + + + + QKeySequenceEdit + + Press shortcut + 按快捷方式 + + + %1, ... + This text is an "unfinished" shortcut, expands like "Ctrl+A, ..." + %1, ... + + + + QLibrary + + '%1' is not an ELF object (%2) + “%1”不是 ELF 对象 (%2) + + + file too small + 文件太小 + + + '%1' is not an ELF object + “%1”不是 ELF 对象 + + + '%1' is an invalid ELF object (%2) + “%1”是一个无效的 ELF 对象 (%2) + + + odd cpu architecture + 奇数 CPU 架构 + + + wrong cpu architecture + 错误的 CPU 架构 + + + odd endianness + 奇字节序 + + + unexpected e_shsize + 异常的 e_shsize + + + unexpected e_shentsize + 异常的 e_shentsize + + + announced %n section(s), each %1 byte(s), exceed file size + + 声明了 %n 个区段,每个 %1 个字节,超出了文件大小 + + + + shstrtab section header seems to be at %1 + shstrtab 区段头似乎位于 %1 + + + string table seems to be at %1 + 字符串表似乎位于 %1 + + + section name %1 of %2 behind end of file + 文件尾后面的第 %1 区段 (共 %2 区段) + + + empty .rodata. not a library. + 空.rodata.不是库。 + + + missing section data. This is not a library. + 缺少区段数据。这不是一个库。 + + + Failed to extract plugin meta data from '%1' + 无法从“%1”中提取插件元数据 + + + The shared library was not found. + 共享库没有被找到。 + + + The file '%1' is not a valid Qt plugin. + 文件“%1”不是有效的 Qt 插件。 + + + The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5] + 插件“%1”使用了不兼容的 Qt 库。(%2.%3.%4) [%5] + + + The plugin '%1' uses incompatible Qt library. (Cannot mix debug and release libraries.) + 插件“%1”使用了不兼容的 Qt 库。(不能混合使用库的调试版本和发布版本。) + + + Unknown error + 未知错误 + + + Cannot load library %1: %2 + 无法加载库 %1:%2 + + + Cannot unload library %1: %2 + 无法卸载库 %1:%2 + + + Cannot resolve symbol "%1" in %2: %3 + 无法解析 %2 中的符号“%1”:%3 + + + '%1' is not a valid Mach-O binary (%2) + “%1”不是有效的 Mach-O 二进制文件 (%2) + + + file is corrupt + 文件已损坏 + + + no suitable architecture in fat binary + FAT 二进制没有合适的架构 + + + invalid magic %1 + 无效魔数 %1 + + + wrong architecture + 错误的架构 + + + not a dynamic library + 不是动态库 + + + '%1' is not a Qt plugin + “%1” 不是 Qt 插件 + + + + QLineEdit + + &Undo + 撤消(&U) + + + &Redo + 重做(&R) + + + Cu&t + 剪切(&T) + + + &Copy + 复制(&C) + + + &Paste + 粘贴(&P) + + + Delete + 删除 + + + Select All + 全选 + + + + QLocalServer + + %1: Name error + %1:名称错误 + + + %1: Permission denied + %1:权限被拒绝 + + + %1: Address in use + %1:地址正在被使用 + + + %1: Unknown error %2 + %1:未知错误 %2 + + + + QLocalSocket + + %1: Connection refused + %1:连接被拒绝 + + + %1: Remote closed + %1:远程已关闭 + + + %1: Invalid name + %1:无效名称 + + + %1: Socket access error + %1:套接字访问错误 + + + %1: Socket resource error + %1:套接字资源错误 + + + %1: Socket operation timed out + %1:套接字操作超时 + + + %1: Datagram too large + %1:数据报太大 + + + %1: Connection error + %1:连接错误 + + + %1: The socket operation is not supported + %1:套接字操作不被支持 + + + %1: Operation not permitted when socket is in this state + %1:当套接字处于这种状态时不允许操作 + + + %1: Unknown error + %1:未知错误 + + + Trying to connect while connection is in progress + 在连接正在进行时尝试连接 + + + %1: Unknown error %2 + %1:未知错误 %2 + + + %1: Access denied + %1:访问被拒绝 + + + Socket is not connected + 套接字未连接 + + + + QMYSQLDriver + + Unable to allocate a MYSQL object + 无法分配 MySQL 对象 + + + Unable to open database '%1' + 无法打开数据库“%1” + + + Unable to connect + 无法连接 + + + Unable to begin transaction + 无法开始事务 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + + QMYSQLResult + + Unable to fetch data + 无法获取数据 + + + Unable to execute query + 无法执行查询 + + + Unable to store result + 无法存储结果 + + + Unable to execute next query + 无法执行下一个查询 + + + Unable to store next result + 无法存储下一个结果 + + + Unable to prepare statement + 无法准备语句 + + + Unable to reset statement + 无法重置语句 + + + Unable to bind value + 无法绑定值 + + + Unable to execute statement + 无法执行语句 + + + Unable to bind outvalues + 无法绑定外值 + + + Unable to store statement results + 无法存储语句结果 + + + + QMdiArea + + (Untitled) + (无标题) + + + + QMdiSubWindow + + - [%1] + - [%1] + + + %1 - [%2] + %1 - [%2] + + + Minimize + 最小化 + + + Maximize + 最大化 + + + Unshade + 根据我翻译窗口管理器时的经验来看,可能是点击后将窗体缩成只有一个标题栏的操作。Shade 指的是百叶窗,模仿百叶窗收起。 + 解除卷起 + + + Shade + 根据我翻译窗口管理器时的经验来看,可能是点击后将窗体缩成只有一个标题栏的操作。Shade 指的是百叶窗,模仿百叶窗收起。 + 卷起 + + + Restore Down + 向下恢复 + + + Restore + 恢复 + + + Close + 关闭 + + + Help + 帮助 + + + Menu + 菜单 + + + &Restore + 恢复(&R) + + + &Move + 移动(&M) + + + &Size + 大小(&S) + + + Mi&nimize + 最小化(&N) + + + Ma&ximize + 最大化(&X) + + + Stay on &Top + 置顶(&T) + + + &Close + 关闭(&C) + + + + QMessageBox + + Show Details... + 显示详情... + + + Hide Details... + 隐藏详情... + + + <h3>About Qt</h3><p>This program uses Qt version %1.</p> + <h3>关于 Qt</h3><p>此程序使用 Qt 版本 %1。</p> + + + <p>Qt is a C++ toolkit for cross-platform application development.</p><p>Qt provides single-source portability across all major desktop operating systems. It is also available for embedded Linux and other embedded and mobile operating systems.</p><p>Qt is available under multiple licensing options designed to accommodate the needs of our various users.</p><p>Qt licensed under our commercial license agreement is appropriate for development of proprietary/commercial software where you do not want to share any source code with third parties or otherwise cannot comply with the terms of GNU (L)GPL.</p><p>Qt licensed under GNU (L)GPL is appropriate for the development of Qt&nbsp;applications provided you can comply with the terms and conditions of the respective licenses.</p><p>Please see <a href="http://%2/">%2</a> for an overview of Qt licensing.</p><p>Copyright (C) %1 The Qt Company Ltd and other contributors.</p><p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p><p>Qt is The Qt Company Ltd product developed as an open source project. See <a href="http://%3/">%3</a> for more information.</p> + Leave this text untranslated or include a verbatim copy of it below and note that it is the authoritative version in case of doubt. + 此段文本原本已被翻译为中文,并在此次提交前进行过改进,但在此次提交的讨论中认为这是法律文本,且原有的翻译从未经过 Qt Company 审核,是不安全的,因此决定不作翻译。 + <p>Qt is a C++ toolkit for cross-platform application development.</p><p>Qt provides single-source portability across all major desktop operating systems. It is also available for embedded Linux and other embedded and mobile operating systems.</p><p>Qt is available under multiple licensing options designed to accommodate the needs of our various users.</p><p>Qt licensed under our commercial license agreement is appropriate for development of proprietary/commercial software where you do not want to share any source code with third parties or otherwise cannot comply with the terms of GNU (L)GPL.</p><p>Qt licensed under GNU (L)GPL is appropriate for the development of Qt&nbsp;applications provided you can comply with the terms and conditions of the respective licenses.</p><p>Please see <a href="http://%2/">%2</a> for an overview of Qt licensing.</p><p>Copyright (C) %1 The Qt Company Ltd and other contributors.</p><p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p><p>Qt is The Qt Company Ltd product developed as an open source project. See <a href="http://%3/">%3</a> for more information.</p> + + + About Qt + 关于 Qt + + + + QNativeSocketEngine + + Unable to initialize non-blocking socket + 无法初始化非阻塞套接字 + + + Unable to initialize broadcast socket + 无法初始化广播套接字 + + + Attempt to use IPv6 socket on a platform with no IPv6 support + 试图在不支持 IPv6 支持的平台上使用 IPv6 套接字 + + + The remote host closed the connection + 远端主机关闭了这个连接 + + + Network operation timed out + 网络操作超时 + + + Out of resources + 资源不足 + + + Unsupported socket operation + 不被支持的套接字操作 + + + Protocol type not supported + 协议类型不被支持 + + + Invalid socket descriptor + 无效的套接字描述符 + + + Host unreachable + 主机无法访问 + + + Network unreachable + 网络无法访问 + + + Permission denied + 权限被拒绝 + + + Connection timed out + 连接超时 + + + Connection refused + 连接被拒绝 + + + The bound address is already in use + 要启用的地址已经被使用 + + + The address is not available + 这个地址不可用 + + + The address is protected + 这个地址被保护了 + + + Datagram was too large to send + 数据报太大无法发送 + + + Unable to send a message + 无法发送一个消息 + + + Unable to receive a message + 无法接收一个消息 + + + Unable to write + 无法写入 + + + Network error + 网络错误 + + + Another socket is already listening on the same port + 另一个套接字正在监听同一端口 + + + Operation on non-socket + 对非套接字操作 + + + The proxy type is invalid for this operation + 对于这个操作代理类型是无效的 + + + Temporary error + 暂时的错误 + + + Network dropped connection on reset + 重置时网络断开连接 + + + Connection reset by peer + 对方将连接复位 + + + Unknown error + 未知错误 + + + + QNetworkAccessCacheBackend + + Error opening %1 + 打开 %1 发生错误 + + + + QNetworkAccessDataBackend + + Invalid URI: %1 + 无效的 URI:%1 + + + + QNetworkAccessDebugPipeBackend + + Write error writing to %1: %2 + 写入 %1 错误:%2 + + + Socket error on %1: %2 + %1 上的套接字错误:%2 + + + Remote host closed the connection prematurely on %1 + 远程主机过早地关闭了在 %1 上的这个连接 + + + + QNetworkAccessFileBackend + + Request for opening non-local file %1 + 正在打开非本地文件 %1 的请求 + + + Error opening %1: %2 + 打开 %1 错误:%2 + + + Write error writing to %1: %2 + 写入 %1 错误:%2 + + + Cannot open %1: Path is a directory + 无法打开 %1:路径是一个目录 + + + Read error reading from %1: %2 + 读取 %1 错误:%2 + + + + QNetworkAccessFtpBackend + + No suitable proxy found + 未找到合适的代理 + + + Cannot open %1: is a directory + 无法读取 %1:是一个目录 + + + Logging in to %1 failed: authentication required + 登录 %1 失败:需要身份验证 + + + Error while downloading %1: %2 + 下载 %1 时错误:%2 + + + Error while uploading %1: %2 + 上传 %1 时错误:%2 + + + + QNetworkAccessManager + + Network access is disabled. + 网络访问已禁用。 + + + + QNetworkReply + + Error transferring %1 - server replied: %2 + 传输 %1 时出错 - 服务器已回复:%2 + + + Network session error. + 网络会话错误。 + + + Background request not allowed. + 不允许后台请求。 + + + backend start error. + 后端启动错误。 + + + Temporary network failure. + 临时网络故障。 + + + Protocol "%1" is unknown + 协议“%1”是未知的 + + + + QNetworkReplyHttpImpl + + Operation canceled + 操作被取消 + + + No suitable proxy found + 未找到合适的代理 + + + + QNetworkReplyImpl + + Operation canceled + 操作被取消 + + + + QNetworkSession + + Invalid configuration. + 无效的配置。 + + + + QNetworkSessionPrivateImpl + + Unknown session error. + 未知的会话错误。 + + + The session was aborted by the user or system. + 会话被用户或系统中止。 + + + The requested operation is not supported by the system. + 系统不支持请求的操作。 + + + The specified configuration cannot be used. + 无法使用指定的配置。 + + + Roaming was aborted or is not possible. + 漫游已中止或无法进行。 + + + + QOCIDriver + + Unable to initialize + QOCIDriver + 无法初始化 + + + Unable to logon + 无法登录 + + + Unable to begin transaction + 无法开始事务 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + + QOCIResult + + Unable to bind column for batch execute + 无法绑定批处理执行的列 + + + Unable to execute batch statement + 无法执行批处理语句 + + + Unable to goto next + 无法进入下一个 + + + Unable to alloc statement + 无法分配语句 + + + Unable to prepare statement + 无法准备语句 + + + Unable to get statement type + 无法获取语句类型 + + + Unable to bind value + 无法绑定值 + + + Unable to execute statement + 无法执行语句 + + + + QODBCDriver + + Unable to connect + 无法连接 + + + Unable to connect - Driver doesn't support all functionality required + 无法连接 - 驱动程序不支持所有必需的功能 + + + Unable to disable autocommit + 无法禁用自动提交 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + Unable to enable autocommit + 无法启用自动提交 + + + + QODBCResult + + QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration + QODBCResult::reset: 无法将“SQL_CURSOR_STATIC”设置为语句属性。请检查您的ODBC驱动程序设置 + + + Unable to execute statement + 无法执行语句 + + + Unable to fetch + 无法获取 + + + Unable to fetch next + 无法获取下一个 + + + Unable to fetch first + 无法获取第一个 + + + Unable to fetch previous + 无法获取上一个 + + + Unable to fetch last + 无法获取最后一个 + + + Unable to prepare statement + 无法准备语句 + + + Unable to bind variable + 无法绑定变量 + + + + QPSQLDriver + + Unable to connect + 无法连接 + + + Could not begin transaction + 无法开始事务 + + + Could not commit transaction + 无法提交事务 + + + Could not rollback transaction + 无法回滚事务 + + + Unable to subscribe + 无法订阅 + + + Unable to unsubscribe + 无法取消订阅 + + + + QPSQLResult + + Query results lost - probably discarded on executing another SQL query. + 查询结果丢失 - 可能在执行另一个 SQL 查询时被丢弃。 + + + Unable to create query + 无法创建查询 + + + Unable to get result + 无法获取结果 + + + Unable to send query + 无法发送查询 + + + Unable to prepare statement + 无法准备语句 + + + + QPageSetupWidget + + Form + 窗体 + + + Paper + 纸张 + + + Page size: + 纸张大小: + + + Width: + 宽度: + + + Height: + 高度: + + + Paper source: + 纸张来源: + + + Orientation + 方向 + + + Portrait + 纵向 + + + Landscape + 横向 + + + Reverse landscape + 横向倒转 + + + Reverse portrait + 纵向倒转 + + + Margins + 边距 + + + top margin + 上边距 + + + left margin + 左边距 + + + right margin + 右边距 + + + bottom margin + 下边距 + + + Page Layout + 页面布局 + + + Page order: + 页面顺序: + + + Pages per sheet: + 每张页数: + + + Millimeters (mm) + 毫米 (mm) + + + Inches (in) + 英寸 (in) + + + Points (pt) + 点 (pt) + + + Pica (P̸) + 派卡 (P̸) + + + Didot (DD) + 迪多 (DD) + + + Cicero (CC) + 西塞罗 (CC) + + + Custom + 自定义 + + + mm + Unit 'Millimeter' + mm + + + pt + Unit 'Points' + pt + + + in + Unit 'Inch' + in + + + P̸ + Unit 'Pica' + + + + DD + Unit 'Didot' + DD + + + CC + Unit 'Cicero' + CC + + + + QPageSize + + Custom (%1mm x %2mm) + Custom size name in millimeters + 自定义 (%1mm x %2mm) + + + Custom (%1pt x %2pt) + Custom size name in points + 自定义 (%1pt x %2pt) + + + Custom (%1in x %2in) + Custom size name in inches + 自定义 (%1in x %2in) + + + Custom (%1pc x %2pc) + Custom size name in picas + 自定义 (%1pc x %2pc) + + + Custom (%1DD x %2DD) + Custom size name in didots + 自定义 (%1DD x %2DD) + + + Custom (%1CC x %2CC) + Custom size name in ciceros + 自定义 (%1CC x %2CC) + + + %1 x %2 in + Page size in 'Inch'. + %1 x %2 in + + + A0 + A0 + + + A1 + A1 + + + A2 + A2 + + + A3 + A3 + + + A4 + A4 + + + A5 + A5 + + + A6 + A6 + + + A7 + A7 + + + A8 + A8 + + + A9 + A9 + + + A10 + A10 + + + B0 + B0 + + + B1 + B1 + + + B2 + B2 + + + B3 + B3 + + + B4 + B4 + + + B5 + B5 + + + B6 + B6 + + + B7 + B7 + + + B8 + B8 + + + B9 + B9 + + + B10 + B10 + + + Executive (7.5 x 10 in) + 美国行政 (Executive) (7.5 x 10 in) + + + Executive (7.25 x 10.5 in) + 美国行政 (Executive) (7.25 x 10.5 in) + + + Folio (8.27 x 13 in) + 欧洲对开 (Folio) (8.27 x 13 in) + + + Legal + 美国法律 (Legal) + + + Letter / ANSI A + 美国信纸 (Letter) / ANSI A + + + Tabloid / ANSI B + 美国小报 (Tabloid) / ANSI B + + + Ledger / ANSI B + 美国账簿 (Ledger) / ANSI B + + + Custom + 自定义 + + + A3 Extra + A3 加大 + + + A4 Extra + A4 加大 + + + A4 Plus + A4 超大 + + + A4 Small + A4 小号 + + + A5 Extra + A5 加大 + + + B5 Extra + B5 加大 + + + JIS B0 + JIS B0 + + + JIS B1 + JIS B1 + + + JIS B2 + JIS B2 + + + JIS B3 + JIS B3 + + + JIS B4 + JIS B4 + + + JIS B5 + JIS B5 + + + JIS B6 + JIS B6 + + + JIS B7 + JIS B7 + + + JIS B8 + JIS B8 + + + JIS B9 + JIS B9 + + + JIS B10 + JIS B10 + + + ANSI C + ANSI C + + + ANSI D + ANSI D + + + ANSI E + ANSI E + + + Legal Extra + 美国法律 (Legal) 加大 + + + Letter Extra + 美国信纸 (Letter) 加大 + + + Letter Plus + 美国信纸 (Letter) 超大 + + + Letter Small + 美国信纸 (Letter) 小号 + + + Tabloid Extra + 美国小报 (Tabloid) 加大 + + + Architect A + 美国图纸 (Architect) A + + + Architect B + 美国图纸 (Architect) B + + + Architect C + 美国图纸 (Architect) C + + + Architect D + 美国图纸 (Architect) D + + + Architect E + 美国图纸 (Architect) E + + + Note + 美国笔记 (Note) + + + Quarto + 美国四分 (Quarto) + + + Statement + 美国声明 (Statement) + + + Super A + 美国相纸 (Super) A + + + Super B + 美国相纸 (Super) B + + + Postcard + 美国明信片 (Postcard) + + + Double Postcard + 美国明信片双倍 (Double Postcard) + + + PRC 16K + 中国大陆 16 开 + + + PRC 32K + 中国大陆 32 开 + + + PRC 32K Big + 中国大陆大 32 开 + + + Fan-fold US (14.875 x 11 in) + 美国连张 (Fan-fold) (14.875 x 11 in) + + + Fan-fold German (8.5 x 12 in) + 德国连张 (Fan-fold) (8.5 x 12 in) + + + Fan-fold German Legal (8.5 x 13 in) + 德国连张法律 (Fan-fold Legal) (8.5 x 13 in) + + + Envelope B4 + 国际信封 (Envelope) B4 + + + Envelope B5 + 国际信封 (Envelope) B5 + + + Envelope B6 + 国际信封 (Envelope) B6 + + + Envelope C0 + 国际信封 (Envelope) C0 + + + Envelope C1 + 国际信封 (Envelope) C1 + + + Envelope C2 + 国际信封 (Envelope) C2 + + + Envelope C3 + 国际信封 (Envelope) C3 + + + Envelope C4 + 国际信封 (Envelope) C4 + + + Envelope C5 + 国际信封 (Envelope) C5 + + + Envelope C6 + 国际信封 (Envelope) C6 + + + Envelope C65 + 国际信封 (Envelope) C65 + + + Envelope C7 + 国际信封 (Envelope) C7 + + + Envelope DL + 国际信封 (Envelope) DL + + + Envelope US 9 + 美国信封 (Envelope) 9 + + + Envelope US 10 + 美国信封 (Envelope) 10 + + + Envelope US 11 + 美国信封 (Envelope) 11 + + + Envelope US 12 + 美国信封 (Envelope) 12 + + + Envelope US 14 + 美国信封 (Envelope) 14 + + + Envelope Monarch + 美国信封君王 (Envelope Monarch) + + + Envelope Personal + 美国信封个人 (Envelope Personal) + + + Envelope Chou 3 + 日本信封长形 3 号 + + + Envelope Chou 4 + 日本信封长形 4 号 + + + Envelope Invite + 国际信封请柬 (Envelope Invite) + + + Envelope Italian + 国际信封意式 (Envelope Italian) + + + Envelope Kaku 2 + 日本信封角形 2 号 + + + Envelope Kaku 3 + 日本信封角形 3 号 + + + Envelope PRC 1 + 中国大陆信封 1 号 + + + Envelope PRC 2 + 中国大陆信封 2 号 + + + Envelope PRC 3 + 中国大陆信封 3 号 + + + Envelope PRC 4 + 中国大陆信封 4 号 + + + Envelope PRC 5 + 中国大陆信封 5 号 + + + Envelope PRC 6 + 中国大陆信封 6 号 + + + Envelope PRC 7 + 中国大陆信封 7 号 + + + Envelope PRC 8 + 中国大陆信封 8 号 + + + Envelope PRC 9 + 中国大陆信封 9 号 + + + Envelope PRC 10 + 中国大陆信封 10 号 + + + Envelope You 4 + 日本信封洋形 4 号 + + + + QPlatformTheme + + OK + 确定 + + + Save + 保存 + + + Save All + 全部保存 + + + Open + 打开 + + + &Yes + 是(&Y) + + + Yes to &All + 全是(&A) + + + &No + 否(&N) + + + N&o to All + 全否(&O) + + + Abort + 中止 + + + Retry + 重试 + + + Ignore + 忽略 + + + Close + 关闭 + + + Cancel + 取消 + + + Discard + 丢弃 + + + Help + 帮助 + + + Apply + 应用 + + + Reset + 重置 + + + Restore Defaults + 恢复默认值 + + + + QPluginLoader + + The plugin was not loaded. + 插件未被加载。 + + + Unknown error + 未知错误 + + + + QPrintDialog + + Print + 打印 + + + Left to Right, Top to Bottom + 从左到右,从上到下 + + + Left to Right, Bottom to Top + 从左到右,从下到上 + + + Right to Left, Bottom to Top + 从右到左,从下到上 + + + Right to Left, Top to Bottom + 从右到左,从上到下 + + + Bottom to Top, Left to Right + 从下到上,从左到右 + + + Bottom to Top, Right to Left + 从下到上,从右到左 + + + Top to Bottom, Left to Right + 从上到下,从左到右 + + + Top to Bottom, Right to Left + 从上到下,从右到左 + + + 1 (1x1) + 1 (1x1) + + + 2 (2x1) + 2 (2x1) + + + 4 (2x2) + 4 (2x2) + + + 6 (2x3) + 6 (2x3) + + + 9 (3x3) + 9 (3x3) + + + 16 (4x4) + 16 (4x4) + + + All Pages + 全部页面 + + + Odd Pages + 奇数页面 + + + Even Pages + 偶数页面 + + + &Options >> + 选项(&O) >> + + + &Print + 打印(&P) + + + &Options << + 选项(&O) << + + + Invalid Pages Definition + 页面定义无效 + + + %1 does not follow the correct syntax. Please use ',' to separate ranges and pages, '-' to define ranges and make sure ranges do not intersect with each other. + 区隔逗号是半角的,不能改成全角 + %1 不符合正确语法。请使用“,”来分隔范围和页面,“-”来定义范围,并确保不同范围不相互重叠。 + + + Duplex Settings Conflicts + 双面打印设置冲突 + + + There are conflicts in duplex settings. Do you want to fix them? + 双面打印设置存在冲突。您想要修复它们吗? + + + Print to File (PDF) + 打印到文件(PDF) + + + Local file + 本机文件 + + + Write PDF file + 写入 PDF 文件 + + + Print To File ... + 打印为文件... + + + %1 is a directory. +Please choose a different file name. + %1 是一个目录。 +请选择一个不同的文件名。 + + + File %1 is not writable. +Please choose a different file name. + 文件 %1 不可写入。 +请选择一个不同的文件名。 + + + %1 already exists. +Do you want to overwrite it? + %1 已存在。 +您想要覆盖它吗? + + + Options 'Pages Per Sheet' and 'Page Set' cannot be used together. +Please turn one of those options off. + “每张页数”和“页面组”选项不可同时使用。 +请关闭它们之中的其中一个。 + + + The 'From' value cannot be greater than the 'To' value. + “开始”数值不得大于“结束”数值。 + + + OK + 确定 + + + Automatic + 自动 + + + + QPrintPreviewDialog + + Page Setup + 页面设置 + + + %1% + %1% + + + Print Preview + 打印预览 + + + Next page + 下一页 + + + Previous page + 上一页 + + + First page + 第一页 + + + Last page + 最后一页 + + + Fit width + 适应宽度 + + + Fit page + 适应页面 + + + Zoom in + 放大 + + + Zoom out + 缩小 + + + Portrait + 纵向 + + + Landscape + 横向 + + + Show single page + 单页显示 + + + Show facing pages + 对页显示 + + + Show overview of all pages + 显示全部页面概览 + + + Print + 打印 + + + Page setup + 打印设置 + + + Export to PDF + 导出到 PDF + + + + QPrintPropertiesDialog + + Printer Properties + 打印机属性 + + + Job Options + 任务选项 + + + Page Setup Conflicts + 页面设置冲突 + + + There are conflicts in page setup options. Do you want to fix them? + 页面设置选项存在冲突。您想要修复它们吗? + + + Advanced Option Conflicts + 高级选项冲突 + + + There are conflicts in some advanced options. Do you want to fix them? + 高级选项存在冲突。您想要修复它们吗? + + + + QPrintPropertiesWidget + + Form + 窗体 + + + Page + + + + Advanced + 高级 + + + There are conflicts in some options. Please fix them. + 某些选项存在冲突。请修复它们。 + + + + QPrintSettingsOutput + + Form + 窗体 + + + Copies + 份数 + + + Print range + 打印范围 + + + Print all + 打印全部 + + + Pages from + 页数从 + + + to + + + + Pages + 页数 + + + Specify pages or ranges separated by commas. Ranges are specified by two numbers separated by a hyphen. E.g: 3,5-7,9 prints pages 3, 5, 6, 7 and 9. + 指定页面或范围,以半角逗号分隔。范围通过在两个数字之间插入半角连字号指定。例如:3,5-7,9 将打印 3、5、6、7、9 页。 + + + Current Page + 当前页面 + + + Selection + 选择 + + + Page Set: + 页面组: + + + Output Settings + 输出设置 + + + Copies: + 份数: + + + Collate + 逐份打印 + + + Reverse + 反向打印 + + + Options + 选项 + + + Color Mode + 彩色模式 + + + Color + 彩色 + + + Grayscale + 灰阶 + + + Duplex Printing + 双面打印 + + + None + + + + Long side + 长侧 + + + Short side + 短侧 + + + Double Sided Printing + 双面打印 + + + Off + 关闭 + + + Long side binding + 长边装订 + + + Short side binding + 短边装订 + + + + QPrintWidget + + Form + 窗体 + + + Printer + 打印机 + + + &Name: + 名称(&N): + + + P&roperties + 属性(&R) + + + Location: + 位置: + + + Preview + 预览 + + + Type: + 类型: + + + Output &file: + 输出文件(&F): + + + ... + + + + + QProcess + + Process failed to start + 启动进程失败 + + + Process crashed + 进程已崩溃 + + + Process operation timed out + 进程处理超时 + + + Error reading from process + 从进程中读取时发生错误 + + + Error writing to process + 向进程写入时发生错误 + + + No program defined + 没有定义程序 + + + Could not open input redirection for reading + 无法打开用于读取的输入重定向 + + + Resource error (fork failure): %1 + 资源错误 (fork 失败):%1 + + + Could not open output redirection for writing + 无法打开用于写入的输出重定向 + + + Process failed to start: %1 + 进程启动失败:%1 + + + + QProgressDialog + + Cancel + 取消 + + + + QRegExp + + no error occurred + 没有错误发生 + + + disabled feature used + 使用了禁用功能 + + + bad char class syntax + 错误的字符类语法 + + + bad lookahead syntax + 错误的预测语法 + + + lookbehinds not supported, see QTBUG-2371 + 不支持向后查看,请参阅 QTBUG-2371 + + + bad repetition syntax + 错误的重复语法 + + + invalid octal value + 无效的八进制数值 + + + missing left delim + 找不到左分隔符 + + + unexpected end + 异常的终止 + + + met internal limit + 遇到内部限制 + + + invalid interval + 无效的间隔 + + + invalid category + 无效的类别 + + + + QRegularExpression + + no error + 无错误 + + + \ at end of pattern + \ 在模式的末尾 + + + \c at end of pattern + \c 在模式的末尾 + + + unrecognized character follows \ + \ 后跟随未识别的字符 + + + numbers out of order in {} quantifier + {} 量词中的数字不正常 + + + number too big in {} quantifier + {} 量词中的数字太大了 + + + missing terminating ] for character class + 字符类缺少终止 ] + + + invalid escape sequence in character class + 字符类中的转义序列无效 + + + range out of order in character class + 字符类中的不正常范围 + + + quantifier does not follow a repeatable item + 量词不在可重复项之后 + + + internal error: unexpected repeat + 内部错误:异常的重复 + + + unrecognized character after (? or (?- + (? 或 (?- 之后未识别的字符 + + + POSIX named classes are supported only within a class + POSIX 命名类仅在类中受支持 + + + POSIX collating elements are not supported + 不支持 POSIX 排序元素 + + + missing closing parenthesis + 缺少右括号 + + + reference to non-existent subpattern + 引用不存在的子模式 + + + pattern passed as NULL + 模式作为 NULL 传递 + + + unrecognised compile-time option bit(s) + 无法识别的编译时选项位 + + + missing ) after (?# comment + (?# 注释 之后缺少 ) + + + parentheses are too deeply nested + 括号嵌套太深 + + + regular expression is too large + 正则表达式太大 + + + failed to allocate heap memory + 分配堆内存失败 + + + unmatched closing parenthesis + 右括号不匹配 + + + internal error: code overflow + 内部错误:代码溢出 + + + letter or underscore expected after (?< or (?' + (?< 或 (?' 之后应为字母或下划线 + + + lookbehind assertion is not fixed length + 后发断言不是固定的长度 + + + malformed number or name after (?( + (?( 之后格式错误的数字或名称 + + + conditional group contains more than two branches + 条件组包含两个以上的分支 + + + assertion expected after (?( or (?(?C) + (?( 或 (?(?C) 之后应为断言 + + + (?R or (?[+-]digits must be followed by ) + (?R 或 (?[+-]数字 必须后跟 ) + + + unknown POSIX class name + 未知的 POSIX 类名 + + + internal error in pcre2_study(): should not occur + pcre2_study() 中的内部错误:不应发生 + + + this version of PCRE2 does not have Unicode support + 此版本的 PCRE2 不支持 Unicode + + + parentheses are too deeply nested (stack check) + 括号嵌套太深( 堆栈检查) + + + character code point value in \x{} or \o{} is too large + \x{} 或 \o{} 中的字符代码点值太大 + + + invalid condition (?(0) + 无效条件 (?(0) + + + \C is not allowed in a lookbehind assertion + \C 不允许在“后发断言”中使用 + + + PCRE does not support \L, \l, \N{name}, \U, or \u + PCRE 不支持 \L,\l,\N{name},\U或 \u + + + number after (?C is greater than 255 + (?C 之后的数字大于 255 + + + closing parenthesis for (?C expected + (?C 需要右括号 + + + invalid escape sequence in (*VERB) name + (*VERB) 名称中的无效转义序列 + + + unrecognized character after (?P + (?P 之后无法识别的字符 + + + syntax error in subpattern name (missing terminator) + 子模式名称中的语法错误(缺少终止符) + + + two named subpatterns have the same name (PCRE2_DUPNAMES not set) + 两个命名的子模式具有相同的名称 (未设置 PCRE2_DUPNAMES) + + + group name must start with a non-digit + 组名必须以非数字开头 + + + this version of PCRE2 does not have support for \P, \p, or \X + 此版本的 PCRE2 不支持 \P,\p 或 \X + + + malformed \P or \p sequence + 格式错误的 \P 或 \p 序列 + + + unknown property name after \P or \p + \P 或 \p 之后的未知属性名称 + + + subpattern name is too long (maximum 10000 characters) + 子模式名称太长(最多10000个字符) + + + too many named subpatterns (maximum 256) + 命名子模式太多(最多256个) + + + invalid range in character class + 字符类中的范围无效 + + + octal value is greater than \377 in 8-bit non-UTF-8 mode + 在 8 位非 UTF-8 模式下,八进制值大于 \377 + + + internal error: overran compiling workspace + 内部错误:超过编译工作区 + + + internal error: previously-checked referenced subpattern not found + 内部错误:找不到先前检查过的引用子模式 + + + DEFINE group contains more than one branch + "定义" 组包含多个分支 + + + missing opening brace after \o + \o 后面缺少左大括号 + + + internal error: unknown newline setting + 内部错误:未知换行设置 + + + \g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number + \g 后面没有括号,角括号或引用的 名称/数字 或普通数字 + + + a numbered reference must not be zero + 编号的引用不能为零 + + + an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT) + (*ACCEPT),(*FAIL) 或 (*COMMIT) 不允许参数 + + + (*VERB) not recognized or malformed + (*VERB) 未识别或格式不当 + + + number is too big + 数字太大 + + + subpattern name expected + 子模式名称是预期的 + + + digit expected after (?+ + 预期的数字 在 (?+ 之后 + + + non-octal character in \o{} (closing brace missing?) + \o{} 中非八进制字符 (右大括号丢失?) + + + different names for subpatterns of the same number are not allowed + 不允许同一编号的子模式有不同的名称 + + + (*MARK) must have an argument + (*MARK) 必须有一个参数 + + + non-hex character in \x{} (closing brace missing?) + \x{} 中非十六进制字符 (右大括号丢失?) + + + \c must be followed by a printable ASCII character + \c 必须后跟可打印的 ASCII 字符 + + + \c must be followed by a letter or one of [\]^_? + \c 后面必须跟一个字母或 [\]^_? 之一 + + + \k is not followed by a braced, angle-bracketed, or quoted name + \k 后面没有括号,角括号或引用的名称 + + + internal error: unknown opcode in find_fixedlength() + 内部错误:find_fixedlength() 中的未知操作码 + + + \N is not supported in a class + 类中不支持 \N + + + SPARE ERROR + 备用错误 + + + disallowed Unicode code point (>= 0xd800 && <= 0xdfff) + 不允许使用 Unicode 代码点 (>= 0xd800 && <= 0xdfff) + + + using UTF is disabled by the application + 应用程序禁止使用 UTF + + + using UCP is disabled by the application + 应用程序禁止使用 UCP + + + name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN) + 名称在 (*MARK),(*PRUNE),(*SKIP) 或 (*THEN) 中太长 + + + character code point value in \u.... sequence is too large + \u.... 序列中的字符代码点值太大 + + + digits missing in \x{} or \o{} + \x{} 或 \o{} 中缺少数值 + + + syntax error in (?(VERSION condition + (?(VERSION 条件 中的语法错误 + + + escape sequence is invalid in character class + + + + missing closing parenthesis for condition + + + + a relative value of zero is not allowed + + + + conditional subpattern contains more than two branches + + + + digit expected after (?+ or (?- + + + + lookbehind is too complicated + + + + \C is not allowed in a lookbehind assertion in UTF-16 mode + + + + PCRE2 does not support \F, \L, \l, \N{name}, \U, or \u + + + + syntax error in subpattern name (missing terminator?) + + + + subpattern name must start with a non-digit + + + + subpattern name is too long (maximum 32 code units) + + + + too many named subpatterns (maximum 10000) + 命名子模式太多 (最多256个) {10000)?} + + + DEFINE subpattern contains more than one branch + + + + (?R (recursive pattern call) must be followed by a closing parenthesis + + + + obsolete error (should not occur) + + + + subpattern number is too big + + + + internal error: parsed pattern overflow + + + + internal error: unknown meta code in check_lookbehinds() + + + + callout string is too long + + + + digits missing in \x{} or \o{} or \N{U+} + + + + syntax error or number too big in (?(VERSION condition + + + + internal error: unknown opcode in auto_possessify() + 内部错误:auto_possessify() 中的未知操作码 + + + missing terminating delimiter for callout with string argument + 带字符串参数的调用缺少终止分隔符 + + + unrecognized string delimiter follows (?C + (?C 之后是无法识别的字符串分隔符 + + + using \C is disabled by the application + 应用程序禁止使用 \C + + + (?| and/or (?J: or (?x: parentheses are too deeply nested + (?| 和/或 (?J: 或 (?x: 括号嵌套太深 + + + using \C is disabled in this PCRE2 library + 在此 PCRE2 库中禁止使用 \C + + + regular expression is too complicated + 正则表达式太复杂 + + + lookbehind assertion is too long + 后发断言太长 + + + pattern string is longer than the limit set by the application + 模式字符串长于应用程序设置的限制 + + + internal error: unknown code in parsed pattern + + + + internal error: bad code value in parsed_skip() + + + + PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode + + + + invalid option bits with PCRE2_LITERAL + + + + \N{U+dddd} is supported only in Unicode (UTF) mode + + + + invalid hyphen in option setting + + + + (*alpha_assertion) not recognized + + + + script runs require Unicode support, which this version of PCRE2 does not have + + + + too many capturing groups (maximum 65535) + + + + atomic assertion expected after (?( or (?(?C) + + + + no match + 不匹配 + + + partial match + 部分匹配 + + + UTF-8 error: 1 byte missing at end + UTF-8 错误:末尾缺少 1 个字节 + + + UTF-8 error: 2 bytes missing at end + UTF-8 错误:末尾缺少 2 个字节 + + + UTF-8 error: 3 bytes missing at end + UTF-8 错误:末尾缺少 3 个字节 + + + UTF-8 error: 4 bytes missing at end + UTF-8 错误:末尾缺少 4 个字节 + + + UTF-8 error: 5 bytes missing at end + UTF-8 错误:末尾缺少 5 个字节 + + + UTF-8 error: byte 2 top bits not 0x80 + UTF-8 错误:字节 2 的高位不是 0x80 + + + UTF-8 error: byte 3 top bits not 0x80 + UTF-8 错误:字节 3 的高位不是 0x80 + + + UTF-8 error: byte 4 top bits not 0x80 + UTF-8 错误:字节 4 的高位不是 0x80 + + + UTF-8 error: byte 5 top bits not 0x80 + UTF-8 错误:字节 5 的高位不是 0x80 + + + UTF-8 error: byte 6 top bits not 0x80 + UTF-8 错误:字节 6 的高位不是 0x80 + + + UTF-8 error: 5-byte character is not allowed (RFC 3629) + UTF-8 错误:不允许使用 5 字节字符 (RFC 3629) + + + UTF-8 error: 6-byte character is not allowed (RFC 3629) + UTF-8 错误:不允许使用 6 字节字符 (RFC 3629) + + + UTF-8 error: code points greater than 0x10ffff are not defined + UTF-8 错误:未定义大于 0x10FFFF 的代码点 + + + UTF-8 error: code points 0xd800-0xdfff are not defined + UTF-8 错误:未定义代码点 0xD800-0xDFFF + + + UTF-8 error: overlong 2-byte sequence + UTF-8 错误:超长 2 字节序列 + + + UTF-8 error: overlong 3-byte sequence + UTF-8 错误:超长 3 字节序列 + + + UTF-8 error: overlong 4-byte sequence + UTF-8 错误:超长 4 字节序列 + + + UTF-8 error: overlong 5-byte sequence + UTF-8 错误:超长 5 字节序列 + + + UTF-8 error: overlong 6-byte sequence + UTF-8 错误:超长 6 字节序列 + + + UTF-8 error: isolated byte with 0x80 bit set + UTF-8 错误:设置为 0x80 位的隔离字节 + + + UTF-8 error: illegal byte (0xfe or 0xff) + UTF-8 错误:非法字节 (0xfe 或 0xff) + + + UTF-16 error: missing low surrogate at end + UTF-16 错误:末尾缺少低代理 + + + UTF-16 error: invalid low surrogate + UTF-16 错误:低代理无效 + + + UTF-16 error: isolated low surrogate + UTF-16 错误:孤立的低代理 + + + UTF-32 error: code points 0xd800-0xdfff are not defined + UTF-32 错误:未定义代码点 0xd800-0xdfff + + + UTF-32 error: code points greater than 0x10ffff are not defined + UTF-32 错误:未定义大于 0x10ffff 的代码点 + + + bad data value + 错误的数据值 + + + patterns do not all use the same character tables + 模式并不都使用相同的字符表 + + + magic number missing + 魔数缺失 + + + pattern compiled in wrong mode: 8/16/32-bit error + 模式编译错误:8/16/32 位错误 + + + bad offset value + 错误的偏移值 + + + bad option value + 错误的选项值 + + + invalid replacement string + 无效的替换字符串 + + + bad offset into UTF string + 到 UTF 字符串的错误偏移量 + + + callout error code + 调用错误代码 + + + invalid data in workspace for DFA restart + 工作区中用于重新启动 DFA 的数据无效 + + + too much recursion for DFA matching + DFA 匹配的递归太多 + + + backreference condition or recursion test is not supported for DFA matching + DFA 匹配不支持反向引用条件或递归测试 + + + function is not supported for DFA matching + DFA 匹配不支持该功能 + + + pattern contains an item that is not supported for DFA matching + 模式包含 DFA 匹配不支持的项 + + + workspace size exceeded in DFA matching + DFA 匹配中超出工作区大小 + + + internal error - pattern overwritten? + 内部错误 - 模式被覆盖? + + + bad JIT option + 错误的 JIT 选项 + + + JIT stack limit reached + 已达到 JIT 堆栈限制 + + + match limit exceeded + 超过匹配限制 + + + no more memory + 没有更多的内存 + + + unknown substring + 未知子字符串 + + + non-unique substring name + 非唯一的子字符串名称 + + + NULL argument passed + 传递了 NULL 参数 + + + nested recursion at the same subject position + 在同一主题位置嵌套递归 + + + matching depth limit exceeded + + + + match with end before start or start moved backwards is not supported + + + + bad serialized data + + + + heap limit exceeded + + + + invalid syntax + + + + internal error - duplicate substitution match + + + + PCRE2_MATCH_INVALID_UTF is not supported for DFA matching + + + + recursion limit exceeded + 超过递归限制 + + + requested value is not available + 请求的值不可用 + + + requested value is not set + 请求的值未设置 + + + offset limit set without PCRE2_USE_OFFSET_LIMIT + 设置偏移量限制,但无 PCRE2_USE_OFFSET_LIMIT + + + bad escape sequence in replacement string + 替换字符串中的转义序列错误 + + + expected closing curly bracket in replacement string + 替换字符串中应有右大括号 + + + bad substitution in replacement string + 替换字符串中的替换错误 + + + match with end before start is not supported + 不支持在开始前与结束匹配 + + + too many replacements (more than INT_MAX) + 替换太多 (超过 INT_MAX) + + + + QSQLite2Driver + + Error opening database + 打开数据库出错 + + + Unable to begin transaction + 无法开始事务 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + + QSQLite2Result + + Unable to fetch results + 无法获取结果 + + + Unable to execute statement + 无法执行语句 + + + + QSQLiteDriver + + Error opening database + 打开数据库时出错 + + + Error closing database + 关闭数据库时出错 + + + Unable to begin transaction + 无法开始事务 + + + Unable to commit transaction + 无法提交事务 + + + Unable to rollback transaction + 无法回滚事务 + + + + QSQLiteResult + + Unable to fetch row + 无法获取行 + + + No query + 没有查询 + + + Unable to execute statement + 无法执行语句 + + + Unable to execute multiple statements at a time + 无法一次执行多个语句 + + + Unable to reset statement + 无法重置语句 + + + Unable to bind parameters + 无法绑定参数 + + + Parameter count mismatch + 参数数量不匹配 + + + + QSaveFile + + Existing file %1 is not writable + 已有文件 %1 不可写入 + + + Filename refers to a directory + 文件名指向一个目录 + + + QSaveFile cannot open '%1' without direct write fallback enabled. + QSaveFile 无法在未启用 direct write fallback 时打开“%1”。 + + + QSaveFile cannot open '%1' without direct write fallback enabled: path contains an Alternate Data Stream specifier + 没有启用直接写回退,QSaveFile 无法打开“%1”:路径包含备用数据流说明符 + + + Writing canceled by application + 写入已被应用程序取消 + + + + QScrollBar + + Scroll here + 滚动至此 + + + Left edge + 左边缘 + + + Top + 顶端 + + + Right edge + 右边缘 + + + Bottom + 底部 + + + Page left + 向左翻页 + + + Page up + 向上翻页 + + + Page right + 向右翻页 + + + Page down + 向下翻页 + + + Scroll left + 向左滚动 + + + Scroll up + 向上滚动 + + + Scroll right + 向右滚动 + + + Scroll down + 向下滚动 + + + + QSctpSocket + + The remote host closed the connection + 远端主机关闭了这个连接 + + + + QSharedMemory + + %1: unable to set key on lock + %1:无法设置锁定的键 + + + %1: create size is less then 0 + %1:创建的大小小于 0 + + + %1: unable to lock + %1:无法锁定 + + + %1: unable to unlock + %1:无法取消锁定 + + + %1: key is empty + %1:键是空的 + + + %1: bad name + %1:错误的名称 + + + %1: UNIX key file doesn't exist + %1:UNIX 密钥文件不存在 + + + %1: ftok failed + %1:ftok 失败 + + + %1: unable to make key + %1:无法生成键 + + + %1: system-imposed size restrictions + %1:系统预设大小限制 + + + %1: not attached + %1:没有附加 + + + %1: permission denied + %1:权限被拒绝 + + + %1: already exists + %1:已经存在 + + + %1: doesn't exist + %1:不存在 + + + %1: out of resources + %1:资源耗尽 + + + %1: unknown error %2 + %1:未知错误 %2 + + + %1: invalid size + %1:无效大小 + + + %1: key error + %1:键错误 + + + %1: size query failed + %1:大小查询失败 + + + + QShortcut + + Space + This and all following "incomprehensible" strings in QShortcut context are key names. Please use the localized names appearing on actual keyboards or whatever is commonly used. + 空格 + + + Esc + Esc + + + Tab + Tab + + + Backtab + Backtab + + + Backspace + 退格 + + + Return + 回车 + + + Enter + 回车 + + + Ins + Ins + + + Del + Del + + + Pause + Pause + + + Print + Print + + + SysReq + SysReq + + + Home + Home + + + End + End + + + Left + 左方向 + + + Up + 上方向 + + + Right + 右方向 + + + Down + 下方向 + + + PgUp + PgUp + + + PgDown + PgDown + + + CapsLock + CapsLock + + + NumLock + NumLock + + + ScrollLock + ScrollLock + + + Menu + 菜单 + + + Help + 帮助 + + + Back + 后退 + + + Forward + 前进 + + + Stop + 停止 + + + Refresh + 刷新 + + + Volume Down + 音量降低 + + + Volume Mute + 音量静音 + + + Volume Up + 音量提高 + + + Bass Boost + 低音增强 + + + Bass Up + 低音提高 + + + Bass Down + 低音降低 + + + Treble Up + 高音提高 + + + Treble Down + 高音降低 + + + Media Play + 媒体播放 + + + Media Stop + 媒体停止 + + + Media Previous + 媒体上一项 + + + Media Next + 媒体下一项 + + + Media Record + 媒体录制 + + + Media Pause + Media player pause button + 媒体暂停 + + + Toggle Media Play/Pause + Media player button to toggle between playing and paused + 切换媒体播放/暂停 + + + Home Page + 主页 + + + Favorites + 收藏 + + + Search + 搜索 + + + Standby + 待机 + + + Open URL + 打开 URL + + + Launch Mail + 启动邮件 + + + Launch Media + 启动媒体 + + + Launch (0) + 启动 (0) + + + Launch (1) + 启动 (1) + + + Launch (2) + 启动 (2) + + + Launch (3) + 启动 (3) + + + Launch (4) + 启动 (4) + + + Launch (5) + 启动 (5) + + + Launch (6) + 启动 (6) + + + Launch (7) + 启动 (7) + + + Launch (8) + 启动 (8) + + + Launch (9) + 启动 (9) + + + Launch (A) + 启动 (A) + + + Launch (B) + 启动 (B) + + + Launch (C) + 启动 (C) + + + Launch (D) + 启动 (D) + + + Launch (E) + 启动 (E) + + + Launch (F) + 启动 (F) + + + Launch (G) + 启动 (G) + + + Launch (H) + 启动 (H) + + + Monitor Brightness Up + 显示器亮度提高 + + + Monitor Brightness Down + 显示器亮度降低 + + + Keyboard Light On/Off + 键盘背光开 + + + Keyboard Brightness Up + 键盘背光提高 + + + Keyboard Brightness Down + 键盘背光降低 + + + Power Off + 关机 + + + Wake Up + 唤醒 + + + Eject + 弹出 + + + Screensaver + 屏幕保护 + + + WWW + 互联网 + + + Sleep + 睡眠 + + + LightBulb + 灯泡 + + + Shop + 商店 + + + History + 历史 + + + Add Favorite + 添加收藏 + + + Hot Links + 热门链接 + + + Adjust Brightness + 调整亮度 + + + Finance + 金融 + + + Community + 社区 + + + Media Rewind + 媒体快退 + + + Back Forward + 后退/前进 + + + Application Left + 应用程序 左 + + + Application Right + 应用程序 右 + + + Book + 预定 + + + CD + CD + + + Calculator + 计算器 + + + Calendar + 日历 + + + Clear + 清除 + + + Clear Grab + 清除抓取 + + + Close + 关闭 + + + Adjust contrast + 调整对比度 + + + Copy + 复制 + + + Cut + 剪切 + + + Display + 显示 + + + DOS + DOS + + + Documents + 文档 + + + Spreadsheet + 电子表格 + + + Browser + 浏览器 + + + Game + 游戏 + + + Go + 转到 + + + iTouch + iTouch + + + Logoff + 注销 + + + Market + 市场 + + + Meeting + 会议 + + + Memo + 备忘 + + + Keyboard Menu + 键盘菜单 + + + Menu PB + 菜单 PB + + + My Sites + 我的网站 + + + News + 新闻 + + + Home Office + 家庭办公室 + + + Option + 选项 + + + Paste + 粘贴 + + + Phone + 电话 + + + Reply + 答复 + + + Reload + 重新加载 + + + Rotate Windows + 旋转窗口 + + + Rotation PB + 旋转 PB + + + Rotation KB + 旋转 KB + + + Save + 保存 + + + Send + 发送 + + + Spellchecker + 拼写检查 + + + Split Screen + 分屏 + + + Support + 支持 + + + Task Panel + 任务面板 + + + Terminal + 终端 + + + To-do list + 待办事项 + + + Tools + 工具 + + + Travel + 旅行 + + + Video + 视频 + + + Word Processor + 文字处理 + + + XFer + XFer + + + Zoom In + 放大 + + + Zoom Out + 缩小 + + + Away + 离开 + + + Messenger + 消息软件 + + + WebCam + 摄像头 + + + Mail Forward + 邮件转发 + + + Pictures + 图片 + + + Music + 音乐 + + + Battery + 电池 + + + Bluetooth + 蓝牙 + + + Wireless + 无线 + + + Ultra Wide Band + 超宽带 + + + Media Fast Forward + 媒体快进 + + + Audio Repeat + 音频循环 + + + Audio Random Play + 音频随机播放 + + + Subtitle + 字幕 + + + Audio Cycle Track + 音频循环音轨 + + + Time + 时间 + + + Hibernate + 休眠 + + + View + 查看 + + + Top Menu + 顶部菜单 + + + Power Down + 关机 + + + Suspend + 挂起 + + + Microphone Mute + 麦克风静音 + + + Red + + + + Green + 绿 + + + Yellow + + + + Blue + + + + Channel Up + 频道上 + + + Channel Down + 频道下 + + + Guide + 指南 + + + Info + 信息 + + + Settings + 设置 + + + Microphone Volume Up + 麦克风音量提高 + + + Microphone Volume Down + 麦克风音量降低 + + + New + 新建 + + + Open + 打开 + + + Find + 查找 + + + Undo + 撤销 + + + Redo + 重做 + + + Print Screen + 打印屏幕 + + + Page Up + Page Up + + + Page Down + Page Down + + + Caps Lock + Caps Lock + + + Num Lock + Num Lock + + + Number Lock + Number Lock + + + Scroll Lock + Scroll Lock + + + Insert + Insert + + + Delete + Delete + + + Escape + Escape + + + System Request + System Request + + + Select + 选择 + + + Yes + + + + No + + + + Context1 + 语境1 + + + Context2 + 语境2 + + + Context3 + 语境3 + + + Context4 + 语境4 + + + Call + Button to start a call (note: a separate button is used to end the call) + 呼叫 + + + Hangup + Button to end a call (note: a separate button is used to start the call) + 挂断 + + + Toggle Call/Hangup + Button that will hang up if we're in call, or make a call if we're not. + 切换呼叫/挂断 + + + Flip + 翻转 + + + Voice Dial + Button to trigger voice dialing + 语音拨号 + + + Last Number Redial + Button to redial the last number called + 拨打上次号码 + + + Camera Shutter + Button to trigger the camera shutter (take a picture) + 相机快门 + + + Camera Focus + Button to focus the camera + 相机对焦 + + + Kanji + 汉字 + + + Muhenkan + 无变换 + + + Henkan + 变换 + + + Romaji + 罗马字 + + + Hiragana + 平假名 + + + Katakana + 片假名 + + + Hiragana Katakana + 平假名/片假名 + + + Zenkaku + 全角 + + + Hankaku + 半角 + + + Zenkaku Hankaku + 全角/半角 + + + Touroku + 登录 + + + Massyo + 抹消 + + + Kana Lock + 假名锁定 + + + Kana Shift + 假名上档 + + + Eisu Shift + 英数上档 + + + Eisu toggle + 英数切换 + + + Code input + 代码输入 + + + Multiple Candidate + 多个候选 + + + Previous Candidate + 上一个候选 + + + Hangul + 谚文 + + + Hangul Start + 谚文开始 + + + Hangul End + 谚文结束 + + + Hangul Hanja + 谚文汉字 + + + Hangul Jamo + 谚文字母 + + + Hangul Romaja + 谚文罗马字 + + + Hangul Jeonja + 谚文转写 + + + Hangul Banja + 谚文反切 + + + Hangul PreHanja + 谚文反切前 + + + Hangul PostHanja + 谚文反切后 + + + Hangul Special + 谚文特殊 + + + Cancel + 取消 + + + Printer + 打印机 + + + Execute + 执行 + + + Play + 播放 + + + Zoom + 缩放 + + + Exit + 退出 + + + Touchpad Toggle + 触摸板切换 + + + Touchpad On + 触摸板开 + + + Touchpad Off + 触控板关 + + + Ctrl + Ctrl + + + Shift + Shift + + + Alt + Alt + + + Meta + Meta + + + Num + Num + + + + + Key separator in shortcut string + + + + + F%1 + F%1 + + + + QSocks5SocketEngine + + Connection to proxy refused + 代理拒绝连接 + + + Connection to proxy closed prematurely + 代理连接过早关闭 + + + Proxy host not found + 代理主机未找到 + + + Connection to proxy timed out + 代理连接超时 + + + Proxy authentication failed + 代理认证失败 + + + Proxy authentication failed: %1 + 代理认证失败:%1 + + + SOCKS version 5 protocol error + SOCKS 版本 5 协议错误 + + + General SOCKSv5 server failure + 常规 SOCKSv5 服务器故障 + + + Connection not allowed by SOCKSv5 server + 连接不被 SOCKSv5 服务器允许 + + + TTL expired + TTL 已过期 + + + SOCKSv5 command not supported + 不支持的 SOCKSv5 命令 + + + Address type not supported + 不支持的地址类型 + + + Unknown SOCKSv5 proxy error code 0x%1 + 未知 SOCKSv5 代理,错误代码 0x%1 + + + Network operation timed out + 网络操作超时 + + + + QSpiAccessibleBridge + + invalid role + Role of an accessible object - the object is in an invalid state or could not be constructed + 无效角色 + + + title bar + Role of an accessible object + 标题栏 + + + menu bar + Role of an accessible object + 菜单栏 + + + scroll bar + Role of an accessible object + 滚动条 + + + grip + Role of an accessible object - the grip is usually used for resizing another object + 底框 + + + sound + Role of an accessible object + 声音 + + + cursor + Role of an accessible object + 光标 + + + text caret + Role of an accessible object + 文字插入符号 + + + alert message + Role of an accessible object + 通知邮件 + + + frame + Role of an accessible object: a window with frame and title +---------- +Role of an accessible object + 框架 + + + filler + Role of an accessible object + 填充 + + + popup menu + Role of an accessible object + 弹出式菜单 + + + menu item + Role of an accessible object + 菜单项目 + + + tool tip + Role of an accessible object + 工具提示 + + + application + Role of an accessible object + 应用 + + + document + Role of an accessible object + 文档 + + + panel + Role of an accessible object + 面板 + + + chart + Role of an accessible object + 图表 + + + dialog + Role of an accessible object + 对话框 + + + separator + Role of an accessible object + 分隔符 + + + tool bar + Role of an accessible object + 工具栏 + + + status bar + Role of an accessible object + 状态栏 + + + table + Role of an accessible object + 表格 + + + column header + Role of an accessible object - part of a table + 列标题 + + + row header + Role of an accessible object - part of a table + 行标题 + + + column + Role of an accessible object - part of a table + + + + row + Role of an accessible object - part of a table + + + + cell + Role of an accessible object - part of a table + 单元格 + + + link + Role of an accessible object + 链接 + + + help balloon + Role of an accessible object + 气球式帮助 + + + assistant + Role of an accessible object - a helper dialog + 助理 + + + list + Role of an accessible object + 列表 + + + list item + Role of an accessible object + 列表项目 + + + tree + Role of an accessible object + + + + tree item + Role of an accessible object + 树项目 + + + page tab + Role of an accessible object + 选项卡 + + + property page + Role of an accessible object + 属性页 + + + indicator + Role of an accessible object + 指示器 + + + graphic + Role of an accessible object + 图形 + + + label + Role of an accessible object + 标签 + + + text + Role of an accessible object + 文本 + + + push button + Role of an accessible object + 按下按钮 + + + check box + Role of an accessible object + 复选框 + + + radio button + Role of an accessible object + 单选按钮 + + + combo box + Role of an accessible object + 组合框 + + + progress bar + Role of an accessible object + 进度栏 + + + dial + Role of an accessible object + 表盘 + + + hotkey field + Role of an accessible object + 热键字段 + + + slider + Role of an accessible object + 滑块 + + + spin box + Role of an accessible object + 数字显示框 + + + canvas + Role of an accessible object + 画布 + + + animation + Role of an accessible object + 动画 + + + equation + Role of an accessible object + 方程式 + + + button with drop down + Role of an accessible object + 下拉按钮 + + + button menu + Role of an accessible object + 按钮菜单 + + + button with drop down grid + Role of an accessible object - a button that expands a grid. + 下拉式网格按钮 + + + space + Role of an accessible object - blank space between other objects. + 空间 + + + page tab list + Role of an accessible object + 选项卡列表 + + + clock + Role of an accessible object + 时钟 + + + splitter + Role of an accessible object + 拆分器 + + + layered pane + Role of an accessible object + 分层窗格 + + + web document + Role of an accessible object + 网络文件 + + + paragraph + Role of an accessible object + 段落 + + + section + Role of an accessible object + 分段 + + + color chooser + Role of an accessible object + 颜色选择器 + + + footer + Role of an accessible object + 页脚 + + + form + Role of an accessible object + 窗体 + + + heading + Role of an accessible object + 标题 + + + note + Role of an accessible object + 便笺 + + + complementary content + Role of an accessible object + 补充内容 + + + terminal + Role of an accessible object + 终端 + + + desktop + Role of an accessible object + 桌面 + + + notification + Role of an accessible object + 通知 + + + unknown + Role of an accessible object + 未知 + + + + QSslDiffieHellmanParameter + + No error + 无错误 + + + Invalid input data + 输入数据无效 + + + The given Diffie-Hellman parameters are deemed unsafe + 给定的 Diffie-Hellman 参数被认为是不安全的 + + + + QSslSocket + + Error when setting the OpenSSL configuration (%1) + 设置 OpenSSL 配置时出错 (%1) + + + Expecting QByteArray for %1 + %1 应为 Qbytearray + + + An error occurred attempting to set %1 to %2 + 试图将 %1 设置为 %2 时出错 + + + Wrong value for %1 (%2) + 错误的值 %1 (%2) + + + Unrecognized command %1 = %2 + 无法识别的命令 %1 = %2 + + + SSL_CONF_finish() failed + SSL_CONF_finish() 失败 + + + SSL_CONF_CTX_new() failed + SSL_CONF_CTX_new() 失败 + + + OpenSSL version too old, need at least v1.0.2 + OpenSSL 版本太旧了,至少需要 v1.0.2 + + + Error when setting the elliptic curves (%1) + 设置椭圆曲线时出错 (%1) + + + Error creating SSL context (%1) + 创建 SSL 上下文错误 (%1) + + + unsupported protocol + 不支持的协议 + + + Error while setting the minimal protocol version + 设置最小协议版本时出错 + + + Error while setting the maximum protocol version + 设置最大协议版本时出错 + + + Invalid or empty cipher list (%1) + 无效或者空白的密码列表 (%1) + + + Cannot provide a certificate with no key, %1 + 不能提供没有密钥的证书,%1 + + + Error loading local certificate, %1 + 加载本地证书时出错,%1 + + + Error loading private key, %1 + 加载私钥时出错,%1 + + + Private key does not certify public key, %1 + 私钥不验证公钥,%1 + + + Diffie-Hellman parameters are not valid + Diffie-Hellman 参数无效 + + + OpenSSL version with disabled elliptic curves + 带禁用椭圆曲线的 OpenSSL 版本 + + + DTLS server requires a 'VerifyNone' mode with your version of OpenSSL + DTLS 服务器需要与您的 OpenSSL 版本一起使用“VerifyNone”模式 + + + No error + 无错误 + + + The issuer certificate could not be found + 无法找到颁发者证书 + + + The certificate signature could not be decrypted + 证书签名无法解密 + + + The public key in the certificate could not be read + 无法读取证书中的公钥 + + + The signature of the certificate is invalid + 证书的签名无效 + + + The certificate is not yet valid + 证书尚未生效 + + + The certificate has expired + 证书已过期 + + + The certificate's notBefore field contains an invalid time + 证书的 notBefore 字段包含无效时间 + + + The certificate's notAfter field contains an invalid time + 证书的 notAfter 字段包含无效时间 + + + The certificate is self-signed, and untrusted + 证书是自签名的,不受信任 + + + The root certificate of the certificate chain is self-signed, and untrusted + 证书链的根证书是自签名的,不可信 + + + The issuer certificate of a locally looked up certificate could not be found + 无法找到本地查找证书的颁发者证书 + + + No certificates could be verified + 无法验证任何证书 + + + One of the CA certificates is invalid + 其中一个 CA 证书无效 + + + The basicConstraints path length parameter has been exceeded + 已超过基本约束路径长度参数 + + + The supplied certificate is unsuitable for this purpose + 提供的证书不适用于此目的 + + + The root CA certificate is not trusted for this purpose + 为此目的,根 CA 证书不可信 + + + The root CA certificate is marked to reject the specified purpose + 根 CA 证书被标记为拒绝指定用途 + + + The current candidate issuer certificate was rejected because its subject name did not match the issuer name of the current certificate + 当前候选颁发者证书被拒绝,因为其主题名称与当前证书的颁发者名称不匹配 + + + The current candidate issuer certificate was rejected because its issuer name and serial number was present and did not match the authority key identifier of the current certificate + 当前候选发行人证书被拒绝,因为其发行人姓名和序列号存在且与当前证书的授权密钥标识符不匹配 + + + The peer did not present any certificate + 对等方没有提供任何证书 + + + The host name did not match any of the valid hosts for this certificate + 主机名与此证书的任何有效主机不匹配 + + + The peer certificate is blacklisted + 对等方证书被列入黑名单 + + + No OCSP status response found + + + + The OCSP status request had invalid syntax + + + + OCSP response contains an unexpected number of SingleResponse structures + + + + OCSP responder reached an inconsistent internal state + + + + OCSP responder was unable to return a status for the requested certificate + + + + The server requires the client to sign the OCSP request in order to construct a response + + + + The client is not authorized to request OCSP status from this server + + + + OCSP responder's identity cannot be verified + + + + The identity of a certificate in an OCSP response cannot be established + + + + The certificate status response has expired + + + + The certificate's status is unknown + + + + Unknown error + 未知错误 + + + TLS initialization failed + TLS 初始化失败 + + + Attempted to use an unsupported protocol. + + + + The TLS/SSL connection has been closed + TLS/SSL 连接已关闭 + + + Error creating SSL session, %1 + 创建 SSL 会话时出错,%1 + + + Error creating SSL session: %1 + 创建 SSL 会话时出错:%1 + + + Server-side QSslSocket does not support OCSP stapling + + + + Failed to enable OCSP stapling + + + + Client-side sockets do not send OCSP responses + + + + Unable to init SSL Context: %1 + 无法初始化 SSL 上下文:%1 + + + Unable to write data: %1 + 无法写入数据:%1 + + + Unable to decrypt data: %1 + 无法解密数据:%1 + + + Error while reading: %1 + 读取时出错:%1 + + + Error during SSL handshake: %1 + SSL 握手过程中出错:%1 + + + Failed to decode OCSP response + + + + Failed to extract basic OCSP response + + + + No certificate verification store, cannot verify OCSP response + + + + Failed to decode a SingleResponse from OCSP status response + + + + Failed to extract 'this update time' from the SingleResponse + + + + Insufficient memory + + + + Internal error + + + + An internal handle was invalid + + + + An internal token was invalid + + + + Access denied + + + + No authority could be contacted for authorization + + + + No credentials + + + + The target is unknown or unreachable + + + + An unsupported function was requested + + + + The hostname provided does not match the one received from the peer + + + + No common protocol exists between the client and the server + + + + Unexpected or badly-formatted message received + + + + The data could not be encrypted + + + + No cipher suites in common + + + + The credentials were not recognized / Invalid argument + + + + The message was tampered with, damaged or out of sequence. + + + + A message was received out of sequence. + + + + Unknown error occurred: %1 + + + + Invalid protocol chosen + + + + The certificate provided cannot be used for a client. + + + + The certificate provided cannot be used for a server. + + + + Server did not accept any certificate we could present. + + + + Algorithm mismatch + + + + Handshake failed: %1 + + + + Failed to query the TLS context: %1 + + + + Did not get the required attributes for the connection. + + + + Unwanted protocol was negotiated + + + + Renegotiation was unsuccessful: %1 + + + + Schannel failed to encrypt data: %1 + + + + Cannot provide a certificate with no key + + + + + QStandardPaths + + Desktop + 桌面 + + + Documents + 文档 + + + Fonts + 字体 + + + Applications + 应用程序 + + + Music + 音乐 + + + Movies + 电影 + + + Pictures + 图片 + + + Temporary Directory + 临时目录 + + + Home + 主文件夹 + + + Cache + 缓存 + + + Shared Data + 共享数据 + + + Runtime + 运行时 + + + Configuration + 配置 + + + Shared Configuration + 共享配置 + + + Shared Cache + 共享缓存 + + + Download + 下载 + + + Application Data + 应用程序数据 + + + Application Configuration + 应用程序配置 + + + + QStateMachine + + Missing initial state in compound state '%1' + 复合状态“%1”中缺少初始状态 + + + Missing default state in history state '%1' + 历史记录状态“%1”中缺少默认状态 + + + No common ancestor for targets and source of transition from state '%1' + 没有用于目标和源从状态“%1”转换的共同祖先 + + + Child mode of state machine '%1' is not 'ExclusiveStates'. + + + + Unknown error + 未知错误 + + + + QSystemSemaphore + + %1: permission denied + %1:权限被拒绝 + + + %1: already exists + %1:已经存在 + + + %1: does not exist + %1:不存在 + + + %1: out of resources + %1:资源耗尽 + + + %1: unknown error %2 + %1:未知错误 %2 + + + + QTDSDriver + + Unable to open connection + 无法打开连接 + + + Unable to use database + 无法使用数据库 + + + + QTabBar + + Scroll Left + 向左滚动 + + + Scroll Right + 向右滚动 + + + + QTcpServer + + Operation on socket is not supported + 套接字操作不被支持 + + + + QTgaFile + + Could not read image data + 无法读取图像数据 + + + Sequential device (eg socket) for image read not supported + 不支持图像读取的顺序设备 (例如套接字) + + + Seek file/device for image read failed + 查找用于读取图像的文件/设备失败 + + + Image header read failed + 图像文件头读取失败 + + + Image type not supported + 图像类型不支持 + + + Image depth not valid + 图像色彩深度无效 + + + Image size exceeds limit + 图像尺寸超出限制 + + + Could not seek to image read footer + 无法找到图像读取脚标 + + + Could not read footer + 无法读取脚标 + + + Image type (non-TrueVision 2.0) not supported + 图像类型 (非 TrueVision 2.0) 不支持 + + + Could not reset to read data + 无法重置以读取数据 + + + + QUdpSocket + + Unable to send a datagram + 无法发送数据报 + + + No datagram available for reading + 没有可供读取的数据报 + + + + QUndoGroup + + Undo %1 + 撤消 %1 + + + Undo + Default text for undo action + 撤销 + + + Redo %1 + 重做 %1 + + + Redo + Default text for redo action + 重做 + + + + QUndoModel + + <empty> + <空白> + + + + QUndoStack + + Undo %1 + 撤消 %1 + + + Undo + Default text for undo action + 撤销 + + + Redo %1 + 重做 %1 + + + Redo + Default text for redo action + 重做 + + + + QUnicodeControlCharacterMenu + + LRM Left-to-right mark + LRM 从左到右标记 + + + RLM Right-to-left mark + RLM 从右向左标记 + + + ZWJ Zero width joiner + ZWJ 零宽连字 + + + ZWNJ Zero width non-joiner + ZWNJ 零宽不连字 + + + ZWSP Zero width space + ZWSP 零宽度空格 + + + LRE Start of left-to-right embedding + LRE 开始从左到右嵌入 + + + RLE Start of right-to-left embedding + RLE 开始从右向左嵌入 + + + LRO Start of left-to-right override + LRO 开始从左向右重写 + + + RLO Start of right-to-left override + RLO 开始从右向左重写 + + + PDF Pop directional formatting + PDF 弹出方向格式 + + + LRI Left-to-right isolate + LRI 从左到右隔离 + + + RLI Right-to-left isolate + RLI 从右到左隔离 + + + FSI First strong isolate + FSI 第一强隔离 + + + PDI Pop directional isolate + PDI 弹出定向隔离 + + + Insert Unicode control character + 插入 Unicode 控制字符 + + + + QWhatsThisAction + + What's This? + 这是什么? + + + + QWidget + + * + * + + + + QWidgetTextControl + + &Undo + 撤消(&U) + + + &Redo + 重做(&R) + + + Cu&t + 剪切(&T) + + + &Copy + 复制(&C) + + + Copy &Link Location + 复制链接地址(&L) + + + &Paste + 粘贴(&P) + + + Delete + 删除 + + + Select All + 全选 + + + + QWindowsDirect2DIntegration + + Qt cannot load the direct2d platform plugin because the Direct2D version on this system is too old. The minimum system requirement for this platform plugin is Windows 7 SP1 with Platform Update. + +The minimum Direct2D version required is %1. The Direct2D version on this system is %2. + Qt 无法加载 direct2d 平台插件,因为此系统上的 Direct2D 版本太旧了。此平台插件的最低系统要求是带有平台更新的 Windows 7 SP1。 + +所需的最低 Direct2D 版本为 %1。此系统上的Direct2D版本为 %2。 + + + Cannot load direct2d platform plugin + 无法加载 direct2d 平台插件 + + + + QWizard + + Go Back + 上一步 + + + < &Back + < 上一步(&B) + + + Continue + 继续 + + + &Next + 下一步(&N) + + + &Next > + 下一步(&N) > + + + Commit + 提交 + + + Done + 完成 + + + &Finish + 完成(&F) + + + Cancel + 取消 + + + Help + 帮助 + + + &Help + 帮助(&H) + + + + QXml + + no error occurred + 没有错误发生 + + + error triggered by consumer + 消费者触发的错误 + + + unexpected end of file + 异常的文件终止 + + + more than one document type definition + 多个文档类型定义 + + + error occurred while parsing element + 解析元素时发生错误 + + + tag mismatch + 标记不匹配 + + + error occurred while parsing content + 解析内容时发生错误 + + + unexpected character + 异常的字符 + + + invalid name for processing instruction + 无效的处理指令名称 + + + version expected while reading the XML declaration + 在读取 XML 声明的时候,版本被期待 + + + wrong value for standalone declaration + 独立声明的值错误 + + + encoding declaration or standalone declaration expected while reading the XML declaration + 读取 XML 声明时需要编码声明或独立声明 + + + standalone declaration expected while reading the XML declaration + 读取 XML 声明时需要独立声明 + + + error occurred while parsing document type definition + 解析文档类型定义时发生错误 + + + letter is expected + 需要字母 + + + error occurred while parsing comment + 解析注释时发生错误 + + + error occurred while parsing reference + 解析参考时发生错误 + + + internal general entity reference not allowed in DTD + 在 DTD 中不允许使用内部解析的通用实体参考 + + + external parsed general entity reference not allowed in attribute value + 在属性值中不允许使用外部解析的通用实体参考 + + + external parsed general entity reference not allowed in DTD + 在 DTD 中不允许使用外部解析的通用实体参考 + + + unparsed entity reference in wrong context + 没有解析的错误上下文中的实体参考 + + + recursive entities + 嵌套实体 + + + error in the text declaration of an external entity + 在一个外部实体的文本声明里有错误 + + + + QXmlStream + + Extra content at end of document. + 文档末尾有额外内容。 + + + Invalid entity value. + 无效的实体值。 + + + Invalid XML character. + 无效的 XML 字符。 + + + Sequence ']]>' not allowed in content. + 内容中不允许有“]]>”序列。 + + + Encountered incorrectly encoded content. + 遇到不正确的编码内容。 + + + Namespace prefix '%1' not declared + 命名空间的“%1”前缀没有被声明 + + + Illegal namespace declaration. + 非法的命名空间声明。 + + + Attribute '%1' redefined. + 属性“%1”已重新定义。 + + + Unexpected character '%1' in public id literal. + 在公有标识文本中有异常的字符“%1”。 + + + Invalid XML version string. + 无效的 XML 版本字符串。 + + + Unsupported XML version. + 不被支持的 XML 版本。 + + + The standalone pseudo attribute must appear after the encoding. + 独立运行伪属性必须出现在编码之后。 + + + %1 is an invalid encoding name. + %1 是无效的编码名称。 + + + Encoding %1 is unsupported + 编码 %1 不被支持 + + + Standalone accepts only yes or no. + 独立运行只允许是或者否。 + + + Invalid attribute in XML declaration. + 在 XML 声明中无效的属性。 + + + Premature end of document. + 文档过早的结束。 + + + Invalid document. + 无效的文档。 + + + '%1' + expected + '<first option>' + “%1” + + + %1 or '%2' + expected + <first option>, '<second option>' + %1 或“%2” + + + %1, '%2' + expected + <options so far>, '<next option>' + %1,“%2” + + + %1, or '%2' + expected + <options so far>, or '<final option>' + %1,或“%2” + + + Expected %1, but got '%2'. + 应为 %1,但得到了“%2”。 + + + Unexpected '%1'. + 异常的“%1”。 + + + Expected character data. + 需要字符数据。 + + + Self-referencing entity detected. + + + + Entity expands to more characters than the entity expansion limit. + + + + Start tag expected. + 需要开始标记。 + + + NDATA in parameter entity declaration. + 在参数实体声明中有 NDATA。 + + + XML declaration not at start of document. + XML 声明没有在文档的开始位置。 + + + %1 is an invalid processing instruction name. + %1 是无效的处理指令名称。 + + + Invalid processing instruction name. + 无效的处理指令名称。 + + + %1 is an invalid PUBLIC identifier. + %1 是一个无效的公有(PUBLIC)标识符。 + + + Invalid XML name. + 无效的 XML 名称。 + + + Opening and ending tag mismatch. + 开始标记和结束标记不匹配。 + + + Entity '%1' not declared. + 实体“%1”没有被声明。 + + + Reference to unparsed entity '%1'. + 未解析实体“%1”的引用。 + + + Reference to external entity '%1' in attribute value. + 在属性值中的外部实体“%1”的引用。 + + + Invalid character reference. + 无效的字符引用。 + + + diff --git a/models/readme.md b/models/readme.md new file mode 100644 index 0000000..1f1d170 --- /dev/null +++ b/models/readme.md @@ -0,0 +1 @@ +This folder is used to store the models by ddddocr. \ No newline at end of file diff --git a/readme,md b/readme,md new file mode 100644 index 0000000..472c898 --- /dev/null +++ b/readme,md @@ -0,0 +1 @@ +## Please see in the [manual.html](./document/manual.html)