交换图像时保持 QGraphicsView 中的视图/滚动位置
我在使用 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
是一个QPixmap
,self.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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我找到了一个比我第一次发布的代码更好的解决方案。它仍然不完美,但已经有了很大改进。以防万一其他人试图解决类似的问题...
当设置低质量图像时,我将此方法添加到我的 QGraphicsView 中:
这会存储在 self.scroll_state 中。设置高质量图像时,我调用另一个函数来恢复前一个函数中使用的百分比。
这将中心位置设置为与交换图像之前相同的位置(百分比)。
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:
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.This sets the center position to the same location (percentage-wise) as I was at before swapping the image.