在Qt中有效地绘制物理上准确的标尺

Eli*_*545 5 c++ qt

我有一个标尺类(称为Graduation),它使用方向来计算应绘制线的位置.像这样:

在此输入图像描述

当方向设置为Qt::Horizontal我做line_xpos = precendent_line_xpos + number.然后,如果方向是Qt::Vertical我添加到y位置.

我在下面有一些代码,这是我如何使用if...else?:语句的一个例子:

/* std::vector<QLineF> m_lines; */

void Graduation::resizeEvent(QResizeEvent *event)
{   
    qreal newLength = (m_orientation == Qt::Horizontal)
        ? event->size().width()
        : event->size().height();
    qreal oldLength = (m_orientation == Qt::Horizontal)
        ? event->oldSize().width()
        : event->oldSize().height();

    if(newLength != oldLength) {
        if(newLength < oldLength) { /* Delete lines */
            int count = m_lines.size();
            if(count == 0)
                return;

            if(m_orientation == Qt::Horizontal) {
                while(m_lines[count-1].x1() > newLength) {
                    --count;
                }
            } else {
                while(m_lines[count-1].y1() > newLength) {
                    --count;
                }
            }

            m_lines.erase(
                m_lines.begin()+count,
                m_lines.begin()+m_lines.size()
            );
        } else
          /* ... Append lines ... */
    }
}
Run Code Online (Sandbox Code Playgroud)

下面是paintEvent来显示如何绘制线条:

void Graduation::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)

    QPainter painter(this);
    painter.setPen(QPen(Qt::black, 0.5));

    for(unsigned int i = 0; i < m_lines.size(); ++i)
        painter.drawLine(m_lines[i]);
}
Run Code Online (Sandbox Code Playgroud)

我想知道我是否可以避免这些条件检查可能数千次.我对可以使用哪种代码模式感兴趣(尽管最好只计算一次行位置并仅绘制可见的内容).

dte*_*ech 8

好的,在您发布该图像后,看起来您可以找到更简单有效的解决方案.

你的线条形成了一个可重复的模式,因此,绘制整个事物,手动管理线条以及诸如此类的东西将是一种浪费.您可以创建一个小像素图,并只绘制重复模式的一个部分,如下所示:

图案

然后根据方向,您可以保持原样或旋转/翻转它.然后,您可以使用它QPainter来填充您的Graduation小部件,它将Qt::TexturePattern使用模式QBrush::setTexture(pixmap)快速有效地填充整个小部件长度/高度,无论它是多少.

如果要更改标尺的样式或间距,您只需重新绘制缓存的图案并使用相同的过程更新标尺.

最后但并非最不重要的是,您可以使用Qt提供的布局将标尺捕捉到正确的位置,以便100%自动管理它们,使您当前使用的逻辑变得多余.

好的,这里有一点帮助,另外这将告诉你你不必每行都画线,代码很简单并且会为给定的DPI生成模式段:

QPixmap drawCMPattern(qreal dpi) {
    qreal dpcm = dpi / 2.54;
    QPixmap rulerCache(dpcm, dpcm / 2);
    rulerCache.fill();
    qreal dpmm = dpcm / 10;
    QPainter p(&rulerCache);
    p.setRenderHint(QPainter::HighQualityAntialiasing);
    qreal lineWidth = dpmm / 5;
    p.setPen(QPen(Qt::black, lineWidth));
    qreal xpos = lineWidth / 2;
    for (int i = 0; i < 10; ++i) {
        if (i == 0) p.drawLine(QLineF(xpos, 0, xpos, rulerCache.height()));
        else if (i == 5) p.drawLine(QLineF(xpos, 0, xpos, rulerCache.height() / 2));
        else p.drawLine(QLineF(xpos, 0, xpos, rulerCache.height() / 4));
        xpos += dpmm;
    }
    return rulerCache;
}
Run Code Online (Sandbox Code Playgroud)

您可能会注意到典型的低DPI台式机显示器上的线路有些模糊,但这是您为"物理上准确"而付出的代价 - 您要么是公制完美的还是像素完美的,除非您的DPI与公制单位匹配,否则您不能同时使用这两者完美地说,大多数线条都必然发生在像素之间.在高DPI移动设备上,它看起来不会那么糟糕.

或者,如果省略HighQualityAntialiasing,则可以获得清晰的线条,但这些线条的距离不会相等.不过,这可能对你有好处,因为对于像Photoshop这样的商业软件来说它显然已经足够了:

PS统治者

然而,PS是棘手的并且在1厘米内具有较少的线,因此差异不像每毫米线具有的那样显着.以下是我的108.79 DPI桌面上的代码结果:

这段代码

  • 让每一行成为一个单独的实体,至少可以说是过度的.我不知道你发布的代码有多少开销,但这完全没必要.即使您想要与线条进行某种"交互",例如捕捉或获取光标反馈,它也可以纯粹在理论上和数量上存在,而不需要将线条作为单独的实体.除了管理所有这些线路的逻辑之外,它还可以节省CPU时间和内存. (2认同)