Files
ViperApps/ViperQtProject/App/main.vp
T
TermiNexus df8e301720 chore: 初始化 ViperApps 仓库
纳入 11 个示例工程(不含 ViperOS,另行开专题)的源码:已完成新语法迁移(R1-R16 机械规则与 R1/R12 改写)。.gitignore 排除 ViperOS/、各工程编译产物(.tpv_build/.native_build/output/temp)、二进制与 ViperMNIST/data 数据集。
2026-10-07 08:11:49 +08:00

858 lines
38 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# main.vp — ViperQtProject:VS Installer 风格安装向导演示
#
# 纯界面演示(无真实安装逻辑):
# 步骤0 欢迎 → 步骤1 许可协议 → 步骤2 组件选择(树状图)
# → 步骤3 安装选项 → 步骤4 安装进度 → 步骤5 完成
#
# 完善功能:
# - 许可协议页:4 份协议逐份查看、逐份勾选同意(下一步才放下一份,不可跳过)
# - 组件选择:QTreeWidget 树状图分组 + 勾选实时统计(数量 / 磁盘占用)
# - 安装选项(独立页):发布渠道 QComboBox + 安装目录 + 快捷方式/PATH/桌面图标
# - 安装进度:进度条 + 阶段文案 + 实时日志(QPlainTextEdit)
# - 安装按钮点击后禁用,防重复
#
# 说明:
# - 回调签名统一 def Cb(slot_id: int, data: t.CPtr)
# - 跨模块 FFI 调用避免返回指针的函数(用 *_utf8 便捷版 + 整数返回函数)
# - 模块级全局只存 t.CPtr 指针(不存对象)
import t
import stdlib
import viperlib
import vqt6
# ============================================================
# 全局指针(回调里访问)
# ============================================================
app_ptr: t.CPtr = None
stack_ptr: t.CPtr = None
nav0_ptr: t.CPtr = None
nav1_ptr: t.CPtr = None
nav2_ptr: t.CPtr = None
nav3_ptr: t.CPtr = None
nav4_ptr: t.CPtr = None
nav5_ptr: t.CPtr = None
progress_ptr: t.CPtr = None
progress_status_ptr: t.CPtr = None
log_ptr: t.CPtr = None
path_label_ptr: t.CPtr = None
summary_ptr: t.CPtr = None
disk_ptr: t.CPtr = None
finish_summary_ptr: t.CPtr = None
timer_ptr: t.CPtr = None
tree_ptr: t.CPtr = None
license_box_ptr: t.CPtr = None
eula_name_ptr: t.CPtr = None
agree_cb_ptr: t.CPtr = None
license_next_ptr: t.CPtr = None
channel_ptr: t.CPtr = None
install_btn_ptr: t.CPtr = None
opt0_ptr: t.CPtr = None
opt1_ptr: t.CPtr = None
opt2_ptr: t.CPtr = None
theme_sel_ptr: t.CPtr = None
central_ptr: t.CPtr = None
big_ptr: t.CPtr = None
sub_ptr: t.CPtr = None
desc_ptr: t.CPtr = None
lh_ptr: t.CPtr = None
h2_ptr: t.CPtr = None
h2b_ptr: t.CPtr = None
h3_ptr: t.CPtr = None
s3_ptr: t.CPtr = None
h4_ptr: t.CPtr = None
ok_ptr: t.CPtr = None
fsum_ptr: t.CPtr = None
oh_ptr: t.CPtr = None
# 全局状态
cur_step: int = 0
progress_val: int = 0
comp_count: int = 0
install_path: str = "C:/Program Files/Viper6"
channel_name: str = "稳定版(Stable)"
eula_idx: int = 0
eula_agreed: int = 0
cur_theme: int = 0
# ============================================================
# 深色主题 QSS(VS Installer 风格,单行字面量)
# ============================================================
dark_qss: str = "QWidget { background-color: #1e1e1e; color: #d4d4d4; font-family: \"Segoe UI\", \"Microsoft YaHei UI\", \"Microsoft YaHei\", \"PingFang SC\", \"Noto Sans CJK SC\", sans-serif; font-size: 13px; } QPushButton { background-color: #333333; border: 1px solid #555555; border-radius: 4px; padding: 7px 16px; color: #ffffff; } QPushButton:hover { background-color: #454545; border-color: #6a6a6a; } QPushButton:pressed { background-color: #2a6da8; border-color: #4a86c8; } QPushButton:disabled { background-color: #2a2a2a; color: #666666; border-color: #3a3a3a; } QLineEdit, QTextEdit { background-color: #252526; border: 1px solid #555555; border-radius: 4px; padding: 5px 8px; color: #ffffff; } QPlainTextEdit { background-color: #1a1a1a; border: 1px solid #555555; border-radius: 4px; color: #cccccc; } QCheckBox { color: #d4d4d4; spacing: 10px; font-size: 14px; } QProgressBar { background-color: #252526; border: 1px solid #555555; border-radius: 4px; text-align: center; color: #ffffff; font-size: 13px; } QProgressBar::chunk { background-color: #4a86c8; border-radius: 3px; } QTreeWidget { background-color: #1a1a1a; border: 1px solid #555555; border-radius: 4px; color: #d4d4d4; } QTreeWidget::item { height: 26px; padding-left: 4px; } QTreeWidget::item:selected { background-color: #2a6da8; color: #ffffff; } QComboBox { background-color: #252526; border: 1px solid #555555; border-radius: 4px; padding: 5px 10px; color: #ffffff; } QComboBox:hover { border-color: #6a6a6a; } QComboBox::drop-down { border: none; width: 22px; } QComboBox QAbstractItemView { background-color: #252526; color: #ffffff; border: 1px solid #555555; selection-background-color: #2a6da8; } QScrollBar:vertical { background: #252526; width: 10px; margin: 0px; border: none; border-radius: 5px; } QScrollBar::handle:vertical { background: #4a4a4a; min-height: 24px; border-radius: 5px; } QScrollBar::handle:vertical:hover { background: #5a5a5a; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: transparent; } QScrollBar:horizontal { background: #252526; height: 10px; margin: 0px; border: none; border-radius: 5px; } QScrollBar::handle:horizontal { background: #4a4a4a; min-width: 24px; border-radius: 5px; } QScrollBar::handle:horizontal:hover { background: #5a5a5a; } QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; } QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: transparent; }"
light_qss: str = "QWidget { background-color: #f5f6f8; color: #1e1e1e; font-family: \"Segoe UI\", \"Microsoft YaHei UI\", \"Microsoft YaHei\", \"PingFang SC\", \"Noto Sans CJK SC\", sans-serif; font-size: 13px; } QPushButton { background-color: #ffffff; border: 1px solid #b9c0cb; border-radius: 4px; padding: 7px 16px; color: #1e1e1e; } QPushButton:hover { background-color: #eef1f6; border-color: #8a97a8; } QPushButton:pressed { background-color: #2a6da8; border-color: #2a6da8; color: #ffffff; } QPushButton:disabled { background-color: #eceef1; color: #9aa3b0; border-color: #d3d8e0; } QLineEdit, QTextEdit { background-color: #ffffff; border: 1px solid #b9c0cb; border-radius: 4px; padding: 5px 8px; color: #1e1e1e; } QPlainTextEdit { background-color: #ffffff; border: 1px solid #c6ccd6; border-radius: 4px; color: #1e1e1e; } QCheckBox { color: #1e1e1e; spacing: 10px; font-size: 14px; } QProgressBar { background-color: #e2e6ec; border: 1px solid #c6ccd6; border-radius: 4px; text-align: center; color: #1e1e1e; font-size: 13px; } QProgressBar::chunk { background-color: #2a6da8; border-radius: 3px; } QTreeWidget { background-color: #ffffff; border: 1px solid #c6ccd6; border-radius: 4px; color: #1e1e1e; } QTreeWidget::item { height: 26px; padding-left: 4px; } QTreeWidget::item:selected { background-color: #d6e4f5; color: #1e1e1e; } QComboBox { background-color: #ffffff; border: 1px solid #b9c0cb; border-radius: 4px; padding: 5px 10px; color: #1e1e1e; } QComboBox:hover { border-color: #8a97a8; } QComboBox::drop-down { border: none; width: 22px; } QComboBox QAbstractItemView { background-color: #ffffff; color: #1e1e1e; border: 1px solid #b9c0cb; selection-background-color: #d6e4f5; } QScrollBar:vertical { background: #e2e6ec; width: 10px; margin: 0px; border: none; border-radius: 5px; } QScrollBar::handle:vertical { background: #b0b9c6; min-height: 24px; border-radius: 5px; } QScrollBar::handle:vertical:hover { background: #9aa3b0; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: transparent; } QScrollBar:horizontal { background: #e2e6ec; height: 10px; margin: 0px; border: none; border-radius: 5px; } QScrollBar::handle:horizontal { background: #b0b9c6; min-width: 24px; border-radius: 5px; } QScrollBar::handle:horizontal:hover { background: #9aa3b0; } QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; } QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: transparent; }"
# 导航项样式
nav_active_qss: str = "font-size: 15px; color: #ffffff; font-weight: bold; background-color: #2a6da8; padding: 10px 14px; border-radius: 4px;"
nav_idle_qss: str = "font-size: 15px; color: #8a8a8a; padding: 10px 14px;"
nav_active_qss_light: str = "font-size: 15px; color: #ffffff; font-weight: bold; background-color: #2a6da8; padding: 10px 14px; border-radius: 4px;"
nav_idle_qss_light: str = "font-size: 15px; color: #5a6470; padding: 10px 14px;"
# ============================================================
# 文字样式角色(深/浅主题变体)
# ============================================================
t_d_huge: str = "font-size: 34px; color: #ffffff; font-weight: bold;"
t_l_huge: str = "font-size: 34px; color: #1e1e1e; font-weight: bold;"
t_d_title: str = "font-size: 22px; color: #ffffff; font-weight: bold;"
t_l_title: str = "font-size: 22px; color: #1e1e1e; font-weight: bold;"
t_d_sub: str = "font-size: 16px; color: #9cdcfe;"
t_l_sub: str = "font-size: 16px; color: #2a6da8;"
t_d_dim: str = "font-size: 13px; color: #9a9a9a;"
t_l_dim: str = "font-size: 13px; color: #5a6470;"
t_d_accent: str = "font-size: 13px; color: #9cdcfe;"
t_l_accent: str = "font-size: 13px; color: #2a6da8;"
t_d_ok: str = "font-size: 30px; color: #4ec9b0; font-weight: bold;"
t_l_ok: str = "font-size: 30px; color: #178a63; font-weight: bold;"
t_d_body: str = "font-size: 14px; color: #d4d4d4;"
t_l_body: str = "font-size: 14px; color: #1e1e1e;"
t_d_pathbox: str = "background-color: #252526; border: 1px solid #555555; border-radius: 4px; padding: 5px 8px; color: #ffffff;"
t_l_pathbox: str = "background-color: #ffffff; border: 1px solid #b9c0cb; border-radius: 4px; padding: 5px 8px; color: #1e1e1e;"
t_d_section: str = "font-size: 15px; color: #ffffff; font-weight: bold; padding-top: 6px;"
t_l_section: str = "font-size: 15px; color: #1e1e1e; font-weight: bold; padding-top: 6px;"
# ============================================================
# 主题辅助:返回当前主题下的样式字符串
# ============================================================
def NavActiveCss() -> str:
if cur_theme == 1:
return nav_active_qss_light
return nav_active_qss
def NavIdleCss() -> str:
if cur_theme == 1:
return nav_idle_qss_light
return nav_idle_qss
def CssHuge() -> str:
if cur_theme == 1:
return t_l_huge
return t_d_huge
def CssTitle() -> str:
if cur_theme == 1:
return t_l_title
return t_d_title
def CssSub() -> str:
if cur_theme == 1:
return t_l_sub
return t_d_sub
def CssDim() -> str:
if cur_theme == 1:
return t_l_dim
return t_d_dim
def CssAccent() -> str:
if cur_theme == 1:
return t_l_accent
return t_d_accent
def CssOk() -> str:
if cur_theme == 1:
return t_l_ok
return t_d_ok
def CssBody() -> str:
if cur_theme == 1:
return t_l_body
return t_d_body
def CssPathBox() -> str:
if cur_theme == 1:
return t_l_pathbox
return t_d_pathbox
def CssSection() -> str:
if cur_theme == 1:
return t_l_section
return t_d_section
# ============================================================
# EULA 文档文本(多份协议文件)
# ============================================================
license_text: str = "VIPER 6.0 最终用户许可协议\n\n重要 - 请仔细阅读:本最终用户许可协议(以下简称\"协议\")是您与 Viper Software Foundation 之间就 Viper 6.0 软件达成的法律协议。\n\n1. 许可授予。在遵守本协议条款的前提下,您被授予一份非独占、不可转让的许可,允许在您的设备上安装和使用本软件用于开发与测试目的。\n\n2. 使用限制。您不得对本软件进行反向工程、反编译或反汇编(适用法律明确允许的除外);不得删除或修改软件中的版权声明。\n\n3. 开源组件。本软件包含根据 MIT、BSD 与 LGPL 许可发布的第三方开源组件,相关许可文本随附于软件文档中。\n\n4. 免责声明。本软件按\"现状\"提供,不附带任何明示或默示的担保,包括但不限于适销性、特定用途适用性及不侵权的担保。\n\n5. 责任限制。在任何情况下,Viper Software Foundation 均不对因使用或无法使用本软件而产生的任何间接、偶然、特殊或后果性损害承担责任。\n\n6. 协议终止。若您违反本协议的任何条款,本协议将自动终止,您应停止使用并删除本软件的全部副本。\n\n请阅读以上条款。要安装 Viper 6.0,必须接受这些条款并勾选下方的\"我接受许可协议中的条款\"。"
oss_text: str = "第三方开源组件许可声明\n\n本软件的部分功能基于以下第三方开源组件构建,相关许可全文可在各组件的发行包或代码仓库中查阅。\n\n1. Qt 6(LGPL v3 / GPL v3 / 商业许可)\nQt 图形界面框架及 Qt Widgets / Qt Network 模块,用于本向导的用户界面。本演示程序以 LGPL 条款使用 Qt 的共享库方式运行。\n\n2. LLVM / Clang 工具链(Apache License 2.0)\nViper 编译器后端与目标代码生成依赖 LLVM 基础设施。\n\n3. 开源许可证文本库(CC0)\n本向导使用的协议模板文本来源于公共领域资料,可自由复制与修改。\n\n4. 其它依赖\nmingw-w64 运行库(BSD/公共领域)与 zlib(zlib License)等。\n\n上述组件仅用于本软件的构建与运行,不构成对 Viper 语言本身的授权限制。"
privacy_text: str = "隐私政策\n\n本安装向导为本地演示程序,在安装过程中不会收集、上传或存储任何个人身份信息。\n\n1. 本地数据处理。您选择的组件、安装目录与安装选项仅保存在本机配置中,用于完成安装流程。\n\n2. 遥测与诊断。本软件默认不发送任何遥测数据。若您日后启用了诊断反馈功能,仅会上传安装日志中与错误定位相关的必要信息。\n\n3. 第三方链接。向导中出现的任何外部链接(如帮助文档)将在您主动点击时打开,相关站点不受本政策约束。\n\n4. 联系我们。如对本政策有任何疑问,可通过 Viper Software Foundation 官网的支持页面与我们联系。"
credits_text: str = "商标与版权声明\n\nViper、Viper 徽标与相关名称是 Viper Software Foundation 及其关联方的商标。\n\n本演示程序界面设计参考了行业通用安装向导的交互范式,仅用于功能与技术展示,不构成对任何第三方安装程序视觉风格的授权主张。\n\n本软件版权归 Viper Software Foundation 所有,保留一切权利。\n\n版本信息:Viper 6.0.1 · 2026"
# EULA 文档名(显示序号用)
doc_name0: str = "Viper 6.0 最终用户许可协议"
doc_name1: str = "第三方开源组件许可声明"
doc_name2: str = "隐私政策"
doc_name3: str = "商标与版权声明"
# 发布渠道文本
channel_stable: str = "稳定版(Stable)"
channel_preview: str = "预览版(Preview)"
channel_dev: str = "开发版(Developer)"
str_yes: str = "是"
str_no: str = "否"
# ============================================================
# 工具:切换步骤 + 导航高亮
# ============================================================
def SetNavStyle(ptr: t.CPtr, qss: str):
vqt6.vqt6_qwidget_set_stylesheet_utf8(ptr, qss)
def GotoStep(step: int):
global cur_step
cur_step = step
vqt6.vqt6_qstackedwidget_set_current_index(stack_ptr, step)
# 导航高亮(按当前主题)
SetNavStyle(nav0_ptr, NavActiveCss() if step == 0 else NavIdleCss())
SetNavStyle(nav1_ptr, NavActiveCss() if step == 1 else NavIdleCss())
SetNavStyle(nav2_ptr, NavActiveCss() if step == 2 else NavIdleCss())
SetNavStyle(nav3_ptr, NavActiveCss() if step == 3 else NavIdleCss())
SetNavStyle(nav4_ptr, NavActiveCss() if step == 4 else NavIdleCss())
SetNavStyle(nav5_ptr, NavActiveCss() if step == 5 else NavIdleCss())
# ============================================================
# 步骤0:欢迎页
# ============================================================
def OnNext(slot_id: int, data: t.CPtr):
EnterEulaPage()
GotoStep(1)
# ============================================================
# 步骤1:许可协议(逐份查看 + 逐份同意)
# ============================================================
def ShowEulaDoc(idx: int):
"""显示第 idx 份协议,并回填该协议的已同意状态"""
txt: str = license_text
name: str = doc_name0
if idx == 1:
txt = oss_text
name = doc_name1
elif idx == 2:
txt = privacy_text
name = doc_name2
elif idx == 3:
txt = credits_text
name = doc_name3
# 标题(x/4 · 协议名)
buf: bytes = stdlib.malloc(128)
viperlib.snprintf(buf, 128, "%d/4 · %s", idx + 1, name)
vqt6.vqt6_qlabel_set_text_utf8(eula_name_ptr, buf)
stdlib.free(buf)
# 正文
vqt6.vqt6_qplaintextedit_set_plain_text_utf8(license_box_ptr, txt)
# 回填该协议已同意状态
agreed: int = (eula_agreed >> idx) & 1
vqt6.vqt6_qcheckbox_set_checked(agree_cb_ptr, agreed)
vqt6.vqt6_qwidget_set_enabled(license_next_ptr, agreed)
def EnterEulaPage():
"""进入许可页:全部已同意则停在末份,否则续接当前进度"""
global eula_idx
if eula_agreed == 15:
ShowEulaDoc(3)
else:
ShowEulaDoc(eula_idx)
def OnLicenseToggled(slot_id: int, data: t.CPtr):
"""勾选当前协议后才允许下一步"""
checked: int = vqt6.vqt6_qcheckbox_is_checked(agree_cb_ptr)
vqt6.vqt6_qwidget_set_enabled(license_next_ptr, checked)
def OnLicenseNext(slot_id: int, data: t.CPtr):
"""逐份同意:点下一步才放下一份;末份同意后进入组件选择"""
global eula_idx, eula_agreed
idx: int = eula_idx
if idx < 0 or idx > 3:
GotoStep(2)
return
if vqt6.vqt6_qcheckbox_is_checked(agree_cb_ptr) != 0:
eula_agreed = eula_agreed | (1 << idx)
if idx >= 3:
GotoStep(2)
else:
eula_idx = idx + 1
ShowEulaDoc(eula_idx)
def OnLicenseBack(slot_id: int, data: t.CPtr):
GotoStep(0)
# ============================================================
# 步骤2:组件选择(树状图)
# ============================================================
def OnBack(slot_id: int, data: t.CPtr):
EnterEulaPage()
GotoStep(1)
def OnCompNext(slot_id: int, data: t.CPtr):
GotoStep(3)
def UpdateCompInfo():
"""统计树勾选组件数与磁盘占用(跨模块用返回 int 的 vqt6 助手)"""
global comp_count
n: int = vqt6.qtree_checked_count(tree_ptr)
mb: int = vqt6.qtree_checked_size(tree_ptr)
comp_count = n
buf1: bytes = stdlib.malloc(96)
viperlib.snprintf(buf1, 96, "已选择 %d 个组件", n)
vqt6.vqt6_qlabel_set_text_utf8(summary_ptr, buf1)
stdlib.free(buf1)
buf2: bytes = stdlib.malloc(96)
viperlib.snprintf(buf2, 96, "所需磁盘空间: %d MB / 可用 51200 MB", mb)
vqt6.vqt6_qlabel_set_text_utf8(disk_ptr, buf2)
stdlib.free(buf2)
def OnCompToggled(slot_id: int, data: t.CPtr):
"""树节点勾选变化:重算组件数与磁盘占用"""
UpdateCompInfo()
# ============================================================
# 步骤3:安装选项(独立页)
# ============================================================
def OnOptionsBack(slot_id: int, data: t.CPtr):
GotoStep(2)
def OnBrowse(slot_id: int, data: t.CPtr):
global install_path
p: str = vqt6.QFileDialog.getExistingDirectory(None, "选择安装目录", "C:/")
if p is not None and p != "":
install_path = p
vqt6.vqt6_qlabel_set_text_utf8(path_label_ptr, p)
def OnChannelChanged(slot_id: int, data: t.CPtr):
"""发布渠道切换"""
global channel_name
idx: int = vqt6.vqt6_qcombobox_current_index(channel_ptr)
if idx == 1: channel_name = channel_preview
elif idx == 2: channel_name = channel_dev
else: channel_name = channel_stable
def OnInstall(slot_id: int, data: t.CPtr):
"""进入安装页并启动模拟进度"""
global progress_val
vqt6.vqt6_qwidget_set_enabled(install_btn_ptr, 0)
GotoStep(4)
progress_val = 0
vqt6.vqt6_qprogressbar_set_value(progress_ptr, 0)
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在准备安装环境...")
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "== 开始安装 Viper 6.0 ==\n")
vqt6.vqt6_qtimer_start(timer_ptr, 80)
# ============================================================
# 步骤4:安装进度(QTimer 模拟)
# ============================================================
def OnProgressTick(slot_id: int, data: t.CPtr):
global progress_val
progress_val += 4
vqt6.vqt6_qprogressbar_set_value(progress_ptr, progress_val)
if progress_val == 4:
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "[1/4] 正在准备安装环境...\n")
elif progress_val == 28:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在复制核心文件...")
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "[2/4] 正在复制核心文件...\n")
elif progress_val == 52:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在配置 Qt6 组件...")
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "[3/4] 正在配置 Qt6 组件...\n")
elif progress_val == 76:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在注册系统组件...")
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "[4/4] 正在注册系统组件...\n")
elif progress_val >= 100:
vqt6.vqt6_qtimer_stop(timer_ptr)
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "安装完成!\n")
# 完成页摘要(含安装选项 + 渠道)
yn0: str = str_yes if vqt6.vqt6_qcheckbox_is_checked(opt0_ptr) != 0 else str_no
yn1: str = str_yes if vqt6.vqt6_qcheckbox_is_checked(opt1_ptr) != 0 else str_no
yn2: str = str_yes if vqt6.vqt6_qcheckbox_is_checked(opt2_ptr) != 0 else str_no
buf: bytes = stdlib.malloc(512)
viperlib.snprintf(buf, 512, "安装位置: %s\n发布渠道: %s\n已安装组件: %d 个\n快捷方式: %s / PATH: %s / 桌面图标: %s\n\nViper 6.0 已成功安装到您的系统。", install_path, channel_name, comp_count, yn0, yn1, yn2)
vqt6.vqt6_qlabel_set_text_utf8(finish_summary_ptr, buf)
stdlib.free(buf)
GotoStep(5)
# ============================================================
# 步骤5:完成页
# ============================================================
def OnFinish(slot_id: int, data: t.CPtr):
vqt6.vqt6_qapplication_quit(app_ptr)
# ============================================================
# 主题切换(右上角)
# ============================================================
def RestyleAll():
"""按当前主题重刷带内联样式的关键控件"""
vqt6.vqt6_qwidget_set_stylesheet_utf8(big_ptr, CssHuge())
vqt6.vqt6_qwidget_set_stylesheet_utf8(sub_ptr, CssSub())
vqt6.vqt6_qwidget_set_stylesheet_utf8(desc_ptr, CssDim())
vqt6.vqt6_qwidget_set_stylesheet_utf8(lh_ptr, CssTitle())
vqt6.vqt6_qwidget_set_stylesheet_utf8(h2_ptr, CssTitle())
vqt6.vqt6_qwidget_set_stylesheet_utf8(h2b_ptr, CssDim())
vqt6.vqt6_qwidget_set_stylesheet_utf8(h3_ptr, CssTitle())
vqt6.vqt6_qwidget_set_stylesheet_utf8(s3_ptr, CssDim())
vqt6.vqt6_qwidget_set_stylesheet_utf8(h4_ptr, CssTitle())
vqt6.vqt6_qwidget_set_stylesheet_utf8(eula_name_ptr, CssAccent())
vqt6.vqt6_qwidget_set_stylesheet_utf8(summary_ptr, CssAccent())
vqt6.vqt6_qwidget_set_stylesheet_utf8(disk_ptr, CssAccent())
vqt6.vqt6_qwidget_set_stylesheet_utf8(oh_ptr, CssSection())
vqt6.vqt6_qwidget_set_stylesheet_utf8(ok_ptr, CssOk())
vqt6.vqt6_qwidget_set_stylesheet_utf8(fsum_ptr, CssBody())
vqt6.vqt6_qwidget_set_stylesheet_utf8(progress_status_ptr, CssDim())
vqt6.vqt6_qwidget_set_stylesheet_utf8(path_label_ptr, CssPathBox())
def ApplyTheme(theme: int):
"""切换深/浅主题:先设应用调色板(原生控件配色),再换 QSS 并重刷内联样式"""
global cur_theme
cur_theme = theme
vqt6.vqt6_qapplication_set_theme_palette(app_ptr, theme)
if theme == 1:
vqt6.vqt6_qwidget_set_stylesheet_utf8(central_ptr, light_qss)
else:
vqt6.vqt6_qwidget_set_stylesheet_utf8(central_ptr, dark_qss)
RestyleAll()
GotoStep(cur_step)
def SetTextPtrs(big: t.CPtr, sub: t.CPtr, desc: t.CPtr, lh: t.CPtr, h2: t.CPtr, h2b: t.CPtr, h3: t.CPtr, s3: t.CPtr, h4: t.CPtr, ok: t.CPtr, fsum: t.CPtr, oh: t.CPtr):
"""记录各页文本标签指针(供主题切换重刷内联样式)"""
global big_ptr, sub_ptr, desc_ptr, lh_ptr
global h2_ptr, h2b_ptr, h3_ptr, s3_ptr, h4_ptr, ok_ptr, fsum_ptr, oh_ptr
big_ptr = big
sub_ptr = sub
desc_ptr = desc
lh_ptr = lh
h2_ptr = h2
h2b_ptr = h2b
h3_ptr = h3
s3_ptr = s3
h4_ptr = h4
ok_ptr = ok
fsum_ptr = fsum
oh_ptr = oh
def OnThemeChanged(slot_id: int, data: t.CPtr):
idx: int = vqt6.vqt6_qcombobox_current_index(theme_sel_ptr)
ApplyTheme(idx)
def main() -> int:
global app_ptr, stack_ptr
global nav0_ptr, nav1_ptr, nav2_ptr, nav3_ptr, nav4_ptr, nav5_ptr
global progress_ptr, progress_status_ptr, log_ptr, path_label_ptr
global summary_ptr, disk_ptr, finish_summary_ptr, timer_ptr
global tree_ptr, license_box_ptr, eula_name_ptr
global agree_cb_ptr, license_next_ptr, install_btn_ptr
global channel_ptr, opt0_ptr, opt1_ptr, opt2_ptr
global theme_sel_ptr, central_ptr
app = vqt6.QApplication(t.CPtr(0))
app_ptr = t.CPtr(app._ptr)
vqt6.vqt6_qapplication_set_theme_palette(app_ptr, 0)
# 启用 item-view(组件树)勾选与复选框同一套自绘渲染
vqt6.vqt6_enable_custom_item_checks(app_ptr)
# 主窗口(更宽更高)
win = vqt6.QMainWindow()
win.setWindowTitle("Viper 6.0 Installer")
win.resize(1180, 780)
vqt6.vqt6_qwidget_set_minimum_size(win._ptr, 1080, 700)
central = vqt6.QWidget()
central.setStyleSheet(dark_qss)
win.setCentralWidget(central._ptr)
central_ptr = t.CPtr(central._ptr)
vroot = vqt6.QVBoxLayout(central)
vroot.setContentsMargins(0, 0, 0, 0)
vroot.setSpacing(0)
# ===== 右上角:主题切换 =====
topbar = vqt6.QHBoxLayout()
topbar.setContentsMargins(0, 6, 14, 2)
topbar.setSpacing(8)
vroot.addLayout(topbar._ptr)
topbar.addStretch(1)
theme_lb = vqt6.QLabel("主题:")
topbar.addWidget(theme_lb._ptr)
theme_sel = vqt6.QComboBox()
theme_sel.addItem("深色")
theme_sel.addItem("浅色")
theme_sel.setCurrentIndex(0)
topbar.addWidget(theme_sel._ptr)
theme_sel_ptr = t.CPtr(theme_sel._ptr)
theme_sel.currentIndexChanged(OnThemeChanged, 0)
root = vqt6.QHBoxLayout()
root.setContentsMargins(0, 0, 0, 0)
vroot.addLayout(root._ptr)
# ===== 左导航面板(宽度固定,高度随窗口拉伸) =====
nav_panel = vqt6.QWidget()
vqt6.vqt6_qwidget_set_minimum_size(nav_panel._ptr, 240, 0)
vqt6.vqt6_qwidget_set_maximum_size(nav_panel._ptr, 240, 10000)
nav_box = vqt6.QVBoxLayout(nav_panel)
nav_box.setContentsMargins(10, 16, 10, 10)
nav_box.setSpacing(6)
title_lb = vqt6.QLabel("Viper 6.0")
vqt6.vqt6_qwidget_set_stylesheet_utf8(title_lb._ptr, "font-size: 20px; color: #4a86c8; font-weight: bold; padding: 6px 14px;")
nav_box.addWidget(title_lb._ptr)
nav0 = vqt6.QLabel("欢迎")
nav0.setStyleSheet(nav_active_qss)
nav_box.addWidget(nav0._ptr)
nav0_ptr = t.CPtr(nav0._ptr)
nav1 = vqt6.QLabel("许可协议")
nav1.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav1._ptr)
nav1_ptr = t.CPtr(nav1._ptr)
nav2 = vqt6.QLabel("选择组件")
nav2.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav2._ptr)
nav2_ptr = t.CPtr(nav2._ptr)
nav3 = vqt6.QLabel("安装选项")
nav3.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav3._ptr)
nav3_ptr = t.CPtr(nav3._ptr)
nav4 = vqt6.QLabel("安装进度")
nav4.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav4._ptr)
nav4_ptr = t.CPtr(nav4._ptr)
nav5 = vqt6.QLabel("完成")
nav5.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav5._ptr)
nav5_ptr = t.CPtr(nav5._ptr)
nav_box.addStretch(1)
ver_lb = vqt6.QLabel("v6.0.1 · 2026")
vqt6.vqt6_qwidget_set_stylesheet_utf8(ver_lb._ptr, "color: #666666; padding: 6px 14px;")
nav_box.addWidget(ver_lb._ptr)
root.addWidget(nav_panel._ptr)
# ===== 右侧步骤容器 =====
stack = vqt6.QStackedWidget()
root.addWidget(stack._ptr)
stack_ptr = t.CPtr(stack._ptr)
# ---------- 页0:欢迎 ----------
page0 = vqt6.QWidget()
p0 = vqt6.QVBoxLayout(page0)
p0.setContentsMargins(36, 40, 36, 24)
p0.setSpacing(12)
big = vqt6.QLabel("Viper 6.0")
vqt6.vqt6_qwidget_set_stylesheet_utf8(big._ptr, "font-size: 34px; color: #ffffff; font-weight: bold;")
p0.addWidget(big._ptr)
sub = vqt6.QLabel("为 Viper 语言平台安装工具链、Qt6 运行时与开发组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(sub._ptr, "font-size: 16px; color: #9cdcfe;")
p0.addWidget(sub._ptr)
desc = vqt6.QLabel("本向导将引导您完成 Viper 6.0 的安装。您可以自定义要安装的组件、\n安装选项与发布渠道,安装过程由模拟进度与日志展示(演示界面,无真实安装行为)。")
vqt6.vqt6_qwidget_set_stylesheet_utf8(desc._ptr, "font-size: 13px; color: #9a9a9a;")
p0.addWidget(desc._ptr)
p0.addStretch(1)
next_btn = vqt6.QPushButton("下一步 >")
p0.addWidget(next_btn._ptr)
next_btn.clicked(OnNext, 0)
stack.addWidget(page0._ptr)
# ---------- 页1:许可协议(逐份同意) ----------
page1 = vqt6.QWidget()
p1 = vqt6.QVBoxLayout(page1)
p1.setContentsMargins(36, 32, 36, 24)
p1.setSpacing(10)
lh = vqt6.QLabel("许可协议")
vqt6.vqt6_qwidget_set_stylesheet_utf8(lh._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p1.addWidget(lh._ptr)
eula_name = vqt6.QLabel("1/4 · Viper 6.0 最终用户许可协议")
vqt6.vqt6_qwidget_set_stylesheet_utf8(eula_name._ptr, "font-size: 13px; color: #9cdcfe;")
p1.addWidget(eula_name._ptr)
eula_name_ptr = t.CPtr(eula_name._ptr)
license_box = vqt6.QPlainTextEdit()
license_box.setPlainText(license_text)
license_box.setReadOnly(1)
p1.addWidget(license_box._ptr)
license_box_ptr = t.CPtr(license_box._ptr)
agree_cb = vqt6.QCheckBox("我接受上述协议条款")
p1.addWidget(agree_cb._ptr)
agree_cb_ptr = t.CPtr(agree_cb._ptr)
agree_cb.toggled(OnLicenseToggled, 0)
lbrow = vqt6.QHBoxLayout()
p1.addLayout(lbrow._ptr)
lbrow.addStretch(1)
lic_back = vqt6.QPushButton("< 上一步")
lbrow.addWidget(lic_back._ptr)
lic_back.clicked(OnLicenseBack, 0)
lic_next = vqt6.QPushButton("下一步 >")
lic_next.setEnabled(0)
lbrow.addWidget(lic_next._ptr)
license_next_ptr = t.CPtr(lic_next._ptr)
lic_next.clicked(OnLicenseNext, 0)
stack.addWidget(page1._ptr)
# ---------- 页2:组件选择(树状图) ----------
page2 = vqt6.QWidget()
p2 = vqt6.QVBoxLayout(page2)
p2.setContentsMargins(36, 28, 36, 20)
p2.setSpacing(8)
h2 = vqt6.QLabel("选择要安装的组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h2._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p2.addWidget(h2._ptr)
h2b = vqt6.QLabel("展开分类并勾选需要的组件,实时统计安装体积与磁盘占用。")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h2b._ptr, "font-size: 13px; color: #9a9a9a;")
p2.addWidget(h2b._ptr)
tree = vqt6.QTreeWidget()
tree.setHeaderHidden(1)
p2.addWidget(tree._ptr)
tree_ptr = t.CPtr(tree._ptr)
# 叶子磁盘占用经 setSize 存入节点 UserRole,统计时自动读取
g0 = vqt6.QTreeWidgetItem("开发工具链")
leaf = vqt6.QTreeWidgetItem("Viper 核心编译器与运行时(编译器 + 标准库运行时)[850 MB]")
leaf.setCheckable(1)
leaf.setChecked(1)
leaf.setSize(850)
g0.addChild(leaf)
leaf = vqt6.QTreeWidgetItem("Viper 标准库与 includes 包 [350 MB]")
leaf.setCheckable(1)
leaf.setChecked(1)
leaf.setSize(350)
g0.addChild(leaf)
tree.addTopLevelItem(g0)
g1 = vqt6.QTreeWidgetItem("GUI 与网络开发")
leaf = vqt6.QTreeWidgetItem("Qt6 桌面开发框架(GUI + vqt6 桥接库)[1200 MB]")
leaf.setCheckable(1)
leaf.setChecked(1)
leaf.setSize(1200)
g1.addChild(leaf)
leaf = vqt6.QTreeWidgetItem("网络与调试工具(QtNetwork + TPV)[220 MB]")
leaf.setCheckable(1)
leaf.setChecked(0)
leaf.setSize(220)
g1.addChild(leaf)
tree.addTopLevelItem(g1)
g2 = vqt6.QTreeWidgetItem("附加内容")
leaf = vqt6.QTreeWidgetItem("示例项目与开发文档 [90 MB]")
leaf.setCheckable(1)
leaf.setChecked(0)
leaf.setSize(90)
g2.addChild(leaf)
tree.addTopLevelItem(g2)
tree.expandAll()
tree.itemChanged(OnCompToggled, 0)
summary = vqt6.QLabel("已选择 3 个组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(summary._ptr, "font-size: 13px; color: #9cdcfe;")
p2.addWidget(summary._ptr)
summary_ptr = t.CPtr(summary._ptr)
disk = vqt6.QLabel("所需磁盘空间: 2400 MB / 可用 51200 MB")
vqt6.vqt6_qwidget_set_stylesheet_utf8(disk._ptr, "font-size: 13px; color: #9cdcfe;")
p2.addWidget(disk._ptr)
disk_ptr = t.CPtr(disk._ptr)
btn_row2 = vqt6.QHBoxLayout()
p2.addLayout(btn_row2._ptr)
btn_row2.addStretch(1)
back_btn = vqt6.QPushButton("< 上一步")
btn_row2.addWidget(back_btn._ptr)
back_btn.clicked(OnBack, 0)
comp_next_btn = vqt6.QPushButton("下一步 >")
btn_row2.addWidget(comp_next_btn._ptr)
comp_next_btn.clicked(OnCompNext, 0)
stack.addWidget(page2._ptr)
# ---------- 页3:安装选项(独立页) ----------
page3 = vqt6.QWidget()
p3 = vqt6.QVBoxLayout(page3)
p3.setContentsMargins(36, 28, 36, 20)
p3.setSpacing(12)
h3 = vqt6.QLabel("安装选项")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h3._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p3.addWidget(h3._ptr)
s3 = vqt6.QLabel("选择发布渠道、界面集成方式与安装目录。")
vqt6.vqt6_qwidget_set_stylesheet_utf8(s3._ptr, "font-size: 13px; color: #9a9a9a;")
p3.addWidget(s3._ptr)
ch_row = vqt6.QHBoxLayout()
p3.addLayout(ch_row._ptr)
ch_lb = vqt6.QLabel("发布渠道:")
ch_row.addWidget(ch_lb._ptr)
channel_cb = vqt6.QComboBox()
channel_cb.addItem(channel_stable)
channel_cb.addItem(channel_preview)
channel_cb.addItem(channel_dev)
channel_cb.setCurrentIndex(0)
ch_row.addWidget(channel_cb._ptr)
ch_row.addStretch(1)
channel_ptr = t.CPtr(channel_cb._ptr)
channel_cb.currentIndexChanged(OnChannelChanged, 0)
oh = vqt6.QLabel("界面集成方式")
vqt6.vqt6_qwidget_set_stylesheet_utf8(oh._ptr, "font-size: 15px; color: #ffffff; font-weight: bold; padding-top: 6px;")
p3.addWidget(oh._ptr)
opt0 = vqt6.QCheckBox("创建开始菜单快捷方式")
opt0.setChecked(1)
p3.addWidget(opt0._ptr)
opt0_ptr = t.CPtr(opt0._ptr)
opt1 = vqt6.QCheckBox("将 viper 命令添加到系统 PATH")
opt1.setChecked(0)
p3.addWidget(opt1._ptr)
opt1_ptr = t.CPtr(opt1._ptr)
opt2 = vqt6.QCheckBox("创建桌面图标")
opt2.setChecked(0)
p3.addWidget(opt2._ptr)
opt2_ptr = t.CPtr(opt2._ptr)
# 安装位置行
path_row = vqt6.QHBoxLayout()
p3.addLayout(path_row._ptr)
path_lb = vqt6.QLabel("安装位置:")
path_row.addWidget(path_lb._ptr)
path_lb2 = vqt6.QLabel(install_path)
vqt6.vqt6_qwidget_set_stylesheet_utf8(path_lb2._ptr, "background-color: #252526; border: 1px solid #555555; border-radius: 4px; padding: 5px 8px; color: #ffffff;")
path_row.addWidget(path_lb2._ptr)
path_label_ptr = t.CPtr(path_lb2._ptr)
browse_btn = vqt6.QPushButton("浏览...")
path_row.addWidget(browse_btn._ptr)
browse_btn.clicked(OnBrowse, 0)
p3.addStretch(1)
btn_row3 = vqt6.QHBoxLayout()
p3.addLayout(btn_row3._ptr)
btn_row3.addStretch(1)
back3_btn = vqt6.QPushButton("< 上一步")
btn_row3.addWidget(back3_btn._ptr)
back3_btn.clicked(OnOptionsBack, 0)
install_btn = vqt6.QPushButton("安装")
vqt6.vqt6_qwidget_set_stylesheet_utf8(install_btn._ptr, "background-color: #2a6da8; border: 1px solid #2a6da8; border-radius: 4px; padding: 8px 22px; color: #ffffff; font-size: 14px; font-weight: bold;")
btn_row3.addWidget(install_btn._ptr)
install_btn_ptr = t.CPtr(install_btn._ptr)
install_btn.clicked(OnInstall, 0)
stack.addWidget(page3._ptr)
# ---------- 页4:安装进度 ----------
page4 = vqt6.QWidget()
p4 = vqt6.QVBoxLayout(page4)
p4.setContentsMargins(36, 36, 36, 24)
p4.setSpacing(12)
h4 = vqt6.QLabel("正在安装 Viper 6.0...")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h4._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p4.addWidget(h4._ptr)
prog = vqt6.QProgressBar()
prog.setRange(0, 100)
prog.setValue(0)
p4.addWidget(prog._ptr)
progress_ptr = t.CPtr(prog._ptr)
pstat = vqt6.QLabel("正在准备安装环境...")
vqt6.vqt6_qwidget_set_stylesheet_utf8(pstat._ptr, "font-size: 13px; color: #9a9a9a;")
p4.addWidget(pstat._ptr)
progress_status_ptr = t.CPtr(pstat._ptr)
log = vqt6.QPlainTextEdit()
log.setReadOnly(1)
p4.addWidget(log._ptr)
log_ptr = t.CPtr(log._ptr)
stack.addWidget(page4._ptr)
# ---------- 页5:完成 ----------
page5 = vqt6.QWidget()
p5 = vqt6.QVBoxLayout(page5)
p5.setContentsMargins(36, 40, 36, 24)
p5.setSpacing(14)
ok = vqt6.QLabel("安装完成")
vqt6.vqt6_qwidget_set_stylesheet_utf8(ok._ptr, "font-size: 30px; color: #4ec9b0; font-weight: bold;")
p5.addWidget(ok._ptr)
fsum = vqt6.QLabel("")
vqt6.vqt6_qwidget_set_stylesheet_utf8(fsum._ptr, "font-size: 14px; color: #d4d4d4;")
p5.addWidget(fsum._ptr)
finish_summary_ptr = t.CPtr(fsum._ptr)
p5.addStretch(1)
finish_btn = vqt6.QPushButton("完成")
vqt6.vqt6_qwidget_set_stylesheet_utf8(finish_btn._ptr, "background-color: #2a6da8; border-radius: 4px; padding: 8px 22px; color: #ffffff; font-size: 14px; font-weight: bold;")
p5.addWidget(finish_btn._ptr)
finish_btn.clicked(OnFinish, 0)
stack.addWidget(page5._ptr)
# ===== 定时器(模拟安装进度) =====
timer = vqt6.QTimer()
timer.timeout(OnProgressTick, 0)
timer_ptr = t.CPtr(timer._ptr)
# 初始化组件统计
SetTextPtrs(big._ptr, sub._ptr, desc._ptr, lh._ptr, h2._ptr, h2b._ptr, h3._ptr, s3._ptr, h4._ptr, ok._ptr, fsum._ptr, oh._ptr)
UpdateCompInfo()
GotoStep(0)
win.show()
return app.exec()