PyQt5动态更改SVG颜色
qt自带的库没有发现可以动态更改SVG图片颜色的,也有可能qt自带但是没发现,反正我没发现,所以在这里分享一个
lxml
SVG本质上还是一种xml文本,因此可以使用lxml库来解析。
lxml一个解析库,lxml提供了对xml,HTML,SVG
等标签语言的解析。
有人可能觉得bs4
也可以解析SVG,事实上bs4
只能查看标签的属性或文本。无法修改,因此使用lxml。
SVG虽然和xml与HTML本质是一个东西,但是使用的定位方式却是不同的。
SVG是有一个<svg></svg>
与多个<path></path>
组成,因此直接定位SVG标签与path标签就好。svg标签设置图片的大小与ID,path标签设置内容与颜色。
注: svg无法通过普通的xpath定位,需要改稍微一下定位方式。
获取所有元素://*
获取svg标签://*[name()="svg"]
获取svg标签下的所有path标签://*[name()="svg"]//*[name()="path"]
知道这个后就可以直接用了
from lxml import etree
from lxml.etree import _Element
filepath = '经营计划.svg'
with open(filepath, mode='r', encoding='utf-8') as fp:
svg_data = fp.read()
svg_tree = etree.fromstring(svg_data, etree.XMLParser(remove_blank_text=True)) # type: _Element
print(svg_tree.xpath('//*[name()="svg"]//*[name()="path"]'))
# 输出:
# [<Element {http://www.w3.org/2000/svg}path at 0x23b385e9a40>, <Element {http://www.w3.org/2000/svg}path at 0x23b385e9a80>, <Element {http://www.w3.org/2000/svg}path at 0x23b385e9ac0>]
为了方便稍微封装一下
class SVGParser:
"""
SVG解析器
"""
filepath = None
svg_tree = None
def __init__(self, filepath):
if not Path(filepath).is_file:
raise FileNotFoundError(f'没有此文件: {filepath}')
with open(filepath, mode='r', encoding='utf-8') as fp:
self.svg_data = fp.read()
self.svg_tree = etree.fromstring(self.svg_data, etree.XMLParser(remove_blank_text=True)) # type: _Element
def get_all_element(self):
"""
获取所有元素
"""
return self.svg_tree.xpath('//*')
def get_tag_element(self, tag_name: str, **kwargs):
"""
获取指定元素
"""
xpath_str = f'//*[name()="' + tag_name + '"'
items = kwargs.items()
for key, value in items:
key = key.replace('_', '-')
xpath_str += ' and @' + key + '="' + value + '"'
xpath_str = xpath_str + ']'
elements = self.svg_tree.xpath(xpath_str) # type: _Element
if len(elements) <= 1:
element = elements[0] # type: _Element
return element
return elements
def toString(self):
"""
将dict转为svg文本
"""
return etree.tostring(self.svg_tree, pretty_print=True)
QSvgWidget显示svg图片
使用QSvgWidget显示修改后的数据
import sys
from pathlib import Path
from PyQt5.Qt import *
from lxml import etree
from lxml.etree import _Element
class SVGParser:
"""
SVG解析器
"""
filepath = None
svg_tree = None
def __init__(self, filepath):
if not Path(filepath).is_file:
raise FileNotFoundError(f'没有此文件: {filepath}')
with open(filepath, mode='r', encoding='utf-8') as fp:
self.svg_data = fp.read()
self.svg_tree = etree.fromstring(self.svg_data, etree.XMLParser(remove_blank_text=True)) # type: _Element
def get_all_element(self):
"""
获取所有元素
"""
return self.svg_tree.xpath('//*')
def get_tag_element(self, tag_name: str, **kwargs):
"""
获取指定元素
"""
xpath_str = f'//*[name()="' + tag_name + '"'
items = kwargs.items()
for key, value in items:
key = key.replace('_', '-')
xpath_str += ' and @' + key + '="' + value + '"'
xpath_str = xpath_str + ']'
elements = self.svg_tree.xpath(xpath_str) # type: _Element
if len(elements) <= 1:
element = elements[0] # type: _Element
return element
return elements
def toString(self):
"""
将dict转为svg文本
"""
return etree.tostring(self.svg_tree, pretty_print=True)
class SvgWidget(QSvgWidget):
def __init__(
self,
filepath, parent=None, *,
default_color: str = '#000000',
hover_color: str = '#ff3d00',
press_color: str = '#6c216d'
):
super(SvgWidget, self).__init__(parent)
# 初始化颜色
self.default_color = default_color # 默认颜色
self.hover_color = hover_color # 鼠标经过颜色
self.press_color = press_color # 鼠标点击颜色
self.isHover = False # 鼠标是否经过
self.isPress = False # 鼠标是否点击
self.svg_data = SVGParser(filepath)
self.svg_tag = self.svg_data.get_tag_element('svg')
self.path_tags = self.svg_data.get_tag_element('path')
self.setSvgColor(self.default_color, self.hover_color, self.press_color)
self.initSvgData(self.default_color)
def initSvgData(self, color: str):
# 初始化svg数据,并进行显示
for tag in self.path_tags:
tag.set('fill', color)
self.renderer().load(QByteArray(self.svg_data.toString()))
self.update()
def setSvgColor(self, default_color: str, hover_color: str, press_color: str):
"""
svg图片颜色
二进制颜色:#a0610e;
英文颜色:red
"""
self.default_color = default_color
self.hover_color = hover_color
self.press_color = press_color
self.update()
def resizeEvent(self, evt: QResizeEvent) -> None:
"""
窗口大小事件
"""
self.svg_tag.set('width', str(evt.size().width()))
self.svg_tag.set('height', str(evt.size().height()))
def enterEvent(self, a0: QEvent) -> None:
"""
鼠标进入事件
"""
self.isHover = True
if self.isHover:
self.initSvgData(self.hover_color)
def leaveEvent(self, a0: QEvent) -> None:
"""
鼠标离开事件
"""
self.isHover = False
if self.isHover is False:
self.initSvgData(self.default_color)
def mousePressEvent(self, a0: QMouseEvent) -> None:
"""
鼠标点击事件
"""
self.isPress = True
if self.isPress and self.isHover:
self.initSvgData(self.press_color)
def mouseReleaseEvent(self, a0: QMouseEvent) -> None:
"""
鼠标松开事件
"""
self.isPress = False
if self.isPress is False:
self.initSvgData(self.hover_color)
if __name__ == '__main__':
QApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
win = SvgWidget('经营计划.svg')
win.show()
sys.exit(app.exec_())