pyqtgraph中的textItem缩放

发布于 2025-02-10 11:02:38 字数 2249 浏览 2 评论 0原文

我在pyqtgraph中的字体缩放率有问题,就像我在主图中缩放/缩放时从以下代码中看到的那样以QGraphicsRectitem的确切方式(速率)比例。我试图查看我认识的所有论坛,但我没有找到答案,所以我真的希望有人为此提供解决方案。

import sys
import pyqtgraph as pg
from PyQt6.QtWidgets import QApplication, QGraphicsRectItem
from pyqtgraph.Qt import QtCore

app = QApplication(sys.argv)
view = pg.GraphicsView()
l = pg.GraphicsLayout()
view.setCentralItem(l)
view.show()
view.resize(800, 600)

p0 = l.addPlot(0, 0)
p0.showGrid(x=True, y=True, alpha=1.0)

# have no x-axis tickmark below the upper plot (coordinate 0,0)
# without these lines, there will be separate coordinate systems with a gap inbetween
ay0 = p0.getAxis('left')      # get handle to y-axis 0
ay0.setStyle(showValues=False)  # this will remove the tick labels and reduces gap b/w plots almost to zero
                                # there will be a double line separating the plot rows
# ay02 = p0.getAxis('right')
# ay02.setStyle(showValues=False)
p0.hideAxis('right')
ax02 = p0.getAxis('top')
ax02.setStyle(showValues=False)

p1 = l.addPlot(0, 1)


# p1.showGrid(x=True, y=True, alpha=1.0)

p1.setYLink(p0)

l.layout.setSpacing(0.5)
l.setContentsMargins(0., 0., 0., 0.)

p1.setFixedWidth(300)
# p1.setFixedHeight(h-451)

p1.setMouseEnabled(x=False)

# ay1 = p1.getAxis('left')
# ay1.setStyle(showValues=False)
ax12 = p1.getAxis('top')
ax12.setStyle(showValues=False)
# ax1 = p1.getAxis('bottom')
# ax1.setStyle(showValues=False)
p1.showAxis('right')
p1.hideAxis('left')
p1.setXRange(0, 6, padding=0)   # Then add others like 1 pip

# p1.getAxis('bottom').setTextPen('black')

board = ['123456',
         'abcdef',
         'ghilmn']


def draw_board(board2):
    for j, row in enumerate(board2):
        for i, cell in enumerate(row):
            rect_w = 1
            rect_h = 1
            r = QGraphicsRectItem(i, -j+2, rect_w, rect_h)
            r.setPen(pg.mkPen((0, 0, 0, 100)))
            r.setBrush(pg.mkBrush((50, 50, 200)))
            p1.addItem(r)

            t_up = pg.TextItem(cell, (255, 255, 255), anchor=(0, 0))
            t_up.setPos(i, -j+1+2)
            p1.addItem(t_up)


draw_board(board)

if __name__ == '__main__':
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QApplication.instance().exec()

I'm having a problem with the font scaling of TextItems in pyqtgraph, like you can see from the following code when I zoom in/zoom out in the main graph the font of the TextItems stays the same while I'm trying to make It scale in the same exact way (rate) of the QGraphicsRectItem. I've tried to look on all the forums I know but I haven't find an answer so I really hope someone has a solution for this.

import sys
import pyqtgraph as pg
from PyQt6.QtWidgets import QApplication, QGraphicsRectItem
from pyqtgraph.Qt import QtCore

app = QApplication(sys.argv)
view = pg.GraphicsView()
l = pg.GraphicsLayout()
view.setCentralItem(l)
view.show()
view.resize(800, 600)

p0 = l.addPlot(0, 0)
p0.showGrid(x=True, y=True, alpha=1.0)

# have no x-axis tickmark below the upper plot (coordinate 0,0)
# without these lines, there will be separate coordinate systems with a gap inbetween
ay0 = p0.getAxis('left')      # get handle to y-axis 0
ay0.setStyle(showValues=False)  # this will remove the tick labels and reduces gap b/w plots almost to zero
                                # there will be a double line separating the plot rows
# ay02 = p0.getAxis('right')
# ay02.setStyle(showValues=False)
p0.hideAxis('right')
ax02 = p0.getAxis('top')
ax02.setStyle(showValues=False)

p1 = l.addPlot(0, 1)


# p1.showGrid(x=True, y=True, alpha=1.0)

p1.setYLink(p0)

l.layout.setSpacing(0.5)
l.setContentsMargins(0., 0., 0., 0.)

p1.setFixedWidth(300)
# p1.setFixedHeight(h-451)

p1.setMouseEnabled(x=False)

# ay1 = p1.getAxis('left')
# ay1.setStyle(showValues=False)
ax12 = p1.getAxis('top')
ax12.setStyle(showValues=False)
# ax1 = p1.getAxis('bottom')
# ax1.setStyle(showValues=False)
p1.showAxis('right')
p1.hideAxis('left')
p1.setXRange(0, 6, padding=0)   # Then add others like 1 pip

# p1.getAxis('bottom').setTextPen('black')

board = ['123456',
         'abcdef',
         'ghilmn']


def draw_board(board2):
    for j, row in enumerate(board2):
        for i, cell in enumerate(row):
            rect_w = 1
            rect_h = 1
            r = QGraphicsRectItem(i, -j+2, rect_w, rect_h)
            r.setPen(pg.mkPen((0, 0, 0, 100)))
            r.setBrush(pg.mkBrush((50, 50, 200)))
            p1.addItem(r)

            t_up = pg.TextItem(cell, (255, 255, 255), anchor=(0, 0))
            t_up.setPos(i, -j+1+2)
            p1.addItem(t_up)


draw_board(board)

if __name__ == '__main__':
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QApplication.instance().exec()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

╭⌒浅淡时光〆 2025-02-17 11:02:39

文本项目的缩放非常困难,因为您需要考虑基础尺度的恒定长宽比,并且与字体相对于原点点的定位和绘制方式相关的问题。

假设显示的文本将 总是 是一个字符,并且使用的字符是标准的ASCII字母和数字,唯一的可能性是循环浏览所有可能的字符,并创建每个人都适当对齐路径。

因此,对于每个角色:

  • 构建QPainterPath;
  • 将字母添加到路径上;
  • 获取该路径宽度和其他路径宽度的max()
  • 获取边界矩形的最小y和最大底部
  • 基于上面计算的所有其他值(在单独的循环中)转换路径;

然后,您必须为字母设置一个参考大小(使用上面的最大宽度和字体指标的高度),并获取该大小的长宽比。

最后一部分是在QgraphicsRectitem子类的paint()函数中实现的,该功能是获得项目的正确几何形状所需的(如果将任何转换应用于父项目,则该项目将不知道它),并根据当前矩形大小获取参考大小的最大矩形。

class NumberRectItem(QGraphicsRectItem):
    textSize = None
    textPaths = {}
    textPath = None
    def __init__(self, x, y, width, height, letter=''):
        super().__init__(x, y, width, height)
        if letter:
            if not self.textPaths:
                self._buildTextPaths()
            self.textPath = self.textPaths[letter]

    def _buildTextPaths(self):
        from string import ascii_letters, digits
        font = QApplication.font()
        fm = QFontMetricsF(font)
        maxWidth = 0
        minY = 1000
        maxY = 0
        for l in ascii_letters + digits:
            path = QPainterPath()
            path.addText(0, 0, font, l)
            br = path.boundingRect()
            maxWidth = max(maxWidth, br.width())
            minY = min(minY, br.y())
            maxY = max(maxY, br.bottom())
            self.textPaths[l] = path
        self.__class__.textSize = QSizeF(maxWidth, fm.height())
        self.__class__.textRatio = self.textSize.height() / self.textSize.width()

        middle = minY + (maxY - minY) / 2
        for path in self.textPaths.values():
            path.translate(
                -path.boundingRect().center().x(), 
                -middle)

    def paint(self, qp, opt, widget=None):
        super().paint(qp, opt, widget)
        if not self.textPath:
            return
        qp.save()
        qp.resetTransform()
        view = widget.parent()
        sceneRect = self.mapToScene(self.rect())
        viewRect = view.mapFromScene(sceneRect).boundingRect()
        rectSize = QSizeF(viewRect.size())
        newSize = self.textSize.scaled(rectSize, Qt.KeepAspectRatio)
        if newSize.width() == rectSize.width():
            # width is the maximum
            ratio = newSize.width() / self.textSize.width()
        else:
            ratio = newSize.height() / self.textSize.height()
        transform = QTransform().scale(ratio, ratio)
        path = transform.map(self.textPath)

        qp.setRenderHint(qp.Antialiasing)
        qp.setPen(Qt.NoPen)
        qp.setBrush(Qt.white)
        qp.drawPath(path.translated(viewRect.center()))

        qp.restore()


def draw_board(board2):
    for j, row in enumerate(board2):
        for i, cell in enumerate(row):
            rect_w = 1
            rect_h = 1
            r = NumberRectItem(i, -j+2, rect_w, rect_h, letter=cell)
            r.setPen(pg.mkPen((150, 0, 0, 255)))
            r.setBrush(pg.mkBrush((50, 50, 200, 128)))
            p1.addItem(r)

注意:对于PYQT6,您需要使用完整的枚举名称:qt.globalcolor.white等。

Scaling of a text item is quite difficult, as you need to consider a constant aspect ratio of the base scale, and the problems related to the way fonts are positioned and drawn relative to the origin point.

Assuming that the displayed text will always be a single character and that the characters used are standard ascii letters and numbers, the only possibility is to cycle through all possible characters, and create properly aligned paths for each of them.

So, for every character:

  • construct a QPainterPath;
  • add the letter to the path;
  • get the max() of that path width and the others;
  • get the minimum Y and maximum bottom of the bounding rectangle;
  • translate the path based on all other values computed above (in a separate loop);

Then, you have to set a reference size for the letter (using the maximum width above and the font metrics' height) and get the aspect ratio for that size.

The last part is implemented in the paint() function of the QGraphicsRectItem subclass, which is required to get the proper geometry of the item (if any transformation is applied to a parent item, the item will not know it), and get the maximum rectangle for the reference size based on the current rectangle size.

class NumberRectItem(QGraphicsRectItem):
    textSize = None
    textPaths = {}
    textPath = None
    def __init__(self, x, y, width, height, letter=''):
        super().__init__(x, y, width, height)
        if letter:
            if not self.textPaths:
                self._buildTextPaths()
            self.textPath = self.textPaths[letter]

    def _buildTextPaths(self):
        from string import ascii_letters, digits
        font = QApplication.font()
        fm = QFontMetricsF(font)
        maxWidth = 0
        minY = 1000
        maxY = 0
        for l in ascii_letters + digits:
            path = QPainterPath()
            path.addText(0, 0, font, l)
            br = path.boundingRect()
            maxWidth = max(maxWidth, br.width())
            minY = min(minY, br.y())
            maxY = max(maxY, br.bottom())
            self.textPaths[l] = path
        self.__class__.textSize = QSizeF(maxWidth, fm.height())
        self.__class__.textRatio = self.textSize.height() / self.textSize.width()

        middle = minY + (maxY - minY) / 2
        for path in self.textPaths.values():
            path.translate(
                -path.boundingRect().center().x(), 
                -middle)

    def paint(self, qp, opt, widget=None):
        super().paint(qp, opt, widget)
        if not self.textPath:
            return
        qp.save()
        qp.resetTransform()
        view = widget.parent()
        sceneRect = self.mapToScene(self.rect())
        viewRect = view.mapFromScene(sceneRect).boundingRect()
        rectSize = QSizeF(viewRect.size())
        newSize = self.textSize.scaled(rectSize, Qt.KeepAspectRatio)
        if newSize.width() == rectSize.width():
            # width is the maximum
            ratio = newSize.width() / self.textSize.width()
        else:
            ratio = newSize.height() / self.textSize.height()
        transform = QTransform().scale(ratio, ratio)
        path = transform.map(self.textPath)

        qp.setRenderHint(qp.Antialiasing)
        qp.setPen(Qt.NoPen)
        qp.setBrush(Qt.white)
        qp.drawPath(path.translated(viewRect.center()))

        qp.restore()


def draw_board(board2):
    for j, row in enumerate(board2):
        for i, cell in enumerate(row):
            rect_w = 1
            rect_h = 1
            r = NumberRectItem(i, -j+2, rect_w, rect_h, letter=cell)
            r.setPen(pg.mkPen((150, 0, 0, 255)))
            r.setBrush(pg.mkBrush((50, 50, 200, 128)))
            p1.addItem(r)

Note: for PyQt6 you need to use the full enum names: Qt.GlobalColor.white, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文