在Qt中有效地绘制物理准确的标尺 [英] Efficiently painting physically accurate ruler in Qt

查看:510
本文介绍了在Qt中有效地绘制物理准确的标尺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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





当方向设置为 Qt :: Horizo​​ntal I do line_xpos = precendent_line_xpos + number 。然后如果方向是 Qt :: Vertical 我添加到y位置。



是如何使用 if ... else ?:语句执行此操作的示例:

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

void毕业:: resizeEvent(QResizeEvent * event)
{
qreal newLength =(m_orientation == Qt :: Horizo​​ntal)
? event-> size()。width()
:event-> size()。
qreal oldLength =(m_orientation == Qt :: Horizo​​ntal)
? event-> oldSize()。width()
:event-> oldSize()。height();

if(newLength!= oldLength){
if(newLength< oldLength){/ *删除行* /
int count = m_lines.size
if(count == 0)
return;

if(m_orientation == Qt :: Horizo​​ntal){
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 b $ b);
} else
/ * ...附加行... * /
}
}

这里是paintEvent来显示如何绘制线:

  void毕业: :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]);
}



我想知道我是否可以阻止这些条件检查次。我感兴趣的是什么样的代码模式,你可以使用避免(虽然它可以更好地计算行位置只有一次,并绘制只有可见的)。



$ b $ b

你的线条形成一个可重复的模式,因此,绘制整个事情,管理线手动和什么都是浪费。您可以创建一个小像素图,并只绘制重复模式的一段,如下所示:





然后根据方向,你可以保持原样,或者旋转/翻转。然后你可以使用 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;
}

您可能会注意到典型的低DPI桌面显示器上的线条有点模糊,但这是你为物理准确支付的价格 - 你是公制完美或像素完美,你不能有两个,除非你的DPI匹配度量单位完美,大多数行必然发生在像素之间。



或者,如果您省略了 HighQualityAntialiasing ,但是然后线将不会在相等的距离。仍然,这可能证明对你很好,因为它显然是足够好的商业软件,如Photoshop:





PS是棘手的,在1厘米内有较少的线,所以差别不如是每mm有一条线的时候。下面是我的108.79 DPI桌面上的代码结果:




I have a ruler class (called Graduation) that uses orientation to calculate where lines should be drawn. So like this:

When the orientation is set to Qt::Horizontal I do line_xpos = precendent_line_xpos + number. Then if the orientation is Qt::Vertical I add to the y position.

I have some code below which is an example of how I do this with if...else and ?: statements:

/* 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 ... */
    }
}

Here is the paintEvent to show how the lines are drawn:

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]);
}

I want to know if I can keep from having those conditions checked possibly thousands of times. I'm interested in what code pattern you can use to avoid that (although it could be better to calculate lines position only one time and draw only what is visible).

解决方案

OK, after you posted that image, it does look like you can go for a much simpler and effective solution.

Your lines form a repeatable pattern and as such, it would be a waste to draw the whole thing, manage lines manually and whatnot. You can create a small pixmap and draw only one segment of the repeating pattern like this:

Then depending on the orientation you can leave it as it is or rotate/flip it. Then you can use QPainter to fill your Graduation widget with it a Qt::TexturePattern using QBrush::setTexture(pixmap) and it will quickly and efficiently fill the entire widget length/height with the pattern, regardless of how much it is.

If you want to change the style or spacing of the ruler, all you have to do is redraw the cached pattern and use the same process to update the rulers.

And last but not least, you can use the layouting Qt provides to snap your rulers to the right place so they are managed 100% automatically, making your currently used logic redundant.

OK, here is a little more help, plus this will show you you don't have to have each and every line to draw lines, the code is simple enough and will generate the pattern segment for a given 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;
}

You may notice the lines are somewhat blurry on a typical low DPI desktop monitor, but that's the price you pay for being "physically accurate" - you are either metric perfect or pixel perfect, you can't have both unless your DPI match metric units perfectly, most lines are bound to happen between pixels. On a high DPI mobile device it will not look as bad.

Alternatively, you can get crisp lines if you omit the HighQualityAntialiasing, but then the lines will not be at equal distance. Still, this may prove good for you, since it is apparently good enough for commercial software like Photoshop:

PS is however tricky and has less lines in 1 cm, so the difference is not as striking as it is when having a line for each mm. Here is how that code result looks on my 108.79 DPI desktop:

这篇关于在Qt中有效地绘制物理准确的标尺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆