QWiget与其QTreeWidgetItem不对齐

2024-10-03 06:19:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个QTreeWidget,它有一个顶级“分支”和一个秒级“分支”。每个分支都有QTreeWidgetItems,该QTreeWidgetItems具有QLabel和Qpixmap,并且为了节省内存,仅当用户展开分支项目时才生成Qpixmap。此QTtreeWidgetItem的每个子级都获得一个带有setItemWidget()的QLabel,并且PIXMap在另一个线程中生成。当我双击QLabel时,我想打印出公式

当我想在qrunbale生成完成后将每个QPixmap设置为每个相应的QLabel时,就会出现问题。第一次扩建支行时,情况如下:

Bad Tree

请注意每个QTreeWidgetItem的高度是如何正确的(有6个公式,悬停在每个项目上,您可以看到有6个项目,这里我悬停在第五个项目上),双击每个项目会打印出正确的公式(第一个项目的v=s/t,最后一个项目的v2-v02as,即使它没有图像). 但是这里的Qpixmap/Qlabel与QTreeWidgetItems不对齐。再次折叠和展开同一项目将产生:

Correct Tree

突然,由于某种原因,每个QPixmap都对齐了。(另外,我的鼠标在两张图片中的位置相同,悬停在相同的QTreeItemWidget上,双击可打印出相同的公式)

代码如下:

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys

import matplotlib
import matplotlib.pyplot as mpl
from matplotlib.backends.backend_agg import FigureCanvasAgg

from sympy.printing import latex
from sympy.parsing import parse_expr
from sympy import Eq

matplotlib.rcParams["mathtext.fontset"] = "cm"

data = [
    {
        "velocity": ["V", "Velocity of object"]
    },
    {
        "Fysik": {
            "Kinematics": {
                "v = s/t": {},
                "a = v/t": {},
                "v = v0+a*t": {},
                "s = v0*t+(a*t**2)/2": {},
                "s = (v+v0)/2*t": {},
                "v**2-v0**2=2*a*s": {},
            }
        }
    }
]


def mathTex_to_QPixmap(mathTex, fs, fig):
    """
    Create QPixMap from LaTeX
    https://stackoverflow.com/questions/32035251/displaying-latex-in-pyqt-pyside-qtablewidget

    :param mathTex: str
        LaTeX string
    :param fs: int
        Font size
    :param fig: matplotlib.figure.Figure
        Matplotlib Figure
    :return: QPixmap
        QPixMap contaning LaTeX image
    """
    fig.clf()
    fig.patch.set_facecolor("none")
    fig.set_canvas(FigureCanvasAgg(fig))
    renderer = fig.canvas.get_renderer()

    ax = fig.add_axes([0, 0, 1, 1])
    ax.axis("off")
    ax.patch.set_facecolor("none")
    t = ax.text(0, 0, mathTex, ha="left", va="bottom", fontsize=fs)

    fwidth, fheight = fig.get_size_inches()
    fig_bbox = fig.get_window_extent(renderer)

    text_bbox = t.get_window_extent(renderer)

    tight_fwidth = text_bbox.width * fwidth / fig_bbox.width
    tight_fheight = text_bbox.height * fheight / fig_bbox.height

    fig.set_size_inches(tight_fwidth, tight_fheight)

    buf, size = fig.canvas.print_to_buffer()
    qimage = QImage.rgbSwapped(QImage(buf, size[0], size[1], QImage.Format_ARGB32))
    qpixmap = QPixmap(qimage)

    return qpixmap


class LaTeXSignals(QObject):
    finished = pyqtSignal()
    current = pyqtSignal(int)
    output = pyqtSignal(list)


class LaTeXWorker(QRunnable):
    def __init__(self, formula_list):
        super(LaTeXWorker, self).__init__()

        self.formula_list = formula_list
        self.fig = mpl.figure()

        self.signals = LaTeXSignals()

    @pyqtSlot()
    def run(self) -> None:
        """
        Create QPixMap from formulas provided
        by self.formula_list
        :return: list
            List of QPixMap
        """
        out = []
        for i, formula in enumerate(self.formula_list):
            expr = formula.split("=")

            left = parse_expr(expr[0], evaluate=False)
            right = parse_expr(expr[1], evaluate=False)

            latex_pixmap = mathTex_to_QPixmap(
                f"${latex(Eq(left, right))}$",
                15,
                fig=self.fig,
            )

            out.append(latex_pixmap)
            self.signals.current.emit(i)

        self.signals.output.emit(out)
        self.signals.finished.emit()


class Testing(QMainWindow):
    def __init__(self):
        super().__init__()

        self.info = data[0]
        self.formulas = data[1]

        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(1)

        self.init_ui()
        self.add_formulas()
        self.init_bindings()

    def init_ui(self):
        """
        Create UI
        """
        self.FormulaTree = QTreeWidget()
        self.setCentralWidget(self.FormulaTree)

    def add_formulas(self):
        """
        Initialize the Tree
        """
        for branch in self.formulas:
            parent = QTreeWidgetItem(self.FormulaTree)
            parent.setText(0, branch)

            for sub_branch in self.formulas[branch]:
                child = QTreeWidgetItem(parent)
                child.setText(0, sub_branch)

                for formula in self.formulas[branch][sub_branch]:
                    formula_child = QTreeWidgetItem(child)
                    formula_label = QLabel()

                    formula_label.setObjectName(f"{formula}")
                    self.FormulaTree.setItemWidget(formula_child, 0, formula_label)

    def init_bindings(self):
        self.FormulaTree.itemDoubleClicked.connect(self.formula_tree_selected)
        self.FormulaTree.itemExpanded.connect(self.expanded_sub)
        self.FormulaTree.itemCollapsed.connect(self.collapsed_sub)

    def expanded_sub(self, item):
        # Collapse everything else and only expand whatever the user clicked on
        root = self.FormulaTree.invisibleRootItem()
        for i in range(root.childCount()):
            branch = root.child(i)
            for j in range(branch.childCount()):
                sub_branch = branch.child(j)
                if sub_branch != item:
                    self.FormulaTree.collapseItem(sub_branch)

        # Start worker
        if item.parent():
            formulas = [
                self.FormulaTree.itemWidget(item.child(i), 0).objectName()
                for i in range(item.childCount())
            ]
            title = item.text(0)
            total = len(formulas)

            worker = LaTeXWorker(formulas)
            worker.signals.current.connect(lambda current: self.update_current(current, total, title, item))
            worker.signals.output.connect(lambda output: self.set_pixmap(output, item))
            worker.signals.finished.connect(lambda: item.setText(0, title))

            self.threadpool.start(worker)

    def update_current(self, curr, total, title, item):
        """
        Updates sub-branch name
        """
        item.setText(0, f"{title} - Generating LaTeX [{curr}/{total}] {int((curr/total)*100)}%")

    def set_pixmap(self, output, item):
        for i in range(item.childCount()):
            pixmap = output[i]

            qlabel = self.FormulaTree.itemWidget(item.child(i), 0)
            item.child(i).setSizeHint(
                0, QSize(QSizePolicy.Expanding, pixmap.height())
            )
            qlabel.setPixmap(pixmap)

        QPixmapCache.clear()

    def collapsed_sub(self, item):
        """
        In order to save memory, LaTeX QPixmaps are generated when shown
        and cleared once the user clicks on another sub-branch.

        :param item: QTreeWidgetItem
            The item the user clicked at
        :return: None
        """
        if item.parent():
            for i in range(item.childCount()):
                qlabel = self.FormulaTree.itemWidget(item.child(i), 0)
                qlabel.clear()
        QPixmapCache.clear()

    def formula_tree_selected(self):
        """
        Prints formula of selected item
        """""
        selected = self.FormulaTree.selectedItems()
        if selected:
            widget = selected[0]
            qlabel = self.FormulaTree.itemWidget(widget, 0)
            print(qlabel.objectName())

app = QApplication(sys.argv)
_app = Testing()
_app.setFixedSize(QSize(500, 500))
_app.show()
sys.exit(app.exec_())

因为它在第二次尝试时起作用,所以我尝试先设置大小提示,然后设置Pixmap, 导致以下变化:

    def set_pixmap(self, output, item):
        for i in range(item.childCount()):
            pixmap = output[i]

            item.child(i).setSizeHint(
                0, QSize(QSizePolicy.Expanding, pixmap.height())
            )

        for i in range(item.childCount()):
            pixmap = output[i]

            qlabel = self.FormulaTree.itemWidget(item.child(i), 0)
            qlabel.setPixmap(pixmap)

        QPixmapCache.clear()

但是QPixmaps仍然没有对齐

如何使Qpixmap/Qlabel与QTreeWidgetItems正确对齐


Tags: infromimportselfbranchchildforoutput
1条回答
网友
1楼 · 发布于 2024-10-03 06:19:56

musicamante的评论回答了我的问题。以下是我为使其工作所做的:

    def set_pixmap(self, qp_list, item):
        """
        Sets pixmaps to respective QLabels

        :param qp_list: list
            List of QPixmaps
        :param item: QTreeWidgetItem
            QTreeWidgetItem being expanded
        """
        for i in range(item.childCount()):
            pixmap = qp_list[i]

            qlabel = self.FormulaTree.itemWidget(item.child(i), 0)
            item.child(i).setSizeHint(
                0, QSize(self.FormulaTree.width(), pixmap.height())
            )
            qlabel.setPixmap(pixmap)

        # Update geometries after setting QPixmaps
        self.FormulaTree.updateGeometries()

        QPixmapCache.clear()

相关问题 更多 >