在PySide6中使用TextObject绘制LaTeX图像
我想在一个pyside6应用中使用TextObjectInterface绘制渲染后的LaTeX,其中对象以TextObject存储,LaTeX在其上绘制。我希望避免TextImage,因为它可以工作,但在删除旧的LaTeX公式并添加新公式时,资源缓存中的bmp会累积。下面的代码不能在LaTeX图像前后只生成文本,图像替换处没有显示任何内容。我已经安装了TeX发行版。使用TextImage时一切都正常,也没有错误信息。我正在使用Python 3.13
import sys
from io import BytesIO
from PySide6.QtWidgets import QApplication, QTextEdit
from PySide6.QtGui import (
QTextObjectInterface,
QTextFormat,
QTextCharFormat,
QImage,
QPainter,
QColor
)
from PySide6.QtCore import QSizeF, QObject, Qt
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
LATEX_FORMAT = QTextFormat.UserObject + 1
class LatexRenderer(QObject, QTextObjectInterface):
def __init__(self):
super().__init__()
self._cache = {}
def intrinsicSize(self, doc, posInDocument, format):
latex = format.property(1)
img = self.render_latex(latex)
return QSizeF(img.width(), img.height())
def drawObject(self, painter, rect, doc, posInDocument, format):
latex = format.property(1)
img = self.render_latex(latex)
# DEBUG: Draw a light gray background so we can see the "box"
painter.fillRect(rect, QColor("#f0f0f0"))
painter.drawImage(rect.topLeft(), img)
def render_latex(self, latex):
if latex in self._cache:
return self._cache[latex]
# Force a specific DPI and small figure
fig = Figure(figsize=(1, 1), dpi=100)
canvas = FigureCanvas(fig)
fig.patch.set_alpha(0)
# Using 'mathtext' specifically
fig.text(0.5, 0.5, f"${latex}$",
fontsize=20,
ha='center',
va='center',
color="black")
buf = BytesIO()
# 'tight' is essential, but let's add a small pad to be safe
fig.savefig(buf, format='png', bbox_inches='tight', pad_inches=0.1, transparent=True)
img = QImage()
img.loadFromData(buf.getvalue(), "PNG")
self._cache[latex] = img
return img
class MainWindow(QTextEdit):
def __init__(self):
super().__init__()
# 1. Register handler BEFORE adding text
self.renderer = LatexRenderer()
self.document().documentLayout().registerHandler(LATEX_FORMAT, self.renderer)
# 2. Insert text and math
cursor = self.textCursor()
cursor.insertText("The Pythagorean theorem is: ")
# Use a RAW string for LaTeX
self.insert_latex(cursor, r"a^2 + b^2 = c^2")
cursor.insertText("\n\nAnd the Quadratic formula: ")
self.insert_latex(cursor, r"x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}")
def insert_latex(self, cursor, latex):
fmt = QTextCharFormat()
fmt.setObjectType(LATEX_FORMAT)
fmt.setProperty(1, latex)
cursor.insertText("\uFFFC", fmt)
if __name__ == "__main__":
app = QApplication(sys.argv)
# Removing the deprecated HighDPI line as it's now default
w = MainWindow()
w.resize(600, 400)
w.show()
sys.exit(app.exec())
我希望程序能在屏幕上显示渲染后的公式,例如:
a^2 + b^2 = c^2
但没有任何显示。我使用QTextObject将 LaTeX图像直接绘制在包含数学表达式的对象上。
新编辑:
我需要Python 3.13以用于Gemini API功能,因此我不能使用PyQt6,因为它无法安装。
请推荐最适合用于QTextObject与 Gemini API的 Python版本。
编辑: 我不知怎的安装了PyQt6,执行命令pip install PyQt6 --only-binary=:all,然后运行PyQt6的版本,工作了。猜测PySide的版本有bug。
编辑: 我正在使用Ubuntu 20.04 LTS(懒得升级Ubuntu)。
解决方案
Code works correctly with PyQt6 but it has some problem with PySide6
but it doesn't show any error messages.
不过正如 @musicamante所建议的,它在 PySide6 与 QPyTextObject 下可以工作
class LatexRenderer(QPyTextObject):
也使用 QPyTextObject 的示例:TextObject Example - Qt for Python
针对 QPyTextObject 的C++源代码 qpytextobject.h, qpytextobject.cpp
但没有什么特别之处。
在以下环境测试
Python: 3.13.12
PySide: 6.11.0
Qt: 6.11.0
LatexRenderer: ['QPyTextObject']
Linux Mint 22 (with Mate)
用于测试的完整代码:
还有针对 genai 的代码,因为我在测试它是否能在其他版本的Python上运行。
# --- info ---
import sys
from PySide6.QtCore import __version__, qVersion
print("Python:", sys.version)
print("PySide:", __version__)
print("Qt:", qVersion())
# ---
import sys
from io import BytesIO
from PySide6.QtWidgets import QApplication, QTextEdit
from PySide6.QtGui import (
QTextObjectInterface,
QTextFormat,
QTextCharFormat,
QImage,
QPainter,
QColor,
)
from PySide6.QtCore import QSizeF, QObject, Qt
# from PySide6.QtGui import QTextObject
from PySide6.QtGui import QPyTextObject
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
LATEX_FORMAT = QTextFormat.ObjectTypes.UserObject + 1
# class LatexRenderer(QObject, QTextObjectInterface): # run but not display
# class LatexRenderer(QTextObjectInterface): # raise error during registration
# class LatexRenderer(QTextObject): # raise error because QTextObject needs some parameter
class LatexRenderer(QPyTextObject): # it works!
def __init__(self):
print("LatexRenderer:", [x.__name__ for x in self.__class__.__bases__])
super().__init__()
self._cache = {}
def intrinsicSize(self, doc, posInDocument, format):
# print("intrinsicSize")
latex = format.property(1)
img = self.render_latex(latex)
# print("size:", img.width(), img.height())
return QSizeF(img.width(), img.height())
def drawObject(self, painter, rect, doc, posInDocument, format):
# print("drawObject")
latex = format.property(1)
img = self.render_latex(latex)
# DEBUG: Draw a light gray background so we can see the "box"
painter.fillRect(rect, QColor("#f0f0f0"))
painter.drawImage(rect.topLeft(), img)
def render_latex(self, latex):
# print("render_latex")
if latex in self._cache:
return self._cache[latex]
# Force a specific DPI and small figure
fig = Figure(figsize=(1, 1), dpi=100)
canvas = FigureCanvas(fig)
fig.patch.set_alpha(0)
# Using 'mathtext' specifically
fig.text(
0.5, 0.5, f"${latex}$", fontsize=20, ha="center", va="center", color="black"
)
buf = BytesIO()
# 'tight' is essential, but let's add a small pad to be safe
fig.savefig(
buf, format="png", bbox_inches="tight", pad_inches=0.1, transparent=True
)
img = QImage()
img.loadFromData(buf.getvalue(), "PNG")
self._cache[latex] = img
return img
class MainWindow(QTextEdit):
def __init__(self):
super().__init__()
# 1. Register handler BEFORE adding text
self.renderer = LatexRenderer()
self.document().documentLayout().registerHandler(LATEX_FORMAT, self.renderer)
# 2. Insert text and math
cursor = self.textCursor()
cursor.insertText("The Pythagorean theorem is: ")
# Use a RAW string for LaTeX
self.insert_latex(cursor, r"a^2 + b^2 = c^2")
cursor.insertText("\n\nAnd the Quadratic formula: ")
self.insert_latex(cursor, r"x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}")
def insert_latex(self, cursor, latex):
# print("insert_latex:", cursor, latex)
fmt = QTextCharFormat()
fmt.setObjectType(LATEX_FORMAT)
fmt.setProperty(1, latex)
cursor.insertText("\ufffc", fmt)
def API():
from google import genai
try:
print("=== GenAI ===")
client = genai.Client()
question = "Explain how AI works in a few words"
print("Question:", question)
response = client.models.generate_content(
model="gemini-3-flash-preview",
contents=question,
)
answer = response.text
print("Answer:", answer)
except Exception as ex:
print("Ex:", ex)
if __name__ == "__main__":
# API()
app = QApplication(sys.argv)
# Removing the deprecated HighDPI line as it's now default
w = MainWindow()
w.resize(600, 400)
w.show()
sys.exit(app.exec())