在Qt中有效地画出精确的尺子 [英] Efficiently painting physically accurate ruler in Qt
问题描述
我有一个标尺类(称为毕业
),它使用方向来计算应绘制线条。所以这样:
当方向设置为 Qt :: Horizontal
我做 line_xpos = precendent_line_xpos + number
。然后,如果方向是 Qt :: Vertical
我添加到y位置。
我有一些代码在下面是如何使用如果...其他
和?:
语句:
/ * std :: vector< QLineF> m_lines; * /
void毕业:: resizeEvent(QResizeEvent * event)
{
qreal newLength =(m_orientation == Qt :: Horizontal)
? event-> size()。width()
:event-> size()。
qreal oldLength =(m_orientation == Qt :: Horizontal)
? event-> oldSize()。width()
:event-> oldSize()。
if(newLength!= oldLength){
if(newLength< oldLength){/ *删除行* /
int count = m_lines.size();
if(count == 0)
return;
if(m_orientation == Qt :: Horizontal){
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屋!