feat(ViperQtProject): 安装向导完善(许可协议/磁盘统计/安装日志/安装选项)

- 新增许可协议步骤:协议文本 + 勾选接受后才可继续
- 组件选择页:磁盘占用实时统计 + 安装选项(快捷方式/PATH/桌面图标)
- 安装进度页:阶段文案 + 实时日志输出(QPlainTextEdit)
- 安装按钮点击后禁用防重复,完成页汇总安装信息
This commit is contained in:
TermiNexus committed 2026-09-02 10:01:44 +08:00
1 parent 74624b1d0a
commit ab068df348
1 file changed
+203 -85
+203 -85
View File
@@ -1,8 +1,13 @@
# main.vp — ViperQtProject:VS Installer 风格安装向导演示
#
# 纯界面演示(无真实安装逻辑):
# 步骤0 欢迎 → 步骤1 组件选择 → 步骤2 安装进度(QTimer 模拟)→ 步骤3 完成
# 左侧导航高亮当前步骤,右侧 QStackedWidget 切换页面,深色主题 QSS。
# 步骤0 欢迎 → 步骤1 许可协议 → 步骤2 组件选择 → 步骤3 安装进度 → 步骤4 完成
#
# 完善功能:
# - 许可协议页:协议文本 + "我接受" 勾选后才可继续
# - 组件选择:磁盘占用实时统计 + 安装选项(快捷方式/PATH/桌面图标)
# - 安装进度:进度条 + 阶段文案 + 实时日志(QPlainTextEdit)
# - 安装按钮点击后禁用,防重复
#
# 说明:
# - 回调签名统一 def Cb(slot_id: int, data: t.CPtr)
@@ -23,10 +28,13 @@ 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
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
comp0_ptr: t.CPtr = None
@@ -34,6 +42,12 @@ comp1_ptr: t.CPtr = None
comp2_ptr: t.CPtr = None
comp3_ptr: t.CPtr = None
comp4_ptr: t.CPtr = None
agree_cb_ptr: t.CPtr = None
license_next_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
# 全局状态
cur_step: int = 0
@@ -53,6 +67,10 @@ nav_active_qss: str = "font-size: 15px; color: #ffffff; font-weight: bold; backg
nav_idle_qss: str = "font-size: 15px; color: #8a8a8a; padding: 10px 14px;"
# 许可协议文本
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,必须接受这些条款并勾选下方的\"我接受许可协议中的条款\"。"
# ============================================================
# 工具:切换步骤 + 导航高亮
# ============================================================
@@ -69,6 +87,7 @@ def GotoStep(step: int):
SetNavStyle(nav1_ptr, nav_active_qss if step == 1 else nav_idle_qss)
SetNavStyle(nav2_ptr, nav_active_qss if step == 2 else nav_idle_qss)
SetNavStyle(nav3_ptr, nav_active_qss if step == 3 else nav_idle_qss)
SetNavStyle(nav4_ptr, nav_active_qss if step == 4 else nav_idle_qss)
# ============================================================
@@ -79,10 +98,27 @@ def OnNext(slot_id: int, data: t.CPtr):
# ============================================================
# 步骤1:组件选择
# 步骤1:许可协议
# ============================================================
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):
GotoStep(2)
def OnLicenseBack(slot_id: int, data: t.CPtr):
GotoStep(0)
# ============================================================
# 步骤2:组件选择
# ============================================================
def OnBack(slot_id: int, data: t.CPtr):
GotoStep(0)
GotoStep(1)
def OnBrowse(slot_id: int, data: t.CPtr):
@@ -93,8 +129,22 @@ def OnBrowse(slot_id: int, data: t.CPtr):
vqt6.vqt6_qlabel_set_text_utf8(path_label_ptr, p)
def UpdateDiskInfo():
"""按组件勾选统计磁盘占用"""
total_mb: int = 0
if vqt6.vqt6_qcheckbox_is_checked(comp0_ptr) != 0: total_mb += 850
if vqt6.vqt6_qcheckbox_is_checked(comp1_ptr) != 0: total_mb += 1200
if vqt6.vqt6_qcheckbox_is_checked(comp2_ptr) != 0: total_mb += 350
if vqt6.vqt6_qcheckbox_is_checked(comp3_ptr) != 0: total_mb += 220
if vqt6.vqt6_qcheckbox_is_checked(comp4_ptr) != 0: total_mb += 90
buf: bytes = stdlib.malloc(96)
viperlib.snprintf(buf, 96, "所需磁盘空间: %d MB / 可用 51200 MB", total_mb)
vqt6.vqt6_qlabel_set_text_utf8(disk_ptr, buf)
stdlib.free(buf)
def OnCompToggled(slot_id: int, data: t.CPtr):
"""组件勾选变化:重算勾选数更新摘要"""
"""组件勾选变化:重算勾选数 + 磁盘占用"""
global comp_count
n: int = 0
if vqt6.vqt6_qcheckbox_is_checked(comp0_ptr) != 0: n += 1
@@ -107,63 +157,65 @@ def OnCompToggled(slot_id: int, data: t.CPtr):
viperlib.snprintf(buf, 64, "已选择 %d 个组件", n)
vqt6.vqt6_qlabel_set_text_utf8(summary_ptr, buf)
stdlib.free(buf)
UpdateDiskInfo()
def OnInstall(slot_id: int, data: t.CPtr):
"""进入安装页并启动模拟进度"""
global progress_val
GotoStep(2)
vqt6.vqt6_qwidget_set_enabled(install_btn_ptr, 0)
GotoStep(3)
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)
# ============================================================
# 步骤2:安装进度(QTimer 模拟)
# 步骤3:安装进度(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 < 25:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在准备安装环境...")
elif progress_val < 50:
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, "正在复制核心文件...")
elif progress_val < 75:
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "[2/4] 正在复制核心文件...\n")
elif progress_val == 52:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在配置 Qt6 组件...")
elif progress_val < 100:
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, "[3/4] 正在配置 Qt6 组件...\n")
elif progress_val == 76:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在注册系统组件...")
else:
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")
# 完成页摘要(含安装选项)
buf: bytes = stdlib.malloc(512)
viperlib.snprintf(buf, 512, "安装位置: %s\n已安装组件: %d 个\nViper 6.0 已成功安装到您的系统。", install_path, comp_count)
vqt6.vqt6_qlabel_set_text_utf8(finish_summary_ptr, buf)
stdlib.free(buf)
GotoStep(3)
GotoStep(4)
# ============================================================
# 步骤3:完成页
# 步骤4:完成页
# ============================================================
def OnFinish(slot_id: int, data: t.CPtr):
vqt6.vqt6_qapplication_quit(app_ptr)
# ============================================================
# 导航点击(左栏 4 项)
# ============================================================
def OnNav0(slot_id: int, data: t.CPtr): GotoStep(0)
def OnNav1(slot_id: int, data: t.CPtr): GotoStep(1)
def OnNav2(slot_id: int, data: t.CPtr): GotoStep(2)
def OnNav3(slot_id: int, data: t.CPtr): GotoStep(3)
def main() -> int:
global app_ptr, stack_ptr, nav0_ptr, nav1_ptr, nav2_ptr, nav3_ptr
global progress_ptr, progress_status_ptr, path_label_ptr, summary_ptr
global finish_summary_ptr, timer_ptr
global app_ptr, stack_ptr
global nav0_ptr, nav1_ptr, nav2_ptr, nav3_ptr, nav4_ptr
global progress_ptr, progress_status_ptr, log_ptr, path_label_ptr
global summary_ptr, disk_ptr, finish_summary_ptr, timer_ptr
global comp0_ptr, comp1_ptr, comp2_ptr, comp3_ptr, comp4_ptr
global agree_cb_ptr, license_next_ptr, install_btn_ptr
global opt0_ptr, opt1_ptr, opt2_ptr
app = vqt6.QApplication(t.CPtr(0))
app_ptr = t.CPtr(app._ptr)
@@ -171,7 +223,7 @@ def main() -> int:
# 主窗口
win = vqt6.QMainWindow()
win.setWindowTitle("Viper 6.0 Installer")
win.resize(880, 560)
win.resize(920, 600)
central = vqt6.QWidget()
central.setStyleSheet(dark_qss)
@@ -181,7 +233,7 @@ def main() -> int:
# ===== 左导航面板 =====
nav_panel = vqt6.QWidget()
vqt6.vqt6_qwidget_set_fixed_size(nav_panel._ptr, 210, 560)
vqt6.vqt6_qwidget_set_fixed_size(nav_panel._ptr, 210, 600)
nav_box = vqt6.QVBoxLayout(nav_panel)
nav_box.setContentsMargins(10, 16, 10, 10)
nav_box.setSpacing(6)
@@ -195,21 +247,26 @@ def main() -> int:
nav_box.addWidget(nav0._ptr)
nav0_ptr = t.CPtr(nav0._ptr)
nav1 = vqt6.QLabel("组件选择")
nav1 = vqt6.QLabel("许可协议")
nav1.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav1._ptr)
nav1_ptr = t.CPtr(nav1._ptr)
nav2 = vqt6.QLabel("安装进度")
nav2 = vqt6.QLabel("组件选择")
nav2.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav2._ptr)
nav2_ptr = t.CPtr(nav2._ptr)
nav3 = vqt6.QLabel("完成")
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)
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;")
@@ -236,7 +293,7 @@ def main() -> int:
vqt6.vqt6_qwidget_set_stylesheet_utf8(sub._ptr, "font-size: 16px; color: #9cdcfe;")
p0.addWidget(sub._ptr)
desc = vqt6.QLabel("本向导将引导您完成 Viper 6.0 的安装。您可以自定义要安装的组件\n和安装位置,安装过程由模拟进度展示(演示界面,无真实安装行为)。")
desc = vqt6.QLabel("本向导将引导您完成 Viper 6.0 的安装。您可以自定义要安装的组件\n和安装位置,安装过程由模拟进度与日志展示(演示界面,无真实安装行为)。")
vqt6.vqt6_qwidget_set_stylesheet_utf8(desc._ptr, "font-size: 13px; color: #9a9a9a;")
p0.addWidget(desc._ptr)
@@ -247,46 +304,79 @@ def main() -> int:
stack.addWidget(page0._ptr)
# ---------- 页1:组件选择 ----------
# ---------- 页1:许可协议 ----------
page1 = vqt6.QWidget()
p1 = vqt6.QVBoxLayout(page1)
p1.setContentsMargins(36, 32, 36, 24)
p1.setSpacing(10)
h1 = vqt6.QLabel("选择要安装的组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h1._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p1.addWidget(h1._ptr)
lh = vqt6.QLabel("许可协议")
vqt6.vqt6_qwidget_set_stylesheet_utf8(lh._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p1.addWidget(lh._ptr)
h1b = vqt6.QLabel("勾选您需要的工作负载与组件,取消勾选可减少安装体积。")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h1b._ptr, "font-size: 13px; color: #9a9a9a;")
p1.addWidget(h1b._ptr)
license_box = vqt6.QPlainTextEdit()
license_box.setPlainText(license_text)
license_box.setReadOnly(1)
p1.addWidget(license_box._ptr)
comp0 = vqt6.QCheckBox("Viper 核心工具链(编译器 + 运行时)")
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)
comp0 = vqt6.QCheckBox("Viper 核心工具链(编译器 + 运行时)[850 MB]")
comp0.setChecked(1)
p1.addWidget(comp0._ptr)
p2.addWidget(comp0._ptr)
comp0_ptr = t.CPtr(comp0._ptr)
comp1 = vqt6.QCheckBox("Qt6 桌面开发(GUI 框架 + vqt6 桥接库)")
comp1 = vqt6.QCheckBox("Qt6 桌面开发(GUI 框架 + vqt6 桥接库)[1200 MB]")
comp1.setChecked(1)
p1.addWidget(comp1._ptr)
p2.addWidget(comp1._ptr)
comp1_ptr = t.CPtr(comp1._ptr)
comp2 = vqt6.QCheckBox("Viper 标准库与 includes 包")
comp2 = vqt6.QCheckBox("Viper 标准库与 includes 包 [350 MB]")
comp2.setChecked(1)
p1.addWidget(comp2._ptr)
p2.addWidget(comp2._ptr)
comp2_ptr = t.CPtr(comp2._ptr)
comp3 = vqt6.QCheckBox("网络与调试工具(QtNetwork + TPV)")
comp3 = vqt6.QCheckBox("网络与调试工具(QtNetwork + TPV)[220 MB]")
comp3.setChecked(0)
p1.addWidget(comp3._ptr)
p2.addWidget(comp3._ptr)
comp3_ptr = t.CPtr(comp3._ptr)
comp4 = vqt6.QCheckBox("示例项目与文档")
comp4 = vqt6.QCheckBox("示例项目与文档 [90 MB]")
comp4.setChecked(0)
p1.addWidget(comp4._ptr)
p2.addWidget(comp4._ptr)
comp4_ptr = t.CPtr(comp4._ptr)
# 组件勾选回调
comp0.toggled(OnCompToggled, comp0._ptr)
comp1.toggled(OnCompToggled, comp1._ptr)
comp2.toggled(OnCompToggled, comp2._ptr)
@@ -295,85 +385,113 @@ def main() -> int:
summary = vqt6.QLabel("已选择 3 个组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(summary._ptr, "font-size: 13px; color: #9cdcfe;")
p1.addWidget(summary._ptr)
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)
# 安装选项
oh = vqt6.QLabel("安装选项")
vqt6.vqt6_qwidget_set_stylesheet_utf8(oh._ptr, "font-size: 15px; color: #ffffff; font-weight: bold; padding-top: 6px;")
p2.addWidget(oh._ptr)
opt0 = vqt6.QCheckBox("创建开始菜单快捷方式")
opt0.setChecked(1)
p2.addWidget(opt0._ptr)
opt0_ptr = t.CPtr(opt0._ptr)
opt1 = vqt6.QCheckBox("将 viper 命令添加到系统 PATH")
opt1.setChecked(0)
p2.addWidget(opt1._ptr)
opt1_ptr = t.CPtr(opt1._ptr)
opt2 = vqt6.QCheckBox("创建桌面图标")
opt2.setChecked(0)
p2.addWidget(opt2._ptr)
opt2_ptr = t.CPtr(opt2._ptr)
# 安装位置行
path_row = vqt6.QHBoxLayout()
p1.addLayout(path_row._ptr)
p2.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: 3px; padding: 6px 8px; color: #ffffff;")
vqt6.vqt6_qwidget_set_stylesheet_utf8(path_lb2._ptr, "background-color: #252526; border: 1px solid #555555; border-radius: 3px; 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)
p1.addStretch(1)
p2.addStretch(1)
# 底部按钮行
btn_row1 = vqt6.QHBoxLayout()
p1.addLayout(btn_row1._ptr)
btn_row1.addStretch(1)
btn_row2 = vqt6.QHBoxLayout()
p2.addLayout(btn_row2._ptr)
btn_row2.addStretch(1)
back_btn = vqt6.QPushButton("< 上一步")
btn_row1.addWidget(back_btn._ptr)
btn_row2.addWidget(back_btn._ptr)
back_btn.clicked(OnBack, 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_row1.addWidget(install_btn._ptr)
btn_row2.addWidget(install_btn._ptr)
install_btn_ptr = t.CPtr(install_btn._ptr)
install_btn.clicked(OnInstall, 0)
stack.addWidget(page1._ptr)
stack.addWidget(page2._ptr)
# ---------- 页2:安装进度 ----------
page2 = vqt6.QWidget()
p2 = vqt6.QVBoxLayout(page2)
p2.setContentsMargins(36, 40, 36, 24)
p2.setSpacing(16)
# ---------- 页3:安装进度 ----------
page3 = vqt6.QWidget()
p3 = vqt6.QVBoxLayout(page3)
p3.setContentsMargins(36, 36, 36, 24)
p3.setSpacing(12)
h2 = vqt6.QLabel("正在安装 Viper 6.0...")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h2._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p2.addWidget(h2._ptr)
h3 = vqt6.QLabel("正在安装 Viper 6.0...")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h3._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p3.addWidget(h3._ptr)
prog = vqt6.QProgressBar()
prog.setRange(0, 100)
prog.setValue(0)
p2.addWidget(prog._ptr)
p3.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;")
p2.addWidget(pstat._ptr)
p3.addWidget(pstat._ptr)
progress_status_ptr = t.CPtr(pstat._ptr)
p2.addStretch(1)
log = vqt6.QPlainTextEdit()
log.setReadOnly(1)
p3.addWidget(log._ptr)
log_ptr = t.CPtr(log._ptr)
stack.addWidget(page2._ptr)
stack.addWidget(page3._ptr)
# ---------- 页3:完成 ----------
page3 = vqt6.QWidget()
p3 = vqt6.QVBoxLayout(page3)
p3.setContentsMargins(36, 40, 36, 24)
p3.setSpacing(14)
# ---------- 页4:完成 ----------
page4 = vqt6.QWidget()
p4 = vqt6.QVBoxLayout(page4)
p4.setContentsMargins(36, 40, 36, 24)
p4.setSpacing(14)
ok = vqt6.QLabel("安装完成")
vqt6.vqt6_qwidget_set_stylesheet_utf8(ok._ptr, "font-size: 30px; color: #4ec9b0; font-weight: bold;")
p3.addWidget(ok._ptr)
p4.addWidget(ok._ptr)
fsum = vqt6.QLabel("")
vqt6.vqt6_qwidget_set_stylesheet_utf8(fsum._ptr, "font-size: 14px; color: #d4d4d4;")
p3.addWidget(fsum._ptr)
p4.addWidget(fsum._ptr)
finish_summary_ptr = t.CPtr(fsum._ptr)
p3.addStretch(1)
p4.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;")
p3.addWidget(finish_btn._ptr)
p4.addWidget(finish_btn._ptr)
finish_btn.clicked(OnFinish, 0)
stack.addWidget(page3._ptr)
stack.addWidget(page4._ptr)
# ===== 定时器(模拟安装进度) =====
timer = vqt6.QTimer()