在Qt中有效地画出精确的尺子 [英] Efficiently painting physically accurate ruler in Qt

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

问题描述

我有一个标尺类(称为毕业),它使用方向来计算应绘制线条。所以这样:





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



我有一些代码在下面是如何使用如果...其他?:语句:

  / * 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()。

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){
-
}
} else {
while(m_lines [count-1] .y1()> newLength){
-
}
}

m_lines.erase(
m_lines.begin()+ count,
m_lines.begin()+ m_lines.size()
);
} else
/ * ...附加行... * /
}
}

以下是paintEvent,以显示如何绘制线条:

  void毕业: :paintEvent(QPaintEvent * event)
{
Q_UNUSED(event)

QPainter画家(这);
painter.setPen(QPen(Qt :: black,0.5));

for(unsigned int i = 0; i< m_lines.size(); ++ i)
painter.drawLine(m_lines [i]);
}

我想知道如果我能保持这些条件可以检查数千倍。我感兴趣的是什么代码模式可以用来避免这种(虽然可以更好地计算行位置只能一次,只画出可见的)

解决方案

确定后,您发布该图像后,看起来您可以选择一个更简单有效的解决方案。



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





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



如果要更改标尺的样式或间距,您只需重新绘制缓存的模式,并使用相同的过程来更新标尺。 / p>

最后但并非最不重要的是,您可以使用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; (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完美匹配度量单位,否则绝大多数行都必须在像素之间发生。在高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天全站免登陆