From 74624b1d0a8d3d302b2140d2112fba8e1e47ef4f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 2 Sep 2026 09:26:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(ViperQtProject):=20VS=20Installer=20?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E5=AE=89=E8=A3=85=E5=90=91=E5=AF=BC=E6=BC=94?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 四步骤:欢迎 → 组件选择 → 安装进度(QTimer 模拟)→ 完成 - 左导航高亮 + QStackedWidget 页面切换,深色主题 QSS - 组件复选 + 计数摘要,安装路径浏览(QFileDialog),完成页汇总 --- ViperQtProject/App/main.vp | 428 +++++++++++++++++++++++++++++-------- 1 file changed, 334 insertions(+), 94 deletions(-) diff --git a/ViperQtProject/App/main.vp b/ViperQtProject/App/main.vp index d096b18..4865a1e 100644 --- a/ViperQtProject/App/main.vp +++ b/ViperQtProject/App/main.vp @@ -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()