Реклама

QGraphicsScene

Добрый день, собственно задачка такая, есть 36 частей панорамы, необходимо подгружать части, которые влезают на Scene в данный момент и выгружать в момент скроллинга части не отображающиеся на Scene

  • #
  • 18 декабря 2017 г. 14:55

День добрый.
А вы эти части панорамы уже разбили на какой-нибудь вектор, наподобие QVector<Image>?
Чтобы мне было понятно, с какими исходными данными вы работаете.

А ещё посмотрите вот этот топик про игру жизнь , там мы обсуждали проблемы производительности и отрисовки большого числа объектов на графической сцене. А также глянуть проект, который есть конкретно в этом посте . Не я его писал, но я его глянул. Кажется там как раз нужныя вам логика отрисовки, ну или схожая. Во всяком случае у меня в памяти осталось, что там не перерисовывались объекты, которые не видны на графической сцене (но могу и ошибаться).

в данный момент, я разрезал панораму 64000 точек на 12000, и сохранил 36 изображений в формате png  в папке, логика, на данный момент подгружать из папки тайлы отображающиеся на Scene  для экономии памяти

Я понял вашу идею. Я сейчас как раз сходной задачей по тайлам занят, но только делаю это для QML. Однако кое-какие наработки и мысли уже есть.

У меня такой вопрос, что именно подразумеваете под экономией памяти?
Сами тайлы для быстродействия я загружал бы все в некоторый пул объектов, как никак чтение и запись в файл тоже не быстрая операция, но вот на графической сцене держал бы только видимые.

экономия оперативной памяти, что бы не грузить к примеру 18 тайлов, которые 2 гб съедят, а грузить 3 к примеру, максимум 4 будут видны на экране, по предварительным расчетам

Понятно. У меня есть кое-какие мысли, но нужно уточнить ещё пару моментов.
Для такого требуется следующая информация:

  • количество тайлов
  • количество тайлов в строке
  • количество тайлов в колонке
  • размер тайла
  • информация о именах тайлов, чтобы получить правильный порядок загрузки тайла из каталога
Уточните это, а я сегодня/завтра накидаю свои мысли об этом
возможно я немного не так понял, но на данный момент:
панорама 360 градусов, я ее режу по вертикали на 36 тайлов, по 10 градусов получается.
при максимальном отдалении на эране будет максимально отображаться 4 тайла
размер тайла примерно 20 мб. (1 800х11 692 точек)
имена тайлов просто от 0.png до 35.png
  • Ulkiorra1992
  • #
  • отредактировано 18 декабря 2017 г. 15:51
  • 18 декабря 2017 г. 15:41

Ага. вот оно что. Для меня (и конкретно в моей задаче) тайлы составляют карту, в виде матрицы, то есть может быть сетка 40 на 40 тайлов и т.д. А у вас получается 1 на 36.

Ну понятно в общем. Я всё равно над этим работаю. Вечером постараюсь глянуть и в сторону вашей задачи.

спасибо большое, хотя бы, направление, как это реализовать, я пока не представляю как реализовать алгоритм

  • Ulkiorra1992
  • #
  • отредактировано 20 декабря 2017 г. 21:39
  • 20 декабря 2017 г. 21:30

В общем смысл программы будет следующий. Вы открываете через диалоговое окно с файлами панорамы. Тайлы при этом должны иметь все одинаковые размеры (это искусственное ограничение, чтобы показать основной алгоритм, необходимые проверки потом сами реализуете, если будут тайлы разных размеров).

 
Записываете нужную информацию во вьюшку, то есть пути ко всем тайлам.
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    m_scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(m_scene);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    // Открыть все необходимые тайлы
    ui->graphicsView->setTilePaths(QFileDialog::getOpenFileNames(this, tr("Open Tiles")));
}

В самой графической вьюшке будут вектора на тайлы, а также и на пути к тайлам, которые можно будет подгружать.
#ifndef TILEDGRAPHICSVIEW_H
#define TILEDGRAPHICSVIEW_H

#include <QGraphicsView>
#include <QGraphicsPixmapItem>

class TiledGraphicsView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit TiledGraphicsView(QWidget *parent = nullptr);

    void setTilePaths(QStringList tilePaths);

protected:
    virtual void paintEvent(QPaintEvent* e) override;

private:
    QStringList m_tilePaths;    // Пути к тайлам
    int m_tileHeight;           // Высота тайла
    int m_tileWidth;            // Ширина тайла
    int m_tileSetWidth;         // Ширина графической сцены под тайлы
    int m_tileSetHeight;        // Высота графической сцены под тайлы

    std::vector<QGraphicsPixmapItem*> m_tileItems;  // Указатели на загруженные тайлы
};

#endif // TILEDGRAPHICSVIEW_H

Далее сам процесс отрисовки с установкой тайлов во вьюшку
#include "TiledGraphicsView.h"

#include <QPainter>
#include <QFile>
#include <QGLWidget>
#include <QScrollBar>

#include <QDebug>

TiledGraphicsView::TiledGraphicsView(QWidget *parent) : QGraphicsView(parent)
{
    setViewport(new QGLWidget(this));
}

void TiledGraphicsView::setTilePaths(QStringList tilePaths)
{
    // При загрузке нового сета тайлов очистим всё.
    scene()->clear();
    m_tilePaths.clear();
    m_tileItems.clear();

    m_tilePaths = tilePaths;

    // С помощью первого тайла найдём параметры графической сцены и тайла
    QImage firstTile(m_tilePaths.first());
    m_tileHeight = firstTile.height();
    m_tileWidth = firstTile.width();
    m_tileSetWidth = m_tileWidth * m_tilePaths.length();
    m_tileSetHeight = m_tileHeight;
    setSceneRect(0, 0, m_tileSetWidth, m_tileSetHeight);
}

void TiledGraphicsView::paintEvent(QPaintEvent *e)
{
    QRect drawRect(horizontalScrollBar()->value(),
                    verticalScrollBar()->value(),
                    viewport()->width(),
                    viewport()->height());

    // При каждом событии перерисовки проверяем, какие из тайлов нужно отрисовать
    for (int i = 0; i < m_tilePaths.length(); ++i)
    {
        int coordX1 = i * m_tileWidth;
        int coordX2 = coordX1 + m_tileWidth;
        // Если тайл попадает в область отрисовки
        if ((coordX1 >= drawRect.x() && coordX1 <= drawRect.x() + drawRect.width()) || (coordX2 >= drawRect.x() && coordX2 <= drawRect.x() + drawRect.width()))
        {
            // то пытаемся его отрисовать при условии, что он ещё не был отрисован
            auto it = std::find_if(m_tileItems.begin(), m_tileItems.end(), [&coordX1](const QGraphicsPixmapItem* pixmap) { return coordX1 == pixmap->pos().x(); } );
            if (it == m_tileItems.end())
            {
                QGraphicsPixmapItem* pixmapItem = scene()->addPixmap(QPixmap(m_tilePaths.at(i)));
                m_tileItems.push_back(pixmapItem);
                pixmapItem->setPos(coordX1, 0);
            }
        }
        else
        {
            // В противном случае удаляем его со сцены и из пула отрисованных тайлов с очисткой памяти
            auto it = std::find_if(m_tileItems.begin(), m_tileItems.end(), [&coordX1](const QGraphicsPixmapItem* pixmap) { return coordX1 == pixmap->pos().x(); } );
            if (it != m_tileItems.end())
            {
                scene()->removeItem(*it);
                delete *it;
                m_tileItems.erase(it);
            }
        }
    }

    QGraphicsView::paintEvent(e);
}

Спасибо большое, за помощь, и с наступающим Новым Годом!!!

Пожалуйста. Вас также с Наступающим Новым Годом!!!

хочу еще поблагодарить за помощь, я в свой проект это все внедрил, но вот строчка, что ниже, мне совсем не понятно как это работает:
// то пытаемся его отрисовать при условии, что он ещё не был отрисован
            auto it = std::find_if(m_tileItems.begin(), m_tileItems.end(), [&coordX1](const QGraphicsPixmapItem* pixmap) { return coordX1 == pixmap->pos().x(); } );
и еще, возникла проблема, когда я приближаю или отдаляю изображение, программа перестает работать (тайлы не грузятся)
    QMatrix matrix;
    matrix.scale(1.5,1.5);
    ui->graphicsView->setMatrix(matrix);
возможно вы подскажите, как сделать что бы программа работала и при зуме.

Добрый день. Что-то я совсем забыл про ваш вопрос.

Чтобы сделать зум, нужно правильно пересчитывать ширину и тайлов.
Возможно матрица преобразования попадает в неправильные пропорции и идёт обращение к неинициализированным тайлам. Пройдитесь для начала дебагером, куда там уходит зум в не ту область памяти.

Что касается той строчки
auto it = std::find_if(m_tileItems.begin(), m_tileItems.end(), [&coordX1](const QGraphicsPixmapItem* pixmap) { return coordX1 == pixmap->pos().x(); } );
auto - это оператор с автоматическим выводом типа, здесь я получаю итератор, auto позволяет особо не нагружать голову с огромным названием класса итератора.

std::find_if - это функция стандартной библиотеки, которая осуществляет поиск по контейнеру с условием, в качестве которого выступает лямбда.

m_tileItems.begin() и m_tileItems.end() возвращают итераторы с указанием на начало и конец контейнера.

[&coordX1](const QGraphicsPixmapItem* pixmap) { return coordX1 == pixmap->pos().x(); } 
Это функция-условие, по которому необходимо найти нужный элемент.
coordX1 - это захват переменной, с помощью которой пытаемся найти нужный элемент. Лямбды могут захватывать внешние переменные, очень удобно в таких ситуациях, как здесь.

const QGraphicsPixmapItem* pixmap - это то, что содержится в контейнере тайлов. Там внутри цикл, в котором в функцию условия передаются все элементы в итераторе поочерёдно. Этот pixmap и есть эта проверяемая переменная. Дальше возвращается результат проверки. Если true , то объект найден. И возвращается итератор на этот объект. В противном случае false , пока все элементы в контейнере не закончатся. Если объект не найден, то будет возвращён пустой итератор, равный m_tileItems.end()

спасибо, за подробное объяснение строчки, а с зумом я разобрался, все работает

Реклама

Ответы

Только авторизованные пользователи могут отвечать на форуме.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
  • falcon
  • 16 января 2018 г. 17:25

Qt - Тест 001. Сигналы и слоты

  • Результат 100 баллов
  • Очки рейтинга 10
  • falcon
  • 16 января 2018 г. 17:22

Qt - Тест 001. Сигналы и слоты

  • Результат 68 баллов
  • Очки рейтинга -1
  • falcon
  • 16 января 2018 г. 17:18

C++ - Тест 001. Первая программа и типы данных

  • Результат 73 баллов
  • Очки рейтинга 1
Последние комментарии

QML - Урок 021. Переключение между окнами в QML

Спасибо всем. Все получилось. Прикручиваю логику.

  • BlinCT
  • 14 января 2018 г. 19:28

Разработка на Qt под iOS

Вот честно, на сколько же муторно под огрызок что то делать. Куча проблем) А вод линь или под Андроид все просто и тривиально))

  • folax
  • 12 января 2018 г. 9:16

QML - Урок 021. Переключение между окнами в QML

Ничего сложного, делаете по тех заданию 3 файла qml, называете их как указанно в тех задании, потом из первого окна через Loader их переключаете, в окне 2 и 3 делаете сигналы которые при закры...

QML - Урок 021. Переключение между окнами в QML

Все верно, я и не говорил что этот кусок кода лично мое произведение. Это тоже верно: Это задание для прохождения на собеседование в одну из крупных украинских IT компаний. Логику ...

  • folax
  • 12 января 2018 г. 8:13

QML - Урок 021. Переключение между окнами в QML

int main(int argc, char *argv[]){ QApplication app(argc, argv); Logic logic; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("logic", &logic)...

Сейчас обсуждают на форуме

Как проверить доступность сервера

Точно!!! Я сейчас так пробую QNetworkReply *replay_news = networkManager_news->get(QNetworkRequest(QUrl(url_news)));connect(networkManager_news, &QNetworkAccessManager::...

ChartView. Отображение метки данных точки серии при наведении курсора

Спасибо большущее за советы! Все получилось через ScatterSeries. Методы remove() как-то сходу не дались, удаляет в первый раз, а потом программа падает... Не стал тратить время и воспользовалс...

QGraphicsScene

спасибо, за подробное объяснение строчки, а с зумом я разобрался, все работает

  • EVILEG
  • 15 января 2018 г. 17:21

Qt webgl

Насчёт проверки подключения клиента я не в курсе. Что касается экземпляров приложения, то из того, что я читал получается, что нет необходимости в нескольких экземплярах для нескольких кл...

  • EVILEG
  • 15 января 2018 г. 11:39

Проблема добавления #DEFINE при сборке CMak'ом

А Вы не пробовали сделать предкомпилированные библиотеки boost под свою систему, а потом уже подключать собранные библиотеки Boost`а? Просто один только boost может собираться на пару гиг...