Qt C++ 復雜界面處理:巧用覆蓋層突破復雜界面處理難題?之二

接上一篇,繼續探索“覆蓋層”的使用方法。

五、覆蓋層進階交互:從 “能繪制” 到 “好操作”?

基礎的繪制功能只能滿足 “看得見” 的需求,實際開發中還需要 “能操作”—— 比如選中線條修改顏色、按 Delete 鍵刪除線條、鼠標 hover 時高亮組件等。這一部分一起探索覆蓋層的進階交互邏輯,讓覆蓋層從 “靜態畫布” 變成 “可交互工具”。?

5.1 交互 1:線條的選中與高亮(鼠標 Hover 效果)?

需求:鼠標移動到覆蓋層的線條上時,線條自動變粗、變色(高亮),離開時恢復原狀,幫助用戶快速識別當前操作的線條。?
實現步驟:?

  • 添加成員變量存儲選中狀態:在OverlayWidget.h中添加變量,記錄當前選中的線條索引和鼠標是否 hover 在線條上:
private:int m_selectedLineIndex; // 當前選中的線條索引(-1表示未選中)int m_hoveredLineIndex;  // 當前hover的線條索引(-1表示無)QColor m_defaultLineColor = Qt::red;   // 線條默認顏色QColor m_hoverLineColor = Qt::blue;    // 線條hover顏色QColor m_selectedLineColor = Qt::green;// 線條選中顏色int m_defaultLineWidth = 2;            // 線條默認寬度int m_hoverLineWidth = 4;              // 線條hover寬度
  • 初始化狀態變量:在initOverlayProps中初始化選中 /hover 狀態:
void OverlayWidget::initOverlayProps()
{// 其他初始化...m_selectedLineIndex = -1; // 初始未選中m_hoveredLineIndex = -1;  // 初始無hover
}
  • 重寫鼠標移動事件,判斷 hover 狀態:通過mouseMoveEvent實時檢測鼠標是否落在線條上,更新m_hoveredLineIndex并觸發重繪:
void OverlayWidget::mouseMoveEvent(QMouseEvent *event)
{// 1. 先處理之前的拖動邏輯(若有)if (m_isDrawing) {m_tempLine.setP2(event->pos());update();QWidget::mouseMoveEvent(event);return;}// 2. 檢測鼠標是否hover在線條上int hoverIndex = -1;QPointF mousePos = event->pos();// 遍歷所有線條,判斷鼠標是否在線條附近(閾值5像素)for (int i = 0; i < m_lines.size(); ++i) {QLineF line = m_lines[i];if (distanceToLine(mousePos, line) <= 5.0) {hoverIndex = i;break; // 只高亮最上層的線條(可根據需求調整為多線條高亮)}}// 3. 若hover狀態變化,更新并觸發重繪if (hoverIndex != m_hoveredLineIndex) {m_hoveredLineIndex = hoverIndex;// 更改鼠標樣式(可選,提升交互體驗)if (m_hoveredLineIndex != -1) {setCursor(Qt::PointingHandCursor); // 鼠標變為手型} else {setCursor(Qt::ArrowCursor);        // 恢復默認鼠標}update(); // 重繪以顯示高亮效果}QWidget::mouseMoveEvent(event);
}
// 手動實現:計算QPoint到QLine(線段)的最短距離,有人說QLineF自帶有計算點到線段距離的函數distanceToPoint(),但我試了沒有
qreal OverlayWidget::distanceToLine(const QPointF& point, const QLineF& line)
{// 線段起點A、終點B,目標點PQPointF A = line.p1();QPointF B = line.p2();QPointF P = point;// 計算向量AB、AP、BPqreal ABx = B.x() - A.x();qreal ABy = B.y() - A.y();qreal APx = P.x() - A.x();qreal APy = P.y() - A.y();qreal BPx = P.x() - B.x();qreal BPy = P.y() - B.y();// 情況1:P的投影在A外側(向量AP與AB夾角>90°),距離=AP長度if (APx * ABx + APy * ABy < 0) {return qSqrt(APx*APx + APy*APy);}// 情況2:P的投影在B外側(向量BP與BA夾角>90°),距離=BP長度if (BPx * (-ABx) + BPy * (-ABy) < 0) {return qSqrt(BPx*BPx + BPy*BPy);}// 情況3:P的投影在線段上,距離=三角形面積*2 / AB長度(叉積公式)qreal area = qAbs(ABx * APy - ABy * APx); // 三角形ABP的面積(絕對值)qreal ABLength = qSqrt(ABx*ABx + ABy*ABy); // 線段AB長度return area / ABLength;
}
  • 修改 paintEvent,根據狀態繪制線條:根據 “默認 /hover/ 選中” 狀態,動態調整線條的顏色和寬度:
void OverlayWidget::paintEvent(QPaintEvent *event)
{QWidget::paintEvent(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing, true);// 繪制已確定的線條(按狀態區分樣式)for (int i = 0; i < m_lines.size(); ++i) {QLineF line = m_lines[i];QPen pen;// 判斷線條狀態:選中 > hover > 默認if (i == m_selectedLineIndex) {pen.setColor(m_selectedLineColor);pen.setWidth(m_hoverLineWidth); // 選中線條用hover寬度} else if (i == m_hoveredLineIndex) {pen.setColor(m_hoverLineColor);pen.setWidth(m_hoverLineWidth);} else {pen.setColor(m_defaultLineColor);pen.setWidth(m_defaultLineWidth);}painter.setPen(pen);painter.drawLine(line);}// 繪制臨時線條(保持之前的邏輯)if (m_isDrawing) {QPen penTemp(Qt::blue, 2, Qt::DashLine);painter.setPen(penTemp);painter.drawLine(m_tempLine);}
}
  • 效果:?
    鼠標移動到線條上:線條從 “紅色 2px” 變為 “藍色 4px”,鼠標變為手型,直觀提示 “可交互”;?
    鼠標離開線條:自動恢復為默認樣式,無操作延遲;?
    后續可基于此擴展 “點擊選中線條”“右鍵菜單修改屬性” 等功能。?
    在這里插入圖片描述
    注意:

5.2 交互 2:線條的選中與刪除(鼠標 + 鍵盤)?

需求:鼠標左鍵點擊線條選中(綠色 4px),按 Delete 鍵刪除選中線條,或右鍵點擊線條彈出 “刪除” 菜單,提升操作靈活性。?

5.2.1 鼠標點擊選中線條?

在mousePressEvent中添加選中邏輯,通過鼠標位置判斷是否點擊線條,更新m_selectedLineIndex:

void OverlayWidget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton && m_isDrawing){m_isDrawing = false;// 將臨時線條添加到線條集合(確保線條有一定長度,避免無效線條)if (qAbs(m_tempLine.x2()-m_tempLine.x1()) > 5 || qAbs(m_tempLine.y2()-m_tempLine.y1()) > 5){m_lines.append(m_tempLine);}// 觸發重繪(更新已確定線條的顯示)update();QWidget::mouseReleaseEvent(event);return;}// 1. 優先處理繪制邏輯(左鍵按下開始繪制)if (event->button() == Qt::RightButton && !m_isDrawing && m_hoveredLineIndex == -1) {// 遍歷線條,判斷是否點擊在線條上int clickIndex = -1;QPointF mousePos = event->pos();for (int i = 0; i < m_lines.size(); ++i) {if (distanceToLine(mousePos, m_lines[i]) <= 5.0) {clickIndex = i;break;}}// 更新選中狀態:點擊線條則選中,點擊空白則取消選中if (clickIndex != -1) {m_selectedLineIndex = clickIndex;qDebug() << QString("selectlinux") << m_selectedLineIndex;} else {m_selectedLineIndex = -1; // 點擊空白,取消選中m_isDrawing = true;       // 開始新的繪制m_tempLine.setP1(event->pos());m_tempLine.setP2(event->pos());}update();event->accept();return;}// 2. 右鍵點擊線條,彈出刪除菜單(需添加QMenu頭文件)if (event->button() == Qt::RightButton && m_hoveredLineIndex != -1) {QMenu menu(this);QAction *deleteAction = menu.addAction("刪除線條");// 連接刪除動作的信號槽connect(deleteAction, &QAction::triggered, this, [this]() {if (m_hoveredLineIndex >= 0 && m_hoveredLineIndex < m_lines.size()) {m_lines.removeAt(m_hoveredLineIndex);m_selectedLineIndex = -1; // 刪除后取消選中m_hoveredLineIndex = -1;update();qDebug() << QString("delete line index") << m_hoveredLineIndex;}});menu.exec(event->globalPos()); // 在鼠標位置彈出菜單event->accept();return;}QWidget::mousePressEvent(event);
}
5.2.2 鍵盤事件:按 Delete 鍵刪除選中線條?

重寫keyPressEvent,監聽 Delete 鍵,刪除當前選中的線條:

// OverlayWidget.h中聲明鍵盤事件
protected:void keyPressEvent(QKeyEvent *event) override;// OverlayWidget.cpp中實現
void OverlayWidget::keyPressEvent(QKeyEvent *event)
{// 只處理Delete鍵,且有選中線條時if (event->key() == Qt::Key_Delete && m_selectedLineIndex != -1) {m_lines.removeAt(m_selectedLineIndex);qDebug() << "按Delete鍵刪除線條,索引:" << m_selectedLineIndex;m_selectedLineIndex = -1; // 取消選中m_hoveredLineIndex = -1;update();event->accept();return;}QWidget::keyPressEvent(event);
}

效果說明:?
左鍵點擊線條:線條變為 “綠色 4px”,標記為選中;?
按 Delete 鍵:選中的線條立即刪除,界面實時更新;?
右鍵點擊線條:彈出 “刪除線條” 菜單,點擊后刪除線條,適合鼠標操作偏好的用戶。?
在這里插入圖片描述

5.3 交互 3:進階穿透交互(區分組件類型)?

之前的 “穿透交互” 只判斷 “是否點擊繪制內容”,實際場景中可能需要 “點擊不同類型的下層組件,執行不同邏輯”—— 比如點擊QPushButton觸發按鈕事件。?
實現步驟:?

  • 添加 “獲取下層組件類型” 的輔助函數:通過鼠標坐標,找到下層被遮擋的組件,判斷其類型:
// OverlayWidget.h中聲明輔助函數
private:QWidget* getUnderlyingWidget(const QPoint& mousePos); // 獲取下層組件// OverlayWidget.cpp中實現
QWidget* OverlayWidget::getUnderlyingWidget(const QPoint& mousePos)
{if (!parentWidget()) return nullptr;// 1. 將覆蓋層的鼠標坐標轉換為父組件(如centralWidget)的坐標QPoint parentPos = mapToParent(mousePos);// 2. 遍歷父組件的所有子組件,找到包含該坐標的組件QList<QWidget*> childWidgets = parentWidget()->findChildren<QWidget*>();// 按Z序從高到低遍歷(確保找到最上層的下層組件)for (int i = childWidgets.size() - 1; i >= 0; --i) {QWidget* child = childWidgets[i];// 組件必須可見且可交互if (child->isVisible() && child->isEnabled() && child->geometry().contains(parentPos)) {return child;}}return parentWidget(); // 未找到子組件,返回父組件
}
  • 在鼠標事件中根據組件類型處理穿透:點擊覆蓋層空白區域時,根據下層組件類型執行不同邏輯:
void OverlayWidget::mousePressEvent(QMouseEvent *event)
{// 先判斷是否點擊繪制內容(線條/臨時線條)bool isClickOnDrawContent = false;// 檢查是否點擊已存在的線條for (auto& line : m_lines) {if (line.distanceToPoint(event->pos()) <= 5.0) {isClickOnDrawContent = true;break;}}// 檢查是否點擊臨時線條(繪制中)if (m_isDrawing && m_tempLine.distanceToPoint(event->pos()) <= 5.0) {isClickOnDrawContent = true;}// 若點擊繪制內容,處理覆蓋層邏輯;否則根據下層組件類型處理if (isClickOnDrawContent) {// 之前的線條選中/繪制邏輯...} else {// 獲取下層組件QWidget* underlyingWidget = getUnderlyingWidget(event->pos());if (underlyingWidget) {// 情況1:下層是QPushButton,觸發按鈕點擊if (qobject_cast<QPushButton*>(underlyingWidget)) {QPushButton* btn = qobject_cast<QPushButton*>(underlyingWidget);btn->click(); // 模擬按鈕點擊qDebug() << "穿透點擊按鈕:" << btn->text();}// 情況2:下層是QLabel,在覆蓋層添加標注else if (qobject_cast<QLabel*>(underlyingWidget)) {QLabel* label = qobject_cast<QLabel*>(underlyingWidget);// 在標簽中心添加“已標注”文本(后續可擴展為自定義標注)m_annotations.append({event->pos(), QString("標注:%1").arg(label->text())});update();qDebug() << "在標簽" << label->text() << "上添加標注";}// 情況3:其他組件,直接穿透事件else {event->ignore(); // 讓事件自然傳遞}}}QWidget::mousePressEvent(event);
}
  • 添加標注繪制邏輯:在paintEvent中繪制標注文本:
// OverlayWidget.h中添加標注存儲結構
private:struct Annotation {QPointF pos;      // 標注位置QString text;     // 標注文本};QVector<Annotation> m_annotations; // 存儲所有標注// paintEvent中添加標注繪制
void OverlayWidget::paintEvent(QPaintEvent *event)
{// 其他繪制邏輯(線條、臨時線條)...// 繪制標注(黑色文本,白色背景半透明)if (!m_annotations.isEmpty()) {QPainter painter(this);QFont annotationFont;annotationFont.setPointSize(9);painter.setFont(annotationFont);foreach (auto& anno, m_annotations) {// 繪制背景(避免文本與下層內容重疊)QRect textRect = painter.boundingRect(QRect(), Qt::AlignLeft, anno.text);textRect.adjust(-5, -3, 5, 3); // 擴展邊距,提升可讀性painter.setBrush(QColor(255, 255, 255, 180)); // 白色半透明背景painter.setPen(Qt::NoPen);painter.drawRect(textRect.translated(anno.pos.x(), anno.pos.y()));// 繪制文本painter.setPen(Qt::black);painter.drawText(anno.pos + QPointF(0, textRect.height()), anno.text);}}
}

這里要注意“mapToParent()”函數,是將本頁面的有個組件或者點位的坐標投射到父頁面上。有事還會用到另一個函數"mapTo()"也是有類似功效。

六、完整實戰案例:工業設備連接圖(覆蓋層綜合應用)?

為了將前面的知識點串聯起來,我們實現一個 “工業設備連接圖” 項目,包含以下核心功能:?
堆疊組件頁面:3 個頁面(設備頁、傳感器頁、數據頁),支持頁面切換;?
覆蓋層跨頁面連線:設備頁的 “設備” 與傳感器頁的 “傳感器” 之間繪制跨頁面連接線條;?
組件交互:設備 / 傳感器按鈕可拖動,線條實時更新;?
線條編輯:選中、刪除、修改線條顏色;?
數據聯動:點擊線條顯示設備與傳感器的連接狀態(如 “正常 / 斷開”)。?

6.1 項目結構與界面設計?

6.1.1 界面布局(Qt 設計器)?
  • 主窗口(QMainWindow):?
    頂部工具欄:3 個QPushButton(“設備頁面”“傳感器頁面”“數據頁面”),用于切換堆疊組件;?
    中心區域:QStackedWidget(命名為stackedWidget),包含 3 個頁面;?
    覆蓋層:OverlayWidget(代碼創建,覆蓋整個中心區域,兩個按鈕,標識來自覆蓋層,并且跟下層連線)。?
    堆疊組件頁面內容:?
    頁面 0(設備頁):2 個QPushButton(btnDevice1“設備 1”、btnDevice2“設備 2”),QLabel“設備狀態:正常”;?
    頁面 1(傳感器頁):3 個QPushButton(btnSensor1“傳感器 1”、btnSensor2“傳感器 2”、btnSensor3“傳感器 3”);?
    頁面 2(數據頁):QTableWidget,顯示設備 - 傳感器的連接數據(備用)。?
6.1.2 核心類結構?

MainWindow:管理堆疊組件、頁面切換、組件拖動(事件過濾器);?
OverlayWidget:負責跨頁面連線繪制;?

6.2 核心功能實現?
6.2.1 1. 主界面頭文件

跨頁面連線的核心是 “獲取被覆蓋的頁面中組件的坐標”——QStackedWidget 隱藏的頁面雖不可見,但組件的pos()和size()仍有效,只需將其坐標轉換為覆蓋層的全局坐標即可。?
在MainWindow中添加 “獲取組件全局坐標” 的函數:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QGridLayout>
#include "overlaywidget.h"#include <QTableWidgetItem>
#include <QVector>
#include <QLineF>
#include <QPoint>// 設備-傳感器連接數據結構體
struct ConnectionData {QWidget* deviceWidget;    // 設備組件(pageDevice內的按鈕)QWidget* sensorWidget;   // 傳感器組件(pageSensor內的按鈕)QColor lineColor;        // 連線顏色(正常綠/斷開紅)QString status;          // 連接狀態("正常"/"斷開")QString deviceName;      // 設備名稱(如"設備1")QString sensorName;      // 傳感器名稱(如"傳感器1")
};QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void init();QPointF getWidgetGlobalPos(QWidget* widget); // 獲取組件相對于覆蓋層的坐標private slots:void on_btHide_clicked();void on_btShow_clicked();void on_btTest_clicked();void on_btData_clicked();// 表格選中行變化(同步線條選中)?void onTableCurrentRowChanged(int currentRow, int previousRow);// 表格數據修改(同步線條狀態)?void onTableItemChanged(QTableWidgetItem *item);private:Ui::MainWindow *ui;//覆蓋層OverlayWidget* m_overlayWidget = nullptr;QVector<ConnectionData> m_connections; // 所有設備-傳感器連接數據?QWidget* m_currentDraggedBtn = nullptr; // 當前正在拖動的按鈕?QPoint m_dragStartPos;           // 拖動起始位置(鼠標全局坐標-按鈕坐標)?// 事件過濾器(處理按鈕拖動)?bool eventFilter(QObject *watched, QEvent *event) override;void updateOverlayCrossPageLines();// 核心函數:獲取組件相對于覆蓋層的中心坐標?QPointF getWidgetCenterInOverlay(QWidget* widget);// 初始化表格數據(同步m_connections)?void initTableWidget();// 同步表格數據與連接數據?void syncTableAndConnections();// MainWindow.h中添加信號signals:void crossPageLinesUpdated(const QVector<QLineF>& lines, const QVector<QColor>& colors);
};
#endif // MAINWINDOW_H
6.2.2 2. 主界面cpp文件?

在MainWindow中管理所有主界面的按鈕響應和事件:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QLayoutItem>
#include <QGridLayout>
#include <QLabel>
#include <QMessageBox>
#include <QDebug>
#include <QMouseEvent>
#include <QTableWidgetItem>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(ui->stackedWidget, &QStackedWidget::currentChanged, this, &MainWindow::updateOverlayCrossPageLines);init();
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::init()
{// 1. 初始化覆蓋層m_overlayWidget = new OverlayWidget(this);m_overlayWidget->setGeometry(0, 0,width(), height());m_overlayWidget->raise(); // 置頂覆蓋層m_overlayWidget->show();// 2. 初始化設備-傳感器連接數據m_connections.append({ui->btnDevice1, ui->btnSensor1, Qt::green, QString("正常"), QString("Device1"), QString("Sencor1")});m_connections.append({ui->btnDevice2, ui->btnSensor3, Qt::red, QString("斷開"), QString("Device2"), QString("Sencor3")});// 3. 初始化表格initTableWidget();// 4. 安裝事件過濾器(處理按鈕拖動)ui->btnDevice1->installEventFilter(this);ui->btnDevice2->installEventFilter(this);ui->btnSensor1->installEventFilter(this);ui->btnSensor2->installEventFilter(this);ui->btnSensor3->installEventFilter(this);// 6. 連接表格信號(數據聯動)//connect(ui->tableWidget, &QTableWidget::currentItemChanged, this, &MainWindow::onTableCurrentRowChanged);connect(ui->tableWidget, &QTableWidget::itemChanged, this, &MainWindow::onTableItemChanged);// 7. 初始更新覆蓋層線條updateOverlayCrossPageLines();QList<QWidget*> fromWidget;fromWidget.append(ui->btnDevice1);fromWidget.append(ui->btnDevice2);fromWidget.append(ui->btnSensor1);fromWidget.append(ui->btnSensor2);fromWidget.append(ui->btnSensor3);m_overlayWidget->addConnection(fromWidget);
}// 表格選中行變化:同步覆蓋層線條選中
void MainWindow::onTableCurrentRowChanged(int currentRow, int previousRow)
{
//    if (currentRow >= 0 && currentRow < m_connections.size()) {
//        // 通知覆蓋層選中對應跨頁面線條(需在OverlayWidget中添加setSelectedCrossLine函數)
//        m_overlayWidget->setSelectedCrossLine(currentRow);
//    } else {
//        // 取消選中
//        m_overlayWidget->setSelectedCrossLine(-1);
//    }
}// 表格數據修改:同步連接狀態與線條顏色
void MainWindow::onTableItemChanged(QTableWidgetItem *item)
{int row = item->row();int col = item->column();if (row >= 0 && row < m_connections.size()) {// 僅處理“狀態”列(第3列)的修改if (col == 3) {QString newStatus = item->text().trimmed();if (newStatus == QString("正常")) {m_connections[row].status = QString("正常");m_connections[row].lineColor = Qt::green;} else if (newStatus == QString("斷開")) {m_connections[row].status = QString("斷開");m_connections[row].lineColor = Qt::red;}// 同步覆蓋層線條updateOverlayCrossPageLines();}}
}// 事件過濾器:處理按鈕拖動
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{// 判斷是否為目標按鈕(設備/傳感器按鈕)bool isTargetBtn = (watched == ui->btnDevice1 || watched == ui->btnDevice2 ||watched == ui->btnSensor1 || watched == ui->btnSensor2 ||watched == ui->btnSensor3);if (!isTargetBtn) {return QMainWindow::eventFilter(watched, event);}QWidget* btn = qobject_cast<QWidget*>(watched);QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);if (!btn || !mouseEvent) {return QMainWindow::eventFilter(watched, event);}// 1. 鼠標按下:記錄拖動起始狀態if (event->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::LeftButton) {m_currentDraggedBtn = btn;// 計算起始偏移(避免拖動時按鈕跳變)m_dragStartPos = mouseEvent->globalPos() - btn->pos();btn->setCursor(Qt::ClosedHandCursor); // 鼠標變為“閉合手”qDebug() << "[MainWindow] 開始拖動按鈕:" << btn->objectName();return true;}// 2. 鼠標移動:更新按鈕位置if (event->type() == QEvent::MouseMove && m_currentDraggedBtn == btn) {if (mouseEvent->buttons() & Qt::LeftButton) {// 計算新位置(限制在centralWidget內)QPoint newPos = mouseEvent->globalPos() - m_dragStartPos;QRect centralRect = ui->centralwidget->geometry();// 邊界限制(避免按鈕拖出主窗口)newPos.setX(qMax(0, qMin(newPos.x(), centralRect.width() - btn->width())));newPos.setY(qMax(0, qMin(newPos.y(), centralRect.height() - btn->height())));// 更新按鈕位置btn->move(newPos);// 同步更新跨頁面線條updateOverlayCrossPageLines();}return true;}// 3. 鼠標釋放:結束拖動if (event->type() == QEvent::MouseButtonRelease && mouseEvent->button() == Qt::LeftButton) {if (m_currentDraggedBtn == btn) {m_currentDraggedBtn = nullptr;btn->setCursor(Qt::ArrowCursor); // 恢復鼠標樣式//qDebug() << "[MainWindow] 結束拖動按鈕:" << btn->objectName();}return true;}return QMainWindow::eventFilter(watched, event);
}// 核心函數:更新覆蓋層跨頁面線條
void MainWindow::updateOverlayCrossPageLines()
{// 通知覆蓋層更新//m_overlayWidget->updateCrossPageLines(crossLines, crossColors, crossStatuses);m_overlayWidget->updatePageShow();
}// 核心函數:獲取組件相對于覆蓋層的中心坐標
QPointF MainWindow::getWidgetCenterInOverlay(QWidget* widget)
{if (!widget || !m_overlayWidget) {return QPointF();}// 1. 組件相對于自身父組件的坐標QPoint widgetPos = widget->pos();QWidget* parent = widget->parentWidget();// 2. 遞歸轉換到stackedWidget的坐標(因按鈕在stackedWidget的子頁面中)while (parent && parent != ui->stackedWidget) {widgetPos += parent->pos();parent = parent->parentWidget();}// 3. stackedWidget相對于centralWidget的坐標QPoint stackedPos = ui->stackedWidget->pos();// 4. 轉換為覆蓋層的坐標(覆蓋層父組件是centralWidget)QPointF overlayPos = m_overlayWidget->mapFromParent(stackedPos + widgetPos);// 5. 返回組件中心坐標return overlayPos + QPointF(widget->width()/2.0, widget->height()/2.0);
}// 初始化表格:設置列名與初始數據
void MainWindow::initTableWidget()
{// 設置表格列數與列名ui->tableWidget->setColumnCount(4);QStringList headers = {QString("DevicesName"), QString("SensorName"), QString("LineColor"), QString("ConnectStatus")};ui->tableWidget->setHorizontalHeaderLabels(headers);// 設置表格屬性ui->tableWidget->setEditTriggers(QAbstractItemView::DoubleClicked); // 雙擊可編輯ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); // 選中整行ui->tableWidget->horizontalHeader()->setStretchLastSection(true); // 最后一列拉伸// 同步初始數據syncTableAndConnections();
}// 同步表格數據與連接數據
void MainWindow::syncTableAndConnections()
{// 清空表格ui->tableWidget->setRowCount(0);// 添加所有連接數據到表格for (int i = 0; i < m_connections.size(); ++i) {auto& conn = m_connections[i];ui->tableWidget->insertRow(i);// 設備名稱(不可編輯)QTableWidgetItem* deviceItem = new QTableWidgetItem(conn.deviceName);deviceItem->setFlags(deviceItem->flags() & ~Qt::ItemIsEditable);ui->tableWidget->setItem(i, 0, deviceItem);// 傳感器名稱(不可編輯)QTableWidgetItem* sensorItem = new QTableWidgetItem(conn.sensorName);sensorItem->setFlags(sensorItem->flags() & ~Qt::ItemIsEditable);ui->tableWidget->setItem(i, 1, sensorItem);// 線條顏色(不可編輯,顯示顏色名稱)QString colorName = conn.lineColor == Qt::green ? QString("綠色") : QString("紅色");QTableWidgetItem* colorItem = new QTableWidgetItem(colorName);colorItem->setFlags(colorItem->flags() & ~Qt::ItemIsEditable);ui->tableWidget->setItem(i, 2, colorItem);// 連接狀態(可編輯,僅允許“正常”/“斷開”)QTableWidgetItem* statusItem = new QTableWidgetItem(conn.status);ui->tableWidget->setItem(i, 3, statusItem);}
}////////////////////////////////////////void MainWindow::on_btHide_clicked()
{ui->stackedWidget->setCurrentIndex(0);m_overlayWidget->raise(); // 切換后重新置頂覆蓋層
}void MainWindow::on_btShow_clicked()
{//QMessageBox::about(this, QString("title"), QString("Penetration response successful"));ui->stackedWidget->setCurrentIndex(1);m_overlayWidget->raise(); // 切換后重新置頂覆蓋層
}
void MainWindow::on_btData_clicked()
{ui->stackedWidget->setCurrentIndex(2);m_overlayWidget->raise(); // 切換后重新置頂覆蓋層
}
void MainWindow::on_btTest_clicked()
{}
6.2.3 3. 覆蓋層繪制跨頁面線條?頭文件

在OverlayWidget中添加跨頁面線條存儲和繪制邏輯:

#ifndef OVERLAYWIDGET_H
#define OVERLAYWIDGET_H#include <QEvent>
#include <QResizeEvent>
#include <QPaintEvent>
#include <QWidget>
#include <QPair>
#include <QList>
#include <QTimer>
#include <QPoint>
#include <QPointF>
#include <QtMath>
#include <QLine>
#include <QLineF>?
#include <QVector>?
#include <QColor>?
#include <QMouseEvent>?
#include <QKeyEvent>
#include <QPushButton>// 標注結構體(線條狀態、組件標注)?
struct Annotation {QPointF pos;      // 標注位置?QString text;     // 標注文本?
};class OverlayWidget : public QWidget
{Q_OBJECT
public:explicit OverlayWidget(QWidget *parent = nullptr);void addConnection(QList<QWidget*> fromWidget);public slots:
//    // 接收MainWindow的選中指令(選中指定跨頁面線條)?
//    void setSelectedCrossLine(int index);
//    void onUpdateCrossPageLines(const QVector<QLineF>& lines, const QVector<QColor>& colors);void updatePageShow();
protected:// 繪制事件(連線、標注、臨時線條)void paintEvent(QPaintEvent *event) override;private:QWidget* m_parent = nullptr;QPushButton* btn1 = nullptr;QPushButton* btn2 = nullptr;// 存儲需要連接的組件對QList<QWidget*> m_lstFromWidget;
};#endif // OVERLAYWIDGET_H
6.2.4 4. 覆蓋層Cpp文件?

當堆疊組件切換頁面時,即使隱藏頁面的組件不可見,覆蓋層仍需繪制跨頁面線條,因此需要在currentChanged信號中更新線條:

#include "overlaywidget.h"#include <QPainter>
#include <QPen>
#include <QtMath>
#include <QtDebug>
#include <QMenu>
#include <QPushButton>
#include <QLabel>
#include <QDebug>
#include <QMenu>
#include <QAction>
#include <QHBoxLayout>const int outR = 10;
const int inR = 6;OverlayWidget::OverlayWidget(QWidget *parent) : QWidget(parent)
{m_parent = parent;this->setParent(parent);// 覆蓋層基礎配置setStyleSheet("background: transparent;"); // 背景透明setMouseTracking(true);                    // 開啟鼠標追蹤(未按下也觸發move)//setAttribute(Qt::WA_TransparentForMouseEvents, false); // 不忽略鼠標事件// 鼠標事件穿透,確保底層組件可交互setAttribute(Qt::WA_TransparentForMouseEvents);//創建兩個按鈕,指定當前窗口為父對象if(btn1 == nullptr){btn1 = new QPushButton(QString("btn1"), this);}if(btn2 == nullptr){btn2 = new QPushButton(QString("btn2"), this);}btn1->setFixedSize (80, 30);btn2->setFixedSize (80, 30);btn1->move(10, 20);QPoint absolutePos = btn1->mapToGlobal(QPoint(0, 0));btn2->move(absolutePos.x() + btn1->width()+40, absolutePos.y());btn1->setStyleSheet(QString("color: rgba(66, 66, 66, 1);background-color: rgba(99, 99, 99,0.6);"));btn2->setStyleSheet(QString("color: rgba(66, 66, 66, 1);background-color: rgba(99, 99, 99,0.6);"));}
void OverlayWidget::addConnection(QList<QWidget*> fromWidget)
{foreach(auto widget, fromWidget){if(widget != nullptr)m_lstFromWidget.append(widget);}update();
}void OverlayWidget::updatePageShow()
{update();
}
// 繪制核心:跨頁面線條→普通線條→臨時線條→標注
void OverlayWidget::paintEvent(QPaintEvent *event)
{QWidget::paintEvent(event);QPainter painter(this);// 啟用抗鋸齒,使線條更平滑painter.setRenderHint(QPainter::Antialiasing, true);// 設置虛線樣式QPen pen(Qt::white, 2, Qt::DashLine);int nSize = m_lstFromWidget.size();QColor color = QColor(117,117,117);pen.setColor(color);int nLabel = 0;QWidget *toWidget = btn1;for(int n = nSize - 1; n >= 0; n--){if(!m_lstFromWidget[n]->isVisible())continue;QWidget *fromWidget = m_lstFromWidget[n];// 只有當兩個組件都可見時才繪制連接線if (fromWidget->isVisible() && toWidget->isVisible() && m_parent){// 計算起點QPoint fromPoint = fromWidget->mapTo(m_parent, QPoint(fromWidget->width()/2, fromWidget->height()/2));// 計算終點(轉換到覆蓋層坐標系)QPoint toPoint = toWidget->mapTo(m_parent, QPoint(toWidget->width(), toWidget->height()/2));// 繪制連接線painter.setPen(pen);painter.drawLine(fromPoint, toPoint);painter.setPen(Qt::NoPen);// 繪制起點圓形標記// 外圓painter.setBrush(Qt::white);painter.drawEllipse(fromPoint, 8, 8);// 內圓painter.setBrush(color);painter.drawEllipse(fromPoint, 5, 5);// 繪制終點圓形標記painter.setBrush(Qt::white);painter.drawEllipse(toPoint, 8, 8);painter.setBrush(color);painter.drawEllipse(toPoint, 5, 5);}}
}

這里有幾個點需要說明:
(1)覆蓋層要設置忽略鼠標事件,以使得點擊或移動被覆蓋層的按鈕時能響應;

// 鼠標事件穿透,確保底層組件可交互setAttribute(Qt::WA_TransparentForMouseEvents);

(2)當被覆蓋的按鈕移動時,要通知到覆蓋層,使得連線能實時跟著刷新重繪;

// 核心函數:更新覆蓋層跨頁面線條
void MainWindow::updateOverlayCrossPageLines()
{// 通知覆蓋層更新m_overlayWidget->updatePageShow();
}

6.3 功能驗證與效果?

運行項目后,可實現以下核心效果:?
頁面切換:點擊 “設備頁面”“傳感器頁面”,堆疊組件切換頁面,覆蓋層的跨頁面線條始終顯示,不隨頁面隱藏而消失;?
組件拖動:拖動 主界面的“設備 1” ,“傳感器2”等按鈕,線條實時跟隨按鈕移動;?
在這里插入圖片描述

在這里插入圖片描述

七、覆蓋層常見問題與解決方案(工程化排查)?

在實際項目中,覆蓋層可能出現 “不顯示”“交互沖突”“頁面切換異常” 等問題,以下是 6 個高頻問題的原因分析與解決方案,幫你快速定位并解決問題。?

7.1 問題 1:覆蓋層不顯示,只看到下層組件?

  • 可能原因:?
    覆蓋層的父組件設置錯誤(如父組件是stackedWidget的子頁面,而非centralWidget);?
    覆蓋層未調用raise(),層級低于其他組件;?
    覆蓋層的styleSheet未設置background: transparent,但windowOpacity設為 0,導致完全透明;?
    覆蓋層的geometry設置錯誤(如x=1000,超出主窗口范圍)。?
  • 解決方案:?
    檢查父組件:確保覆蓋層的父組件是centralWidget或MainWindow,而非堆疊組件的子頁面:
// 正確:父組件為centralWidget
m_overlayWidget = new OverlayWidget(ui->centralWidget);
// 錯誤:父組件為堆疊組件的子頁面
// m_overlayWidget = new OverlayWidget(ui->page0);

強制提升層級:創建覆蓋層后立即調用raise(),并在頁面切換后重新調用:

m_overlayWidget->raise();
connect(ui->stackedWidget, &QStackedWidget::currentChanged, [this]() {m_overlayWidget->raise(); // 頁面切換后重新置頂
});

驗證透明度設置:確保背景透明且整體不透明:

m_overlayWidget->setStyleSheet("background: transparent;");
m_overlayWidget->setWindowOpacity(0.9); // 0.9表示90%不透明

檢查幾何區域:打印覆蓋層的geometry,確保在主窗口范圍內:

qDebug() << "覆蓋層幾何區域:" << m_overlayWidget->geometry();
qDebug() << "中心區域幾何:" << ui->centralWidget->geometry();
// 確保覆蓋層的geometry與中心區域一致
m_overlayWidget->setGeometry(ui->centralWidget->geometry());

7.2 問題 2:覆蓋層遮擋下層組件,無法點擊?

  • 可能原因:?
    覆蓋層的Qt::WA_TransparentForMouseEvents屬性設為false,且未處理穿透邏輯;?
    覆蓋層的mousePressEvent中未調用event->ignore(),導致事件被攔截;?
    覆蓋層的windowFlags設置了Qt::Window,成為頂層窗口,遮擋所有組件。?
  • 解決方案:?
    開啟條件穿透:在mousePressEvent中判斷是否點擊繪制內容,非繪制區域則穿透:
void OverlayWidget::mousePressEvent(QMouseEvent *event)
{if (isClickOnDrawContent(event->pos())) {// 處理覆蓋層邏輯} else {event->ignore(); // 穿透事件到下層組件}
}

正確設置窗口標志:去除Qt::Window標志,確保覆蓋層是子組件:

// 正確:子組件標志
m_overlayWidget->setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
// 錯誤:頂層窗口標志
// m_overlayWidget->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);

臨時測試:將Qt::WA_TransparentForMouseEvents設為true,驗證是否能點擊下層組件:

m_overlayWidget->setAttribute(Qt::WA_TransparentForMouseEvents, true);
// 若能點擊,說明穿透邏輯未正確實現,需重新處理mousePressEvent

7.3 問題 3:頁面切換后,覆蓋層線條消失?

  • 可能原因:?
    覆蓋層的線條數據存儲在堆疊組件的子頁面中,頁面隱藏時數據被銷毀;?
    跨頁面線條的坐標計算依賴當前顯示頁面的組件,隱藏頁面的坐標獲取錯誤;?
    頁面切換時未觸發覆蓋層重繪,線條未重新繪制。?
  • 解決方案:?
    全局存儲線條數據:將線條數據存儲在MainWindow或全局單例中,而非子頁面:
// 正確:在MainWindow中存儲線條數據
QVector<QLineF> MainWindow::m_globalLines;
// 錯誤:在Page0Widget中存儲線條數據(頁面隱藏時可能被銷毀)

獲取隱藏組件坐標:即使頁面隱藏,組件的pos()仍有效,直接計算坐標:

// 無需判斷頁面是否顯示,直接獲取組件坐標
QPoint btnPos = ui->btnDevice1->pos();
QPointF btnCenter = btnPos + QPointF(ui->btnDevice1->width()/2, ui->btnDevice1->height()/2);

頁面切換時強制重繪:在stackedWidget的currentChanged信號中調用覆蓋層的update():

connect(ui->stackedWidget, &QStackedWidget::currentChanged, [this]() {m_overlayWidget->update(); // 強制重繪覆蓋層
});

7.4 問題 4:覆蓋層繪制出現閃爍?

  • 可能原因:?
    未開啟雙緩沖繪圖,復雜繪制時出現擦除 - 繪制的空白期;?
    頻繁調用update(),導致繪制任務堆積;?
    覆蓋層的paintEvent中執行了耗時操作(如讀取文件、網絡請求)。?
  • 解決方案:?
    開啟雙緩沖:手動實現雙緩沖繪圖(見 7.3 節);?
    批量更新:用定時器合并更新請求,減少update()調用次數(見 7.2 節);?
    移除耗時操作:確保paintEvent中只執行繪制邏輯,耗時操作移到其他線程:
void OverlayWidget::paintEvent(QPaintEvent *event)
{// 錯誤:在paintEvent中執行耗時操作// readDataFromFile(); // 正確:只執行繪制邏輯QPainter painter(this);// 繪制...
}

7.5 問題 5:組件拖動時,線條更新卡頓?

  • 可能原因:?
    組件拖動時每秒觸發數十次mouseMoveEvent,每次都調用update(),導致繪制頻繁;?
    線條更新時執行了全量重繪,而非局部重繪;?
    線條存儲在QList中,遍歷速度慢。?
  • 解決方案:?
    局部重繪:只重繪線條變化的區域(看前文 7.1 說明);?
    降低更新頻率:在mouseMoveEvent中添加 “距離閾值”,只有當鼠標移動超過 5px 時才更新線條:
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{static QPoint lastMousePos;// 鼠標移動超過5px才更新線條if (qAbs((event->pos() - lastMousePos).manhattanLength()) > 5) {updateOverlayLines();lastMousePos = event->pos();}
}

7.6 問題 6:覆蓋層在高 DPI 屏幕上繪制模糊?

  • 可能原因:?
    未開啟高 DPI 支持,Qt 自動縮放導致繪制模糊;?
    繪制時使用整數坐標,高 DPI 下像素對齊錯誤;?
    未設置QPainter的devicePixelRatio,導致圖像縮放比例錯誤。?
  • 解決方案:?
    開啟高 DPI 支持:在main.cpp中添加高 DPI 配置:
#include <QApplication>
#include <QHighDpiScaleFactorRoundingPolicy>int main(int argc, char *argv[])
{QApplication a(argc, argv);// 開啟高DPI支持QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);// 設置縮放策略(按屏幕比例)a.setHighDpiScaleFactorRoundingPolicy(QHighDpiScaleFactorRoundingPolicy::PassThrough);MainWindow w;w.show();return a.exec();
}

使用浮點數坐標:繪制時用QPointF和QLineF,避免整數坐標的像素對齊問題:

// 正確:浮點數坐標
QLineF line(QPointF(100.5, 200.5), QPointF(300.5, 400.5));
// 錯誤:整數坐標(高DPI下模糊)
// QLine line(QPoint(100, 200), QPoint(300, 400));

設置 QPainter 的設備像素比:在paintEvent中獲取屏幕的設備像素比,設置給QPainter:

void OverlayWidget::paintEvent(QPaintEvent *event)
{QWidget::paintEvent(event);QPainter painter(this);// 獲取設備像素比(高DPI屏幕可能為2.0或3.0)qreal dpr = devicePixelRatioF();painter.setWindow(0, 0, width() * dpr, height() * dpr);painter.setViewport(0, 0, width(), height());// 后續繪制邏輯...
}

八、總結:覆蓋層技術的核心價值與擴展?

通過前面的練習說明,我們從 “基礎原理” 到 “工程落地”,完整覆蓋了 Qt 覆蓋層的使用場景、實現方法、交互邏輯與性能優化。最后,我們梳理覆蓋層的運用。?

8.1 覆蓋層的核心價值?

  • 解耦復雜繪制與基礎界面:將線條、標注等復雜繪制邏輯集中在覆蓋層,基礎界面(按鈕、表格)只負責核心功能,代碼耦合度降低 50% 以上;?
  • 突破組件層級限制:覆蓋層可顯示在所有組件上方,解決 “線條被遮擋”“跨頁面連線” 等堆疊組件無法實現的需求;?
  • 靈活的交互擴展:支持鼠標、鍵盤、穿透交互,可快速實現 “線條編輯”“動態標注”“跨組件聯動” 等功能;?
  • 低學習成本:基于 QWidget 和 QPainter,無需動用 QGraphicsView 等復雜框架。?

8.2 覆蓋層的擴展方向?

  • 結合 Qt Quick:在 Qt Quick(QML)中,可通過Item作為覆蓋層,配合Canvas實現繪制,適合移動端或高交互需求的界面;?
  • 3D 場景覆蓋層:在 Qt 3D 中,通過QWidgetOverlay或QQuickWidget在 3D 場景上方添加 2D 覆蓋層,實現 “3D 模型標注”“交互控件”;?
  • 多覆蓋層分層管理:復雜項目中可創建多個覆蓋層(如 “繪制層”“標注層”“交互層”),每層負責單一功能,便于維護;?
  • 硬件加速繪制:對于超大規模繪制(如數千條線條),可使用QOpenGLWidget作為覆蓋層,利用 GPU 加速繪制,提升性能 3-5 倍。?

8.3 最終建議?

小項目 / 簡單繪制:直接使用 QWidget 作為覆蓋層,配合 QPainter 實現,開發效率最高;?
中大規模繪制:使用 QOpenGLWidget 作為覆蓋層,開啟硬件加速,避免卡頓;?
跨平臺 / 移動端:優先使用 Qt Quick 的Canvas覆蓋層,適配不同屏幕分辨率;?
長期維護項目:做好分層設計(基礎層、覆蓋層、數據層),并添加完整的注釋和測試用例,便于后續迭代。?

總結:

覆蓋層不是 Qt 的 “黑科技”,而是基于基礎組件的 “巧思應用”。只要掌握其核心原理和優化方法,就能在工業控制、數據可視化、流程圖設計等場景中,快速實現高質量的復雜界面。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/96338.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/96338.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/96338.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

神經網絡構成框架-理論學習

一、神經網絡的基本組成與分類 1.1 神經網絡的核心組成部分 神經網絡是現代人工智能的基石&#xff0c;其設計靈感來源于生物神經系統的信息處理方式。作為工程師&#xff0c;了解神經網絡的基本組成部分對于構建和優化模型至關重要。一個典型的神經網絡主要由以下幾個關鍵部分…

從0開始開發app(AI助手版)-架構及環境搭建

架構選擇 前端React Native 后端Firebase 原因 環境準備 安裝node 安裝JDK 命令行工具&#xff1a;Node.js command prompt命令行查詢Javav版本&#xff1a;javac -version使用nrm工具切換淘寶源&#xff1a;npx nrm use taobao安裝yarn&#xff0c;替代npm下載工具&#x…

【性能測試】Jmeter工具快速上手-搭建壓力測試腳本

&#x1f525;個人主頁&#xff1a; 中草藥 &#x1f525;專欄&#xff1a;【Java】登神長階 史詩般的Java成神之路 概念 性能測試是軟件測試的重要分支&#xff0c;核心目標是通過模擬真實業務場景和負載壓力&#xff0c;評估系統在不同條件下的性能表現&#xff0c;發現系統性…

oracle里的int類型

oracle里的int類型 在 ANSI SQL 標準 中&#xff0c;INTEGER 和 SMALLINT 是定義好的精確數值類型&#xff0c;但它們的 “長度”或“大小”并不是通過 (N) 括號來指定的&#xff08;如 INT(4)&#xff09;&#xff0c;這一點與 MySQL 等數據庫的非標準擴展完全不同。 SMALLI…

前端學習之后端java小白(二)-sql約束/建表

一、約束SQL約束&#xff08;Constraints&#xff09;是用于限制表中數據的規則&#xff0c;確保數據的完整性和準確性。以下是主要的SQL約束類型&#xff1a; 主要約束類型&#xff1a; 1. NOT NULL 約束: 確保列不能包含空值 CREATE TABLE users (id INT NOT NULL,name VARC…

OpenCV:圖像金字塔

文章目錄一、什么是圖像金字塔&#xff1f;二、圖像金字塔的核心操作&#xff1a;采樣與逆采樣1. 向下采樣&#xff08;pyrDown&#xff09;&#xff1a;從高分辨率到低分辨率步驟1&#xff1a;高斯濾波步驟2&#xff1a;刪除偶數行與偶數列OpenCV實戰代碼效果特點2. 向上采樣&…

LVS與Keepalived詳解(一)負載均衡集群介紹

文章目錄前言一、什么是LVS&#xff1f;二、四層&#xff08;L4&#xff09;負載均衡的最佳解決方案是什么&#xff1f;2.1解決方案分類與對比&#xff08;負載均衡的三種方式介紹&#xff09;2.1.1 硬件負載均衡 (Hardware Load Balancer)2.1.2 軟件負載均衡 (Software Load B…

消息隊列-kafka完結

基本概念和操作 基本概念 簡單概念:::color4 Broker&#xff1a;如果將kafka比喻成數據倉庫網絡&#xff0c;那么Broker就是網絡中的倉庫節點&#xff0c;比如快遞站&#xff0c;在該節點內可以獨立運行&#xff0c;并且多個Broker可以連接起來&#xff0c;構建kafka集群Topic&…

Chromium 138 編譯指南 Windows篇:環境變量配置與構建優化(三)

引言配置&#xff0c;往往決定成敗。在軟件開發的世界里&#xff0c;環境變量就像是一位無聲的指揮家&#xff0c;默默地協調著各個組件的協同工作。對于Chromium 138這樣一個擁有數千萬行代碼的超大型項目而言&#xff0c;正確的環境變量配置更是編譯成功的關鍵所在。也許您曾…

LabVIEW加載 STL 模型至 3D 場景 源碼見附件

LabVIEW 中 STL 模型的導入與 3D 場景顯示&#xff0c;基于示例代碼邏輯&#xff0c;結合格式兼容性、功能實現步驟及多樣化顯示方式&#xff0c;適用于三維可視化溫控、機械零件模擬等場景。 1示例代碼 NI 社區案例 “Add an STL file to 3D scene using LabVIEW” 提供了經…

硅基計劃3.0 Map類Set類

文章目錄一、二叉搜索樹&#xff08;排序樹&#xff09;1. 概念初識2. 模擬實現1. 創建搜索樹節點2. 查找指定元素是否存在3. 插入4. 刪除二、Map類1. put——設置單詞以及其頻次2. get——獲取單詞頻次3. getOrDefault——獲取單詞頻次或返回默認值4. remove——刪除單詞頻次信…

LeetCode 刷題【73. 矩陣置零】

73. 矩陣置零 自己做 解&#xff1a;標記消除 class Solution { public:void setZeroes(vector<vector<int>>& matrix) {vector<bool> x(matrix.size(), false); //要置0的行vector<bool> y(matrix[0].size(), false); //…

Unity學習----【進階】TextMeshPro學習(一)--基礎知識點

來源于唐老獅的視頻教學&#xff0c;僅作記錄和感悟記錄&#xff0c;方便日后復習或者查找 一.導入TextMeshPro 對于新創建的工程&#xff0c;可以直接在這里導入TMP必要的資源&#xff08;上面&#xff09;&#xff0c;以及TMP的實例和擴展&#xff08;下面&#xff09; 導入之…

BigDecimal(用于處理超出double范圍的浮點數)

BigDecimal 是 Java 中 java.math 包提供的高精度十進制浮點數類&#xff0c;專為解決基本類型&#xff08;float/double&#xff09;的精度缺陷而設計&#xff0c;廣泛用于金融、科學計算等對精度要求極高的場景。以下從核心特性、使用方法、常見問題對比、注意事項等方面詳細…

Nginx 優化

文章目錄1、隱藏版本號2、修改用戶與組3、緩存時間4、日志切割5、連接超時6、更改進程數7、配置網頁8、防盜鏈1、隱藏版本號 隱藏nginx的版本號&#xff0c;為了防止惡意用戶利用已知漏洞進行攻擊 ## 查看版本號 curl -I http://192.168.10.23方法一&#xff1a;修改配置文件…

基于多模態與主動學習的車船飛機圖像識別系統研究與應用技術方案

技術方案 一、技術背景與研究現狀 圖像識別是計算機視覺的核心任務之一&#xff0c;隨著深度學習的發展&#xff0c;基于 卷積神經網絡&#xff08;CNN&#xff09; 與 視覺Transformer&#xff08;ViT&#xff09; 的圖像分類方法已成為主流。 根據《圖像分類技術選型——截止…

Word2Vec詞嵌入技術和動態詞嵌入技術

Word2Vec&#xff08;Word to Vector&#xff09;是 2013 年由 Google 團隊提出的無監督詞嵌入模型&#xff0c;是一種靜態詞嵌入技術&#xff0c;核心目標是將自然語言中的離散詞匯映射為低維、稠密的實數向量&#xff08;即 “詞向量”&#xff09;&#xff0c;讓向量空間的距…

Netty從0到1系列之Netty邏輯架構【上】

文章目錄一、Netty邏輯架構【上】1.1 網絡通信層1.1.1 BootStrap & ServerBootStrap1. ?核心方法鏈與配置2. ? 架構與流程3. ? 底層實現與原理分析4. ? 實踐經驗與總結1.1.2 Channel1.2 事件調度層1.2.1 事件調度層概述1.2.2 EventLoop【事件循環】1.2.3 EventLoopGrou…

Spring Cloud 高頻面試題詳解(含代碼示例與深度解析)

文章目錄Spring Cloud 高頻面試題詳解&#xff08;含代碼示例與深度解析&#xff09;1. 什么是 Spring Cloud&#xff1f;它與 Spring Boot 有什么關系&#xff1f;2. 服務發現&#xff1a;Eureka 和 Nacos 的區別與選型&#xff1f;Eureka 示例與原理Eureka vs Nacos 對比表3.…

Ascend310B重構驅動run包

在Atlas 200I AI加速模塊(Ascend310B)移植過程中如需要將自己編譯的Image、dt.img及內核模塊打包到啟動鏡像包中需要對"Ascend-hdk-310b-npu-driver-soc_<version>_linux-aarch64.run"(下面統稱驅動run包)進行重構。下面將介紹如何重構run包。 重構驅動run包需…