From ab1f08f0361f32fdc94be93d69fe15067a79281d Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Wed, 8 Jul 2026 14:57:29 +0800 Subject: [PATCH 1/5] =?UTF-8?q?build(gui):=20=E5=BC=95=E5=85=A5=20QuarkWid?= =?UTF-8?q?gets=20=E6=8E=A7=E4=BB=B6=E5=BA=93(add=5Fsubdirectory)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GUI 接入组织级 Qt 控件库(开发期 ~/MCUUtils/QuarkWidgets,发布期 third_party/QuarkWidgets submodule)。gui/CMakeLists.txt 里 MCUUTILS_DIR 解析本地路径,缺失回退 submodule,都没有则 FATAL_ERROR 报清晰指引。 接线隔离在 gui/,根 CMakeLists 零改动(避并行 worktree 冲突热点)。 验证:MICRO_FORGE_GUI=ON 全量编译零 error;ctest 367/367 绿; micro-forge-gui offscreen(QT_QPA_PLATFORM=offscreen)启动不崩。 --- gui/CMakeLists.txt | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 672efb6..7abdc85 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -7,6 +7,28 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets) set(CMAKE_AUTOMOC ON) +# QuarkWidgets — org-level Qt control library. Pure Qt, zero micro-forge +# dependency; this target is its only consumer here (DIRECTIVES §E: GUI is the +# consumer, core stays zero-dep). Dev: MCUUTILS_DIR points at the local +# ~/MCUUtils clone; release/CI: third_party/QuarkWidgets submodule. Wiring lives +# in gui/ (not the root CMakeLists) so the parallel-worktree collision hotspot +# is untouched. +set(MCUUTILS_DIR "$ENV{MCUUTILS_DIR}" CACHE PATH + "MCUUtils monorepo root (holds QuarkWidgets/); needed for MICRO_FORGE_GUI=ON") +set(quarkwidgets_dir "${MCUUTILS_DIR}/QuarkWidgets") +if(NOT EXISTS "${quarkwidgets_dir}/CMakeLists.txt") + # Release/CI fallback: in-repo submodule (init via git submodule update). + set(quarkwidgets_dir "${CMAKE_SOURCE_DIR}/third_party/QuarkWidgets") +endif() +if(NOT EXISTS "${quarkwidgets_dir}/CMakeLists.txt") + message(FATAL_ERROR + "QuarkWidgets not found under '${MCUUTILS_DIR}/QuarkWidgets' nor " + "'${CMAKE_SOURCE_DIR}/third_party/QuarkWidgets'. " + "Set MCUUTILS_DIR (e.g. export MCUUTILS_DIR=$HOME/MCUUtils) or init " + "the third_party/QuarkWidgets submodule. Required for MICRO_FORGE_GUI=ON.") +endif() +add_subdirectory("${quarkwidgets_dir}" "${CMAKE_BINARY_DIR}/quarkwidgets-build") + add_executable(micro-forge-gui main.cpp main_window.cpp @@ -24,4 +46,4 @@ add_executable(micro-forge-gui # main.cpp / main_window.cpp include "gui/main_window.hpp" (header sits next to # the .cpp here), so the include root is the repo top, not src/. target_include_directories(micro-forge-gui PRIVATE ${CMAKE_SOURCE_DIR}) -target_link_libraries(micro-forge-gui PRIVATE micro_forge Qt6::Widgets) +target_link_libraries(micro-forge-gui PRIVATE micro_forge Qt6::Widgets QuarkWidgets) From ade89bccd70dece0586d912f4b55a316b8e4f048 Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Wed, 8 Jul 2026 17:27:49 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat(gui):=20=E6=9D=BF=E8=A7=86=E5=9B=BE=20?= =?UTF-8?q?PA0..PA7=20=E6=94=B9=E7=94=A8=20LedPanel=20=E5=AD=90=E6=8E=A7?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stm32_board_widget 那排 flat 绿/灰椭圆 LED 换成 quark::LedPanel 子控件: - 8 个 QuarkBulb(纵向;绿色保板语义「亮=引脚驱动高」)+ PA 标签 + ODR hex - setBulbSize(24×24) 适配密排;refresh() 从 odr_[0] 驱动 setLevels - resizeEvent 把 LedPanel 摆在 UART 线右侧(chip_x+kChipW+kWireLen+20) - 删 paintEvent 里手画的 PA0..PA7 rightLed lambda + 线 + 标签 - 板最小尺寸 480×420 → 600×440(腾出 LedPanel 空间) - PA9/PA10 UART + SWD + PC13 仍手画不变 验证:GUI 编译链通 + offscreen 启动不崩 + ctest 367/367;LedPanel 自身渲染已 standalone 验过(ledpanel.png) --- gui/view/widgets/stm32_board_widget.cpp | 56 ++++++++++++------------- gui/view/widgets/stm32_board_widget.hpp | 23 ++++++---- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/gui/view/widgets/stm32_board_widget.cpp b/gui/view/widgets/stm32_board_widget.cpp index 7f941cb..21447a9 100644 --- a/gui/view/widgets/stm32_board_widget.cpp +++ b/gui/view/widgets/stm32_board_widget.cpp @@ -1,6 +1,8 @@ // STM32F103 board widget — see stm32_board_widget.hpp. #include "gui/view/widgets/stm32_board_widget.hpp" +#include "QuarkWidgets/LedPanel.hpp" + #include #include #include @@ -8,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -25,8 +29,16 @@ constexpr int kLedR = 10; // LED radius } // namespace Stm32BoardWidget::Stm32BoardWidget(QWidget* parent) : QWidget(parent) { - setMinimumSize(480, 420); + setMinimumSize(600, 440); // widened from 480×420 to fit the LED panel setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + // Port-A LED row (PA0..PA7 + ODR hex) — a LedPanel child. Green tint keeps + // the board's "lit = pin drives high" semantics; small bulbs fit a dense + // pin column beside the chip. + ledPanel_ = new quark::LedPanel(8, Qt::Vertical, this); + ledPanel_->setLabelPrefix(QStringLiteral("PA")); + ledPanel_->setColor(QColor(90, 220, 100)); + ledPanel_->setBulbSize(QSize(24, 24)); } void Stm32BoardWidget::refresh( @@ -34,9 +46,21 @@ void Stm32BoardWidget::refresh( odr_[0] = snap.peripherals.gpio[0].odr; // A odr_[1] = snap.peripherals.gpio[1].odr; // B (rendered pins use A/C) odr_[2] = snap.peripherals.gpio[2].odr; // C + if (ledPanel_ != nullptr) ledPanel_->setLevels(odr_[0]); // PA0..PA7 update(); // async — never block the tick loop on a repaint. } +void Stm32BoardWidget::resizeEvent(QResizeEvent*) { + if (ledPanel_ == nullptr) return; + const int chip_x = (width() - kChipW) / 2; + const int chip_y = (height() - kChipH) / 2; + // Place the LED panel just past the UART wire stubs, aligned to the chip. + const int x = chip_x + kChipW + kWireLen + 20; + const QSize hint = ledPanel_->sizeHint(); + ledPanel_->setGeometry(x, chip_y, qMax(96, hint.width()), + qMax(kChipH, hint.height())); +} + void Stm32BoardWidget::paintEvent(QPaintEvent*) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); @@ -69,34 +93,10 @@ void Stm32BoardWidget::paintEvent(QPaintEvent*) { return (odr >> pin) & 1u; }; - // ── right side: PA0..PA7 LED row ── any port-A pin a firmware drives shows. - const auto rightLed = [&](int row, int pin) { - const int y = chip_y + kPinStart + row * kPinGap; - const int x0 = chip_x + kChipW; - const int x1 = x0 + kWireLen; - // pin stub - p.setPen(QPen(QColor(200, 200, 200), 1)); - p.setBrush(QColor(210, 210, 210)); - p.drawRect(x0 - 4, y - 3, 8, 6); - // wire (red when driven high, grey when low) - const bool on = bit(odr_[0], pin); - p.setPen(QPen(on ? QColor(200, 40, 40) : QColor(180, 180, 180), 2)); - p.drawLine(x0, y, x1, y); - // LED - const QRect led_rect(x1, y - kLedR, kLedR * 2, kLedR * 2); - p.setPen(QPen(QColor(60, 60, 60), 1)); - p.setBrush(on ? QColor(90, 220, 100) : QColor(70, 70, 70)); - p.drawEllipse(led_rect); - // pin name - p.setPen(QColor(40, 40, 40)); - p.setFont(small); - p.drawText(x0 + 6, y - 6, QString("PA%1").arg(pin)); - }; - for (int i = 0; i < 8; ++i) { - rightLed(i, i); // row i ↔ pin i (PA0..PA7) - } + // ── PA0..PA7 LEDs: hosted by the ledPanel_ child (see refresh / resizeEvent). + // Only the UART markers (PA9/PA10) + SWD + PC13 are painted below. - // Below the LED row: PA9/PA10 UART markers (wire + label, no LED). + // PA9/PA10 UART markers (wire + label, no LED). const auto rightTag = [&](int row, const QString& name, const QColor& wire, const QString& tag) { const int y = chip_y + kPinStart + (8 + row) * kPinGap; diff --git a/gui/view/widgets/stm32_board_widget.hpp b/gui/view/widgets/stm32_board_widget.hpp index 4496378..a8f055f 100644 --- a/gui/view/widgets/stm32_board_widget.hpp +++ b/gui/view/widgets/stm32_board_widget.hpp @@ -1,12 +1,13 @@ -// STM32F103 board widget — paints the chip + pins wired to external LEDs / -// UART / SWD. The LEDs reflect GPIO ODR bits in real time: micro-forge's -// distinctive advantage over a real-hardware debugger (which can't show "the -// light is on" without staring at a physical board). Read-only output; -// pin-level input injection is handled in the gpio_panel (A2 buttons). +// STM32F103 board widget — paints the chip + pins wired to UART / SWD / PC13, +// and hosts a LedPanel child for the Port-A LED row (PA0..PA7). The LEDs reflect +// GPIO ODR bits in real time: micro-forge's distinctive advantage over a +// real-hardware debugger (which can't show "the light is on" without staring at +// a physical board). Read-only output; pin-level input injection is handled in +// the gpio_panel (A2 buttons). // -// Port A is drawn as a row of 8 LEDs (PA0..PA7) so ANY demo firmware driving a -// port-A pin shows up — gpio_blink uses PA5, the F103 CubeMX example uses PA1. -// PC13 (board LED) + USART1 TX/RX + SWD are also drawn. +// Port A (PA0..PA7) is shown via a quark::LedPanel child — any demo firmware +// driving a port-A pin lights up (gpio_blink uses PA5, the F103 CubeMX example +// uses PA1). PA9/PA10 UART markers + SWD + PC13 are still custom-painted here. #pragma once #include "introspection/introspection.hpp" @@ -16,6 +17,8 @@ #include +namespace quark { class LedPanel; } // hosted child for PA0..PA7 + namespace micro_forge::gui::view { class Stm32BoardWidget : public QWidget { @@ -28,11 +31,15 @@ class Stm32BoardWidget : public QWidget { protected: void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; private: // GPIO ODR per port A/B/C — bit i = pin i driven high. Saved by refresh(), // read by paintEvent() (Qt may repaint at any time, independent of ticks). std::array odr_{}; + + // Port-A LED row (PA0..PA7 + ODR hex), driven from odr_[0]. + quark::LedPanel* ledPanel_{}; }; } // namespace micro_forge::gui::view From 61e7d9c8b80ca2b8e917844c280ceea3a662aeee Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Wed, 8 Jul 2026 17:53:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat(gui):=20serial=5Fpanel=20=E6=94=B9?= =?UTF-8?q?=E7=94=A8=20UartTerminalView(delta=20=E8=BF=BD=E5=8A=A0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把只读 QTextEdit(还挂着 QSS font-family:monospace,违反不用 QSS 的规矩) 换成 quark::UartTerminalView:终端体 + RX/TX 计数 + auto-scroll + 清空都现成。 适配层做的事:snapshot 每帧带的是 host 累积的整段 USART 缓冲,这里记一个 shownBytes_,只把新增的尾部 appendText 进去——保持终端「只追加」语义(它的 RX 计数和 auto-scroll 才有意义),而不是每帧整段重灌。缓冲收缩(会话 rebuild → 旧输出作废)时 clear() + shownBytes_ 归零重新对齐。 QSS 行随之删除。 --- gui/view/panels/serial_panel.cpp | 20 ++++++++++++++------ gui/view/panels/serial_panel.hpp | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/gui/view/panels/serial_panel.cpp b/gui/view/panels/serial_panel.cpp index 7b256f1..df42f7c 100644 --- a/gui/view/panels/serial_panel.cpp +++ b/gui/view/panels/serial_panel.cpp @@ -1,9 +1,10 @@ // Serial panel — see serial_panel.hpp. #include "gui/view/panels/serial_panel.hpp" +#include "QuarkWidgets/UartTerminalView.hpp" + #include #include -#include #include namespace micro_forge::gui::panels { @@ -11,9 +12,7 @@ namespace micro_forge::gui::panels { SerialPanel::SerialPanel(QWidget* parent) : QWidget(parent) { auto* lay = new QVBoxLayout(this); - view_ = new QTextEdit; - view_->setReadOnly(true); - view_->setStyleSheet("font-family: monospace;"); + view_ = new quark::UartTerminalView; lay->addWidget(view_); input_ = new QLineEdit; @@ -28,8 +27,17 @@ SerialPanel::SerialPanel(QWidget* parent) : QWidget(parent) { void SerialPanel::refresh(const introspection::IntrospectionSnapshot& snap) { const auto sv = snap.peripherals.usart_output; - view_->setPlainText( - QString::fromUtf8(sv.data(), static_cast(sv.size()))); + // Buffer shrank → session rebuilt: old output is stale, resync from zero. + if (sv.size() < shownBytes_) { + view_->clear(); + shownBytes_ = 0; + } + if (sv.size() > shownBytes_) { + const auto len = static_cast(sv.size()) - shownBytes_; + view_->appendText(QString::fromUtf8(sv.data() + shownBytes_, + static_cast(len))); + shownBytes_ = sv.size(); + } } } // namespace micro_forge::gui::panels diff --git a/gui/view/panels/serial_panel.hpp b/gui/view/panels/serial_panel.hpp index ff75e88..0273db9 100644 --- a/gui/view/panels/serial_panel.hpp +++ b/gui/view/panels/serial_panel.hpp @@ -2,15 +2,24 @@ // plus a text input to inject bytes into USART RX (A2). The input only emits // a signal; MainWindow forwards it to model::Session so the panel itself stays // free of simulator coupling. +// +// The terminal body is a quark::UartTerminalView (org-level control). The +// snapshot carries the host's whole accumulated USART buffer each tick, so this +// adapter tracks how many bytes it has already appended and feeds only the new +// tail — keeping the terminal append-only (its RX counter + autoscroll stay +// meaningful) instead of replacing the whole text every tick. #pragma once #include "introspection/introspection.hpp" +#include + #include #include class QLineEdit; -class QTextEdit; + +namespace quark { class UartTerminalView; } namespace micro_forge::gui::panels { @@ -26,8 +35,12 @@ class SerialPanel : public QWidget { void inputSubmitted(const QString& text); private: - QTextEdit* view_; + quark::UartTerminalView* view_; QLineEdit* input_; + // Bytes of the snapshot's USART buffer already shown in the terminal. Used + // to append only the delta each tick. Reset when the buffer shrinks (the + // session rebuilt → old output is stale → terminal cleared). + std::size_t shownBytes_ = 0; }; } // namespace micro_forge::gui::panels From 9b9baf156889cc9f1daeca1d17c945b35f343ea3 Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Wed, 8 Jul 2026 18:13:37 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat(gui):=20=E6=9D=BF=E8=A7=86=E5=9B=BE=20?= =?UTF-8?q?PC13=20=E6=94=B9=E7=94=A8=20QuarkBulb=20=E5=AD=90=E6=8E=A7?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 板视图最后一个手画 LED(PC13 那个绿/灰椭圆)换成 quark::QuarkBulb 子控件, 和 PA 灯排统一外观/语义(同绿色「亮=引脚驱动高」)。只留引脚焊盘 + 连线 + 「PC13 / LED」文字仍手画,灯泡本身自绘。绿色 setColor + 24×24 setFixedSize, resizeEvent 摆位(芯片正下方),refresh 从 odr_[2] bit 13 驱动。删掉随之没用的 bit lambda 和 kLedR 常量。 至此板视图所有指示灯(PA0..PA7 + PC13)全部 QuarkBulb 化,无手画 LED 残留。 --- gui/view/widgets/stm32_board_widget.cpp | 54 ++++++++++++++----------- gui/view/widgets/stm32_board_widget.hpp | 8 +++- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/gui/view/widgets/stm32_board_widget.cpp b/gui/view/widgets/stm32_board_widget.cpp index 21447a9..4b51a5a 100644 --- a/gui/view/widgets/stm32_board_widget.cpp +++ b/gui/view/widgets/stm32_board_widget.cpp @@ -2,6 +2,7 @@ #include "gui/view/widgets/stm32_board_widget.hpp" #include "QuarkWidgets/LedPanel.hpp" +#include "QuarkWidgets/QuarkBulb.hpp" #include #include @@ -15,8 +16,6 @@ #include #include -#include - namespace micro_forge::gui::view { namespace { @@ -25,7 +24,7 @@ constexpr int kChipH = 250; constexpr int kPinGap = 22; constexpr int kPinStart = 30; // first pin offset from chip top (room for label) constexpr int kWireLen = 70; // pin wire length out to the LED -constexpr int kLedR = 10; // LED radius +constexpr int kPc13Bulb = 24; // PC13 on-board bulb side, px } // namespace Stm32BoardWidget::Stm32BoardWidget(QWidget* parent) : QWidget(parent) { @@ -39,6 +38,11 @@ Stm32BoardWidget::Stm32BoardWidget(QWidget* parent) : QWidget(parent) { ledPanel_->setLabelPrefix(QStringLiteral("PA")); ledPanel_->setColor(QColor(90, 220, 100)); ledPanel_->setBulbSize(QSize(24, 24)); + + // On-board PC13 LED — same green "lit" semantics as the port-A row. + pc13Bulb_ = new quark::QuarkBulb(this); + pc13Bulb_->setColor(QColor(90, 220, 100)); + pc13Bulb_->setFixedSize(kPc13Bulb, kPc13Bulb); } void Stm32BoardWidget::refresh( @@ -47,18 +51,29 @@ void Stm32BoardWidget::refresh( odr_[1] = snap.peripherals.gpio[1].odr; // B (rendered pins use A/C) odr_[2] = snap.peripherals.gpio[2].odr; // C if (ledPanel_ != nullptr) ledPanel_->setLevels(odr_[0]); // PA0..PA7 + if (pc13Bulb_ != nullptr) // PC13 = port C bit 13 + pc13Bulb_->setState(static_cast((odr_[2] >> 13) & 1u)); update(); // async — never block the tick loop on a repaint. } void Stm32BoardWidget::resizeEvent(QResizeEvent*) { - if (ledPanel_ == nullptr) return; const int chip_x = (width() - kChipW) / 2; const int chip_y = (height() - kChipH) / 2; - // Place the LED panel just past the UART wire stubs, aligned to the chip. - const int x = chip_x + kChipW + kWireLen + 20; - const QSize hint = ledPanel_->sizeHint(); - ledPanel_->setGeometry(x, chip_y, qMax(96, hint.width()), - qMax(kChipH, hint.height())); + + if (ledPanel_ != nullptr) { + // Place the LED panel just past the UART wire stubs, aligned to the chip. + const int x = chip_x + kChipW + kWireLen + 20; + const QSize hint = ledPanel_->sizeHint(); + ledPanel_->setGeometry(x, chip_y, qMax(96, hint.width()), + qMax(kChipH, hint.height())); + } + if (pc13Bulb_ != nullptr) { + // Below the chip, on centre: the pin pad + wire are painted in + // paintEvent; the bulb self-draws here. + const int cx = chip_x + kChipW / 2; + const int y1 = chip_y + kChipH + 38; // wire length, matches paintEvent + pc13Bulb_->setGeometry(cx - kPc13Bulb / 2, y1, kPc13Bulb, kPc13Bulb); + } } void Stm32BoardWidget::paintEvent(QPaintEvent*) { @@ -89,12 +104,9 @@ void Stm32BoardWidget::paintEvent(QPaintEvent*) { p.setFont(small); p.drawText(QPoint(chip.x() + kChipW / 2 - 24, chip.y() + 26), "Cortex-M3"); - const auto bit = [](std::uint16_t odr, int pin) -> bool { - return (odr >> pin) & 1u; - }; - // ── PA0..PA7 LEDs: hosted by the ledPanel_ child (see refresh / resizeEvent). - // Only the UART markers (PA9/PA10) + SWD + PC13 are painted below. + // Only the UART markers (PA9/PA10) + SWD + the PC13 pad/wire/label are + // painted below — PC13's LED itself is the pc13Bulb_ child. // PA9/PA10 UART markers (wire + label, no LED). const auto rightTag = [&](int row, const QString& name, @@ -136,24 +148,20 @@ void Stm32BoardWidget::paintEvent(QPaintEvent*) { p.drawText(QPoint(chip_x - kWireLen, chip_y + kPinStart + 2 * kPinGap + 14), "SWD"); - // ── bottom: PC13 board LED ── + // ── bottom: PC13 — pin pad + wire + label painted here; the LED itself + // is the pc13Bulb_ child (positioned in resizeEvent, lit in refresh) ── { const int x = chip_x + kChipW / 2; const int y0 = chip_y + kChipH; const int y1 = y0 + 38; p.setPen(QPen(QColor(200, 200, 200), 1)); p.setBrush(QColor(210, 210, 210)); - p.drawRect(x - 3, y0 - 4, 6, 8); + p.drawRect(x - 3, y0 - 4, 6, 8); // pin pad on the chip edge p.setPen(QPen(QColor(40, 160, 200), 2)); - p.drawLine(x, y0, x, y1); - const QRect led_rect(x - kLedR, y1, kLedR * 2, kLedR * 2); - const bool on = bit(odr_[2], 13); - p.setPen(QPen(QColor(60, 60, 60), 1)); - p.setBrush(on ? QColor(90, 220, 100) : QColor(70, 70, 70)); - p.drawEllipse(led_rect); + p.drawLine(x, y0, x, y1); // wire down to the bulb p.setPen(QColor(40, 40, 40)); p.setFont(small); - p.drawText(led_rect.x() - 10, led_rect.bottom() + 12, "PC13 / LED"); + p.drawText(x - 30, y1 + kPc13Bulb + 14, "PC13 / LED"); } // ── legend ── diff --git a/gui/view/widgets/stm32_board_widget.hpp b/gui/view/widgets/stm32_board_widget.hpp index a8f055f..29192d0 100644 --- a/gui/view/widgets/stm32_board_widget.hpp +++ b/gui/view/widgets/stm32_board_widget.hpp @@ -7,7 +7,8 @@ // // Port A (PA0..PA7) is shown via a quark::LedPanel child — any demo firmware // driving a port-A pin lights up (gpio_blink uses PA5, the F103 CubeMX example -// uses PA1). PA9/PA10 UART markers + SWD + PC13 are still custom-painted here. +// uses PA1). PC13 is a quark::QuarkBulb child. PA9/PA10 UART markers + SWD + +// the PC13 pin pad/wire/label are still custom-painted here. #pragma once #include "introspection/introspection.hpp" @@ -17,7 +18,7 @@ #include -namespace quark { class LedPanel; } // hosted child for PA0..PA7 +namespace quark { class LedPanel; class QuarkBulb; } // hosted children namespace micro_forge::gui::view { @@ -40,6 +41,9 @@ class Stm32BoardWidget : public QWidget { // Port-A LED row (PA0..PA7 + ODR hex), driven from odr_[0]. quark::LedPanel* ledPanel_{}; + + // On-board PC13 LED, driven from odr_[2] bit 13. + quark::QuarkBulb* pc13Bulb_{}; }; } // namespace micro_forge::gui::view From fc5f988d862da3e2b3893181d980cba5be2aa828 Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Wed, 8 Jul 2026 18:39:38 +0800 Subject: [PATCH 5/5] =?UTF-8?q?build(gui):=20QuarkWidgets=20=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=E6=94=B9=E6=8C=87=E5=90=91=20embedded/=20=E5=AD=90?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QuarkWidgets 仓并入了 MD3 控件库(origin/main),root 现在是 SHARED+aex 的 MD3 构建。micro-forge 只用嵌入式控件(QuarkBulb/LedPanel/UartTerminalView),所以 add_subdirectory 改指向 ${quarkwidgets_dir}/embedded,link 改 QuarkWidgets::Embedded (target 在合并后改名避撞 MD3 的 quarkwidgets)。C++ 代码不动(命名空间 quark::、 include 目录 QuarkWidgets/ 都没变)。 验证:reconfigure + 全量编译 + ctest 367/367 + GUI offscreen 启动不崩。 --- gui/CMakeLists.txt | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 7abdc85..b5dfc14 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -7,27 +7,31 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets) set(CMAKE_AUTOMOC ON) -# QuarkWidgets — org-level Qt control library. Pure Qt, zero micro-forge -# dependency; this target is its only consumer here (DIRECTIVES §E: GUI is the -# consumer, core stays zero-dep). Dev: MCUUTILS_DIR points at the local -# ~/MCUUtils clone; release/CI: third_party/QuarkWidgets submodule. Wiring lives -# in gui/ (not the root CMakeLists) so the parallel-worktree collision hotspot -# is untouched. +# QuarkWidgets — org-level Qt control library. The repo now holds two +# independent sub-libraries: the MD3 widget library at the root (SHARED + aex, +# for CFDesktop) and the embedded-debug controls under embedded/ (STATIC + +# QTest, zero deps — these). micro-forge consumes ONLY the embedded/ controls +# (QuarkBulb / LedPanel / UartTerminalView); pointing at the subdir avoids +# pulling in the root's aex dependency. Dev: MCUUTILS_DIR → ~/MCUUtils; +# release/CI: third_party/QuarkWidgets submodule. Wiring lives in gui/ (not the +# root CMakeLists) so the parallel-worktree collision hotspot is untouched. set(MCUUTILS_DIR "$ENV{MCUUTILS_DIR}" CACHE PATH "MCUUtils monorepo root (holds QuarkWidgets/); needed for MICRO_FORGE_GUI=ON") set(quarkwidgets_dir "${MCUUTILS_DIR}/QuarkWidgets") -if(NOT EXISTS "${quarkwidgets_dir}/CMakeLists.txt") +if(NOT EXISTS "${quarkwidgets_dir}/embedded/CMakeLists.txt") # Release/CI fallback: in-repo submodule (init via git submodule update). set(quarkwidgets_dir "${CMAKE_SOURCE_DIR}/third_party/QuarkWidgets") endif() -if(NOT EXISTS "${quarkwidgets_dir}/CMakeLists.txt") +if(NOT EXISTS "${quarkwidgets_dir}/embedded/CMakeLists.txt") message(FATAL_ERROR - "QuarkWidgets not found under '${MCUUTILS_DIR}/QuarkWidgets' nor " - "'${CMAKE_SOURCE_DIR}/third_party/QuarkWidgets'. " + "QuarkWidgets embedded controls not found under " + "'${MCUUTILS_DIR}/QuarkWidgets/embedded' nor " + "'${CMAKE_SOURCE_DIR}/third_party/QuarkWidgets/embedded'. " "Set MCUUTILS_DIR (e.g. export MCUUTILS_DIR=$HOME/MCUUtils) or init " "the third_party/QuarkWidgets submodule. Required for MICRO_FORGE_GUI=ON.") endif() -add_subdirectory("${quarkwidgets_dir}" "${CMAKE_BINARY_DIR}/quarkwidgets-build") +add_subdirectory("${quarkwidgets_dir}/embedded" + "${CMAKE_BINARY_DIR}/quarkwidgets-embedded-build") add_executable(micro-forge-gui main.cpp @@ -46,4 +50,4 @@ add_executable(micro-forge-gui # main.cpp / main_window.cpp include "gui/main_window.hpp" (header sits next to # the .cpp here), so the include root is the repo top, not src/. target_include_directories(micro-forge-gui PRIVATE ${CMAKE_SOURCE_DIR}) -target_link_libraries(micro-forge-gui PRIVATE micro_forge Qt6::Widgets QuarkWidgets) +target_link_libraries(micro-forge-gui PRIVATE micro_forge Qt6::Widgets QuarkWidgets::Embedded)