Черепица с QGraphicsScene и QGraphicsView - PullRequest
8 голосов
/ 08 мая 2011

Я создаю визуализатор изображений, который открывает большие изображения (2 ГБ +) в Qt.Я делаю это, разбивая большое изображение на несколько плиток 512X512.Затем я загружаю QGraphicsScene исходного размера изображения и использую addPixmap, чтобы добавить каждую плитку в QGraphic Scene.Таким образом, в конечном итоге это выглядит как огромное изображение для конечного пользователя, когда на самом деле это непрерывный массив небольших изображений, слипшихся на сцене. Во-первых, это хороший подход?сцена занимает много памяти.Так что я думаю только о загрузке плиток, которые видны на виде.Мне уже удалось создать подкласс QGraphicsScene и переопределить его событие перетаскивания, что позволило мне узнать, какие плитки должны быть загружены дальше, основываясь на движении.Моя проблема заключается в отслеживании движения на полосах прокрутки.Можно ли как-нибудь создать событие, которое будет вызываться при каждом перемещении полосы прокрутки.Подкласс QGraphicsView в не вариант.

Ответы [ 3 ]

8 голосов
/ 08 мая 2011

QGraphicsScene достаточно умен, чтобы не отображать то, что не видно, поэтому вот что вам нужно сделать:

Вместо загрузки и добавления растровых изображений, добавьте классы, которые обертывают растровое изображение, и загружайте его только при первом отображении. (Компьютерные ученые любят называть это «шаблон прокси»). Затем вы можете выгрузить растровое изображение на основе таймера. (Они будут прозрачно перезагружены, если выгрузится слишком рано.) Вы можете даже уведомить этот путь прокси-сервера о текущем уровне масштабирования, чтобы он загружал изображения с более низким разрешением, когда они будут отображаться меньше.


Редактировать: вот код, с которого можно начать. Обратите внимание, что все, что рисует 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(...)

5 голосов
/ 09 мая 2011

Хотя ответ уже выбран, я хотел бы высказать свое мнение.

Мне не нравится выбранный ответ, особенно из-за использования таймеров.Таймер для выгрузки растровых изображений?Скажем, что пользователь действительно хочет хорошенько взглянуть на изображение, и через пару секунд - бац, изображение выгружается, ему придется что-то сделать, чтобы изображение снова появилось.Или, может быть, вы поставите другой таймер, который загружает растровые изображения через пару секунд?Или вы проверите среди своих тысяч предметов, если они видны?Это не только очень раздражает и неправильно, но это означает, что ваша программа будет использовать ресурсы все время.Скажем, пользователь сворачивает вашу программу и воспроизводит фильм, он будет удивляться, почему мой фильм останавливается каждые несколько секунд ...

Что ж, если я неправильно понял предложенную идею использования таймеров, извините меня.

На самом деле идея, предложенная Мутцем, лучше.Это напомнило мне пример Мандельброта .Посмотрите на это.Вместо того, чтобы вычислять, что рисовать, вы можете переписать эту часть для загрузки той части изображения, которую вам нужно показать.

В заключение я предложу другое решение, использующее QGraphicsView в гораздо более простой форме:

1) проверьте размер изображения без загрузки изображения (используйте QImageReader)

2) сделайте размер вашей сцены равным размеру изображения

3) вместо использования элементов растрового изображения переопределитеФункция DrawBackground ().Один из параметров даст вам новый открытый прямоугольник - это означает, что если пользователь прокрутит чуть-чуть, вы загрузите и нарисуете только эту новую часть (для загрузки только части изображения используйте setClipRect () и затем read () методыкласса QImageReader).Если есть какие-то преобразования, вы можете получить их из другого параметра (который является QPainter) и применить их к изображению перед его рисованием.

По моему мнению, лучшим решением будет объединить мое решение с потокамипоказано в примере Мандельброта .

Единственная проблема, о которой я могу думать сейчас, - это если пользователь масштабирует с большим масштабным коэффициентом.Тогда вам понадобится много ресурсов для загрузки и масштабирования огромного изображения.Ну, теперь я вижу, что есть некоторая функция QImageReader, которую я еще не пробовал - setScaledSize (), которая, возможно, делает то, что нам нужно, - если вы установите размер масштаба, а затем загрузите изображение, возможно, оно не загрузится первымвсе изображение - попробуйте.Другой способ - просто ограничить коэффициент масштабирования, что вам все равно следует делать, если вы придерживаетесь метода с элементами растрового изображения.

Надеюсь, это поможет.

4 голосов
/ 09 мая 2011

Если вам абсолютно не нужно, чтобы представление было QGraphicsView (например, потому что вы помещаете другие объекты поверх большого фонового растрового изображения), я бы действительно рекомендовал просто создать подклассы QAbstractScrollArea и переопределить scrollContentsBy() и paintEvent().

Добавьте в LRU-кэш растровых изображений (см. QPixmapCache для вдохновения, хотя это глобально), и заставьте paintEvent() вытянуть использованные растровые изображения на передний план и установить.

Если это звучит как большая работа, чем QGraphicsItem, поверьте мне, это не так:

...