feat(ViperQtProject): VS Installer 风格安装向导演示

- 四步骤:欢迎 → 组件选择 → 安装进度(QTimer 模拟)→ 完成
- 左导航高亮 + QStackedWidget 页面切换,深色主题 QSS
- 组件复选 + 计数摘要,安装路径浏览(QFileDialog),完成页汇总
This commit is contained in:
TermiNexus committed 2026-09-02 09:26:23 +08:00
1 parent fe0f9e5857
commit 74624b1d0a
1 file changed
+334 -94
+334 -94
View File
@@ -1,145 +1,385 @@
# main.vp — ViperQtProject:vqt6 扩展功能测试
# main.vp — ViperQtProject:VS Installer 风格安装向导演示
#
# 演示:QMainWindow + 布局 + 信号槽回调 + 多控件 + QTimer 自动退出
# 纯界面演示(无真实安装逻辑):
# 步骤0 欢迎 → 步骤1 组件选择 → 步骤2 安装进度(QTimer 模拟)→ 步骤3 完成
# 左侧导航高亮当前步骤,右侧 QStackedWidget 切换页面,深色主题 QSS。
#
# 注意:
# - QApplication(argv) 的 argv 被包装层忽略(桥接层提供虚拟 argv),传 t.CPtr(0) 即可
# - 回调签名统一: def cb(slot_id: int, data: t.CPtr),data 默认传 0(null)
# - 布局 addWidget 接收控件指针(控件 ._ptr)
# - 回调里更新文本用 UTF-8 便捷 FFI(vqt6_qlabel_set_text_utf8 等),
# 避免跨模块调用返回 QString 指针时 TPV 无法推断返回类型
# 说明:
# - 回调签名统一 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
status_ptr: t.CPtr = None
log_ptr: t.CPtr = None
count: int = 0
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
progress_ptr: t.CPtr = None
progress_status_ptr: t.CPtr = None
path_label_ptr: t.CPtr = None
summary_ptr: t.CPtr = None
finish_summary_ptr: t.CPtr = None
timer_ptr: t.CPtr = None
comp0_ptr: t.CPtr = None
comp1_ptr: t.CPtr = None
comp2_ptr: t.CPtr = None
comp3_ptr: t.CPtr = None
comp4_ptr: t.CPtr = None
# 全局状态
cur_step: int = 0
progress_val: int = 0
comp_count: int = 0
install_path: str = "C:/Program Files/Viper6"
def on_btn_clicked(slot_id: int, data: t.CPtr):
"""按钮点击 → 弹消息框"""
vqt6.QMessageBox.information(None, "Clicked", "Button clicked!")
# ============================================================
# 深色主题 QSS(VS Installer 风格,单行字面量)
# ============================================================
dark_qss: str = "QWidget { background-color: #1e1e1e; color: #d4d4d4; font-size: 13px; } QPushButton { background-color: #333333; border: 1px solid #555555; border-radius: 4px; padding: 7px 16px; color: #ffffff; } QPushButton:hover { background-color: #454545; } QPushButton:pressed { background-color: #2a6da8; } QPushButton:disabled { background-color: #2a2a2a; color: #666666; border-color: #3a3a3a; } QLineEdit { background-color: #252526; border: 1px solid #555555; border-radius: 3px; padding: 5px 8px; color: #ffffff; } QCheckBox { color: #d4d4d4; spacing: 10px; font-size: 14px; } QCheckBox::indicator { width: 18px; height: 18px; } QProgressBar { background-color: #252526; border: 1px solid #555555; border-radius: 5px; text-align: center; color: #ffffff; font-size: 13px; } QProgressBar::chunk { background-color: #4a86c8; border-radius: 4px; } QPlainTextEdit { background-color: #1a1a1a; border: 1px solid #444444; color: #cccccc; }"
def on_check_toggled(slot_id: int, data: t.CPtr):
vqt6.vqt6_qlabel_set_text_utf8(status_ptr, "CheckBox toggled")
# 导航项样式
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;"
def on_combo_changed(slot_id: int, data: t.CPtr):
vqt6.vqt6_qlabel_set_text_utf8(status_ptr, "Combo changed")
# ============================================================
# 工具:切换步骤 + 导航高亮
# ============================================================
def SetNavStyle(ptr: t.CPtr, qss: str):
vqt6.vqt6_qwidget_set_stylesheet_utf8(ptr, qss)
def on_slider_changed(slot_id: int, data: t.CPtr):
val: int = vqt6.vqt6_qslider_value(data)
def GotoStep(step: int):
global cur_step
cur_step = step
vqt6.vqt6_qstackedwidget_set_current_index(stack_ptr, step)
# 导航高亮
SetNavStyle(nav0_ptr, nav_active_qss if step == 0 else nav_idle_qss)
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)
# ============================================================
# 步骤0:欢迎页
# ============================================================
def OnNext(slot_id: int, data: t.CPtr):
GotoStep(1)
# ============================================================
# 步骤1:组件选择
# ============================================================
def OnBack(slot_id: int, data: t.CPtr):
GotoStep(0)
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 OnCompToggled(slot_id: int, data: t.CPtr):
"""组件勾选变化:重算勾选数更新摘要"""
global comp_count
n: int = 0
if vqt6.vqt6_qcheckbox_is_checked(comp0_ptr) != 0: n += 1
if vqt6.vqt6_qcheckbox_is_checked(comp1_ptr) != 0: n += 1
if vqt6.vqt6_qcheckbox_is_checked(comp2_ptr) != 0: n += 1
if vqt6.vqt6_qcheckbox_is_checked(comp3_ptr) != 0: n += 1
if vqt6.vqt6_qcheckbox_is_checked(comp4_ptr) != 0: n += 1
comp_count = n
buf: bytes = stdlib.malloc(64)
viperlib.snprintf(buf, 64, "Slider: %d", val)
vqt6.vqt6_qlabel_set_text_utf8(status_ptr, buf)
viperlib.snprintf(buf, 64, "已选择 %d 个组件", n)
vqt6.vqt6_qlabel_set_text_utf8(summary_ptr, buf)
stdlib.free(buf)
def on_spin_changed(slot_id: int, data: t.CPtr):
val: int = vqt6.vqt6_qspinbox_value(data)
buf: bytes = stdlib.malloc(64)
viperlib.snprintf(buf, 64, "Spin: %d", val)
vqt6.vqt6_qlabel_set_text_utf8(status_ptr, buf)
stdlib.free(buf)
def OnInstall(slot_id: int, data: t.CPtr):
"""进入安装页并启动模拟进度"""
global progress_val
GotoStep(2)
progress_val = 0
vqt6.vqt6_qprogressbar_set_value(progress_ptr, 0)
vqt6.vqt6_qtimer_start(timer_ptr, 80)
def on_tick(slot_id: int, data: t.CPtr):
"""定时器:日志计数,3 次后退出"""
global count
count += 1
buf: bytes = stdlib.malloc(64)
viperlib.snprintf(buf, 64, "tick %d\n", count)
vqt6.vqt6_qplaintextedit_append_utf8(log_ptr, buf)
stdlib.free(buf)
if count >= 3:
vqt6.vqt6_qapplication_quit(app_ptr)
# ============================================================
# 步骤2:安装进度(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:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在复制核心文件...")
elif progress_val < 75:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在配置 Qt6 组件...")
elif progress_val < 100:
vqt6.vqt6_qlabel_set_text_utf8(progress_status_ptr, "正在注册系统组件...")
else:
vqt6.vqt6_qtimer_stop(timer_ptr)
# 完成页摘要
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)
# ============================================================
# 步骤3:完成页
# ============================================================
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, status_ptr, log_ptr
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 comp0_ptr, comp1_ptr, comp2_ptr, comp3_ptr, comp4_ptr
app = vqt6.QApplication(t.CPtr(0))
app_ptr = t.CPtr(app._ptr)
# 主窗口 + 中央控件
# 主窗口
win = vqt6.QMainWindow()
win.setWindowTitle("Viper Qt6 Demo 2")
win.resize(560, 420)
win.setWindowTitle("Viper 6.0 Installer")
win.resize(880, 560)
central = vqt6.QWidget()
central.setStyleSheet(dark_qss)
win.setCentralWidget(central._ptr)
# 垂直布局
layout = vqt6.QVBoxLayout(central)
root = vqt6.QHBoxLayout(central)
label = vqt6.QLabel("Hello from Viper + Qt6!")
layout.addWidget(label._ptr)
# ===== 左导航面板 =====
nav_panel = vqt6.QWidget()
vqt6.vqt6_qwidget_set_fixed_size(nav_panel._ptr, 210, 560)
nav_box = vqt6.QVBoxLayout(nav_panel)
nav_box.setContentsMargins(10, 16, 10, 10)
nav_box.setSpacing(6)
# 第一行:按钮 + 复选框 + 输入框
row1 = vqt6.QHBoxLayout()
layout.addLayout(row1._ptr)
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)
btn = vqt6.QPushButton("Click Me")
row1.addWidget(btn._ptr)
btn.clicked(on_btn_clicked, 0)
nav0 = vqt6.QLabel("欢迎")
nav0.setStyleSheet(nav_active_qss)
nav_box.addWidget(nav0._ptr)
nav0_ptr = t.CPtr(nav0._ptr)
check = vqt6.QCheckBox("Enable")
row1.addWidget(check._ptr)
check.toggled(on_check_toggled, 0)
nav1 = vqt6.QLabel("组件选择")
nav1.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav1._ptr)
nav1_ptr = t.CPtr(nav1._ptr)
input_box = vqt6.QLineEdit("Type here")
row1.addWidget(input_box._ptr)
nav2 = vqt6.QLabel("安装进度")
nav2.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav2._ptr)
nav2_ptr = t.CPtr(nav2._ptr)
# 第二行:下拉框 + 滑块 + 数值框
row2 = vqt6.QHBoxLayout()
layout.addLayout(row2._ptr)
nav3 = vqt6.QLabel("完成")
nav3.setStyleSheet(nav_idle_qss)
nav_box.addWidget(nav3._ptr)
nav3_ptr = t.CPtr(nav3._ptr)
combo = vqt6.QComboBox()
combo.addItem("Item 1")
combo.addItem("Item 2")
combo.addItem("Item 3")
combo.setCurrentIndex(0)
row2.addWidget(combo._ptr)
combo.currentIndexChanged(on_combo_changed, combo._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)
slider = vqt6.QSlider(1) # QT_HORIZONTAL = 1(跨模块 CDefine 不可用,直接写值)
slider.setRange(0, 100)
slider.setValue(50)
row2.addWidget(slider._ptr)
slider.valueChanged(on_slider_changed, slider._ptr)
root.addWidget(nav_panel._ptr)
spin = vqt6.QSpinBox()
spin.setRange(0, 100)
spin.setValue(42)
row2.addWidget(spin._ptr)
spin.valueChanged(on_spin_changed, spin._ptr)
# ===== 右侧步骤容器 =====
stack = vqt6.QStackedWidget()
root.addWidget(stack._ptr)
stack_ptr = t.CPtr(stack._ptr)
# 进度条 + 状态标签
progress = vqt6.QProgressBar()
progress.setRange(0, 100)
progress.setValue(50)
layout.addWidget(progress._ptr)
# ---------- 页0:欢迎 ----------
page0 = vqt6.QWidget()
p0 = vqt6.QVBoxLayout(page0)
p0.setContentsMargins(36, 40, 36, 24)
p0.setSpacing(12)
status = vqt6.QLabel("Status: ready")
layout.addWidget(status._ptr)
status_ptr = t.CPtr(status._ptr)
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)
# 日志区
log = vqt6.QPlainTextEdit()
log.setReadOnly(1)
layout.addWidget(log._ptr)
log_ptr = t.CPtr(log._ptr)
sub = vqt6.QLabel("为 Viper 语言平台安装工具链、Qt6 运行时与开发组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(sub._ptr, "font-size: 16px; color: #9cdcfe;")
p0.addWidget(sub._ptr)
# 定时器(3 秒后自动退出验证)
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)
h1 = vqt6.QLabel("选择要安装的组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h1._ptr, "font-size: 22px; color: #ffffff; font-weight: bold;")
p1.addWidget(h1._ptr)
h1b = vqt6.QLabel("勾选您需要的工作负载与组件,取消勾选可减少安装体积。")
vqt6.vqt6_qwidget_set_stylesheet_utf8(h1b._ptr, "font-size: 13px; color: #9a9a9a;")
p1.addWidget(h1b._ptr)
comp0 = vqt6.QCheckBox("Viper 核心工具链(编译器 + 运行时)")
comp0.setChecked(1)
p1.addWidget(comp0._ptr)
comp0_ptr = t.CPtr(comp0._ptr)
comp1 = vqt6.QCheckBox("Qt6 桌面开发(GUI 框架 + vqt6 桥接库)")
comp1.setChecked(1)
p1.addWidget(comp1._ptr)
comp1_ptr = t.CPtr(comp1._ptr)
comp2 = vqt6.QCheckBox("Viper 标准库与 includes 包")
comp2.setChecked(1)
p1.addWidget(comp2._ptr)
comp2_ptr = t.CPtr(comp2._ptr)
comp3 = vqt6.QCheckBox("网络与调试工具(QtNetwork + TPV)")
comp3.setChecked(0)
p1.addWidget(comp3._ptr)
comp3_ptr = t.CPtr(comp3._ptr)
comp4 = vqt6.QCheckBox("示例项目与文档")
comp4.setChecked(0)
p1.addWidget(comp4._ptr)
comp4_ptr = t.CPtr(comp4._ptr)
# 组件勾选回调
comp0.toggled(OnCompToggled, comp0._ptr)
comp1.toggled(OnCompToggled, comp1._ptr)
comp2.toggled(OnCompToggled, comp2._ptr)
comp3.toggled(OnCompToggled, comp3._ptr)
comp4.toggled(OnCompToggled, comp4._ptr)
summary = vqt6.QLabel("已选择 3 个组件")
vqt6.vqt6_qwidget_set_stylesheet_utf8(summary._ptr, "font-size: 13px; color: #9cdcfe;")
p1.addWidget(summary._ptr)
summary_ptr = t.CPtr(summary._ptr)
# 安装位置行
path_row = vqt6.QHBoxLayout()
p1.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;")
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)
# 底部按钮行
btn_row1 = vqt6.QHBoxLayout()
p1.addLayout(btn_row1._ptr)
btn_row1.addStretch(1)
back_btn = vqt6.QPushButton("< 上一步")
btn_row1.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)
install_btn.clicked(OnInstall, 0)
stack.addWidget(page1._ptr)
# ---------- 页2:安装进度 ----------
page2 = vqt6.QWidget()
p2 = vqt6.QVBoxLayout(page2)
p2.setContentsMargins(36, 40, 36, 24)
p2.setSpacing(16)
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)
prog = vqt6.QProgressBar()
prog.setRange(0, 100)
prog.setValue(0)
p2.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)
progress_status_ptr = t.CPtr(pstat._ptr)
p2.addStretch(1)
stack.addWidget(page2._ptr)
# ---------- 页3:完成 ----------
page3 = vqt6.QWidget()
p3 = vqt6.QVBoxLayout(page3)
p3.setContentsMargins(36, 40, 36, 24)
p3.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)
fsum = vqt6.QLabel("")
vqt6.vqt6_qwidget_set_stylesheet_utf8(fsum._ptr, "font-size: 14px; color: #d4d4d4;")
p3.addWidget(fsum._ptr)
finish_summary_ptr = t.CPtr(fsum._ptr)
p3.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)
finish_btn.clicked(OnFinish, 0)
stack.addWidget(page3._ptr)
# ===== 定时器(模拟安装进度) =====
timer = vqt6.QTimer()
timer.timeout(on_tick, 0)
timer.start(1000)
timer.timeout(OnProgressTick, 0)
timer_ptr = t.CPtr(timer._ptr)
GotoStep(0)
win.show()
return app.exec()