我正在创建一个在Qt中打开大图像(2gb +)的图像可视化工具。 我是通过将大图像分成几个512X512的图块来做到这一点的。然后我加载原始图像大小的QGraphicsScene并使用addPixmap将每个图块添加到QGraphic场景上。因此,最终它看起来像是最终用户的巨大图像,实际上它是一个连续阵列的小图像粘在一起的场景。首先,这是一个很好的方法吗?
尝试将所有切片加载到场景中会占用大量内存。所以我想只加载视图中可见的图块。我已经设法将QGraphicsScene子类化并覆盖其拖动事件,从而使我能够根据移动知道接下来需要加载哪些切片。我的问题是跟踪滚动条上的移动。有没有什么办法可以创建一个每次滚动条移动时调用的事件。不能选择将QGraphicsView子类化。
答案 0 :(得分:8)
QGraphicsScene
非常聪明,不会呈现不可见的内容,所以这就是您需要做的事情:
不是加载和添加像素图,而是添加 wrap 像素图的类,并且只在首次渲染时加载它们。 (计算机科学家喜欢称之为“代理模式”)。然后,您可以根据计时器卸载像素图。 (如果过早卸载,它们将被透明地重新加载。)您甚至可以通知当前缩放级别的此代理路径,以便在它们渲染得更小时加载较低分辨率的图像。
编辑:这里有一些代码可以帮助您入门。请注意,QGraphicsScene
绘制的所有内容都是QGraphicsItem
,(如果您调用::addPixmap
,它在后台转换为...GraphicsItem
,那么这就是您想要的子类:
(我甚至没有编译过这个,所以“告诫主义者”,但它正在做正确的事情;)
class MyPixmap: public QGraphicsItem{
public:
// make sure to set `item` to nullptr in the constructor
MyPixmap()
: QGraphicsItem(...), item(nullptr){
}
// you will need to add a destructor
// (and probably a copy constructor and assignment operator)
QRectF boundingRect() const{
// return the size
return QRectF( ... );
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget){
if(nullptr == item){
// load item:
item = new QGraphicsPixmapItem( ... );
}
item->paint(painter, option, widget);
}
private:
// you'll probably want to store information about where you're
// going to load the pixmap from, too
QGraphicsPixmapItem *item;
};
然后您可以使用QGraphicsScene
QGraphicsScene::addItem(...)
答案 1 :(得分:5)
虽然已经选择了答案,但我想表达我的意见。
我不喜欢所选择的答案,特别是因为使用了计时器。卸载pixmaps的计时器?假设用户实际上想要好好看一下图像,几秒钟后--Bam,图像被卸载,他将不得不做一些事情以使图像重新出现。或者你可能会放另一个计时器,在几秒钟之后加载pixmaps?或者,如果可见,您将检查您的数千件物品?这不仅非常令人烦恼和错误,而且这意味着您的程序将始终使用资源。假设用户最小化你的节目并播放电影,他会想知道为什么我的电影每隔几秒就会冻结......
好吧,如果我误解了使用计时器的提议,请执行我。
实际上mmutz建议的想法更好。它让我想起了Mandelbrot example。看看吧。而不是计算绘制的内容,您可以重写此部分以加载您需要显示的图像部分。
总之,我将以更简单的方式使用QGraphicsView提出另一种解决方案:
1)检查图像的大小而不加载图像(使用QImageReader)
2)使场景的大小等于图像的大小
3)而不是使用pixmap项重新实现DrawBackground()函数。其中一个参数将为您提供新的外露矩形 - 这意味着如果用户只滚动一点,您将只加载和绘制这个新部分(仅加载部分图像使用setClipRect()然后读取()方法的QImageReader类)。如果有一些变换,您可以从其他参数(即QPainter)获取它们,并在绘制之前将它们应用于图像。
在我看来,最好的解决方案是将我的解决方案与Mandelbrot example中显示的线程相结合。
我现在能想到的唯一问题是用户是否缩小了比例因子。然后,您需要一些时间来加载和扩展巨大的图像。好吧,我现在看到有一些QImageReader的功能,我还没有尝试过 - setScaledSize(),这可能正是我们需要的 - 如果你设置一个比例大小然后加载图像可能它不会先加载整个形象 - 尝试一下。另一种方法是限制比例因子,如果你坚持使用pixmap项目的方法,你应该做的事情。
希望这有帮助。
答案 2 :(得分:4)
除非您绝对需要视图为QGraphicsView
(例如,因为您将其他对象放在大背景像素图的顶部),我真的建议只是继承QAbstractScrollArea
并重新实现{{1 }和scrollContentsBy()
。
添加pixmaps的LRU缓存(请参阅paintEvent()
获取灵感,尽管那是全局的),然后将QPixmapCache
拉动使用pixmaps放到前面,然后进行设置。
如果这听起来像paintEvent()
更多的工作,相信我,那不是:)