@@ -0,0 +1,419 @@
|
||||
; *** Inno Setup version 6.5.0+ Chinese Simplified messages ***
|
||||
;
|
||||
; To download user-contributed translations of this file, go to:
|
||||
; https://jrsoftware.org/files/istrans/
|
||||
;
|
||||
; Note: When translating this text, do not add periods (.) to the end of
|
||||
; messages that didn't have them already, because on those messages Inno
|
||||
; Setup adds the periods automatically (appending a period would result in
|
||||
; two periods being displayed).
|
||||
;
|
||||
; Maintainer: Zhenghan Yang (Kira)
|
||||
; Email: 847320916@QQ.com
|
||||
; Github: https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation
|
||||
; Encoding: UTF-8
|
||||
; Translation based on network resource
|
||||
;
|
||||
|
||||
[LangOptions]
|
||||
; The following three entries are very important. Be sure to read and
|
||||
; understand the '[LangOptions] section' topic in the help file.
|
||||
LanguageName=简体中文
|
||||
; If Language Name display incorrect, uncomment next line
|
||||
; LanguageName=<7B80><4F53><4E2D><6587>
|
||||
; About LanguageID, to reference link:
|
||||
; https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c
|
||||
LanguageID=$0804
|
||||
; LanguageCodePage should always be set if possible, even if this file is Unicode
|
||||
; For English it's set to zero anyway because English only uses ASCII characters
|
||||
LanguageCodePage=936
|
||||
; If the language you are translating to requires special font faces or
|
||||
; sizes, uncomment any of the following entries and change them accordingly.
|
||||
;DialogFontName=
|
||||
;DialogFontSize=9
|
||||
;DialogFontBaseScaleWidth=7
|
||||
;DialogFontBaseScaleHeight=15
|
||||
;WelcomeFontName=Segoe UI
|
||||
;WelcomeFontSize=14
|
||||
|
||||
[Messages]
|
||||
|
||||
; *** Application titles
|
||||
SetupAppTitle=安装
|
||||
SetupWindowTitle=安装 - %1
|
||||
UninstallAppTitle=卸载
|
||||
UninstallAppFullTitle=%1 卸载
|
||||
|
||||
; *** Misc. common
|
||||
InformationTitle=信息
|
||||
ConfirmTitle=确认
|
||||
ErrorTitle=错误
|
||||
|
||||
; *** SetupLdr messages
|
||||
SetupLdrStartupMessage=现在将安装 %1。您想要继续吗?
|
||||
LdrCannotCreateTemp=无法创建临时文件。安装程序已中止
|
||||
LdrCannotExecTemp=无法执行临时目录中的文件。安装程序已中止
|
||||
HelpTextNote=
|
||||
|
||||
; *** Startup error messages
|
||||
LastErrorMessage=%1。%n%n错误 %2: %3
|
||||
SetupFileMissing=安装目录中缺少文件 %1。请修正这个问题或者获取程序的新副本。
|
||||
SetupFileCorrupt=安装文件已损坏。请获取程序的新副本。
|
||||
SetupFileCorruptOrWrongVer=安装文件已损坏,或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。
|
||||
InvalidParameter=无效的命令行参数:%n%n%1
|
||||
SetupAlreadyRunning=安装程序正在运行。
|
||||
WindowsVersionNotSupported=此程序不支持当前计算机运行的 Windows 版本。
|
||||
WindowsServicePackRequired=此程序需要 %1 服务包 %2 或更高版本。
|
||||
NotOnThisPlatform=此程序不能在 %1 上运行。
|
||||
OnlyOnThisPlatform=此程序只能在 %1 上运行。
|
||||
OnlyOnTheseArchitectures=此程序只能安装到为下列处理器架构设计的 Windows 版本中:%n%n%1
|
||||
WinVersionTooLowError=此程序需要 %1 版本 %2 或更高。
|
||||
WinVersionTooHighError=此程序不能安装于 %1 版本 %2 或更高。
|
||||
AdminPrivilegesRequired=在安装此程序时您必须以管理员身份登录。
|
||||
PowerUserPrivilegesRequired=在安装此程序时您必须以管理员身份或高级用户组身份登录。
|
||||
SetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭正在运行的程序,然后点击“确定”继续,或点击“取消”退出。
|
||||
UninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭正在运行的程序,然后点击“确定”继续,或点击“取消”退出。
|
||||
|
||||
; *** Startup questions
|
||||
PrivilegesRequiredOverrideTitle=选择安装程序模式
|
||||
PrivilegesRequiredOverrideInstruction=选择安装模式
|
||||
PrivilegesRequiredOverrideText1=%1 可以为所有用户安装(需要管理员权限),或仅为您安装。
|
||||
PrivilegesRequiredOverrideText2=%1 可以仅为您安装,或为所有用户安装(需要管理员权限)。
|
||||
PrivilegesRequiredOverrideAllUsers=为所有用户安装(&A)
|
||||
PrivilegesRequiredOverrideAllUsersRecommended=为所有用户安装(&A)(建议选项)
|
||||
PrivilegesRequiredOverrideCurrentUser=仅为我安装(&M)
|
||||
PrivilegesRequiredOverrideCurrentUserRecommended=仅为我安装(&M)(建议选项)
|
||||
|
||||
; *** Misc. errors
|
||||
ErrorCreatingDir=安装程序无法创建目录“%1”
|
||||
ErrorTooManyFilesInDir=无法在目录“%1”中创建文件,因为里面包含太多文件
|
||||
|
||||
; *** Setup common messages
|
||||
ExitSetupTitle=退出安装程序
|
||||
ExitSetupMessage=安装程序尚未完成。如果现在退出,将不会安装该程序。%n%n您之后可以再次运行安装程序完成安装。%n%n现在退出安装程序吗?
|
||||
AboutSetupMenuItem=关于安装程序(&A)...
|
||||
AboutSetupTitle=关于安装程序
|
||||
AboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页:%n%4
|
||||
AboutSetupNote=
|
||||
TranslatorNote=简体中文翻译由 Kira(847320916@qq.com)维护。项目地址:https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation
|
||||
|
||||
; *** Buttons
|
||||
ButtonBack=< 上一步(&B)
|
||||
ButtonNext=下一步(&N) >
|
||||
ButtonInstall=安装(&I)
|
||||
ButtonOK=确定
|
||||
ButtonCancel=取消
|
||||
ButtonYes=是(&Y)
|
||||
ButtonYesToAll=全是(&A)
|
||||
ButtonNo=否(&N)
|
||||
ButtonNoToAll=全否(&O)
|
||||
ButtonFinish=完成(&F)
|
||||
ButtonBrowse=浏览(&B)...
|
||||
ButtonWizardBrowse=浏览(&R)...
|
||||
ButtonNewFolder=新建文件夹(&M)
|
||||
|
||||
; *** "Select Language" dialog messages
|
||||
SelectLanguageTitle=选择安装语言
|
||||
SelectLanguageLabel=选择安装时使用的语言。
|
||||
|
||||
; *** Common wizard text
|
||||
ClickNext=点击“下一步”继续,或点击“取消”退出安装程序。
|
||||
BeveledLabel=
|
||||
BrowseDialogTitle=浏览文件夹
|
||||
BrowseDialogLabel=在下面的列表中选择一个文件夹,然后点击“确定”。
|
||||
NewFolderName=新建文件夹
|
||||
|
||||
; *** "Welcome" wizard page
|
||||
WelcomeLabel1=欢迎使用 [name] 安装向导
|
||||
WelcomeLabel2=即将在您的计算机上安装 [name/ver]。%n%n建议您在继续安装前关闭所有其他应用程序。
|
||||
|
||||
; *** "Password" wizard page
|
||||
WizardPassword=密码
|
||||
PasswordLabel1=此安装程序需要密码验证。
|
||||
PasswordLabel3=请输入密码,然后点击“下一步”继续。密码区分大小写。
|
||||
PasswordEditLabel=密码(&P):
|
||||
IncorrectPassword=您输入的密码不正确,请重新输入。
|
||||
|
||||
; *** "License Agreement" wizard page
|
||||
WizardLicense=许可协议
|
||||
LicenseLabel=请在继续安装前阅读以下重要信息。
|
||||
LicenseLabel3=请仔细阅读下列许可协议。在继续安装前您必须同意这些协议条款。
|
||||
LicenseAccepted=我同意此协议(&A)
|
||||
LicenseNotAccepted=我不同意此协议(&D)
|
||||
|
||||
; *** "Information" wizard pages
|
||||
WizardInfoBefore=信息
|
||||
InfoBeforeLabel=请在继续安装前阅读以下重要信息。
|
||||
InfoBeforeClickLabel=准备好继续安装后,点击“下一步”。
|
||||
WizardInfoAfter=信息
|
||||
InfoAfterLabel=请在继续安装前阅读以下重要信息。
|
||||
InfoAfterClickLabel=准备好继续安装后,点击“下一步”。
|
||||
|
||||
; *** "User Information" wizard page
|
||||
WizardUserInfo=用户信息
|
||||
UserInfoDesc=请输入您的信息。
|
||||
UserInfoName=用户名(&U):
|
||||
UserInfoOrg=组织(&O):
|
||||
UserInfoSerial=序列号(&S):
|
||||
UserInfoNameRequired=请输入用户名。
|
||||
|
||||
; *** "Select Destination Location" wizard page
|
||||
WizardSelectDir=选择目标位置
|
||||
SelectDirDesc=您想将 [name] 安装在哪里?
|
||||
SelectDirLabel3=安装程序将安装 [name] 到下面的文件夹中。
|
||||
SelectDirBrowseLabel=点击“下一步”继续。如果您想选择其他文件夹,点击“浏览”。
|
||||
DiskSpaceGBLabel=至少需要有 [gb] GB 的可用磁盘空间。
|
||||
DiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。
|
||||
CannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。
|
||||
CannotInstallToUNCPath=安装程序无法安装到一个 UNC 路径。
|
||||
InvalidPath=您必须输入一个带驱动器卷标的完整路径,例如:%n%nC:\App%n%n或UNC路径:%n%n\\server\share
|
||||
InvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选择其他位置。
|
||||
DiskSpaceWarningTitle=磁盘空间不足
|
||||
DiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装,但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗?
|
||||
DirNameTooLong=文件夹名称或路径太长。
|
||||
InvalidDirName=文件夹名称无效。
|
||||
BadDirName32=文件夹名称不能包含下列任何字符:%n%n%1
|
||||
DirExistsTitle=文件夹已存在
|
||||
DirExists=文件夹:%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗?
|
||||
DirDoesntExistTitle=文件夹不存在
|
||||
DirDoesntExist=文件夹:%n%n%1%n%n不存在。您想要创建此文件夹吗?
|
||||
|
||||
; *** "Select Components" wizard page
|
||||
WizardSelectComponents=选择组件
|
||||
SelectComponentsDesc=您想安装哪些程序组件?
|
||||
SelectComponentsLabel2=选中您想安装的组件;取消您不想安装的组件。然后点击“下一步”继续。
|
||||
FullInstallation=完全安装
|
||||
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
|
||||
CompactInstallation=简洁安装
|
||||
CustomInstallation=自定义安装
|
||||
NoUninstallWarningTitle=组件已存在
|
||||
NoUninstallWarning=安装程序检测到下列组件已安装在您的计算机中:%n%n%1%n%n取消选中这些组件不会卸载它们。%n%n确定要继续吗?
|
||||
ComponentSize1=%1 KB
|
||||
ComponentSize2=%1 MB
|
||||
ComponentsDiskSpaceGBLabel=当前选择的组件需要至少 [gb] GB 的磁盘空间。
|
||||
ComponentsDiskSpaceMBLabel=当前选择的组件需要至少 [mb] MB 的磁盘空间。
|
||||
|
||||
; *** "Select Additional Tasks" wizard page
|
||||
WizardSelectTasks=选择附加任务
|
||||
SelectTasksDesc=您想要安装程序执行哪些附加任务?
|
||||
SelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务,然后点击“下一步”。
|
||||
|
||||
; *** "Select Start Menu Folder" wizard page
|
||||
WizardSelectProgramGroup=选择开始菜单文件夹
|
||||
SelectStartMenuFolderDesc=安装程序应该在哪里放置程序的快捷方式?
|
||||
SelectStartMenuFolderLabel3=安装程序将在下列“开始”菜单文件夹中创建程序的快捷方式。
|
||||
SelectStartMenuFolderBrowseLabel=点击“下一步”继续。如果您想选择其他文件夹,点击“浏览”。
|
||||
MustEnterGroupName=您必须输入一个文件夹名称。
|
||||
GroupNameTooLong=文件夹名称或路径太长。
|
||||
InvalidGroupName=文件夹名称无效。
|
||||
BadGroupName=文件夹名称不能包含下列任何字符:%n%n%1
|
||||
NoProgramGroupCheck2=不创建开始菜单文件夹(&D)
|
||||
|
||||
; *** "Ready to Install" wizard page
|
||||
WizardReady=准备安装
|
||||
ReadyLabel1=安装程序准备就绪,现在可以开始安装 [name] 到您的计算机。
|
||||
ReadyLabel2a=点击“安装”继续此安装程序。如果您想重新考虑或修改任何设置,点击“上一步”。
|
||||
ReadyLabel2b=点击“安装”继续此安装程序。
|
||||
ReadyMemoUserInfo=用户信息:
|
||||
ReadyMemoDir=目标位置:
|
||||
ReadyMemoType=安装类型:
|
||||
ReadyMemoComponents=已选择组件:
|
||||
ReadyMemoGroup=开始菜单文件夹:
|
||||
ReadyMemoTasks=附加任务:
|
||||
|
||||
; *** TDownloadWizardPage wizard page and DownloadTemporaryFile
|
||||
DownloadingLabel2=正在下载文件...
|
||||
ButtonStopDownload=停止下载(&S)
|
||||
StopDownload=您确定要停止下载吗?
|
||||
ErrorDownloadAborted=下载已中止
|
||||
ErrorDownloadFailed=下载失败:%1 %2
|
||||
ErrorDownloadSizeFailed=获取大小失败:%1 %2
|
||||
ErrorProgress=无效的进度:%1 / %2
|
||||
ErrorFileSize=文件大小错误:预期 %1,实际 %2
|
||||
|
||||
; *** TExtractionWizardPage wizard page and ExtractArchive
|
||||
ExtractingLabel=正在提取文件...
|
||||
ButtonStopExtraction=停止提取(&S)
|
||||
StopExtraction=您确定要停止提取吗?
|
||||
ErrorExtractionAborted=提取已中止
|
||||
ErrorExtractionFailed=提取失败:%1
|
||||
|
||||
; *** Archive extraction failure details
|
||||
ArchiveIncorrectPassword=密码不正确
|
||||
ArchiveIsCorrupted=压缩包已损坏
|
||||
ArchiveUnsupportedFormat=不支持的压缩包格式
|
||||
|
||||
; *** "Preparing to Install" wizard page
|
||||
WizardPreparing=正在准备安装
|
||||
PreparingDesc=安装程序正在准备安装 [name] 到您的计算机。
|
||||
PreviousInstallNotCompleted=先前的程序安装或卸载未完成,需要您重启计算机。%n%n在重启计算机后,再次运行安装程序以完成 [name] 的安装。
|
||||
CannotContinue=安装程序不能继续。请点击“取消”退出。
|
||||
ApplicationsFound=以下应用程序正在使用将由安装程序更新的文件。建议您允许安装程序自动关闭这些应用程序。
|
||||
ApplicationsFound2=以下应用程序正在使用将由安装程序更新的文件。建议您允许安装程序自动关闭这些应用程序。安装完成后,安装程序将尝试重新启动这些应用程序。
|
||||
CloseApplications=自动关闭应用程序(&A)
|
||||
DontCloseApplications=不要关闭应用程序(&D)
|
||||
ErrorCloseApplications=安装程序无法自动关闭所有应用程序。建议您在继续之前,关闭所有在使用需要由安装程序更新的文件的应用程序。
|
||||
PrepareToInstallNeedsRestart=安装程序必须重启您的计算机。计算机重启后,请再次运行安装程序以完成 [name] 的安装。%n%n要立即重启吗?
|
||||
|
||||
; *** "Installing" wizard page
|
||||
WizardInstalling=正在安装
|
||||
InstallingLabel=安装程序正在安装 [name] 到您的计算机,请稍候。
|
||||
|
||||
; *** "Setup Completed" wizard page
|
||||
FinishedHeadingLabel=[name] 安装完成
|
||||
FinishedLabelNoIcons=安装程序已在您的计算机中安装了 [name]。
|
||||
FinishedLabel=安装程序已在您的计算机中安装了 [name]。您可以通过已安装的快捷方式运行此应用程序。
|
||||
ClickFinish=点击“完成”退出安装程序。
|
||||
FinishedRestartLabel=为完成 [name] 的安装,安装程序必须重新启动您的计算机。要立即重启吗?
|
||||
FinishedRestartMessage=为完成 [name] 的安装,安装程序必须重新启动您的计算机。%n%n要立即重启吗?
|
||||
ShowReadmeCheck=是,我想查阅自述文件
|
||||
YesRadio=是,立即重启计算机(&Y)
|
||||
NoRadio=否,稍后重启计算机(&N)
|
||||
; used for example as 'Run MyProg.exe'
|
||||
RunEntryExec=运行 %1
|
||||
; used for example as 'View Readme.txt'
|
||||
RunEntryShellExec=查阅 %1
|
||||
|
||||
; *** "Setup Needs the Next Disk" stuff
|
||||
ChangeDiskTitle=安装程序需要下一张磁盘
|
||||
SelectDiskLabel2=请插入磁盘 %1 并点击“确定”。%n%n如果这个磁盘中的文件可以在下列文件夹之外的文件夹中找到,请输入正确的路径或点击“浏览”。
|
||||
PathLabel=路径(&P):
|
||||
FileNotInDir2=“%2”中找不到文件“%1”。请插入正确的磁盘或选择其他文件夹。
|
||||
SelectDirectoryLabel=请指定下一张磁盘的位置。
|
||||
|
||||
; *** Installation phase messages
|
||||
SetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。
|
||||
AbortRetryIgnoreSelectAction=选择操作
|
||||
AbortRetryIgnoreRetry=重试(&T)
|
||||
AbortRetryIgnoreIgnore=忽略错误并继续(&I)
|
||||
AbortRetryIgnoreCancel=取消安装
|
||||
RetryCancelSelectAction=选择操作
|
||||
RetryCancelRetry=重试(&T)
|
||||
RetryCancelCancel=取消
|
||||
|
||||
; *** Installation status messages
|
||||
StatusClosingApplications=正在关闭应用程序...
|
||||
StatusCreateDirs=正在创建目录...
|
||||
StatusExtractFiles=正在提取文件...
|
||||
StatusDownloadFiles=正在下载文件...
|
||||
StatusCreateIcons=正在创建快捷方式...
|
||||
StatusCreateIniEntries=正在创建 INI 条目...
|
||||
StatusCreateRegistryEntries=正在创建注册表条目...
|
||||
StatusRegisterFiles=正在注册文件...
|
||||
StatusSavingUninstall=正在保存卸载信息...
|
||||
StatusRunProgram=正在完成安装...
|
||||
StatusRestartingApplications=正在重启应用程序...
|
||||
StatusRollback=正在撤销更改...
|
||||
|
||||
; *** Misc. errors
|
||||
ErrorInternal2=内部错误:%1
|
||||
ErrorFunctionFailedNoCode=%1 失败
|
||||
ErrorFunctionFailed=%1 失败;错误代码 %2
|
||||
ErrorFunctionFailedWithMessage=%1 失败;错误代码 %2.%n%3
|
||||
ErrorExecutingProgram=无法执行文件:%n%1
|
||||
|
||||
; *** Registry errors
|
||||
ErrorRegOpenKey=打开注册表项时出错:%n%1\%2
|
||||
ErrorRegCreateKey=创建注册表项时出错:%n%1\%2
|
||||
ErrorRegWriteKey=写入注册表项时出错:%n%1\%2
|
||||
|
||||
; *** INI errors
|
||||
ErrorIniEntry=在文件“%1”中创建 INI 条目时出错。
|
||||
|
||||
; *** File copying errors
|
||||
FileAbortRetryIgnoreSkipNotRecommended=跳过此文件(&S)(不推荐)
|
||||
FileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续(&I)(不推荐)
|
||||
SourceIsCorrupted=源文件已损坏
|
||||
SourceDoesntExist=源文件“%1”不存在
|
||||
SourceVerificationFailed=源文件验证失败:%1
|
||||
VerificationSignatureDoesntExist=签名文件“%1”不存在
|
||||
VerificationSignatureInvalid=签名文件“%1”无效
|
||||
VerificationKeyNotFound=签名文件“%1”使用了未知的密钥
|
||||
VerificationFileNameIncorrect=文件名不正确
|
||||
VerificationFileTagIncorrect=文件标签不正确
|
||||
VerificationFileSizeIncorrect=文件大小不正确
|
||||
VerificationFileHashIncorrect=文件哈希值不正确
|
||||
ExistingFileReadOnly2=无法替换现有文件,它是只读的。
|
||||
ExistingFileReadOnlyRetry=移除只读属性并重试(&R)
|
||||
ExistingFileReadOnlyKeepExisting=保留现有文件(&K)
|
||||
ErrorReadingExistingDest=尝试读取现有文件时出错:
|
||||
FileExistsSelectAction=选择操作
|
||||
FileExists2=文件已经存在。
|
||||
FileExistsOverwriteExisting=覆盖已存在的文件(&O)
|
||||
FileExistsKeepExisting=保留现有的文件(&K)
|
||||
FileExistsOverwriteOrKeepAll=为所有冲突文件执行此操作(&D)
|
||||
ExistingFileNewerSelectAction=选择操作
|
||||
ExistingFileNewer2=现有的文件比安装程序将要安装的文件还要新。
|
||||
ExistingFileNewerOverwriteExisting=覆盖已存在的文件(&O)
|
||||
ExistingFileNewerKeepExisting=保留现有的文件(&K)(推荐)
|
||||
ExistingFileNewerOverwriteOrKeepAll=为所有冲突文件执行此操作(&D)
|
||||
ErrorChangingAttr=尝试更改下列现有文件的属性时出错:
|
||||
ErrorCreatingTemp=尝试在目标目录创建文件时出错:
|
||||
ErrorReadingSource=尝试读取下列源文件时出错:
|
||||
ErrorCopying=尝试复制下列文件时出错:
|
||||
ErrorDownloading=尝试下载文件时出错:
|
||||
ErrorExtracting=尝试提取压缩包时出错:
|
||||
ErrorReplacingExistingFile=尝试替换现有文件时出错:
|
||||
ErrorRestartReplace=重启并替换失败:
|
||||
ErrorRenamingTemp=尝试重命名下列目标目录中的一个文件时出错:
|
||||
ErrorRegisterServer=无法注册 DLL/OCX:%1
|
||||
ErrorRegSvr32Failed=RegSvr32 失败;退出代码 %1
|
||||
ErrorRegisterTypeLib=无法注册类库:%1
|
||||
|
||||
; *** Uninstall display name markings
|
||||
; used for example as 'My Program (32-bit)'
|
||||
UninstallDisplayNameMark=%1 (%2)
|
||||
; used for example as 'My Program (32-bit, All users)'
|
||||
UninstallDisplayNameMarks=%1 (%2, %3)
|
||||
UninstallDisplayNameMark32Bit=32 位
|
||||
UninstallDisplayNameMark64Bit=64 位
|
||||
UninstallDisplayNameMarkAllUsers=所有用户
|
||||
UninstallDisplayNameMarkCurrentUser=当前用户
|
||||
|
||||
; *** Post-installation errors
|
||||
ErrorOpeningReadme=尝试打开自述文件时出错。
|
||||
ErrorRestartingComputer=安装程序无法重启计算机,请手动重启。
|
||||
|
||||
; *** Uninstaller messages
|
||||
UninstallNotFound=文件“%1”不存在。无法卸载。
|
||||
UninstallOpenError=文件“%1”不能被打开。无法卸载
|
||||
UninstallUnsupportedVer=此版本的卸载程序无法识别卸载日志文件“%1”的格式。无法卸载
|
||||
UninstallUnknownEntry=卸载日志中遇到一个未知条目(%1)
|
||||
ConfirmUninstall=您确认要完全移除 %1 及其所有组件吗?
|
||||
UninstallOnlyOnWin64=仅允许在 64 位 Windows 中卸载此程序。
|
||||
OnlyAdminCanUninstall=仅使用管理员权限的用户能完成此卸载。
|
||||
UninstallStatusLabel=正在从您的计算机中移除 %1,请稍候。
|
||||
UninstalledAll=已顺利从您的计算机中移除 %1。
|
||||
UninstalledMost=%1 卸载完成。%n%n有部分内容未能被删除,但您可以手动删除它们。
|
||||
UninstalledAndNeedsRestart=为完成 %1 的卸载,需要重启您的计算机。%n%n要立即重启吗?
|
||||
UninstallDataCorrupted=文件“%1”已损坏。无法卸载
|
||||
|
||||
; *** Uninstallation phase messages
|
||||
ConfirmDeleteSharedFileTitle=删除共享的文件吗?
|
||||
ConfirmDeleteSharedFile2=系统表示下列共享的文件已不再有其他程序使用。您希望卸载程序删除这些共享的文件吗?%n%n如果删除这些文件,但仍有程序在使用这些文件,则这些程序可能出现异常。如果您不能确定,请选择“否”,保留该文件在系统中不会造成任何损害。
|
||||
SharedFileNameLabel=文件名:
|
||||
SharedFileLocationLabel=位置:
|
||||
WizardUninstalling=卸载状态
|
||||
StatusUninstalling=正在卸载 %1...
|
||||
|
||||
; *** Shutdown block reasons
|
||||
ShutdownBlockReasonInstallingApp=正在安装 %1。
|
||||
ShutdownBlockReasonUninstallingApp=正在卸载 %1。
|
||||
|
||||
; The custom messages below aren't used by Setup itself, but if you make
|
||||
; use of them in your scripts, you'll want to translate them.
|
||||
|
||||
[CustomMessages]
|
||||
|
||||
NameAndVersion=%1 版本 %2
|
||||
AdditionalIcons=附加快捷方式:
|
||||
CreateDesktopIcon=创建桌面快捷方式(&D)
|
||||
CreateQuickLaunchIcon=创建快速启动栏快捷方式(&Q)
|
||||
ProgramOnTheWeb=%1 网站
|
||||
UninstallProgram=卸载 %1
|
||||
LaunchProgram=运行 %1
|
||||
AssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A)
|
||||
AssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联...
|
||||
AutoStartProgramGroupDescription=启动:
|
||||
AutoStartProgram=自动启动 %1
|
||||
AddonHostProgramNotFound=您选择的文件夹中无法找到 %1。%n%n您要继续吗?
|
||||
@@ -0,0 +1,126 @@
|
||||
"""生成应用图标 — 白色 squircle 背景 + 紫色 Logo。
|
||||
|
||||
设计 (与 frontend Logo.tsx / favicon.svg 一致):
|
||||
背景: 白色 squircle (macOS Big Sur+ 要求不透明 squircle 背景, 用白色填充)
|
||||
内容: 紫色 #5B21B6 方括号 + K线 (上影短/下影长, bullish 站稳)
|
||||
|
||||
蜡烛几何 (32x32 viewBox):
|
||||
wick: y=7 ~ y=25
|
||||
body: y=9 ~ y=19 (偏上)
|
||||
→ 上影 = 2 (短), 下影 = 6 (长)
|
||||
|
||||
每尺寸独立绘制: 线条占比全尺寸统一 (~9.4%), 小尺寸微调补偿, 不随尺寸递减。
|
||||
运行: python packaging/generate_icon.py
|
||||
产物:
|
||||
packaging/icon.ico — Windows (16/32/48/64/128/256)
|
||||
packaging/icon.icns — macOS (16/32/64/128/256/512, 含 @2x)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
OUT_ICO = Path(__file__).parent / "icon.ico"
|
||||
OUT_ICNS = Path(__file__).parent / "icon.icns"
|
||||
|
||||
# 背景: 白色 squircle (不透明, macOS 规范)
|
||||
BG = (255, 255, 255, 255)
|
||||
# logo 线条色: 紫色 #5B21B6 (与 Logo.tsx / favicon.svg 一致)
|
||||
LOGO = (91, 33, 182, 255) # #5B21B6
|
||||
# wick 影线: 同色不透明 (半透明在小尺寸会糊掉)
|
||||
LOGO_WICK = (91, 33, 182, 255)
|
||||
|
||||
|
||||
def _draw(size: int, sw_b: float, sw_w: float, body_w: float) -> Image.Image:
|
||||
"""绘制单尺寸图标 (像素直接映射 32-viewBox)。"""
|
||||
factor = max(8, 2048 // size)
|
||||
s = size * factor
|
||||
img = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
# 品牌色 squircle 背景 (圆角方块, macOS Big Sur+ 规范形状, 不透明)
|
||||
# 纯色填充, 不用渐变 — 渐变在 16px 小尺寸下看不出, 反而易引入 alpha 合成 bug
|
||||
d.rounded_rectangle([0, 0, s - 1, s - 1], radius=int(s * 0.22), fill=BG)
|
||||
|
||||
def p(v):
|
||||
return v * s / 32
|
||||
|
||||
swb = max(1, int(round(sw_b * factor)))
|
||||
sww = max(1, int(round(sw_w * factor)))
|
||||
|
||||
# 方括号 [ ]
|
||||
for pts in [[(10, 4), (4, 4), (4, 28), (10, 28)], [(22, 4), (28, 4), (28, 28), (22, 28)]]:
|
||||
scaled = [(p(x), p(y)) for x, y in pts]
|
||||
for i in range(len(scaled) - 1):
|
||||
d.line([scaled[i], scaled[i + 1]], fill=LOGO, width=swb, joint="curve")
|
||||
|
||||
# wick 影线 (上短下长)
|
||||
d.line([(p(16), p(7)), (p(16), p(25))], fill=LOGO_WICK, width=sww)
|
||||
wcap = sww // 2 + 1
|
||||
for cy in [7, 25]:
|
||||
d.ellipse([p(16) - wcap, p(cy) - wcap, p(16) + wcap, p(cy) + wcap], fill=LOGO_WICK)
|
||||
|
||||
# body 实体 (偏上 → 上影短下影长)
|
||||
d.rounded_rectangle(
|
||||
[p(16 - body_w / 2), p(9), p(16 + body_w / 2), p(19)],
|
||||
radius=p(0.5), fill=LOGO,
|
||||
)
|
||||
|
||||
return img.resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
def draw_logo(size: int) -> Image.Image:
|
||||
"""按尺寸选参数, 保证各尺寸视觉粗细一致。
|
||||
|
||||
核心原则: 所有尺寸线条占图标的视觉比例统一, 不能出现"大尺寸反而显细"。
|
||||
基准定为 sw=4.0/32 (12.5%) — 经多轮对比选定, Dock 放大时线条仍清晰有力。
|
||||
小尺寸在基准上略加粗, 抵消像素化糊掉; 大尺寸统一用基准, 不递减。
|
||||
"""
|
||||
if size <= 16:
|
||||
return _draw(size, sw_b=4.5, sw_w=3.8, body_w=9)
|
||||
elif size <= 32:
|
||||
return _draw(size, sw_b=4.2, sw_w=3.6, body_w=8)
|
||||
else:
|
||||
# ≥48px 统一用基准 4.0, 不随尺寸递减
|
||||
return _draw(size, sw_b=4.0, sw_w=3.4, body_w=8)
|
||||
|
||||
|
||||
def _save_ico(images_by_size: dict[int, Image.Image]) -> None:
|
||||
"""Windows .ico (16/32/48/64/128/256)。主图 256 优先, 资源管理器大图清晰。"""
|
||||
sizes = [16, 32, 48, 64, 128, 256]
|
||||
images = [images_by_size[s] for s in sizes]
|
||||
images[-1].save(
|
||||
OUT_ICO, format="ICO",
|
||||
sizes=[(s, s) for s in sizes],
|
||||
append_images=images[:-1],
|
||||
)
|
||||
print(f"生成: {OUT_ICO} (尺寸 {sizes})")
|
||||
|
||||
|
||||
def _save_icns(images_by_size: dict[int, Image.Image]) -> None:
|
||||
"""macOS .icns (16/32/64/128/256/512)。
|
||||
|
||||
Apple 要求 .icns 至少含一张大图 (512 或 1024) 才合法, 否则 Finder/Dock
|
||||
不显示。这里用 512 作主图 (Pillow 12 写 1024 需额外编码, 512 已覆盖
|
||||
Retina @2x)。@2x 高清由系统从大图自动缩放, 无需单独提供。
|
||||
"""
|
||||
sizes = [16, 32, 64, 128, 256, 512]
|
||||
images = [images_by_size[s] for s in sizes]
|
||||
images[-1].save(
|
||||
OUT_ICNS, format="ICNS",
|
||||
append_images=images[:-1],
|
||||
)
|
||||
print(f"生成: {OUT_ICNS} (尺寸 {sizes})")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# 一次绘制所有用到的尺寸 (ico 和 icns 取并集), 避免重复绘制
|
||||
all_sizes = {16, 32, 48, 64, 128, 256, 512}
|
||||
images_by_size = {sz: draw_logo(sz) for sz in all_sizes}
|
||||
_save_ico(images_by_size)
|
||||
_save_icns(images_by_size)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,283 @@
|
||||
"""图标设计方案预览 — 生成多个精致方案供挑选。
|
||||
|
||||
每个方案以 1024x1024 超清绘制 (4x 超采样抗锯齿), 输出 PNG 预览。
|
||||
选定后, 用 generate_icon.py 产出最终多尺寸 .ico。
|
||||
|
||||
品牌色系 (从 favicon #5B21B6 延伸):
|
||||
深紫 #2E1065 主紫 #5B21B6 亮紫 #7C3AED 浅紫 #A78BFA
|
||||
强调金 #FBBF24 (金融上涨感)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import math
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFilter
|
||||
|
||||
OUT = Path(__file__).parent / "designs"
|
||||
OUT.mkdir(exist_ok=True)
|
||||
|
||||
# 品牌色
|
||||
DEEP = (46, 16, 101) # #2E1065
|
||||
MAIN = (91, 33, 182) # #5B21B6
|
||||
BRIGHT = (124, 58, 237) # #7C3AED
|
||||
LIGHT = (167, 139, 250) # #A78BFA
|
||||
GOLD = (251, 191, 36) # #FBBF24
|
||||
WHITE = (255, 255, 255)
|
||||
|
||||
|
||||
def _supersample(size: int, factor: int = 4):
|
||||
"""返回 (大画布, 缩放比, 最终尺寸)。绘制后缩放回 size, 抗锯齿。"""
|
||||
s = size * factor
|
||||
return Image.new("RGBA", (s, s), (0, 0, 0, 0)), factor, s
|
||||
|
||||
|
||||
def _finish(img: Image.Image, size: int) -> Image.Image:
|
||||
"""超采样缩小到目标尺寸。"""
|
||||
return img.resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
def _v_gradient(size: int, c_top, c_bot, radius_ratio: float = 0.0):
|
||||
"""竖直渐变填充 (可选圆角)。返回 RGBA Image。"""
|
||||
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||
px = img.load()
|
||||
for y in range(size):
|
||||
t = y / max(1, size - 1)
|
||||
r = int(c_top[0] + (c_bot[0] - c_top[0]) * t)
|
||||
g = int(c_top[1] + (c_bot[1] - c_top[1]) * t)
|
||||
b = int(c_top[2] + (c_bot[2] - c_top[2]) * t)
|
||||
for x in range(size):
|
||||
px[x, y] = (r, g, b, 255)
|
||||
if radius_ratio > 0:
|
||||
mask = Image.new("L", (size, size), 0)
|
||||
md = ImageDraw.Draw(mask)
|
||||
r = int(size * radius_ratio)
|
||||
md.rounded_rectangle([0, 0, size - 1, size - 1], radius=r, fill=255)
|
||||
img.putalpha(mask)
|
||||
return img
|
||||
|
||||
|
||||
def _glow(size: int, draw_fn, color, blur_ratio: float = 0.04):
|
||||
"""对绘制内容做柔和光晕: 先画到独立图层, 模糊后叠加。"""
|
||||
layer = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(layer)
|
||||
draw_fn(d)
|
||||
glow = layer.filter(ImageFilter.GaussianBlur(size * blur_ratio))
|
||||
return glow
|
||||
|
||||
|
||||
# ── 方案 1: 渐变紫底 + 白色发光主体 (现代 App 风格) ──────────────
|
||||
def design_1(size: int = 1024) -> Image.Image:
|
||||
img, f, s = _supersample(size)
|
||||
|
||||
# 圆角渐变背景 (深紫 → 亮紫)
|
||||
bg = _v_gradient(s, BRIGHT, DEEP, radius_ratio=0.22)
|
||||
img.alpha_composite(bg)
|
||||
|
||||
d = ImageDraw.Draw(img)
|
||||
u = s / 32 # 单位 (基于32基准)
|
||||
|
||||
# 白色方括号 (圆头粗线)
|
||||
sw = 2.8 * u
|
||||
def draw_brackets(dd):
|
||||
# 左 [
|
||||
dd.line([(11, 6), (5, 6)], fill=WHITE, width=int(sw), joint="curve")
|
||||
dd.line([(5, 6), (5, 26)], fill=WHITE, width=int(sw))
|
||||
dd.line([(5, 26), (11, 26)], fill=WHITE, width=int(sw))
|
||||
# 右 ]
|
||||
dd.line([(21, 6), (27, 6)], fill=WHITE, width=int(sw))
|
||||
dd.line([(27, 6), (27, 26)], fill=WHITE, width=int(sw))
|
||||
dd.line([(27, 26), (21, 26)], fill=WHITE, width=int(sw))
|
||||
|
||||
# 白色 K 线 wick
|
||||
def draw_wick(dd):
|
||||
dd.line([(16, 8), (16, 24)], fill=LIGHT, width=int(1.6 * u))
|
||||
# 白色 K 线 body
|
||||
def draw_body(dd):
|
||||
dd.rounded_rectangle(
|
||||
[12.5 * u, 12 * u, 19.5 * u, 21 * u],
|
||||
radius=1.2 * u, fill=WHITE,
|
||||
)
|
||||
|
||||
# 光晕层 (放大模糊)
|
||||
glow = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
gd = ImageDraw.Draw(glow)
|
||||
draw_body(gd)
|
||||
glow = glow.filter(ImageFilter.GaussianBlur(s * 0.025))
|
||||
# 光晕染色为亮紫
|
||||
tint = Image.new("RGBA", (s, s), BRIGHT + (0,))
|
||||
glow = Image.composite(tint, Image.new("RGBA", (s, s), (0, 0, 0, 0)),
|
||||
glow.point(lambda a: min(255, int(a * 0.6))))
|
||||
img.alpha_composite(glow)
|
||||
|
||||
draw_brackets(d)
|
||||
draw_wick(d)
|
||||
draw_body(d)
|
||||
|
||||
return _finish(img, size)
|
||||
|
||||
|
||||
# ── 方案 2: 透明底 + 渐变紫发光主体 (优雅线条) ──────────────────
|
||||
def design_2(size: int = 1024) -> Image.Image:
|
||||
img, f, s = _supersample(size)
|
||||
d = ImageDraw.Draw(img)
|
||||
u = s / 32
|
||||
|
||||
sw = 3.0 * u
|
||||
|
||||
# 渐变括号: 用浅紫到亮紫。Pillow line 不支持渐变, 分段填色模拟。
|
||||
def gradient_brackets(dd):
|
||||
# 左括号三段, 从上(浅)到下(亮) — 简化为整体亮紫, 配光晕显层次
|
||||
col = BRIGHT
|
||||
# 左 [
|
||||
dd.line([(11, 5), (5, 5)], fill=col, width=int(sw))
|
||||
dd.line([(5, 5), (5, 27)], fill=col, width=int(sw))
|
||||
dd.line([(5, 27), (11, 27)], fill=col, width=int(sw))
|
||||
# 右 ]
|
||||
dd.line([(21, 5), (27, 5)], fill=col, width=int(sw))
|
||||
dd.line([(27, 5), (27, 27)], fill=col, width=int(sw))
|
||||
dd.line([(27, 27), (21, 27)], fill=col, width=int(sw))
|
||||
|
||||
# 主体光晕 (亮紫大模糊)
|
||||
glow_layer = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
gd = ImageDraw.Draw(glow_layer)
|
||||
gradient_brackets(gd)
|
||||
gd.rounded_rectangle([12.5 * u, 12 * u, 19.5 * u, 21 * u], radius=1.2 * u, fill=BRIGHT)
|
||||
glow = glow_layer.filter(ImageFilter.GaussianBlur(s * 0.05))
|
||||
img.alpha_composite(glow)
|
||||
|
||||
# 主体 (亮紫)
|
||||
gradient_brackets(d)
|
||||
d.line([(16, 8), (16, 24)], fill=LIGHT, width=int(1.8 * u))
|
||||
d.rounded_rectangle([12.5 * u, 12 * u, 19.5 * u, 21 * u], radius=1.2 * u, fill=BRIGHT)
|
||||
|
||||
# 高光: body 顶部一道浅紫
|
||||
d.rounded_rectangle([13.5 * u, 12.8 * u, 18.5 * u, 14.5 * u], radius=0.8 * u, fill=LIGHT)
|
||||
|
||||
return _finish(img, size)
|
||||
|
||||
|
||||
# ── 方案 3: 深色底 + 金紫上涨 K 线柱 (金融图表感) ────────────────
|
||||
def design_3(size: int = 1024) -> Image.Image:
|
||||
img, f, s = _supersample(size)
|
||||
|
||||
# 深紫黑圆角底
|
||||
bg = _v_gradient(s, (30, 27, 75), (15, 12, 41), radius_ratio=0.22)
|
||||
img.alpha_composite(bg)
|
||||
d = ImageDraw.Draw(img)
|
||||
u = s / 32
|
||||
|
||||
# 三根上涨 K 线柱 (紫→亮紫→金), 从左到右升高
|
||||
bars = [
|
||||
# (x_center, body_top, body_bottom, wick_top, wick_bottom, color)
|
||||
(10, 17, 24, 14, 26, LIGHT),
|
||||
(16, 13, 20, 10, 22, BRIGHT),
|
||||
(22, 8, 15, 5, 17, GOLD),
|
||||
]
|
||||
bw = 3.2 * u
|
||||
for cx, bt, bb, wt, wb, col in bars:
|
||||
# wick
|
||||
d.line([(cx * u, wt * u), (cx * u, wb * u)], fill=col, width=int(1.2 * u))
|
||||
# body (圆角)
|
||||
d.rounded_rectangle(
|
||||
[cx * u - bw / 2, bt * u, cx * u + bw / 2, bb * u],
|
||||
radius=0.8 * u, fill=col,
|
||||
)
|
||||
|
||||
# 金色柱的光晕
|
||||
glow = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
gd = ImageDraw.Draw(glow)
|
||||
gd.rounded_rectangle([22 * u - bw / 2, 8 * u, 22 * u + bw / 2, 15 * u], radius=0.8 * u, fill=GOLD)
|
||||
glow = glow.filter(ImageFilter.GaussianBlur(s * 0.03))
|
||||
img.alpha_composite(glow)
|
||||
|
||||
# 上涨趋势线 (淡, 连接柱顶)
|
||||
d.line([(10 * u, 17 * u), (16 * u, 13 * u), (22 * u, 8 * u)],
|
||||
fill=(255, 255, 255, 120), width=int(0.6 * u))
|
||||
|
||||
return _finish(img, size)
|
||||
|
||||
|
||||
# ── 方案 4: 渐变底 + K线被方括号"框选" (品牌强调) ────────────────
|
||||
def design_4(size: int = 1024) -> Image.Image:
|
||||
img, f, s = _supersample(size)
|
||||
|
||||
# 斜向渐变背景 (浅紫→主紫), 圆角
|
||||
bg = _v_gradient(s, (139, 92, 246), MAIN, radius_ratio=0.22)
|
||||
img.alpha_composite(bg)
|
||||
d = ImageDraw.Draw(img)
|
||||
u = s / 32
|
||||
|
||||
# 白色粗括号 (圆角连接)
|
||||
sw = 2.2 * u
|
||||
def brackets(dd):
|
||||
dd.rounded_rectangle(
|
||||
[5 * u, 6 * u, 11 * u, 26 * u],
|
||||
radius=2.0 * u, width=int(sw), outline=WHITE,
|
||||
) # 左括号外形
|
||||
dd.rounded_rectangle(
|
||||
[21 * u, 6 * u, 27 * u, 26 * u],
|
||||
radius=2.0 * u, width=int(sw), outline=WHITE,
|
||||
) # 右括号外形
|
||||
# 上面的画法画出的是矩形框, 改回线条式括号但圆头
|
||||
def brackets2(dd):
|
||||
cap = int(sw / 2)
|
||||
# 左 [
|
||||
dd.line([(10.5, 6), (6, 6)], fill=WHITE, width=int(sw))
|
||||
dd.rounded_rectangle([6 * u - cap, 6 * u - cap, 6 * u + cap, 26 * u + cap],
|
||||
radius=cap, fill=WHITE) # 竖干
|
||||
dd.line([(6, 26), (10.5, 26)], fill=WHITE, width=int(sw))
|
||||
# 圆角补点
|
||||
for cx, cy in [(6, 6), (6, 26)]:
|
||||
dd.ellipse([cx * u - cap, cy * u - cap, cx * u + cap, cy * u + cap], fill=WHITE)
|
||||
# 右 ]
|
||||
dd.line([(21.5, 6), (26, 6)], fill=WHITE, width=int(sw))
|
||||
dd.rounded_rectangle([26 * u - cap, 6 * u - cap, 26 * u + cap, 26 * u + cap],
|
||||
radius=cap, fill=WHITE)
|
||||
dd.line([(26, 26), (21.5, 26)], fill=WHITE, width=int(sw))
|
||||
for cx, cy in [(26, 6), (26, 26)]:
|
||||
dd.ellipse([cx * u - cap, cy * u - cap, cx * u + cap, cy * u + cap], fill=WHITE)
|
||||
|
||||
# K 线光晕
|
||||
glow = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
gd = ImageDraw.Draw(glow)
|
||||
gd.rounded_rectangle([12.8 * u, 11.5 * u, 19.2 * u, 21.5 * u], radius=1.4 * u, fill=WHITE)
|
||||
glow = glow.filter(ImageFilter.GaussianBlur(s * 0.02))
|
||||
img.alpha_composite(glow)
|
||||
|
||||
brackets2(d)
|
||||
# wick
|
||||
d.line([(16, 8), (16, 24)], fill=(255, 255, 255, 200), width=int(1.5 * u))
|
||||
# body (实心白)
|
||||
d.rounded_rectangle([12.8 * u, 11.5 * u, 19.2 * u, 21.5 * u], radius=1.4 * u, fill=WHITE)
|
||||
|
||||
return _finish(img, size)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
designs = [
|
||||
("方案1_渐变紫底白色发光", design_1),
|
||||
("方案2_透明底渐变紫线条", design_2),
|
||||
("方案3_深色底金紫上涨K线", design_3),
|
||||
("方案4_浅紫底框选K线", design_4),
|
||||
]
|
||||
# 拼一张对比图 (2x2)
|
||||
cell = 512
|
||||
grid = Image.new("RGBA", (cell * 2 + 60, cell * 2 + 60), (245, 245, 247, 255))
|
||||
gd = ImageDraw.Draw(grid)
|
||||
positions = [(20, 20), (cell + 40, 20), (20, cell + 40), (cell + 40, cell + 40)]
|
||||
for (name, fn), pos in zip(designs, positions):
|
||||
img = fn(cell)
|
||||
# 棋盘格背景透显
|
||||
grid.alpha_composite(img, pos)
|
||||
# 单独存
|
||||
fn(1024).save(OUT / f"{name}.png")
|
||||
print(f" 生成: {name}.png")
|
||||
|
||||
grid.save(OUT / "对比图_2x2.png")
|
||||
print(f"\n对比图: {OUT / '对比图_2x2.png'}")
|
||||
print(f"单图目录: {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,178 @@
|
||||
; ===========================================================================
|
||||
; TickFlow Stock Panel — Inno Setup 安装包脚本
|
||||
; ===========================================================================
|
||||
; 用途: 把 PyInstaller 产出的 dist/TickFlowStockPanel/ 文件夹封装成
|
||||
; 单个 Setup.exe 安装程序 (双击→安装向导→快捷方式→可卸载)。
|
||||
;
|
||||
; 构建 (本地):
|
||||
; 1. 先跑 PyInstaller: cd backend && uv run pyinstaller ../packaging/tickflow.spec
|
||||
; 2. 再跑 Inno Setup: ISCC.exe packaging\tickflow.iss
|
||||
; 3. 产物: packaging\Output\TickFlowStockPanel-Setup-x.x.x.exe
|
||||
;
|
||||
; 设计决策:
|
||||
; - 装到用户目录 {localappdata}\Programs\ (不弹 UAC, 不需管理员)
|
||||
; - 用户数据存在 {app}\data\ (与程序同处一个总目录, 视觉直观)
|
||||
; - 卸载时询问是否删除用户数据 ({app}\data\)
|
||||
; - 覆盖安装(升级)不动 data\: Inno Setup 只写程序文件, data 不在安装清单
|
||||
; - 桌面 + 开始菜单快捷方式
|
||||
; - 卸载入口 (控制面板可见)
|
||||
; ===========================================================================
|
||||
|
||||
#define MyAppName "TickFlow 股票面板"
|
||||
#define MyAppNameEN "TickFlow Stock Panel"
|
||||
#define MyAppExeName "TickFlowStockPanel.exe"
|
||||
#define MyAppPublisher "TickFlow"
|
||||
|
||||
; 版本号: 从 frontend/package.json 读取, 与 Release tag 保持一致
|
||||
; 手动指定更可靠 (CI 传入 /DMyAppVersion)
|
||||
#ifndef MyAppVersion
|
||||
#define MyAppVersion "0.0.0"
|
||||
#endif
|
||||
|
||||
[Setup]
|
||||
; 基本信息
|
||||
AppName={#MyAppName}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
; 默认装到 D 盘 (非系统盘), 用户可在向导中改任意位置
|
||||
; 若 D 盘不存在, [Code] 段 InitializeWizard 会自动回退到用户目录
|
||||
DefaultDirName=D:\TickFlowStockPanel
|
||||
DefaultGroupName={#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir=Output
|
||||
OutputBaseFilename=TickFlowStockPanel-Setup-{#MyAppVersion}
|
||||
|
||||
; 关键: 不需要管理员权限, 永不弹 UAC
|
||||
; 装到 D 盘普通目录 (非 Program Files) 不需要管理员权限
|
||||
PrivilegesRequired=lowest
|
||||
; 允许用户在向导中自由选择安装目录
|
||||
DisableDirPage=no
|
||||
|
||||
; 压缩
|
||||
Compression=lzma2/ultra64
|
||||
SolidCompression=yes
|
||||
LZMAUseSeparateProcess=yes
|
||||
|
||||
; 界面
|
||||
WizardStyle=modern
|
||||
DisableWelcomePage=no
|
||||
DisableReadyPage=no
|
||||
SetupIconFile=icon.ico
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
|
||||
; 卸载相关
|
||||
Uninstallable=yes
|
||||
CreateUninstallRegKey=yes
|
||||
|
||||
[Languages]
|
||||
; 简中语言包内置在 packaging/ 下 (从 Inno Setup 官方仓库获取),
|
||||
; 不依赖安装目录是否含该文件 (CI 友好)。
|
||||
Name: "chinesesimp"; MessagesFile: "ChineseSimplified.isl"
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
|
||||
|
||||
[Files]
|
||||
; 把 PyInstaller 产出的整个文件夹搬进安装目录
|
||||
; Source 路径相对于 .iss 文件所在目录
|
||||
Source: "..\backend\dist\TickFlowStockPanel\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Icons]
|
||||
; 开始菜单
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{group}\卸载 {#MyAppName}"; Filename: "{uninstallexe}"
|
||||
|
||||
; 桌面 (可选, 由 Task 控制)
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
; 安装完成后启动应用
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#MyAppName}}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[UninstallRun]
|
||||
; 卸载前先关闭正在运行的应用 (否则 exe 被占用删不掉)
|
||||
Filename: "{cmd}"; Parameters: "/C taskkill /F /IM {#MyAppExeName}"; Flags: runhidden; RunOnceId: "KillApp"
|
||||
|
||||
; [UninstallDelete] 故意不删 {app}:
|
||||
; 用户数据在 {app}\data\, 若这里写 Type: filesandordirs; Name: "{app}" 会连数据一起删。
|
||||
; 卸载默认行为已足够 —— Inno Setup 会删除它安装清单内的所有程序文件, 只留下运行时
|
||||
; 生成的 data\ 目录。是否清理 data\ 由下方 [Code] 的卸载询问逻辑决定。
|
||||
|
||||
[Code]
|
||||
// ── 辅助函数: 判断目录是否为空 ─────────────────────────────────
|
||||
// Inno Setup 内置无 IsDirEmpty, 用 FindFirst/FindNext 自行实现。
|
||||
// 用于卸载后清理空的 {app} 壳目录。
|
||||
function IsDirEmpty(const Dir: String): Boolean;
|
||||
var
|
||||
FindRec: TFindRec;
|
||||
begin
|
||||
Result := True;
|
||||
if FindFirst(AddBackslash(Dir) + '*', FindRec) then
|
||||
begin
|
||||
try
|
||||
repeat
|
||||
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
|
||||
begin
|
||||
Result := False;
|
||||
Break;
|
||||
end;
|
||||
until not FindNext(FindRec);
|
||||
finally
|
||||
FindClose(FindRec);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// ── 启动时: 若 D 盘不存在, 回退默认路径到用户目录 ───────────────
|
||||
// 避免默认 D:\... 但系统没 D 盘时向导显示无效路径
|
||||
function InitializeSetup(): Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
procedure InitializeWizard();
|
||||
var
|
||||
DefaultDir: String;
|
||||
begin
|
||||
// D 盘存在 → 用 D 盘; 否则回退用户目录 (无需管理员权限)
|
||||
if not DirExists('D:\') then
|
||||
begin
|
||||
DefaultDir := ExpandConstant('{localappdata}\Programs\TickFlowStockPanel');
|
||||
WizardForm.DirEdit.Text := DefaultDir;
|
||||
end;
|
||||
end;
|
||||
|
||||
// ── 卸载时询问是否删除用户数据 ─────────────────────────────────
|
||||
// 用户数据在 {app}\data\ (策略/选股/回测/监控/行情), 与程序同处 {app} 总目录。
|
||||
// Inno Setup 卸载默认只删它装过的程序文件, data\ 会被保留 (覆盖安装/常规卸载都不丢)。
|
||||
// 这里仅在用户明确「彻底卸载」时, 才询问是否清理 data\ + {app} 空壳。
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
var
|
||||
DataDir, AppDir: String;
|
||||
begin
|
||||
if CurUninstallStep = usPostUninstall then
|
||||
begin
|
||||
// {app}\data = 用户数据目录 (与程序同总目录, 子文件夹)
|
||||
DataDir := ExpandConstant('{app}\data');
|
||||
if DirExists(DataDir) then
|
||||
begin
|
||||
if SuppressibleMsgBox(
|
||||
'是否同时删除用户数据?' + #13#10 + #13#10 +
|
||||
'位置: ' + DataDir + #13#10 +
|
||||
'内容: 行情数据、策略、选股结果、回测记录、监控规则等' + #13#10 + #13#10 +
|
||||
'选「是」彻底卸载, 选「否」保留数据(重装后可恢复)。',
|
||||
mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then
|
||||
begin
|
||||
DelTree(DataDir, True, True, True);
|
||||
end;
|
||||
end;
|
||||
// 清理可能残留的空 {app} 壳目录 (程序文件已被 Inno Setup 删除)
|
||||
AppDir := ExpandConstant('{app}');
|
||||
if DirExists(AppDir) and IsDirEmpty(AppDir) then
|
||||
begin
|
||||
DelTree(AppDir, True, True, True);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@@ -0,0 +1,216 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
"""PyInstaller 打包配置 — 桌面客户端 (onedir 模式)。
|
||||
|
||||
为什么 onedir 而非 onefile:
|
||||
- onefile 每次启动都解压到临时 _MEIxxxxx, 与 APScheduler/多线程冲突
|
||||
- onedir 启动更快, 调试更方便 (可看到目录结构), 原生库直接在目录里
|
||||
- 体积差异通过压缩安装包弥补 (CI 里 zip 打包)
|
||||
|
||||
入口: backend/app/desktop.py (桌面版入口, 含 uvicorn + pywebview)
|
||||
|
||||
构建 (在项目根目录):
|
||||
cd frontend && pnpm build # 先构建前端到 frontend/dist
|
||||
pyinstaller packaging/tickflow.spec # 产物在 dist/TickFlowStockPanel/
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PyInstaller.utils.hooks import (
|
||||
collect_all,
|
||||
collect_submodules,
|
||||
collect_data_files,
|
||||
copy_metadata,
|
||||
)
|
||||
|
||||
_IS_MACOS = sys.platform == "darwin"
|
||||
|
||||
block_cipher = None
|
||||
|
||||
# ── 资源路径基准: 项目根 (spec 文件在 packaging/ 下) ──────────────────
|
||||
ROOT = Path(SPECPATH).parent
|
||||
FRONTEND_DIST = str(ROOT / "frontend" / "dist")
|
||||
TIERS_YAML = str(ROOT / "tiers.yaml")
|
||||
BUILTIN_STRATEGIES = str(ROOT / "backend" / "app" / "strategy" / "builtin")
|
||||
# 图标按平台选: Windows 用 .ico, macOS 用 .icns (PyInstaller 对 .ico 在
|
||||
# mac 上静默忽略, 不换格式 Dock/Finder 会显示通用图标)。两者都由
|
||||
# packaging/generate_icon.py 一并生成。
|
||||
APP_ICON = str(ROOT / "packaging" / ("icon.icns" if _IS_MACOS else "icon.ico"))
|
||||
|
||||
# ── 收集带原生库的依赖 (.libs/ 目录必须完整, 否则启动崩) ─────────────
|
||||
# polars / pyarrow / duckdb / fastexcel 都自带共享库子目录
|
||||
datas = []
|
||||
binaries = []
|
||||
hiddenimports = []
|
||||
|
||||
for pkg in ("polars", "pyarrow", "duckdb", "fastexcel"):
|
||||
d, b, h = collect_all(pkg)
|
||||
datas += d
|
||||
binaries += b
|
||||
hiddenimports += h
|
||||
|
||||
# polars-runtime-32 (rtcompat 兼容内核): release.yml 用 --extra legacy-cpu 安装。
|
||||
# 它是独立的伴侣二进制包 (含 .pyd/.so), 与 polars 主包分开发布,
|
||||
# collect_all("polars") 抓不到它的目录 —— 必须显式收集, 否则老 CPU 用户
|
||||
# 运行时 rtcompat 加载器找不到兼容库仍会崩 (Illegal instruction)。
|
||||
# 不存在时 (未装 legacy-cpu) collect_all 返回空, 不影响普通构建。
|
||||
try:
|
||||
rt_d, rt_b, rt_h = collect_all("polars_runtime_32")
|
||||
datas += rt_d
|
||||
binaries += rt_b
|
||||
hiddenimports += rt_h
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# polars 新 ABI 运行时目录 (_polars_runtime_32) 需显式收集子模块
|
||||
hiddenimports += collect_submodules("polars")
|
||||
|
||||
# ── pywebview 平台后端 (动态导入, PyInstaller 默认抓不到) ────────────
|
||||
hiddenimports += collect_submodules("webview")
|
||||
hiddenimports += collect_submodules("webview.platforms")
|
||||
|
||||
# ── 系统通知后端 (winotify/plyer 按平台动态导入) ─────────────────────
|
||||
if sys.platform == "win32":
|
||||
hiddenimports += collect_submodules("winotify")
|
||||
hiddenimports += collect_submodules("plyer")
|
||||
hiddenimports += collect_submodules("plyer.platforms")
|
||||
|
||||
# ── uvicorn 动态导入的模块 (loop/protocol/logging 按字符串加载) ──────
|
||||
hiddenimports += [
|
||||
"uvicorn.logging",
|
||||
"uvicorn.loops",
|
||||
"uvicorn.loops.auto",
|
||||
"uvicorn.loops.asyncio",
|
||||
"uvicorn.protocols",
|
||||
"uvicorn.protocols.http",
|
||||
"uvicorn.protocols.http.auto",
|
||||
"uvicorn.protocols.http.h11_impl",
|
||||
"uvicorn.protocols.websockets",
|
||||
"uvicorn.protocols.websockets.auto",
|
||||
"uvicorn.lifespan",
|
||||
"uvicorn.lifespan.on",
|
||||
]
|
||||
|
||||
# ── fastapi / pydantic 元数据 (版本检测用) ───────────────────────────
|
||||
# 注意: 任何用 importlib.metadata.version() 读版本的包, 都必须 copy_metadata,
|
||||
# 否则 frozen 后报 PackageNotFoundError。tickflow 包内部就是这么读的。
|
||||
# 用容错写法: 不存在的包跳过, 避免不同环境 (有无装某依赖) 导致构建失败。
|
||||
def _safe_metadata(pkg):
|
||||
"""收集包元数据, 包不存在时静默跳过。"""
|
||||
try:
|
||||
return copy_metadata(pkg)
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
for pkg in (
|
||||
"fastapi", "pydantic", "pydantic_settings", "starlette", "anyio",
|
||||
"tickflow", # tickflow/__version__.py 用 importlib.metadata 读版本
|
||||
"uvicorn", "polars", "duckdb", "pyarrow", "httpx", "numpy", "pandas",
|
||||
"openai", "platformdirs", "winotify", "plyer", "apscheduler",
|
||||
"python-dotenv", "fastexcel",
|
||||
):
|
||||
datas += _safe_metadata(pkg)
|
||||
|
||||
# ── 随包资源 (只读, 放进 _MEIPASS) ────────────────────────────────────
|
||||
# 前端 dist → static/ (config.py frozen 模式读 _MEIPASS/static)
|
||||
datas += [(FRONTEND_DIST, "static")]
|
||||
# tiers.yaml → 包根 (config.py frozen 模式读 _MEIPASS/tiers.yaml)
|
||||
datas += [(TIERS_YAML, ".")]
|
||||
# 内置策略 → app/strategy/builtin/ (importlib 动态加载, 不能进 PYZ)
|
||||
datas += [(BUILTIN_STRATEGIES, "app/strategy/builtin")]
|
||||
|
||||
# ── 排除不需要的重型依赖 (主包不含 vectorbt 回测链) ──────────────────
|
||||
excludes = [
|
||||
"vectorbt",
|
||||
"numba",
|
||||
"llvmlite",
|
||||
"matplotlib",
|
||||
"plotly",
|
||||
"ipywidgets",
|
||||
"nbformat",
|
||||
"nbconvert",
|
||||
"jupyter",
|
||||
"IPython",
|
||||
"pytest",
|
||||
"pytest_asyncio",
|
||||
"ruff",
|
||||
"mypy",
|
||||
]
|
||||
|
||||
a = Analysis(
|
||||
[str(ROOT / "backend" / "app" / "desktop.py")],
|
||||
pathex=[str(ROOT / "backend")],
|
||||
binaries=binaries,
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=excludes,
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name="TickFlowStockPanel",
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=False, # UPX 压缩原生库常导致崩溃, 关闭
|
||||
console=False, # 桌面应用: 不显示控制台窗口 (调试时临时改 True 抓日志)
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=APP_ICON, # 应用图标 (与 favicon/logo 一致)
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
name="TickFlowStockPanel",
|
||||
)
|
||||
|
||||
# ── macOS: 封装成 .app 包 ────────────────────────────────────────────
|
||||
# Windows/Linux: 上面 COLLECT 产出的 onedir 目录即是最终产物。
|
||||
# macOS: 额外加 BUNDLE, 把目录包装成标准 .app (Contents/MacOS/...),
|
||||
# 这样 Dock/Finder 能识别, 用户可双击启动, 并显示自定义 .icns 图标。
|
||||
# BUNDLE 必须引用上面的 COLLECT (coll), 它会把 coll 的产物搬进 .app。
|
||||
#
|
||||
# PyInstaller 6 BUNDLE 关键参数 (见 building/osx.py):
|
||||
# - version: → CFBundleShortVersionString (默认 0.0.0, 必须显式传)
|
||||
# - info_plist (单数!): 用户自定义键, update 合并进默认 plist, 可覆盖任意字段
|
||||
if _IS_MACOS:
|
||||
import json
|
||||
|
||||
# 版本号从 frontend/package.json 读, 与 Release tag 对齐
|
||||
_pkg_json = json.loads((ROOT / "frontend" / "package.json").read_text(encoding="utf-8"))
|
||||
APP_VERSION = _pkg_json["version"]
|
||||
|
||||
app = BUNDLE(
|
||||
coll,
|
||||
name="TickFlowStockPanel.app",
|
||||
icon=APP_ICON,
|
||||
bundle_identifier="com.tickflow.stockpanel",
|
||||
version=APP_VERSION, # → CFBundleShortVersionString / CFBundleVersion
|
||||
info_plist={
|
||||
"CFBundleName": "TickFlow Stock Panel",
|
||||
"CFBundleDisplayName": "TickFlow 股票面板",
|
||||
"CFBundleVersion": APP_VERSION,
|
||||
"NSHighResolutionCapable": True,
|
||||
"LSMinimumSystemVersion": "10.13",
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user