qgis电子地图二次开发---比例尺

一 问题

基于qgis 3.16.3版本二次开发,在linux系统下平移电子地图时,比例尺大小会随着平移数据会不断变化,正常情况只有缩放时数据才会变化 。

二 解决方案

1 使用地图宽度:extent().width()比scale()更稳定,受浮点误差影响更小

2 相对容差机制:使用currentMapWidth * 0001作为容差,自动适应不同比例尺

3 多维度判断:综合考虑地图宽度、输出尺寸、单位像素比等多个参数

4 增强缓存:缓存更多相关参数,确保平移时完全复用

关键改进点

1. 缓存机制重构

原方案

复制代码
static double sLastMapScale = -1;
// ...
if (qAbs(currentMapScale - sLastMapScale) > 0.001)

新方案

复制代码
static double sLastMapWidth = -1;
static QgsRectangle sLastExtent;
static double sLastMapUnitsPerPixel = 0.0;
// ...
if (!qgsDoubleNear(currentMapWidth, sLastMapWidth, currentMapWidth * 0.001))
2. 多维度判断逻辑
复制代码
// 多维度判断是否需要重新计算
bool needRecalculate = false;

// 判断条件1:第一次渲染
if (sLastMapWidth < 0)
{
    needRecalculate = true;
}
// 判断条件2:地图宽度显著变化(缩放操作)
else if (!qgsDoubleNear(currentMapWidth, sLastMapWidth, currentMapWidth * 0.001))
{
    needRecalculate = true;
}
// 判断条件3:输出尺寸变化(窗口大小调整)
else if (outputSize != sLastOutputSize)
{
    needRecalculate = true;
}
// 判断条件4:单位像素比显著变化
else if (!qgsDoubleNear(currentMapUnitsPerPixel, sLastMapUnitsPerPixel, currentMapUnitsPerPixel * 0.001))
{
    needRecalculate = true;
}
3. 相对容差机制

关键创新 :使用相对容差 而非绝对容差

复制代码
// 相对容差:0.1%的变化才触发重算
!qgsDoubleNear(currentMapWidth, sLastMapWidth, currentMapWidth * 0.001)

// 而不是绝对容差:
// qAbs(currentMapWidth - sLastMapWidth) > 0.001  // 问题:小比例尺时过于敏感
4. 增强的日志记录
复制代码
qDebug() << "QgsDecorationScaleBar: Recalculating scale bar - MapWidth changed from" 
         << sLastMapWidth << "to" << currentMapWidth 
         << "or OutputSize changed from" << sLastOutputSize << "to" << outputSize;

三 修复效果

平移操作:比例尺数值、宽度、文字完全固定不变

缩放操作:比例尺正常准确更新 跨平台一致:

Windows 和 Linux 表现完全相同

性能优化:减少不必要的重算,平移时零计算开销

四 技术原理分析

为什么地图范围宽度比scale()更稳定?

  1. 计算链更短

    • extent().width() = xmax - xmin(简单减法)
    • scale() = extent().width() / (outputSize.width() * mapUnitsPerPixel)(多次运算)
  2. 浮点误差影响更小

    • 平移时xminxmax通常同时增加或减少相同的值
    • 它们的差值(width)受浮点误差影响更小
  3. 物理意义更明确

    • 地图宽度直接反映缩放级别
    • 平移不应该改变地图宽度,这是明确的物理约束

相对容差vs绝对容差

绝对容差问题

复制代码
if (qAbs(currentMapWidth - sLastMapWidth) > 0.001)  // 绝对容差
  • 对于大比例尺地图(如1:1000000),0.001的变化可能微不足道
  • 对于小比例尺地图(如1:100),0.001的变化可能已经很显著

相对容差优势

复制代码
if (!qgsDoubleNear(currentMapWidth, sLastMapWidth, currentMapWidth * 0.001))  // 相对容差
  • 自动适应不同比例尺
  • 始终使用当前值的0.1%作为容差
  • 大比例尺时容差自动变大,小比例尺时容差自动变小

五 关键代码

以前代码

cpp 复制代码
void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
  if ( !enabled() )
    return;

  //Get canvas dimensions
  QPaintDevice *device = context.painter()->device();
  const int deviceHeight = device->height() / device->devicePixelRatioF();
  const int deviceWidth = device->width() / device->devicePixelRatioF();
  QgsSettings settings;
  bool ok = false;
  QgsUnitTypes::DistanceUnit preferredUnits = QgsUnitTypes::decodeDistanceUnit( settings.value( QStringLiteral( "qgis/measure/displayunits" ) ).toString(), &ok );
  if ( !ok )
    preferredUnits = QgsUnitTypes::DistanceMeters;

  QgsUnitTypes::DistanceUnit scaleBarUnits = mapSettings.mapUnits();

  //Get map units per pixel
  const double scaleBarUnitsPerPixel = ( mapWidth( mapSettings ) / mapSettings.outputSize().width() ) * QgsUnitTypes::fromUnitToUnitFactor( mSettings.units(), preferredUnits );
  scaleBarUnits = preferredUnits;

  qDebug() << "mapWidth( mapSettings ):" << mapWidth( mapSettings ) << "mapSettings.outputSize().width():" << mapSettings.outputSize().width();

  // Exit if the canvas width is 0 or layercount is 0 or QGIS will freeze
  if ( mapSettings.layers().isEmpty() || !deviceWidth || !scaleBarUnitsPerPixel )
    return;

  double unitsPerSegment = mPreferredSize;

  //Calculate size of scale bar for preferred number of map units
  double scaleBarWidth = mPreferredSize / scaleBarUnitsPerPixel;
  qDebug() << "@@@scaleBarWidth:" << scaleBarWidth << "scaleBarUnitsPerPixel:" << scaleBarUnitsPerPixel;
  //If scale bar is very small reset to 1/4 of the canvas wide
  if ( scaleBarWidth < 30 )
  {
    scaleBarWidth = deviceWidth / 4.0; // value in pixels
    unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel; // value in map units
  }

  //if scale bar is more than half the canvas wide keep halving until not
  while ( scaleBarWidth > deviceWidth / 3.0 )
  {
    scaleBarWidth = scaleBarWidth / 3;
  }
  qDebug() << "unitsPerSegment: "<< unitsPerSegment <<  "scaleBarWidth :" << scaleBarWidth << "scaleBarUnitsPerPixel :" << scaleBarUnitsPerPixel;
  unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel;

  // Work out the exponent for the number - e.g, 1234 will give 3,
  // and .001234 will give -3
  const double powerOf10 = std::floor( std::log10( unitsPerSegment ) );

  // snap to integer < 10 times power of 10
  if ( mSnapping )
  {
    const double scaler = std::pow( 10.0, powerOf10 );
    unitsPerSegment = std::round( unitsPerSegment / scaler ) * scaler;
    scaleBarWidth = unitsPerSegment / scaleBarUnitsPerPixel;
  }
  qDebug() << "##unitsPerSegment: "<< unitsPerSegment <<  "scaleBarWidth :" << scaleBarWidth << "scaleBarUnitsPerPixel :" << scaleBarUnitsPerPixel;
  const double segmentSizeInMm = scaleBarWidth / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );

  unitsPerSegment /= 10000;
  unitsPerSegment /= mDebugParams;
  QString scaleBarUnitLabel;
  switch ( scaleBarUnits )
  {
    case QgsUnitTypes::DistanceMeters:
      if ( unitsPerSegment > 1000.0 )
      {
        scaleBarUnitLabel = tr( "km" );
        unitsPerSegment = unitsPerSegment / 1000;
      }
      else if ( unitsPerSegment < 0.01 )
      {
        scaleBarUnitLabel = tr( "mm" );
        unitsPerSegment = unitsPerSegment * 1000;
      }
      else if ( unitsPerSegment < 0.1 )
      {
        scaleBarUnitLabel = tr( "cm" );
        unitsPerSegment = unitsPerSegment * 100;
      }
      else
        scaleBarUnitLabel = tr( "m" );
      break;
    case QgsUnitTypes::DistanceFeet:
      if ( unitsPerSegment > 5280.0 ) //5280 feet to the mile
      {
        scaleBarUnitLabel = tr( "miles" );
        // Adjust scale bar width to get even numbers
        unitsPerSegment = unitsPerSegment / 5000;
        //scaleBarWidth = ( scaleBarWidth * 5280 ) / 5000;
      }
      else if ( unitsPerSegment == 5280.0 ) //5280 feet to the mile
      {
        scaleBarUnitLabel = tr( "mile" );
        // Adjust scale bar width to get even numbers
        unitsPerSegment = unitsPerSegment / 5000;
        //scaleBarWidth = ( scaleBarWidth * 5280 ) / 5000;
      }
      else if ( unitsPerSegment < 1 )
      {
        scaleBarUnitLabel = tr( "inches" );
        unitsPerSegment = unitsPerSegment * 10;
        //scaleBarWidth = ( scaleBarWidth * 10 ) / 12;
      }
      else if ( unitsPerSegment == 1.0 )
      {
        scaleBarUnitLabel = tr( "foot" );
      }
      else
      {
        scaleBarUnitLabel = tr( "feet" );
      }
      break;
    case QgsUnitTypes::DistanceDegrees:
      if ( unitsPerSegment == 1.0 )
        scaleBarUnitLabel = tr( "degree" );
      else
        scaleBarUnitLabel = tr( "degrees" );
      break;
    case QgsUnitTypes::DistanceKilometers:
    case QgsUnitTypes::DistanceNauticalMiles:
    case QgsUnitTypes::DistanceYards:
    case QgsUnitTypes::DistanceMiles:
    case QgsUnitTypes::DistanceCentimeters:
    case QgsUnitTypes::DistanceMillimeters:
    case QgsUnitTypes::DistanceUnknownUnit:
      scaleBarUnitLabel = QgsUnitTypes::toAbbreviatedString( scaleBarUnits );
      break;
  }

  mSettings.setUnits( scaleBarUnits );
  mSettings.setNumberOfSegments( mStyleIndex == 3 ? 2 : 1 );
  mSettings.setUnitsPerSegment( mStyleIndex == 3 ? unitsPerSegment / 2 : unitsPerSegment );
  mSettings.setUnitLabel( scaleBarUnitLabel );
  mSettings.setLabelHorizontalPlacement( mPlacement == TopCenter || mPlacement == BottomCenter ? QgsScaleBarSettings::LabelCenteredSegment : QgsScaleBarSettings::LabelCenteredEdge );

  QgsScaleBarRenderer::ScaleBarContext scaleContext;
  scaleContext.segmentWidth = mStyleIndex == 3 ? segmentSizeInMm / 2 : segmentSizeInMm;
  scaleContext.scale = mapSettings.scale();

  //Calculate total width of scale bar and label
  QSizeF size = mStyle->calculateBoxSize( context, mSettings, scaleContext );
  size.setWidth( context.convertToPainterUnits( size.width(), QgsUnitTypes::RenderMillimeters ) );
  size.setHeight( context.convertToPainterUnits( size.height(), QgsUnitTypes::RenderMillimeters ) );

  int originX = 0;
  int originY = 0;

  // Set  margin according to selected units
  switch ( mMarginUnit )
  {
    case QgsUnitTypes::RenderMillimeters:
    {
      int pixelsInchX = context.painter()->device()->logicalDpiX();
      int pixelsInchY = context.painter()->device()->logicalDpiY();
      originX = pixelsInchX * INCHES_TO_MM * mMarginHorizontal;
      originY = pixelsInchY * INCHES_TO_MM * mMarginVertical;
      break;
    }

    case QgsUnitTypes::RenderPixels:
      originX = mMarginHorizontal - 5.; // Minus 5 to shift tight into corner
      originY = mMarginVertical - 5.;
      break;

    case QgsUnitTypes::RenderPercentage:
    {
      originX = ( ( deviceWidth - size.width() ) / 100. ) * mMarginHorizontal;
      originY = ( ( deviceHeight ) / 100. ) * mMarginVertical;
      break;
    }
    case QgsUnitTypes::RenderMapUnits:
    case QgsUnitTypes::RenderPoints:
    case QgsUnitTypes::RenderInches:
    case QgsUnitTypes::RenderUnknownUnit:
    case QgsUnitTypes::RenderMetersInMapUnits:
      break;
  }

  //Determine the origin of scale bar depending on placement selected
  switch ( mPlacement )
  {
    case TopLeft:
      break;
    case TopRight:
      originX = deviceWidth - originX - size.width();
      break;
    case BottomLeft:
      originY = deviceHeight - originY - size.height();
      break;
    case BottomRight:
      originX = deviceWidth - originX - size.width();
      originY = deviceHeight - originY - size.height();
      break;
    case TopCenter:
      originX = deviceWidth / 2 - size.width() / 2 + originX;
      break;
    case BottomCenter:
      originX = deviceWidth / 2 - size.width() / 2 + originX;
      originY = deviceHeight - originY - size.height();
      break;
    default:
      QgsDebugMsg( QStringLiteral( "Unsupported placement index of %1" ).arg( static_cast<int>( mPlacement ) ) );
  }

  QgsScopedQPainterState painterState( context.painter() );
  context.painter()->translate( originX, originY );
  mStyle->draw( context, mSettings, scaleContext );
}

修复后代码

cpp 复制代码
// QGIS 3.16.3兼容版:比例尺平移不变修复
void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
    if ( !enabled() )
        return;

    // ========== 高级修复:固定DPI与设备像素比 ==========
    QPaintDevice *device = context.painter()->device();

    // 固定DPI参数
    static const double sFixedDpi = 96.0;
    static const double sFixedDevicePixelRatio = 1.0;

    // 固定设备尺寸计算
    const int deviceHeight = device->height() / sFixedDevicePixelRatio;
    const int deviceWidth = device->width() / sFixedDevicePixelRatio;

    // QGIS 3.16.3兼容:设置缩放因子
    context.setScaleFactor( sFixedDpi / 25.4 ); // 像素/毫米转换
    // ==========================================================

    // ========== 高级缓存机制:基于地图范围宽度而非scale() ==========
    static double sLastMapWidth = -1;       // 缓存地图宽度(更稳定)
    static QSize sLastOutputSize;
    static double sCachedUnitsPerSegment = 0;
    static double sCachedScaleBarWidth = 0;
    static QgsUnitTypes::DistanceUnit sCachedUnits = QgsUnitTypes::DistanceUnknownUnit;
    static QString sCachedUnitLabel;
    static QgsRectangle sLastExtent;        // 缓存地图范围
    static double sLastMapUnitsPerPixel = 0.0; // 缓存单位像素比

    // 获取用户首选显示单位
    QgsSettings settings;
    bool ok = false;
    QgsUnitTypes::DistanceUnit preferredUnits = QgsUnitTypes::decodeDistanceUnit( settings.value( QStringLiteral( "qgis/measure/displayunits" ) ).toString(), &ok );
    if ( !ok )
        preferredUnits = QgsUnitTypes::DistanceMeters;

    QgsUnitTypes::DistanceUnit scaleBarUnits = mapSettings.mapUnits();

    // ========== 关键改进:使用地图范围宽度而非scale()来判断是否需要重算 ==========
    const QgsRectangle currentExtent = mapSettings.extent();
    const double currentMapWidth = currentExtent.width();
    const QSize outputSize = mapSettings.outputSize();

    // 安全检查
    if ( outputSize.isEmpty() || currentMapWidth <= 0 || mapSettings.layers().isEmpty() || !deviceWidth )
        return;

    // ========== 计算单位像素比 ==========
    const double currentMapUnitsPerPixel = currentMapWidth / outputSize.width();
    const double scaleBarUnitsPerPixel = currentMapUnitsPerPixel * QgsUnitTypes::fromUnitToUnitFactor( mapSettings.mapUnits(), preferredUnits );
    scaleBarUnits = preferredUnits;

    // 安全检查
    if ( qgsDoubleNear( scaleBarUnitsPerPixel, 0.0 ) )
        return;

    // ========== 高级判断逻辑:多维度判断是否需要重新计算 ==========
    bool needRecalculate = false;

    // 判断条件1:第一次渲染
    if ( sLastMapWidth < 0 )
    {
        needRecalculate = true;
    }
    // 判断条件2:地图宽度显著变化(缩放操作)
    else if ( !qgsDoubleNear( currentMapWidth, sLastMapWidth, currentMapWidth * 0.001 ) ) // 0.1%的容差
    {
        needRecalculate = true;
    }
    // 判断条件3:输出尺寸变化(窗口大小调整)
    else if ( outputSize != sLastOutputSize )
    {
        needRecalculate = true;
    }
    // 判断条件4:单位像素比显著变化
    else if ( !qgsDoubleNear( currentMapUnitsPerPixel, sLastMapUnitsPerPixel, currentMapUnitsPerPixel * 0.001 ) )
    {
        needRecalculate = true;
    }

    // 如果需要重新计算
    if ( needRecalculate )
    {
        // 记录日志(可选)
        qDebug() << "QgsDecorationScaleBar: Recalculating scale bar - MapWidth changed from"
                 << sLastMapWidth << "to" << currentMapWidth
                 << "or OutputSize changed from" << sLastOutputSize << "to" << outputSize;

        // ========== 比例尺基础计算 ==========
        double unitsPerSegment = mPreferredSize;
        double scaleBarWidth = mPreferredSize / scaleBarUnitsPerPixel;

        // 最小宽度限制
        if ( scaleBarWidth < 30 )
        {
            scaleBarWidth = deviceWidth / 4.0;
            unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel;
        }

        // 最大宽度限制
        while ( scaleBarWidth > deviceWidth / 3.0 )
        {
            scaleBarWidth /= 3;
            unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel;
        }

        // 数字取整(保持美观)
        if ( mSnapping && !qgsDoubleNear( unitsPerSegment, 0.0 ) )
        {
            const double powerOf10 = std::floor( std::log10( unitsPerSegment ) );
            const double scaler = std::pow( 10.0, powerOf10 );
            unitsPerSegment = std::round( unitsPerSegment / scaler ) * scaler;
            scaleBarWidth = unitsPerSegment / scaleBarUnitsPerPixel;
        }

        // ========== 用户需求:整体缩小10000倍 ==========
        unitsPerSegment /= 10000;
        unitsPerSegment /= mDebugParams;

        // ========== 单位换算逻辑 ==========
        double tempUnits = unitsPerSegment;
        QString unitLabel;
        switch ( scaleBarUnits )
        {
        case QgsUnitTypes::DistanceMeters:
            if ( tempUnits >= 1000.0 )
            {
                unitLabel = tr( "km" );
                tempUnits /= 1000;
            }
            else if ( tempUnits < 0.001 )
            {
                unitLabel = tr( "mm" );
                tempUnits *= 1000;
            }
            else if ( tempUnits < 0.01 )
            {
                unitLabel = tr( "cm" );
                tempUnits *= 100;
            }
            else
            {
                unitLabel = tr( "m" );
            }
            break;
        case QgsUnitTypes::DistanceFeet:
            if ( tempUnits >= 5280.0 )
            {
                unitLabel = tr( "mi" );
                tempUnits /= 5280.0;
            }
            else
            {
                unitLabel = tr( "ft" );
            }
            break;
        case QgsUnitTypes::DistanceDegrees:
            unitLabel = tr( "°" );
            break;
        default:
            unitLabel = QgsUnitTypes::toAbbreviatedString( scaleBarUnits );
            break;
        }

        // 更新缓存
        sLastMapWidth = currentMapWidth;
        sLastOutputSize = outputSize;
        sLastExtent = currentExtent;
        sLastMapUnitsPerPixel = currentMapUnitsPerPixel;

        // 缓存计算结果
        sCachedUnitsPerSegment = unitsPerSegment;
        sCachedScaleBarWidth = scaleBarWidth;
        sCachedUnits = scaleBarUnits;
        sCachedUnitLabel = unitLabel;

        // 记录计算结果(可选)
        qDebug() << "QgsDecorationScaleBar: New scale bar - UnitsPerSegment:" << unitsPerSegment
                 << "ScaleBarWidth:" << scaleBarWidth << "UnitLabel:" << unitLabel;
    }
    else
    {
        // 平移时直接复用缓存
        qDebug() << "QgsDecorationScaleBar: Using cached scale bar values";
    }

    // ========== 直接复用缓存(关键!) ==========
    double unitsPerSegment = sCachedUnitsPerSegment;
    double scaleBarWidth = sCachedScaleBarWidth;
    scaleBarUnits = sCachedUnits;
    QString scaleBarUnitLabel = sCachedUnitLabel;

    // 安全检查
    if ( qgsDoubleNear( unitsPerSegment, 0.0 ) || qgsDoubleNear( scaleBarWidth, 0.0 ) )
        return;

    // ========== 毫米尺寸转换 ==========
    const double segmentSizeInMm = scaleBarWidth / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );

    // 设置比例尺参数
    mSettings.setUnits( scaleBarUnits );
    mSettings.setNumberOfSegments( mStyleIndex == 3 ? 2 : 1 );
    mSettings.setUnitsPerSegment( mStyleIndex == 3 ? unitsPerSegment / 2 : unitsPerSegment );
    mSettings.setUnitLabel( scaleBarUnitLabel );

    // 标签位置设置
    mSettings.setLabelHorizontalPlacement( mPlacement == TopCenter || mPlacement == BottomCenter ?
                                               QgsScaleBarSettings::LabelCenteredSegment :
                                               QgsScaleBarSettings::LabelCenteredEdge );

    // ScaleBarContext设置
    QgsScaleBarRenderer::ScaleBarContext scaleContext;
    scaleContext.segmentWidth = mStyleIndex == 3 ? segmentSizeInMm / 2 : segmentSizeInMm;
    scaleContext.scale = mapSettings.scale(); // 注意:这里仍然使用mapSettings.scale(),但不影响缓存判断

    // 计算尺寸
    QSizeF size = mStyle->calculateBoxSize( context, mSettings, scaleContext );
    size.setWidth( context.convertToPainterUnits( size.width(), QgsUnitTypes::RenderMillimeters ) );
    size.setHeight( context.convertToPainterUnits( size.height(), QgsUnitTypes::RenderMillimeters ) );

    // 边距计算
    int originX = 0;
    int originY = 0;
    switch ( mMarginUnit )
    {
    case QgsUnitTypes::RenderMillimeters:
    {
        // 使用固定DPI计算边距
        const int pixelsPerMm = sFixedDpi / 25.4;
        originX = pixelsPerMm * mMarginHorizontal;
        originY = pixelsPerMm * mMarginVertical;
        break;
    }
    case QgsUnitTypes::RenderPixels:
        originX = mMarginHorizontal;
        originY = mMarginVertical;
        break;
    case QgsUnitTypes::RenderPercentage:
        originX = ( ( deviceWidth - size.width() ) / 100.0 ) * mMarginHorizontal;
        originY = ( deviceHeight / 100.0 ) * mMarginVertical;
        break;
    default:
        break;
    }

    // 位置定位
    switch ( mPlacement )
    {
    case TopLeft: break;
    case TopRight: originX = deviceWidth - originX - size.width(); break;
    case BottomLeft: originY = deviceHeight - originY - size.height(); break;
    case BottomRight:
        originX = deviceWidth - originX - size.width();
        originY = deviceHeight - originY - size.height();
        break;
    case TopCenter: originX = deviceWidth / 2 - size.width() / 2 + originX; break;
    case BottomCenter:
        originX = deviceWidth / 2 - size.width() / 2 + originX;
        originY = deviceHeight - originY - size.height();
        break;
    default: break;
    }

    // 绘制
    QgsScopedQPainterState painterState( context.painter() );
    context.painter()->translate( originX, originY );
    mStyle->draw( context, mSettings, scaleContext );
}
相关推荐
GIS阵地3 小时前
QGIS的分类渲染核心类解析
c++·qgis·开源gis
雾岛听蓝5 小时前
Qt操作指南:状态栏、浮动窗口与对话框使用
开发语言·经验分享·笔记·qt
楚Y6同学7 小时前
QT C++之保存界面设置为配置文件
c++·qt·保存配置
Ulyanov7 小时前
《PySide6 GUI开发指南:QML核心与实践》 第十篇:综合实战——构建完整的跨平台个人管理应用
开发语言·python·qt·ui·交互·qml·雷达电子战系统仿真
-凌凌漆-8 小时前
【QML】QQmlEngine::setObjectOwnership()的作用
qt
Ulyanov10 小时前
《PySide6 GUI开发指南:QML核心与实践》 第八篇:性能优化大师——QML应用性能调优实战
python·qt·ui·性能优化·qml·系统仿真
小短腿的代码世界12 小时前
Qwt性能优化与源码级深度解析:工业级图表控件的极限性能调优
开发语言·qt·信息可视化·性能优化
-凌凌漆-12 小时前
【Qt】qt延时
开发语言·qt
楚Y6同学13 小时前
QT之下拉框自动填充功能
开发语言·c++·qt·qt开发技巧·串口下拉填充·网口下拉填充