Qt源码分析:窗体绘制与响应

作为一套开源跨平台的UI代码库,窗体绘制与响应自然是最为基本的功能。在前面的博文中,已就Qt中的元对象系统(反射机制)、事件循环等基础内容进行了分析,并捎带阐述了窗体响应相关的内容。因此,本文着重分析Qt中窗体绘制相关的内容。

在本文最后,通过FreeCAD SheetTableView单元格缩放功能的实现,来对研究分析予以检验与测试。

注1:限于研究水平,分析难免不当,欢迎批评指正。

注2:文章内容会不定期更新。

一、坐标系统

在Qt中,每个窗口(准确说是派生于QPaintDevice的C++类)均有一个以像素为单位的二维窗体坐标系,默认情况下,坐标原点位于窗体左上角,x轴水平向右,y轴竖直向下。

Ref. from QPaintDevice 

A paint device is an abstraction of a two-dimensional space that can be drawn on using a QPainter. Its default coordinate system has its origin located at the top-left position. X increases to the right and Y increases downwards. The unit is one pixel.

The drawing capabilities of QPaintDevice are currently implemented by the QWidget, QImage, QPixmap, QGLPixelBuffer, QPicture, and QPrinter subclasses.

 整体上,世界坐标(也称作逻辑坐标)需要首先转换成窗体坐标,然后再转换为设备坐标(也称作物理坐标)。

Ref. from Qt Coordinate System

将上述坐标变换写成矩阵变换的形式,如下

\left ( x_{device}^{'},y_{device}^{'}, w\right )=\left ( x_{world},y_{world}, 1\right )\cdot \mathbf{W}\cdot \mathbf{V}

x_{device}=\frac{x_{device}^{'}}{w}

y_{device}=\frac{y_{device}^{'}}{w}

其中,

默认值
\mathbf{W}=\begin{bmatrix} w_{11} & w_{12} & w_{13}\\ w_{21} & w_{22} & w_{23}\\ w_{31}& w_{32} & w_{33} \end{bmatrix}\begin{bmatrix} 1 & 0 & 0\\ 0& 1& 0\\ 0& 0 & 1 \end{bmatrix}
\mathbf{V}=\begin{bmatrix} v_{11} & v_{12} & v_{13}\\ v_{21} & v_{22} & v_{23}\\ v_{31}& v_{32} & v_{33} \end{bmatrix}\begin{bmatrix} \frac{vw}{ww} & 0 &0 \\ 0& \frac{vh}{wh} & 0\\ vx-wx\frac{vw}{ww} & vy-wy\frac{vh}{wh} &1 \end{bmatrix}

wx:窗体左边界坐标

wy:窗体上边界坐标

ww: 窗体宽度

wh: 窗体高度

vx:设备左边界坐标

vy:设备上边界坐标

vw: 设备宽度

vh: 设备高度

从中看可以看出,默认情况下,世界坐标与窗体坐标是重合的;但设备坐标则由窗体尺寸、设备尺寸等决定。上述分析,也可以通过QPainter的代码分析印证。

// src/gui/painting/qpainter.cppvoid QPainter::setViewport(const QRect &r)
{
#ifdef QT_DEBUG_DRAWif (qt_show_painter_debug_output)printf("QPainter::setViewport(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
#endifQ_D(QPainter);if (!d->engine) {qWarning("QPainter::setViewport: Painter not active");return;}d->state->vx = r.x();d->state->vy = r.y();d->state->vw = r.width();d->state->vh = r.height();d->state->VxF = true;d->updateMatrix();
}QTransform QPainterPrivate::viewTransform() const
{if (state->VxF) {qreal scaleW = qreal(state->vw)/qreal(state->ww);qreal scaleH = qreal(state->vh)/qreal(state->wh);return QTransform(scaleW, 0, 0, scaleH,state->vx - state->wx*scaleW, state->vy - state->wy*scaleH);}return QTransform();
}

二、窗体绘制

2.1 整体流程

QWidget::update()
QWidget::repaint()

从上述流程分析,可以得到以下结论,

  • QWidget update()、repaint()绘制流程几乎相似,最终均会路由到QWidget::paintEvent函数。不同点在于update通过QCoreApplication::postEvent触发了QEvent::UpdateLater事件;而repaint()通过QCoreApplication::sendEvent触发了QEvent::UpdateRequest事件。

Ref. from QWidget::update() 

Updates the widget unless updates are disabled or the widget is hidden.

This function does not cause an immediate repaint; instead it schedules a paint event for processing when Qt returns to the main event loop. This permits Qt to optimize for more speed and less flicker than a call to repaint() does.

Calling update() several times normally results in just one paintEvent() call.

Ref. from QWidget::repaint() 

Repaints the widget directly by calling paintEvent() immediately, unless updates are disabled or the widget is hidden.

We suggest only using repaint() if you need an immediate repaint, for example during animation. In almost all circumstances update() is better, as it permits Qt to optimize for speed and minimize flicker.

  • 对于QPaintEvent事件,相关的绘制区域实际上是在窗口坐标系下描述的,这可通过  QWidgetRepaintManager::paintAndFlush()看出。
void QWidgetRepaintManager::paintAndFlush()
{qCInfo(lcWidgetPainting) << "Painting and flushing dirty"<< "top level" << dirty << "and dirty widgets" << dirtyWidgets;const bool updatesDisabled = !tlw->updatesEnabled();bool repaintAllWidgets = false;const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize;const QRect tlwRect = tlw->data->crect;const QRect surfaceGeometry(tlwRect.topLeft(), store->size());if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {if (hasStaticContents() && !store->size().isEmpty() ) {// Repaint existing dirty area and newly visible area.const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());const QRegion staticRegion(staticContents(0, clipRect));QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());newVisible -= staticRegion;dirty += newVisible;store->setStaticContents(staticRegion);} else {// Repaint everything.dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());for (int i = 0; i < dirtyWidgets.size(); ++i)resetWidget(dirtyWidgets.at(i));dirtyWidgets.clear();repaintAllWidgets = true;}}// ... ...
}

三、分析演练:FreeCAD SheetTableView鼠标滚动缩放

学以致用”,作为前面分析研究的验证与测试,本文抛出下面一个小功能的实现,以期将上述原理串联起来。

在FreeCAD中,通过引用Sheet内的单元格数据,可以方便的实现几何参数化建模,同时FreeCAD SpreadsheetGui模块也提供了SheetTableView来显示/编辑电子表格。

当参数数据较多时,希望滚动缩放电子表格从而可以在屏幕内完整的显示整个电子表格,也就是说,鼠标滚动缩放时要求单元格尺寸单元格内容同等比例缩放。但SheetTableView目前并不支持此功能。

SheetTableView继承自QTableView,由horizontal header、vertical header、view port、horizontal scrollbar、vertical scrollbar、corner widget等组成。而且horizontal header、vertical header、QTableView共享了相同的model。

QTableView portrait

QTableView实际上使用水平/竖直QHeaderView来定位(表格)单元格,也就是说,QTableView利用水平/竖直QHeaderView将窗体坐标转换成单元格索引,这一点可由QTableView::paintEvent(QPaintEvent *event)看出。

void QTableView::paintEvent(QPaintEvent *event)
{// ... ...for (QRect dirtyArea : region) {dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));if (rightToLeft) {dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));} else {dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));}// dirtyArea may be invalid when the horizontal header is not stretchedif (!dirtyArea.isValid())continue;// get the horizontal start and end visual sectionsint left = horizontalHeader->visualIndexAt(dirtyArea.left());int right = horizontalHeader->visualIndexAt(dirtyArea.right());if (rightToLeft)qSwap(left, right);if (left == -1) left = 0;if (right == -1) right = horizontalHeader->count() - 1;// get the vertical start and end visual sections and if alternate colorint bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());if (bottom == -1) bottom = verticalHeader->count() - 1;int top = 0;bool alternateBase = false;if (alternate && verticalHeader->sectionsHidden()) {const int verticalOffset = verticalHeader->offset();int row = verticalHeader->logicalIndex(top);for (int y = 0;((y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom);++top) {row = verticalHeader->logicalIndex(top);if (alternate && !verticalHeader->isSectionHidden(row))alternateBase = !alternateBase;}} else {top = verticalHeader->visualIndexAt(dirtyArea.top());alternateBase = (top & 1) && alternate;}if (top == -1 || top > bottom)continue;// Paint each row itemfor (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {int row = verticalHeader->logicalIndex(visualRowIndex);if (verticalHeader->isSectionHidden(row))continue;int rowY = rowViewportPosition(row);rowY += offset.y();int rowh = rowHeight(row) - gridSize;// Paint each column itemfor (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)+ visualColumnIndex - firstVisualColumn;if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))continue;drawn.setBit(currentBit);int col = horizontalHeader->logicalIndex(visualColumnIndex);if (horizontalHeader->isSectionHidden(col))continue;int colp = columnViewportPosition(col);colp += offset.x();int colw = columnWidth(col) - gridSize;const QModelIndex index = d->model->index(row, col, d->root);if (index.isValid()) {option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);if (alternate) {if (alternateBase)option.features |= QStyleOptionViewItem::Alternate;elseoption.features &= ~QStyleOptionViewItem::Alternate;}d->drawCell(&painter, option, index);}}alternateBase = !alternateBase && alternate;}if (showGrid) {// Find the bottom right (the last rows/columns might be hidden)while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom;QPen old = painter.pen();painter.setPen(gridPen);// Paint each rowfor (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {int row = verticalHeader->logicalIndex(visualIndex);if (verticalHeader->isSectionHidden(row))continue;int rowY = rowViewportPosition(row);rowY += offset.y();int rowh = rowHeight(row) - gridSize;painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);}// Paint each columnfor (int h = left; h <= right; ++h) {int col = horizontalHeader->logicalIndex(h);if (horizontalHeader->isSectionHidden(col))continue;int colp = columnViewportPosition(col);colp += offset.x();if (!rightToLeft)colp +=  columnWidth(col) - gridSize;painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());}painter.setPen(old);}}// ... ...
}

因此,要实现QTableView支持鼠标滚动缩放,就需要使QTableView在x方向缩放与水平QHeaderView保持一致,而在y方向伸缩与竖直QHeaderView保持一致。

以水平QHeaderView为例,设坐标变换表示为\boldsymbol{h}_{d}=\boldsymbol{h}\cdot \boldsymbol{ W}_{1}\cdot \boldsymbol{V}_{1};对于QTableView,设坐标变换为\boldsymbol{x}_{d}=\boldsymbol{x}\cdot \boldsymbol{ W}_{2}\cdot \boldsymbol{V}_{2}。则有,\boldsymbol{x}_{d}=\boldsymbol{x}\cdot \left (\boldsymbol{ W}_{2}\cdot \boldsymbol{ W}_{1}^{-1} \right )\cdot \boldsymbol{ W}_{1}\cdot \boldsymbol{V}_{2}

另一方面,从代码实现可以看出,QTableView::paintEvent(QPaintEvent *event)绘制函数采用了默认的变换矩阵\boldsymbol{W}=\boldsymbol{I}

/*!Paints the table on receipt of the given paint event \a event.
*/
void QTableView::paintEvent(QPaintEvent *event)
{// ... ...QPainter painter(d->viewport);// if there's nothing to do, clear the area and returnif (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)return;const int x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);const int y = verticalHeader->length() - verticalHeader->offset() - 1;// ... ...
}

因此,一种实现方法,就是通过重写QTableView::paintEvent(QPaintEvent *event),指定合适的变换矩阵\boldsymbol{W}来实现QTableView滚动缩放。

具体来说,首先重写wheelEvent(QWheelEvent* event)以将鼠标滚动输入转化成缩放比例,

// following the zoom strategy in SALOME GraphicsView_Viewer 
// see SALOME gui/src/GraphicsView/GraphicsView_Viewer.cpp
void SheetTableView::wheelEvent(QWheelEvent* event)
{if (QApplication::keyboardModifiers() & Qt::ControlModifier) {const double d = 1.05;double q = pow(d, -event->delta() / 120.0);this->scale(q, q);event->accept();return;}return QTableView::wheelEvent(event);
}
void SheetTableView::scale(qreal sx, qreal sy)
{//Q_D(QGraphicsView);QTransform matrix = myMatrix;matrix.scale(sx, sy);setTransform(matrix);for (int i = 0; i < horizontalHeader()->count(); ++i) {int s = horizontalHeader()->sectionSize(i);horizontalHeader()->resizeSection(i, s * sx);}for (int i = 0; i < verticalHeader()->count(); ++i) {int s = verticalHeader()->sectionSize(i);verticalHeader()->resizeSection(i, s * sy);}this->update();
}

 然后重写paintEvent(QPaintEvent* event),依据缩放比例将单元格内容进行缩放。需要注意的是,由于水平/竖直 QHeaderView已经进行了缩放,而QTableView是依据水平/竖直QHeaderView计算单元格坐标,因此,在绘制窗体时,需要使用传入的窗体坐标;但为了缩放单元格内容,为QPainter指定了变换矩阵\boldsymbol{W},所以单元格坐标要施加矩阵变化\boldsymbol{W}^{-1}

void SheetTableView::paintEvent(QPaintEvent* event)
{// ... ...auto matrix = viewportTransform();auto inv_matrix = matrix.inverted();QPainter painter(viewport());painter.setWorldTransform(matrix);// ...for (QRect dirtyArea : region) {// ... ...// Paint each row itemfor (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {int row = verticalHeader->logicalIndex(visualRowIndex);if (verticalHeader->isSectionHidden(row))continue;int rowY = rowViewportPosition(row);rowY += offset.y();int rowh = rowHeight(row) - gridSize;// Paint each column itemfor (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {int currentBit =(visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)+ visualColumnIndex - firstVisualColumn;if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))continue;drawn.setBit(currentBit);int col = horizontalHeader->logicalIndex(visualColumnIndex);if (horizontalHeader->isSectionHidden(col))continue;int colp = columnViewportPosition(col);colp += offset.x();int colw = columnWidth(col) - gridSize;const QModelIndex index = model()->index(row, col, rootIndex());if (index.isValid()) {//option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);option.rect = inv_matrix.mapRect(QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh));if (alternate) {if (alternateBase)option.features |= QStyleOptionViewItem::Alternate;elseoption.features &= ~QStyleOptionViewItem::Alternate;}this->drawCell(&painter, option, index);}}alternateBase = !alternateBase && alternate;}if (showGrid) {// Find the bottom right (the last rows/columns might be hidden)while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom)))--bottom;QPen old = painter.pen();painter.setPen(gridPen);// Paint each rowfor (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {int row = verticalHeader->logicalIndex(visualIndex);if (verticalHeader->isSectionHidden(row))continue;int rowY = rowViewportPosition(row);rowY += offset.y();int rowh = rowHeight(row) - gridSize;//painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);QPoint p1(dirtyArea.left(), rowY + rowh), p2(dirtyArea.right(), rowY + rowh);painter.drawLine(inv_matrix.map(p1), inv_matrix.map(p2));}// Paint each columnfor (int h = left; h <= right; ++h) {int col = horizontalHeader->logicalIndex(h);if (horizontalHeader->isSectionHidden(col))continue;int colp = columnViewportPosition(col);colp += offset.x();if (!rightToLeft)colp += columnWidth(col) - gridSize;//painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());QPoint p1(colp, dirtyArea.top()), p2(colp, dirtyArea.bottom());painter.drawLine(inv_matrix.map(p1), inv_matrix.map(p2));}painter.setPen(old);}}//#if QT_CONFIG(draganddrop)
//    // Paint the dropIndicator
//    d->paintDropIndicator(&painter);
//#endif
}

依据上述方案,的确可以实现QTableView单元格尺寸、单元格内容的滚动缩放,但是存在以下问题:

  • 性能问题

QHeaderView由一串连续的section组成,每个section对应一个列/行字段,在section移动过程中,visualIndex会发生变化,但logicalIndex不变。

Ref. from  QHeaderView 

Each header has an orientation() and a number of sections, given by the count() function. A section refers to a part of the header - either a row or a column, depending on the orientation.

Sections can be moved and resized using moveSection() and resizeSection(); they can also be hidden and shown with hideSection() and showSection().

Each section of a header is described by a section ID, specified by its section(), and can be located at a particular visualIndex() in the header. 

You can identify a section using the logicalIndex() and logicalIndexAt() functions, or by its index position, using the visualIndex() and visualIndexAt() functions. The visual index will change if a section is moved, but the logical index will not change.

虽然QHeaderView::resizeSection(int logical, int size)可以调整单元格大小,但如果section数较多,逐个调整section尺寸比较卡。

  • 缩放QHeaderView

在QHeaderView::paintEvent(QPaintEvent *e)中,所使用QPainter的没有施加缩放变换矩阵。因此,无法对QHeaderView section内容进行缩放。

网络资料

Qt源码分析:QMetaObject实现原理icon-default.png?t=N7T8https://blog.csdn.net/qq_26221775/article/details/137023709?spm=1001.2014.3001.5502

Qt源码分析: QEventLoop实现原理icon-default.png?t=N7T8https://blog.csdn.net/qq_26221775/article/details/136776793?spm=1001.2014.3001.5502

QWidgeticon-default.png?t=N7T8https://doc.qt.io/qt-5/qwidget.html
QPaintericon-default.png?t=N7T8https://doc.qt.io/qt-5/qpainter.html
QPaintDeviceicon-default.png?t=N7T8https://doc.qt.io/qt-5/qpaintdevice.html
QPaintEngineicon-default.png?t=N7T8https://doc.qt.io/qt-5/qpaintengine.html
Coordinate Systemicon-default.png?t=N7T8https://doc.qt.io/qt-5/coordsys.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/38454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何完成域名解析验证

一&#xff1a;什么是DNS解析&#xff1a; DNS解析是互联网上将人类可读的域名&#xff08;如www.example.com&#xff09;转换为计算机可识别的IP地址&#xff08;如192.0.2.1&#xff09;的过程&#xff0c;大致遵循以下步骤&#xff1a; 查询本地缓存&#xff1a;当用户尝…

顺序串算法库构建

学习贺利坚老师顺序串算法库 数据结构之自建算法库——顺序串_创建顺序串s1,创建顺序串s2-CSDN博客 本人详细解析博客 串的概念及操作_串的基本操作-CSDN博客 版本更新日志 V1.0: 在贺利坚老师算法库指导下, 结合本人详细解析博客思路基础上,进行测试, 加入异常弹出信息 v1.0补…

已解决java.awt.geom.NoninvertibleTransformException:在Java2D中无法逆转的转换的正确解决方法,亲测有效!!!

已解决java.awt.geom.NoninvertibleTransformException&#xff1a;在Java2D中无法逆转的转换的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 1. 检查缩放因子 修改后的缩放变换 …

关键路径——C语言(理论)

关键路径&#xff0c;是项目网络中从起始事件到终止事件的最长路径&#xff0c;决定了项目的最短完成时间。 关键路径中的任务没有任何可调整的余地&#xff0c;如果任何一个任务被延迟&#xff0c;整个项目的完成时间也会被延迟。 假设我们现在有一个图&#xff1a;把图的边…

【CH32V305FBP6】USBD HS 虚拟串口分析

文章目录 前言分析端点 0USBHS_UIS_TOKEN_OUT 端点 2USBHS_UIS_TOKEN_OUTUSBHS_UIS_TOKEN_IN 前言 虚拟串口&#xff0c;端口 3 单向上报&#xff0c;端口 2 双向收发。 分析 端点 0 USBHS_UIS_TOKEN_OUT 设置串口参数&#xff1a; 判断 USBHS_SetupReqCode CDC_SET_LIN…

从零开始实现大语言模型(一):概述

1. 前言 大家好&#xff0c;我是何睿智。我现在在做大语言模型相关工作&#xff0c;我用业余时间写一个专栏&#xff0c;给大家讲讲如何从零开始实现大语言模型。 从零开始实现大语言模型是了解其原理及领域大语言模型实现路径的最好方法&#xff0c;没有之一。已有研究证明&…

Windows10录屏,教你3个方法,简单快速录屏

“我的电脑系统是Windows10的系统&#xff0c;今晚要进行线上开会&#xff0c;但我实在有事没办法参加会议&#xff0c;想把会议的内容录制下来方便我后续观看。但却找不到电脑录屏功能在哪里打开&#xff1f;求助一下&#xff0c;谁能帮帮我&#xff1f;” 在数字化时代&…

mysql 命令 —— 查看表信息(show table status)

查询表信息&#xff0c;如整个表的数据量大小、表的索引占用空间大小等 1、查询某个库下面的所有表信息&#xff1a; SHOW TABLE STATUS FROM your_database_name;2、查询指定的表信息&#xff1a; SHOW TABLE STATUS LIKE your_table_name;如&#xff1a;Data_length 显示表…

闲聊 .NET Standard

前言 有时候&#xff0c;我们从 Nuget 下载第三方包时&#xff0c;会看到这些包的依赖除了要求 .NET FrameWork、.NET Core 等的版本之外&#xff0c;还会要求 .NET Standard 的版本&#xff0c;比如这样&#xff1a; 这个神秘的 .NET Standard 是什么呢&#xff1f; .NET St…

从零开始搭建spring boot多模块项目

一、搭建父级模块 1、打开idea,选择file–new–project 2、选择Spring Initializr,选择相关java版本,点击“Next” 3、填写父级模块信息 选择/填写group、artifact、type、language、packaging(后面需要修改)、java version(后面需要修改成和第2步中版本一致)。点击“…

工业触摸一体机优化MES应用开发流程

工业触摸一体机在现代工业生产中扮演着至关重要的角色&#xff0c;它集成了智能触摸屏和工业计算机的功能&#xff0c;广泛应用于各种生产场景中。而制造执行系统&#xff08;MES&#xff09;作为工业生产管理的重要工具&#xff0c;对于提高生产效率、降低成本、优化资源利用具…

力扣hot100-普通数组

文章目录 题目&#xff1a;最大子数组和方法1 动态规划方法2 题目&#xff1a;合并区间题解 题目&#xff1a;最大子数组和 原题链接&#xff1a;最大子数组和 方法1 动态规划 public class T53 {//动态规划public static int maxSubArray(int[] nums) {if (nums.length 0…

C++基础知识-编译相关

记录C语言相关的基础知识 1 C源码到可执行文件的四个阶段 预处理(.i)、编译(.s)、汇编(.obj)、链接。 1.1 预处理 预处理阶段&#xff0c;主要完成宏替换、文件展开、注释删除、条件编译展开、添加行号和文件名标识&#xff0c;输出.i/.ii预处理文件。 宏替换&#xff0c;…

【UML用户指南】-26-对高级行为建模-状态图

目录 1、概念 2、组成结构 3、一般用法 4、常用建模技术 4.1、对反应型对象建模 一个状态图显示了一个状态机。在为对象的生命期建模中 活动图展示的是跨过不同的对象从活动到活动的控制流 状态图展示的是单个对象内从状态到状态的控制流。 在UML中&#xff0c;用状态图…

tcpdump命令详解及使用实例

1、抓所有网卡数据包&#xff0c;保存到指定路径 tcpdump -i any -w /oemdata/123.pcap&一、tcpdump简介 tcpdump可以将网络中传送的数据包完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤&#xff0c;并提供and、or、not等逻辑语句来去掉无用的信…

【Python】已解决:SyntaxError: positional argument follows keyword argument

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;SyntaxError: positional argument follows keyword argument 一、分析问题背景 在Python编程中&#xff0c;当我们在调用函数时混合使用位置参数&#xff08;p…

RabbitMQ进阶篇

文章目录 发送者的可靠性生产者重试机制实现生产者确认 MQ的可靠性数据持久化交换机持久化队列持久化消息持久化 Lazy Queue(可配置~)控制台配置Lazy模式代码配置Lazy模式更新已有队列为lazy模式 消费者的可靠性消费者确认机制失败重试机制失败处理策略 业务幂等性唯一消息ID业…

西部智慧健身小程序+华为运动健康服务

1、 应用介绍 西部智慧健身小程序为用户提供一站式全流程科学健身综合服务。用户通过登录微信小程序&#xff0c;可享用健康筛查、运动风险评估、体质检测评估、运动处方推送、个人运动数据监控与评估等公益服务。 2、 体验介绍西部智慧健身小程序华为运动健康服务核心体验如…

idea xml ctrl+/ 注释格式不对齐

处理前 处理后 解决办法 取消这两个勾选

核方法总结(三)———核主成分(kernel PCA)学习笔记

一、核主成分 1.1 和PCA的区别 PCA &#xff08;主成分分析&#xff09;对应一个线性高斯模型&#xff08;参考书的第二章&#xff09;&#xff0c;其基本假设是数据由一个符合正态分布的隐变量通过一个线性映射得到&#xff0c;因此可很好描述符合高斯分布的数据。然而在很多实…