使用自定义颜色渲染SVG(Qt)

huangapple go评论72阅读模式
英文:

(Qt) Render SVG with custom color

问题

要通过选择绘制颜色在QPixmap中渲染SVG图标可以使用以下PyQt代码

```python
def svg_to_pixmap(svg_filename: str, width: int, height: int, color: QColor) -> QPixmap:
    renderer = QSvgRenderer(svg_filename)
    pixmap = QPixmap(width, height)
    pixmap.fill(Qt.GlobalColor.transparent)
    painter = QPainter(pixmap)
    painter.setPen(QPen(color))
    renderer.render(painter)
    painter.end()
    return pixmap

然后使用以下测试代码显示pixmap:

app = QApplication([])
pixmap = svg_to_pixmap("test.svg", 512, 512, Qt.GlobalColor.red)
label = QLabel()
label.setStyleSheet("background-color: yellow;")
label.setPixmap(pixmap)
label.show()
app.exec()

问题在于painter.setPen没有预期的效果(绘制保持为黑色)。背景是透明的,可以看到标签背后的背景颜色。可用于测试的SVG文件示例在这里

我的配置:Ubuntu 22.10,X11,PyQt 6.3.1


<details>
<summary>英文:</summary>

I want to render a SVG icon in a QPixmap by choosing the drawing color.

This is my code using PyQt:

def svg_to_pixmap(svg_filename: str, width: int, height: int, color: QColor) -> QPixmap:
renderer = QSvgRenderer(svg_filename)
pixmap = QPixmap(width, height)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
painter.setPen(QPen(color))
renderer.render(painter)
painter.end()
return pixmap


And a test code to display the pixmap:

app = QApplication([])
pixmap = svg_to_pixmap("test.svg", 512, 512, Qt.GlobalColor.red)
label = QLabel()
label.setStyleSheet("background-color: yellow;")
label.setPixmap(pixmap)
label.show()
app.exec()


The issue is that painter.setPen has no effect as expected (the drawing remains black). The background is transparent as expected and we can see the label background color behind.
An example of SVG file to test [here](https://fonts.google.com/icons?selected=Material%20Symbols%20Outlined%3Adelete%3AFILL%400%3Bwght%40400%3BGRAD%400%3Bopsz%4048)

My configuration: Ubuntu22.10, X11, PyQt6.3.1

</details>


# 答案1
**得分**: 3

答案并不简单。但只要您确信您只想使用原始SVG的*形状*(或*掩码*),那么是可行的。

设置画笔不改变结果的原因是SVG是一种矢量图像格式:通常明确告诉要绘制的任何东西的颜色,因此设置绘图笔几乎是无用的,除非SVG内容太简单以至于*不*指定它,这显然不是情况:图标通常定义它们形状的颜色,有很好的理由。

问题出在尝试理解[*合成*](https://en.wikipedia.org/wiki/Compositing)以及QPainter提供的可能的替代方案。

说实话,即使我努力理解结果并将其与[`QPainter.CompositionMode`](https://doc.qt.io/qt-5/qpainter.html#CompositionMode-enum)枚举关联起来,我仍然需要进行测试和检查以了解我需要的适当模式。

你可能需要的是`CompositionMode_SourceIn`,它在[文档](https://doc.qt.io/qt-5/qpainter.html#CompositionMode-enum)中以神秘的方式解释:

> 输出是源,其中 alpha 减少了目标的 alpha。

据我理解,**源**是将要绘制的内容,而**目标**是*先前*绘制的内容(将源视为画刷绘制,目标视为绘制的画布)。

考虑到这一点,我们需要使用原始SVG作为*目标*,并用所需的颜色填充整个像素图。由于只使用了目标的 alpha 值(SVG的可见部分),我们实际上在执行某种类型的"模板"操作。

```py
def svg_to_pixmap(svg_filename: str, width: int, height: int, color: QColor) -> QPixmap:
    renderer = QSvgRenderer(svg_filename)
    pixmap = QPixmap(width, height)
    pixmap.fill(Qt.GlobalColor.transparent)
    painter = QPainter(pixmap)
    renderer.render(painter) # 这是目标,只使用其 alpha 值!
    painter.setCompositionMode(
        painter.CompositionMode.CompositionMode_SourceIn)
    painter.fillRect(pixmap.rect(), color)
    painter.end()
    return pixmap

这似乎可以得到所需的结果。

使用自定义颜色渲染SVG(Qt)

英文:

The answer is not simple. But, as long as you are completely sure that you only want to use the shape (or mask) of the original SVG, then it is feasible.

The reason for which setting the pen doesn't change the result is that SVG is a vector image format: it usually explicitly tells the colors of anything it wants to draw, so, setting the painter pen is almost useless, unless the SVG content is so simple to not specify it, which is clearly not the case: icons normally define colors of their shapes, and for good reasons.

The problem comes when trying to understand compositing and the possible alternatives that QPainter provides.

To be honest, even after trying to put efforts in understanding the results and associating them with the QPainter.CompositionMode enums, I still need to testing and checking in order to understand the proper mode I need.

What you probably need is the CompositionMode_SourceIn, which is cryptically explained in the documentation:

> The output is the source, where the alpha is reduced by that of the destination.

As far as I can understand, the source is what is going to be painted, while the destination is what was previously painted (consider the source as a brush painting, and the destination as the canvas on which you paint).

With that in mind, we need to use the original svg as the destination and fill the whole pixmap with the color we want. Since only the alpha of the destination will be used (the visible part of the svg), we will be practically doing some sort of "stencil".

def svg_to_pixmap(svg_filename: str, width: int, height: int, color: QColor) -&gt; QPixmap:
    renderer = QSvgRenderer(svg_filename)
    pixmap = QPixmap(width, height)
    pixmap.fill(Qt.GlobalColor.transparent)
    painter = QPainter(pixmap)
    renderer.render(painter) # this is the destination, and only its alpha is used!
    painter.setCompositionMode(
        painter.CompositionMode.CompositionMode_SourceIn)
    painter.fillRect(pixmap.rect(), color)
    painter.end()
    return pixmap

Which seems to give the wanted result:

使用自定义颜色渲染SVG(Qt)

huangapple
  • 本文由 发表于 2023年2月14日 03:30:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75440429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定