QPainter绘制文本 二

2024-03-23 14:18
文章标签 绘制 文本 qpainter

本文主要是介绍QPainter绘制文本 二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

先从最简单任务开始:在 widget 的左上角(0, 0)处绘制字符串 jEh。

void MainWidget::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setFont(QFont("Times", 150, QFont::Bold)); // 使用大一些的字体int x = 0;int y = 0;painter.drawText(x, y, "jEh");
}

这里写图片描述
…… 出师不利,本以为是很简单的一件事,那还不手到擒来么,不曾想结果却让人大跌眼镜,只显示出了 j 的小尾巴。如果把 y 的值设置大一些,如 150,则就可以完全显示出来了。但是,y 要多大才合适?不能一点一点的试吧,否则字体变了,y 的值又不合适了,完全不科学,这要如何是好?
FontMetrics

相信大多 数人和我一样,刚开始的时候都认为 drawText() 的 x, y 是字符串左上角的坐标,其实不然,它是字符串的第一个字符的 origin 的坐标,y 是字体的 base line 的 y 坐标,什么是 origin,base line? 看完下图基本上就明白了:
这里写图片描述
文本是基于 base line 绘制的,而不是文本的左上角,所以上面的文本显示不全就很好理解了。

还是原来的问题,从 widget 的左上角开始绘制文本,那么 y 就应该和 ascent 一样大,但是怎么得到 ascent 的值呢?难到需要我们记住每种字体的 ascent 的值吗?这也是一种方法,如果愿意,未尝不可,但是,脑子够用么,幸好 QFontMetrics 就能够给我们提供字体的信息,提供了很多函数,如取得 line height 用 height(),用 width() 计算字符串的宽度,ascent(), descent(), xHeight() 等, 函数的名字已经很好的表明它的作用,在此就不再一一介绍,更多的函数请参考 Qt 的帮助文档。所以为了达到我们的目的,只需要把 y = 0 修改为 int y = metrics.ascent() 就可以了:

void MainWidget::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setFont(QFont("Times", 150, QFont::Bold));QFontMetrics metrics = painter.fontMetrics();int x = 0;int y = metrics.ascent();painter.drawText(x, y, "jEh");
}

居中绘制文本

这里写图片描述
考虑一个问题,像下图这样,在一个矩形里居中显示字符串,应该怎么做呢?

有了 QFontMetrics,想必对大家来说问题已经不大,得到字符串的宽、高、ascent,简单的居中计算,就可以得到 origin 的坐标了。


void MainWidget::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setFont(QFont("Times", 150, QFont::Bold));QRect rect(20, 20, 300, 200);painter.drawRect(rect);// 居中绘制文本QFontMetrics metrics = painter.fontMetrics();int stringHeight = metrics.ascent() + metrics.descent(); // 不算 line gapint stringWidth = metrics.width("jEh"); // 字符串的宽度int x = rect.x() + (rect.width() - stringWidth) / 2;int y = rect.y() + (rect.height() - stringHeight) / 2 + metrics.ascent();painter.drawText(x, y, "jEh");// 绘制字符串的包围矩形y = rect.y() + (rect.height() - stringHeight) / 2;painter.setPen(Qt::lightGray);painter.drawRect(x, y, stringWidth, stringHeight);
}

把字体的包围矩形也画出来,这样就能很清晰的看到字符串的居中效果了。也许你还会问,这不是还有一点点没有居中吗?这个和字体有关系,换成等宽字体如 Menlo 后就可以看到确实是完全居中的,说明 QFontMetrics 得到的字体信息没问题,只是有的字体为了美观漂亮作了一些调整,对于这些字体如果要完全的居中效果的话,只好在使用上面的计算方式后再手动的微调一下就好了。

换行绘制文本

这里写图片描述
开始的时候说过,drawText() 绘制文本有两种方式,不会自动换行和在给定的矩形中自动换行,下面就举例说明,先绘制一行很长但不会自动换行的文本,然后在给定的矩形 QRect(20, 35, 200, 80) 里绘制会自动换行,向右靠齐的文本,效果如下图(发现超出矩形的字符不显示):

void MainWidget::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);QString text = "QPainter provides highly optimized functions to do ""most of the drawing GUI programs require. It can draw ""everything from simple lines to complex shapes ""like pies and chords. ""看看是否也支持中文呢,如果不支持那就悲剧了!";QRect rect(20, 35, 200, 80);int flags = Qt::TextWordWrap | Qt::AlignRight; // 自动换行,向右对齐painter.drawText(20, 20, text);painter.drawRect(rect); // 画出矩形,可以看到超出此矩形的部分文本不可见painter.drawText(rect, flags, text);
}

文本的包围矩形

在给定的矩形里面绘制文本,超出的部分不显示,例如 QTableView 的 item 显示文本时就是这样的,文本超出了 item 的范围,就用 … 表示。例如开发一个聊天软件,也用这种方式绘制文本,消息太长时,超出了范围的部分就不显示,万一有两个异地恋人用这个软件聊天,异地恋是很敏感的,消息显示不全,有些话很有可能理解成相反的意思,可想而知会造成多少误会,而且自己还不知道为什么,都无从解释,如果因此而断送了一桩美好的姻缘,那是何等的罪过,显然这种显示文本的方式在这里是不适合的。观察 QQ 显示消息的样式,是由消息来决定显示的范围,有的很长,有的很短,而不是在固定大小的矩形内显示消息。
这里写图片描述
那么,怎么根据消息的大小来确定显示的范围呢(所谓的自适应)?

一般显示的宽度应该是确定的,关键是高度的计算,可以逐个字符的把他们的宽度加起来(不同的字体每个字符的宽度不一样),当大于显示的宽度就换行,高度也对应的加上一行的高度,这样就能计算出最终的高度了,也就知道了显示消息的矩形大小,使用这个方法就能自适应的显示消息了。

虽然我们已经知道了自适应显示消息的原理,但是如果是我们自己来计算实现,难度还是不小的,其实 Qt 已经给我们提供了相关的 API,使用 QFontMetrics::boundingRect() 可以计算出包围文本的矩形,然后在用上面的方法绘制文本就可以了,下面的程序,改变窗口的宽度,能动态的计算出显示文本所有内容的包围矩形,解决了上面提到的在给定的矩形中,文本太长时显示不全的问题:


void MainWidget::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);QString text = "QPainter provides highly optimized functions to do ""most of the drawing GUI programs require. It can draw ""everything from simple lines to complex shapes ""like pies and chords. ""看看是否也支持中文呢,如果不支持那就悲剧了!";int width = this->width() - 40; // 显示文本的宽度,为窗口的宽度减去 40 像素int flags = Qt::TextWordWrap; // 自动换行// 计算文本在指定宽度下的包围矩形QFontMetrics metrics = painter.fontMetrics();QRect textBoundingRect = metrics.boundingRect(QRect(0, 0, width, 0), flags, text);painter.translate(20, 20);painter.drawRect(textBoundingRect);painter.drawText(textBoundingRect, flags, text);
}

metrics.boundingRect(QRect(0, 0, width, 0), flags, text),第一个参数 rect 中最关键的是 width(显示文本的宽度),至于其中的 x, y 坐标和得到的 textBoundingRect 中的 x, y 是一样的,只是为了计算方便而已,height 没有什么意义,随便给个值就行,flags 和前面说过的 flags 一样。

QTableView 中没有显示完的字符串后面都会跟着一个 …,这个怎么来的呢?不妨看看 QFontMetrics::elidedText(),一切就真像大白了。

    elide: 省略

baseLine 基线
drawText绘制文字时候,也是有规则的,这个规则就是baseLine(基线)。什么又是基线了,说白了就是一条直线,我们这里理解的是确定它的位置。我们先来看一下基线:
这里写图片描述

void QPainter::drawText(int x, int y, const QString &text)
需要注意的是x,y并不是文字左上角的坐标点,它比较特殊,y所代表的是基线坐标y的坐标。
这里写图片描述

为了帮助理解,我特此搜索了不同的示意图。对照示意图,会很容易理解FontMetrics的参数。

要点如下:

  1. 基线是baseline

  2. Ascent是baseline之上至字符最高处的距离

  3. Descent是baseline之下至字符最低处的距离

  4. Leading文档说的很含糊,其实是上一行字符的descent到下一行的ascent之间的距离

  5. Top指的是指的是最高字符到baseline的值,即ascent的最大值

  6. 同上,bottom指的是最下字符到baseline的值,即descent的最大值
    这里写图片描述

这里写图片描述

这里写图片描述

QFontMetrics Class

width(QString s): 获取字符串s的总像素宽度。

int QFontMetrics::width ( const QString & text, int len = -1 ) const
Returns the width in pixels of the first len characters of text. If len is negative (the default), the entire string is used.

height(): 获取字体的高度。

int QFontMetrics::height () const
Returns the height of the font.
This is always equal to ascent()+descent()+1 (the 1 is for the base line).

lineSpacing(): 获取字体的高度,包括文字的实际宽度和行距。

Returns the distance from one base line to the next.

This value is always equal to leading()+height().

qreal leading() const: 行间距

int QFontMetrics::leading () const

Returns the leading of the font.

This is the natural inter-line spacing.

See also height() and lineSpacing().

boundingRect

Returns the bounding rectangle of the characters in the string specified by text. The bounding rectangle always covers at least the set of pixels the text would cover if drawn at (0, 0).

这篇关于QPainter绘制文本 二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/838551

相关文章

通过C#获取PDF中指定文本或所有文本的字体信息

《通过C#获取PDF中指定文本或所有文本的字体信息》在设计和出版行业中,字体的选择和使用对最终作品的质量有着重要影响,然而,有时我们可能会遇到包含未知字体的PDF文件,这使得我们无法准确地复制或修改文... 目录引言C# 获取PDF中指定文本的字体信息C# 获取PDF文档中用到的所有字体信息引言在设计和出

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

使用Python绘制可爱的招财猫

《使用Python绘制可爱的招财猫》招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常出现在亚洲文化的商店、餐厅和家庭中,今天,我将带你用Python和matplotlib库从零开始绘制一... 目录1. 为什么选择用 python 绘制?2. 绘图的基本概念3. 实现代码解析3.1 设置绘图画

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

如何用Python绘制简易动态圣诞树

《如何用Python绘制简易动态圣诞树》这篇文章主要给大家介绍了关于如何用Python绘制简易动态圣诞树,文中讲解了如何通过编写代码来实现特定的效果,包括代码的编写技巧和效果的展示,需要的朋友可以参考... 目录代码:效果:总结 代码:import randomimport timefrom math

Java操作xls替换文本或图片的功能实现

《Java操作xls替换文本或图片的功能实现》这篇文章主要给大家介绍了关于Java操作xls替换文本或图片功能实现的相关资料,文中通过示例代码讲解了文件上传、文件处理和Excel文件生成,需要的朋友可... 目录准备xls模板文件:template.xls准备需要替换的图片和数据功能实现包声明与导入类声明与

python解析HTML并提取span标签中的文本

《python解析HTML并提取span标签中的文本》在网页开发和数据抓取过程中,我们经常需要从HTML页面中提取信息,尤其是span元素中的文本,span标签是一个行内元素,通常用于包装一小段文本或... 目录一、安装相关依赖二、html 页面结构三、使用 BeautifulSoup javascript

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87