• 修改Qt源码支持DPI粒度到QWidget


    修改Qt源码支持DPI粒度到QWidget

    1、背景

    在项目中总是遇到各种奇怪的需求,这不,碰到一个。

    我们想要的我们的程序支持一种特性,就是有的界面支持高DPI。但是有的保持原来的大小。

    这可怎么办?Qt中支持高DPI很简单的一行代码 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling),但是它针对整个进程中所有的UI。

    那没办法了,只能硬着头皮修改Qt的源码重新编译了。

    对了,我的Qt版本是Qt 5.15.2.0。

    其实我刚开始思路就是很简单,就是给QWidget设置一个flag,凡是这个flag为false表示不支持DPI缩放。这就是我整个的思路。

    2、修改的文件

    这里我就把修改的代码,生成patch文件,放在这里。可以通过git将patch应用到Qt源码中。

    这个里面的.gitattributes,command.bat,copy_qch.sh这三个文件的修改直接忽略掉。

    保存的文件格式要注意:unix格式换行符,GB2312编码

    From cedae90de66f68638162198374be5ac848a5a6f9 Mon Sep 17 00:00:00 2001
    From: "Shixiong.Liu" <635672377@qq.com>
    Date: Fri, 16 Sep 2022 14:28:27 +0800
    Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0widget=E5=8A=A8=E6=80=81?=
     =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=94=AF=E6=8C=81=E9=AB=98DPI=E7=89=B9?=
     =?UTF-8?q?=E6=80=A7?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     .gitattributes                                |  1 +
     command.bat                                   | 17 ++++++++++
     copy_qch.sh                                   | 31 +++++++++++++++++++
     qtbase/src/gui/kernel/qhighdpiscaling.cpp     |  3 +-
     qtbase/src/gui/kernel/qplatformwindow.cpp     |  7 ++++-
     qtbase/src/gui/kernel/qwindow.cpp             | 28 +++++++++++++----
     qtbase/src/gui/kernel/qwindow.h               |  3 ++
     qtbase/src/gui/kernel/qwindow_p.h             |  1 +
     .../platforms/windows/qwindowscontext.cpp     |  5 +++
     .../platforms/windows/qwindowswindow.cpp      |  4 +--
     qtbase/src/widgets/kernel/qwidget.cpp         |  2 ++
     qtbase/src/widgets/kernel/qwidget.h           |  7 ++++-
     12 files changed, 97 insertions(+), 12 deletions(-)
     create mode 100644 .gitattributes
     create mode 100644 command.bat
     create mode 100644 copy_qch.sh
    
    diff --git a/.gitattributes b/.gitattributes
    new file mode 100644
    index 0000000000..dfdb8b771c
    --- /dev/null
    +++ b/.gitattributes
    @@ -0,0 +1 @@
    +*.sh text eol=lf
    diff --git a/command.bat b/command.bat
    new file mode 100644
    index 0000000000..7d7b2af180
    --- /dev/null
    +++ b/command.bat
    @@ -0,0 +1,17 @@
    +REM 找到你的vs2019初始化环境的bat目录,我用的enterprise版本,不用的版本只需要替换下面的"Enterprise"就可以了
    +CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
    +
    +SET _ROOT=F:\qt5.15.2_doc
    +SET PATH=D:\soft_dev\build_qt_devs\cmake-3.22.0\bin;%PATH%
    +SET PATH=%_ROOT%\qtbase\bin;%PATH%
    +SET PATH=D:\soft_dev\build_qt_devs\strawberry-perl-5.32.1.1\perl\bin;%PATH%
    +SET PATH=D:\soft_dev\build_qt_devs\ninja-win;%PATH%
    +SET LLVM_INSTALL_DIR=C:\Program Files (x86)\LLVM\
    +
    +set "MY_INSTALL_PATH=F:\qt5.15.2_doc\bin"
    + 
    +REM configure.bat -prefix %MY_INSTALL_PATH% -DQT_NO_EXCEPTIONS=1 -debug-and-release -force-debug-info -platform win32-msvc -opensource -confirm-license 
    +
    +REM configure.bat -prefix %MY_INSTALL_PATH% -DQT_NO_EXCEPTIONS=0 -nomake tests -nomake examples -debug-and-release -force-debug-info -platform win32-msvc -opensource -confirm-license  OPENSSL_PREFIX=D:\qt\openssl-1.1.1p\win32-release -openssl-linked -I D:\qt\openssl-1.1.1p\win32-release\include -L D:\qt\openssl-1.1.1p\win32-release\lib OPENSSL_LIBS="libssl.lib libcrypto.lib Ws2_32.lib  Gdi32.lib Advapi32.lib Crypt32.lib User32.lib"
    +
    +configure.bat -prefix %MY_INSTALL_PATH% -nomake tests -nomake examples -debug -force-debug-info -platform win32-msvc -opensource -confirm-license  OPENSSL_PREFIX=D:\qt\openssl-1.1.1p\win32-release -openssl-linked -I F:\openssl-1.1.1p\win32-release\include -L F:\openssl-1.1.1p\win32-release\lib OPENSSL_LIBS="libssl.lib libcrypto.lib Ws2_32.lib  Gdi32.lib Advapi32.lib Crypt32.lib User32.lib"
    diff --git a/copy_qch.sh b/copy_qch.sh
    new file mode 100644
    index 0000000000..b00835a808
    --- /dev/null
    +++ b/copy_qch.sh
    @@ -0,0 +1,31 @@
    +#!/bin/bash
    +
    +dst="./bin/doc/"
    +blank=""
    +
    +RED='\e[1;31m' # 绾?+RES='\e[0m'
    +
    +function echo_color {
    +	echo -e "${RED}$1${RES}"
    +}
    +
    +for f in $(find . -name '*.qch'); 
    +	do 
    +		# cp ${f} ${dst}
    +		file_name=`basename ${f}`
    +		if [ ! -f "${dst}${file_name}" ];then
    +			cp ${f} ${dst}
    +		else
    +			echo_color "${f} already exist"
    +		fi
    +		# echo ${file_name}
    +		file_name_no_postfix=${file_name//.qch/${blank}}
    +		dir_name="${dst}${file_name_no_postfix}"
    +		if [ ! -d ${dir_name} ];then
    +			mkdir ${dir_name}
    +			cp -r ${f//.qch/${blank}} ${dst}
    +		else
    +			echo_color "${dir_name} already exist"
    +		fi
    +	done;
    diff --git a/qtbase/src/gui/kernel/qhighdpiscaling.cpp b/qtbase/src/gui/kernel/qhighdpiscaling.cpp
    index 9bbf2773a9..11e5c72447 100644
    --- a/qtbase/src/gui/kernel/qhighdpiscaling.cpp
    +++ b/qtbase/src/gui/kernel/qhighdpiscaling.cpp
    @@ -716,9 +716,8 @@ QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QScreen *s
     
     QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *window, QPoint *nativePosition)
     {
    -    if (!m_active)
    +    if (!m_active || (window && !window->isAdaptDPI()))
             return { qreal(1), QPoint() };
    -
         QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
         const bool searchScreen = !window || window->isTopLevel();
         return scaleAndOrigin(screen, searchScreen ? nativePosition : nullptr);
    diff --git a/qtbase/src/gui/kernel/qplatformwindow.cpp b/qtbase/src/gui/kernel/qplatformwindow.cpp
    index fc736033c2..be91c10792 100644
    --- a/qtbase/src/gui/kernel/qplatformwindow.cpp
    +++ b/qtbase/src/gui/kernel/qplatformwindow.cpp
    @@ -730,7 +730,12 @@ QRect QPlatformWindow::initialGeometry(const QWindow *w, const QRect &initialGeo
                 }
             }
         }
    -    return QHighDpi::toNativePixels(rect, screen);
    +
    +    QRect geometry = rect;
    +    if (w->isAdaptDPI()) {
    +       geometry = QHighDpi::toNativePixels(rect, screen);
    +    }
    +    return geometry;
     }
     
     /*!
    diff --git a/qtbase/src/gui/kernel/qwindow.cpp b/qtbase/src/gui/kernel/qwindow.cpp
    index fd89e479b8..d1b5bc98c8 100644
    --- a/qtbase/src/gui/kernel/qwindow.cpp
    +++ b/qtbase/src/gui/kernel/qwindow.cpp
    @@ -791,6 +791,18 @@ void QWindow::setModality(Qt::WindowModality modality)
         emit modalityChanged(modality);
     }
     
    +bool QWindow::isAdaptDPI() const
    +{
    +    Q_D(const QWindow);
    +    return d->adapt_dpi;
    +}
    +
    +void QWindow::setAdaptDPI(bool adaptdpi)
    +{
    +    Q_D(QWindow);
    +    d->adapt_dpi = adaptdpi;
    +}
    +
     /*! \fn void QWindow::modalityChanged(Qt::WindowModality modality)
     
         This signal is emitted when the Qwindow::modality property changes to \a modality.
    @@ -1733,10 +1745,14 @@ void QWindow::setGeometry(const QRect &rect)
         if (d->platformWindow) {
             QRect nativeRect;
             QScreen *newScreen = d->screenForGeometry(rect);
    -        if (newScreen && isTopLevel())
    -            nativeRect = QHighDpi::toNativePixels(rect, newScreen);
    -        else
    -            nativeRect = QHighDpi::toNativeLocalPosition(rect, newScreen);
    +        if (this->isAdaptDPI()) {
    +            if (newScreen && isTopLevel())
    +                nativeRect = QHighDpi::toNativePixels(rect, newScreen);
    +            else
    +                nativeRect = QHighDpi::toNativeLocalPosition(rect, newScreen);
    +        } else {
    +            nativeRect = rect;
    +        }
             d->platformWindow->setGeometry(nativeRect);
         } else {
             d->geometry = rect;
    @@ -2645,7 +2661,7 @@ QPoint QWindow::mapToGlobal(const QPoint &pos) const
             return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapToGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this);
         }
     
    -    if (QHighDpiScaling::isActive())
    +    if (QHighDpiScaling::isActive() && d->adapt_dpi)
             return QHighDpiScaling::mapPositionToGlobal(pos, d->globalPosition(), this);
     
         return pos + d->globalPosition();
    @@ -2669,7 +2685,7 @@ QPoint QWindow::mapFromGlobal(const QPoint &pos) const
             return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapFromGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this);
         }
     
    -    if (QHighDpiScaling::isActive())
    +    if (QHighDpiScaling::isActive() && d->adapt_dpi)
             return QHighDpiScaling::mapPositionFromGlobal(pos, d->globalPosition(), this);
     
         return pos - d->globalPosition();
    diff --git a/qtbase/src/gui/kernel/qwindow.h b/qtbase/src/gui/kernel/qwindow.h
    index 7aae7ffffa..e9586c4413 100644
    --- a/qtbase/src/gui/kernel/qwindow.h
    +++ b/qtbase/src/gui/kernel/qwindow.h
    @@ -168,6 +168,9 @@ public:
         Qt::WindowModality modality() const;
         void setModality(Qt::WindowModality modality);
     
    +    bool isAdaptDPI() const;
    +    void setAdaptDPI(bool adaptdpi);
    +
         void setFormat(const QSurfaceFormat &format);
         QSurfaceFormat format() const override;
         QSurfaceFormat requestedFormat() const;
    diff --git a/qtbase/src/gui/kernel/qwindow_p.h b/qtbase/src/gui/kernel/qwindow_p.h
    index 5a7ec518fd..f1514c76d1 100644
    --- a/qtbase/src/gui/kernel/qwindow_p.h
    +++ b/qtbase/src/gui/kernel/qwindow_p.h
    @@ -139,6 +139,7 @@ public:
         bool visible= false;
         bool visibilityOnDestroy = false;
         bool exposed = false;
    +    bool adapt_dpi = true;
         QSurfaceFormat requestedFormat;
         QString windowTitle;
         QString windowFilePath;
    diff --git a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp
    index fa757b0edc..ec84daa554 100644
    --- a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp
    +++ b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp
    @@ -1438,6 +1438,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
     #endif
         }   break;
         case QtWindows::DpiChangedEvent: {
    +       // ignore WM_DPICHANGEC message, if window adapt DPI property is false.
    +       if (!platformWindow->window()->isAdaptDPI()) {
    +            qCDebug(lcQpaWindows) << __FUNCTION__ << "Ignore WM_DPICHANGED event";
    +            return false;
    +        }
             // Try to apply the suggested size first and then notify ScreenChanged
             // so that the resize event sent from QGuiApplication incorporates it
             // WM_DPICHANGED is sent with a size that avoids resize loops (by
    diff --git a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp
    index d2c22f4100..63b2301ce6 100644
    --- a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp
    +++ b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp
    @@ -1013,8 +1013,8 @@ void QWindowsGeometryHint::frameSizeConstraints(const QWindow *w, const QScreen
                                                     const QMargins &margins,
                                                     QSize *minimumSize, QSize *maximumSize)
     {
    -    *minimumSize = toNativeSizeConstrained(w->minimumSize(), screen);
    -    *maximumSize = toNativeSizeConstrained(w->maximumSize(), screen);
    +    *minimumSize = w->isAdaptDPI() ? toNativeSizeConstrained(w->minimumSize(), screen) : w->minimumSize();
    +    *maximumSize = w->isAdaptDPI() ? toNativeSizeConstrained(w->maximumSize(), screen) : w->maximumSize();
     
         const int maximumWidth = qMax(maximumSize->width(), minimumSize->width());
         const int maximumHeight = qMax(maximumSize->height(), minimumSize->height());
    diff --git a/qtbase/src/widgets/kernel/qwidget.cpp b/qtbase/src/widgets/kernel/qwidget.cpp
    index 479d91be0e..05b52f3e7c 100644
    --- a/qtbase/src/widgets/kernel/qwidget.cpp
    +++ b/qtbase/src/widgets/kernel/qwidget.cpp
    @@ -1021,6 +1021,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
         data.in_show = 0;
         data.in_set_window_state = 0;
         data.in_destructor = false;
    +    data.is_adapt_high_dpi = true;
     
         // Widgets with Qt::MSWindowsOwnDC (typically QGLWidget) must have a window handle.
         if (f & Qt::MSWindowsOwnDC) {
    @@ -1311,6 +1312,7 @@ void QWidgetPrivate::create()
             QWindowPrivate::WindowFrameInclusive : QWindowPrivate::WindowFrameExclusive;
     
         if (q->windowType() != Qt::Desktop || q->testAttribute(Qt::WA_NativeWindow)) {
    +        win->setAdaptDPI(data.is_adapt_high_dpi);
             win->create();
             // Enable nonclient-area events for QDockWidget and other NonClientArea-mouse event processing.
             if (QPlatformWindow *platformWindow = win->handle())
    diff --git a/qtbase/src/widgets/kernel/qwidget.h b/qtbase/src/widgets/kernel/qwidget.h
    index 415a738eb4..ebdc8e6de3 100644
    --- a/qtbase/src/widgets/kernel/qwidget.h
    +++ b/qtbase/src/widgets/kernel/qwidget.h
    @@ -117,7 +117,9 @@ public:
         uint context_menu_policy : 3;
         uint window_modality : 2;
         uint in_destructor : 1;
    -    uint unused : 13;
    +    uint is_adapt_high_dpi: 1;
    +    // uint unused : 13;
    +    uint unused : 12;
         QRect crect;
         mutable QPalette pal;
         QFont fnt;
    @@ -180,6 +182,7 @@ class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
         Q_PROPERTY(QString windowIconText READ windowIconText WRITE setWindowIconText NOTIFY windowIconTextChanged) // deprecated
         Q_PROPERTY(double windowOpacity READ windowOpacity WRITE setWindowOpacity)
         Q_PROPERTY(bool windowModified READ isWindowModified WRITE setWindowModified)
    +    Q_PROPERTY(bool adaptHighDPI READ adaptHighDPI WRITE setAdaptHighDPI)
     #ifndef QT_NO_TOOLTIP
         Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip)
         Q_PROPERTY(int toolTipDuration READ toolTipDuration WRITE setToolTipDuration)
    @@ -360,6 +363,8 @@ public:
         void grabGesture(Qt::GestureType type, Qt::GestureFlags flags = Qt::GestureFlags());
         void ungrabGesture(Qt::GestureType type);
     #endif
    +    bool adaptHighDPI() const { return data->is_adapt_high_dpi; }
    +    void setAdaptHighDPI(bool on) { data->is_adapt_high_dpi = on; }
     
     public Q_SLOTS:
         void setWindowTitle(const QString &);
    -- 
    2.33.0.windows.2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298

    3、理解过程

    1.几个文件,解释下。

    qhighdpiscaling.cpp 这个文件就是UI缩放有着密切的关系,从名字就可以看出。这个基本是个静态类,内容不多。

    qplatformwindow.cpp 这个class很复杂,应该是封装了平台相关的window。

    qwindow.cpp 这个class和QWidget关系相当密切,在Qt的内部通过操作这个class,来达到修改QWidget属性。

    qwidget.cpp我们最熟悉的QWidget控件。

    这里有个class需要特别的注意,就是QScreen这就是屏幕分辨率密切相关的。每个QWiget下面都会带着个QScreen实例,而且这个是实例会不断的切换。

    比如:你有多个屏幕,从屏幕1切到到屏幕2,此时QScreen就会被修改,同时根据此屏幕的DPI进行缩放。

    再简单的画出class的关系:
    在这里插入图片描述

    再根据堆栈信息看下整个QWidget创建过程:
    在这里插入图片描述

    2.开始修改

    我最开始是定位到qhighdpiscaling_p.h这个文件,找到如下的函数。这里是起点。

    但是我一开始怎么找到这里的。没有任何的技巧,就是全局搜索关键字dpi,有时候第一次并不总是成功,所以需要多尝试和调试,就是需要耐心。

    QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *window, QPoint *nativePosition)
    {
        if (!m_active || (window && !window->isAdaptDPI()))
            return { qreal(1), QPoint() };
        QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
        const bool searchScreen = !window || window->isTopLevel();
        return scaleAndOrigin(screen, searchScreen ? nativePosition : nullptr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    找到了起点之后,就是漫漫的编译调试之路了。接下来我一个个解释修改的地方含义。对照相应的patch片段来介绍。

    1、首先QWidget增加了一个自定义的bool属性,这样可以在Designer中可视化操作。

    ​ 在QWidgetWindow调用QWindow父类create()函数之前就提前将属性值设置进去。否则就会出现在高DPI屏幕时初始化就会进行放大。

    在这里插入图片描述

    2、拒接接受系统发送的DPI change Event消息

    在这里插入图片描述

    3、下面这几处就是调试过程发现,同时需要屏蔽的地方。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    4、在到了进行缩放时根据属性值判断是否真的需要缩放。
    在这里插入图片描述

    在回顾下思路:就是给QWidget提供一个对用户的接口,让用户自己去决定。然后QWidget将属性值赋值给QWindow,然后QWindow带着属性值满世界的跑,需要的地方就进行判断。

    整个思路还是很简单的。

    4、总结

    也是第一次修改Qt的源码,整个过程很是费劲,而且编译等待过程也是相当的漫长。所以每次编译之前一定要确保代码的正确性。

    在阅读的过程中,也是受益良多。

  • 相关阅读:
    【应用回归分析】CH4 假设检验与预测1——一般线性假设
    如何快速两个整数互质,已知的六种方法
    Linux入门
    Go编程项目实战教程
    Win:在 Windows 中配置 Multiple VLANs 接口
    【Java】Assert.assertEquals断言
    中秋佳节,华为手机如何拍月亮?
    Vue项目实战之电商后台管理系统(五) 商品分类模块
    m4a怎么转换成mp3格式?
    重磅开赛!“山东工行杯”山东省第五届数据应用创新创业大赛报名火热进行中!
  • 原文地址:https://blog.csdn.net/Mingyueruya/article/details/127782899