交换图像时保持 QGraphicsView 中的视图/滚动位置

发布于 2024-12-28 04:22:17 字数 3006 浏览 0 评论 0原文

我在使用 QGraphicsPixmapItem 缩放加载到 QGraphicsView 中的 TIFF 图像时遇到问题。

问题更多的是保持图像质量以及不会使应用程序阻塞的缩放速度。首先,我只是用缩放的 QPixmap 替换图像 - 当用户按住水平滑块时,我使用 Qt.FastTransformation ,然后当释放滑块时替换图像使用 Qt.SmoothTransformation 再次使用另一个缩放的像素图。这提供了质量很好的缩放图像,但在图像尺寸开始增加到大于其原始尺寸后,缩放变得不稳定;缩小图像很好。

在 QGraphicsView 上使用 QTransform.fromScale() 可以提供更平滑的缩放,但图像质量较低,即使将 .setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.HighQualityAntialiasing) 应用于 QGraphicsView 时也是如此。

我最新的方法是将这两种方法结合起来,并在 QGraphicsView 上使用 QTransform 来实现平滑缩放,但是当用户释放滑块时替换 中的图像QGraphicsView 带有缩放的像素图。这很好用,但视图中的位置会丢失 - 用户放大到一个区域,并且由于缩放后的像素图更大,因此当释放滑块时视图会跳转到另一个位置,并且更高质量的缩放后的像素图会替换前一个图像。

我认为由于两个图像的宽度高度比相同,我可以在图像交换之前获取滚动条的百分比,并在交换之后应用相同的百分比,事情应该会顺利进行。这在大多数情况下效果很好,但有时在交换图像后视图仍然会“跳跃”。

我很确定我在这里做了一些非常错误的事情。有谁知道更好的方法来做到这一点,或者有人可以在下面的代码中发现一些可能导致这种跳跃的东西吗?

这是保存/恢复滚动条位置的代码。它们是 QGraphicsView 子类的方法:

def store_scrollbar_position(self):
    x_max = self.horizontalScrollBar().maximum()
    if x_max:
        x = self.horizontalScrollBar().sliderPosition()
        self.scroll_x_percentage = x * (100 / float(x_max))

    y_max = self.verticalScrollBar().maximum()
    if y_max:
        y = self.verticalScrollBar().sliderPosition()
        self.scroll_y_percentage = y * (100 / float(y_max))


def restore_scrollbar_position(self):
    x_max = self.horizontalScrollBar().maximum()
    if self.scroll_x_percentage and x_max:
        x = x_max * (float(self.scroll_x_percentage) / 100)
        self.horizontalScrollBar().setSliderPosition(x)

    y_max = self.verticalScrollBar().maximum()
    if self.scroll_y_percentage and y_max:
        y = y_max * (float(self.scroll_y_percentage) / 100)
        self.verticalScrollBar().setSliderPosition(y)

以下是我进行缩放的方法。 self.imageFile 是一个QPixmapself.image 是我的QGraphicsPixmapItem。同样,它是 QGraphicsView 子类的一部分。该方法附加到滑块移动,并将 highQuality 参数设置为 False。在滑块释放时再次调用它,并将 highQuality 设置为 True 来交换图像。

def setImageScale(self, scale=None, highQuality=True):
    if self.imageFile.isNull():
        return
    if scale is None:
        scale = self.scale
    self.scale = scale

    self.image.setPixmap(self.imageFile)
    self.scene.setSceneRect(self.image.boundingRect())
    self.image.setPos(0, 0)
    if not highQuality:
        self.setTransform(QTransform.fromScale(self.scaleFactor, self.scaleFactor))
        self.store_scrollbar_position()
    else:
        self.image.setPixmap(self.imageFile.scaled(self.scaleFactor * self.imageFile.size(),
                                                   Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self.setTransform(self.transform)
        self.scene.setSceneRect(self.image.boundingRect())
        self.image.setPos(0, 0)
        self.restore_scrollbar_position()
    return

任何帮助将不胜感激。我现在开始对此感到非常沮丧。

I'm having trouble with zooming TIFF images loaded into a QGraphicsView with QGraphicsPixmapItem.

The problem is more maintaining image quality along with having a zoom speed that doesn't make the application choke. To begin with I was just replacing the image with a scaled QPixmap - I used Qt.FastTransformation while the user was holding down a horizontal slider and then when the slider was released replaced the pixmap again with another scaled pixmap using Qt.SmoothTransformation. This gave a nice quality zoomed image but the zooming was jerky after the image size started to increase to larger than its original size; zooming out of the image was fine.

Using QTransform.fromScale() on the QGraphicsView gives much smoother zooming but a lower quality image, even when applying .setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.HighQualityAntialiasing) to the QGraphicsView.

My latest approach is to combine the two methods and use a QTransform on the QGraphicsView to have the smooth zooming but when the user releases the slider replace the image in the QGraphicsView with a scaled pixmap. This works great, but the position in the view is lost - the user zooms in to one area and because the scaled pixmap is larger the view jumps to another location when the slider is released and the higher quality scaled pixmap replaces the previous image.

I figured that as the width height ratio is the same in both images I could take the percentages of the scrollbars before the image swap and apply the same percentages after the swap and things should work out fine. This works well mostly, but there are still times when the view 'jumps' after swapping the image.

I'm pretty sure I'm doing something quite wrong here. Does anybody know of a better way to do this, or can anyone spot something in the code below that could cause this jumping?

This is the code to save/restore the scrollbar location. They are methods of a subclassed QGraphicsView:

def store_scrollbar_position(self):
    x_max = self.horizontalScrollBar().maximum()
    if x_max:
        x = self.horizontalScrollBar().sliderPosition()
        self.scroll_x_percentage = x * (100 / float(x_max))

    y_max = self.verticalScrollBar().maximum()
    if y_max:
        y = self.verticalScrollBar().sliderPosition()
        self.scroll_y_percentage = y * (100 / float(y_max))


def restore_scrollbar_position(self):
    x_max = self.horizontalScrollBar().maximum()
    if self.scroll_x_percentage and x_max:
        x = x_max * (float(self.scroll_x_percentage) / 100)
        self.horizontalScrollBar().setSliderPosition(x)

    y_max = self.verticalScrollBar().maximum()
    if self.scroll_y_percentage and y_max:
        y = y_max * (float(self.scroll_y_percentage) / 100)
        self.verticalScrollBar().setSliderPosition(y)

And here is how I'm doing the scaling. self.imageFile is a QPixmap and self.image is my QGraphicsPixmapItem. Again, part of a subclassed QGraphicsView. The method is attached to the slider movement with the highQuality parameter set to False. It is called again on slider release with highQuality as True to swap the image.

def setImageScale(self, scale=None, highQuality=True):
    if self.imageFile.isNull():
        return
    if scale is None:
        scale = self.scale
    self.scale = scale

    self.image.setPixmap(self.imageFile)
    self.scene.setSceneRect(self.image.boundingRect())
    self.image.setPos(0, 0)
    if not highQuality:
        self.setTransform(QTransform.fromScale(self.scaleFactor, self.scaleFactor))
        self.store_scrollbar_position()
    else:
        self.image.setPixmap(self.imageFile.scaled(self.scaleFactor * self.imageFile.size(),
                                                   Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self.setTransform(self.transform)
        self.scene.setSceneRect(self.image.boundingRect())
        self.image.setPos(0, 0)
        self.restore_scrollbar_position()
    return

Any help would be appreciated. I'm starting to get quite frustrated with this now.

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

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

发布评论

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

评论(1

━╋う一瞬間旳綻放 2025-01-04 04:22:17

我找到了一个比我第一次发布的代码更好的解决方案。它仍然不完美,但已经有了很大改进。以防万一其他人试图解决类似的问题...

当设置低质量图像时,我将此方法添加到我的 QGraphicsView 中:

def get_scroll_state(self):
    """
    Returns a tuple of scene extents percentages. 
    """
    centerPoint = self.mapToScene(self.viewport().width()/2,
                                  self.viewport().height()/2)
    sceneRect = self.sceneRect()
    centerWidth = centerPoint.x() - sceneRect.left()
    centerHeight = centerPoint.y() - sceneRect.top()
    sceneWidth =  sceneRect.width()
    sceneHeight = sceneRect.height()

    sceneWidthPercent = centerWidth / sceneWidth if sceneWidth != 0 else 0
    sceneHeightPercent = centerHeight / sceneHeight if sceneHeight != 0 else 0
    return sceneWidthPercent, sceneHeightPercent

这会存储在 self.scroll_state 中。设置高质量图像时,我调用另一个函数来恢复前一个函数中使用的百分比。

def set_scroll_state(self, scroll_state):
    sceneWidthPercent, sceneHeightPercent = scroll_state
    x = (sceneWidthPercent * self.sceneRect().width() +
         self.sceneRect().left())
    y = (sceneHeightPercent * self.sceneRect().height() +
         self.sceneRect().top())
    self.centerOn(x, y)

这将中心位置设置为与交换图像之前相同的位置(百分比)。

I found a solution that works better than the code I first posted. It's still not perfect, but is much improved. Just in case anyone else is trying to solve a similar problem...

When setting the low quality image I call this method added to my QGraphicsView:

def get_scroll_state(self):
    """
    Returns a tuple of scene extents percentages. 
    """
    centerPoint = self.mapToScene(self.viewport().width()/2,
                                  self.viewport().height()/2)
    sceneRect = self.sceneRect()
    centerWidth = centerPoint.x() - sceneRect.left()
    centerHeight = centerPoint.y() - sceneRect.top()
    sceneWidth =  sceneRect.width()
    sceneHeight = sceneRect.height()

    sceneWidthPercent = centerWidth / sceneWidth if sceneWidth != 0 else 0
    sceneHeightPercent = centerHeight / sceneHeight if sceneHeight != 0 else 0
    return sceneWidthPercent, sceneHeightPercent

This gets stored in self.scroll_state. When setting the high quality image I call another function to restore the percentages used in the previous function.

def set_scroll_state(self, scroll_state):
    sceneWidthPercent, sceneHeightPercent = scroll_state
    x = (sceneWidthPercent * self.sceneRect().width() +
         self.sceneRect().left())
    y = (sceneHeightPercent * self.sceneRect().height() +
         self.sceneRect().top())
    self.centerOn(x, y)

This sets the center position to the same location (percentage-wise) as I was at before swapping the image.

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