2016-11-18 8 views
2

Я потерял понимание значения масштаба QGraphicsScene/View.QGraphicsScene/View Scale Understanding

Вот как я размещаю свои объекты в сцене.

QPointF Mainwindow::pointLocation(double bearing, double range){ 
    int offset = 90; //used to offset Cartesian system 
    double centerX = baseSceneSize/2;//push my center location out to halfway point 
    double centerY = baseSceneSize/2; 
    double newX = centerX + qCos(qDegreesToRadians(bearing - offset)) * range; 
    double newY = centerY + qSin(qDegreesToRadians(bearing - offset)) * range; 
    QPointF newPoint = QPointF(newX, newY); 
    return newPoint; 

} 

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

Вот где дела идут не так:

У меня есть цель на подшипник 270, Диапазон 10.

Когда приложение работает, и мой вертикальный слайдер на нулевое значение, я могу видеть эту цель на мой взгляд. Мне не следует. Мне нужно, чтобы эта цель появилась, когда слайдер получил значение 10. Просто подумайте, что каждое значение позиции на слайдере равно 1 морской миле. Так что, если цель находится в 10 НМ он должен быть виден только один раз ползунок> = 10.

вот как я делаю масштабирование:

void MainWindow:: on_PlotSlider_sliderMoved(int position){ 
    const qreal factor = 1.01; 
    viewScaleValue = qPow(factor, -position);//-position to invert the scale 
    QMatrix matrix; 
    matrix.scale(viewScaleValue, viewScaleValue); 
    view->setMatrix(matrix); 
} 

Я пытался сделать Посмотреть больше , сцена больше, но ничего не имеет надлежащего эффекта.

Вот моя установка Сцена:

view = ui->GraphicsView; 
scene = new QGraphicsScene(this); 
int baseSize = 355; 
scene->setSceneRect(0,0,baseSize,baseSize); 
baseSceneSize = scene->sceneRect().width(); 
view->setScene(scene); 

Как принимать диапазон моей цели и вытолкнуть на сцену так, чтобы она выстраивается со значением ползунка?

+0

Я думаю, что идея «базового» размер сцены не требуется. Используйте любые единицы, имеющие смысл, например. морские мили, как ваши единицы сцены, и предположите, что ваше судно находится в начале координат. Затем поместите цели в координаты 'QPoint (диапазон * cos (подшипник), диапазон * sin (подшипник)'. Зум выбирает отображение между сценой и видом таким образом, чтобы заданное количество миль соответствовало виду. сцена будет позаботиться об остальном. –

+0

Размер базовой сцены состоял в том, чтобы я помещал объекты в центр обзора перед смещением для преобразования диапазона и подшипника.Кроме того, это был элемент тестирования, который использовался, чтобы попытаться посмотреть, что изменит измерение единицы сцены. Как я могу использовать любые единицы, как мои единицы сцены? – bauervision

+0

Просто для уточнения, 355 baseSize происходит из фактических размеров QGraphicsView в моем пользовательском интерфейсе. Если я не centerX = baseSceneSize/2; , тогда элементы создаются исходя из верхнего левого угла обзора, а не центра. – bauervision

ответ

1

QGraphicsView::fitInView - все, что вам нужно для выбора отображаемого диапазона и центра обзора.

Вот как вы можете это сделать. Это полный пример.

screenshot of the example

// https://github.com/KubaO/stackoverflown/tree/master/questions/scene-radar-40680065 
#include <QtWidgets> 
#include <random> 

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

QPointF randomPosition() { 
    static std::random_device dev; 
    static std::default_random_engine eng(dev()); 
    static std::uniform_real_distribution<double> posDis(-100., 100.); // NM 
    return {posDis(eng), posDis(eng)}; 
} 

Затем, чтобы помочь в превращении группы элементов сцены и выключаться (например КАРТОГРАФИЧЕСКАЯ СЕТКА), это помогает иметь пустой родительский элемент для них:

class EmptyItem : public QGraphicsItem { 
public: 
    QRectF boundingRect() const override { return QRectF(); } 
    void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {} 
}; 

Менеджер настроит дисплей , Пустые элементы действуют как коллекции элементов, и их можно легко сделать скрытыми/видимыми без необходимости изменять дочерние элементы. Они также применяют относительный Z-порядок своих детей.

class SceneManager : public QObject { 
    Q_OBJECT 
    Q_PROPERTY(bool microGraticuleVisible READ microGraticuleVisible WRITE setMicroGraticuleVisible) 
    QGraphicsScene m_scene; 
    QPen m_targetPen{Qt::green, 1}; 
    EmptyItem m_target, m_center, m_macroGraticule, m_microGraticule; 

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

bool eventFilter(QObject *watched, QEvent *event) override { 
     if (event->type() == QEvent::Resize 
       && qobject_cast<QGraphicsView*>(watched)) 
      emit viewResized(); 
     return QObject::eventFilter(watched, event); 
    } 

Сцена имеет следующие Z-порядок: кросс-центр, макро- и микро-координатная сетка, а затем цели находятся на вершине.

public: 
    SceneManager() { 
     m_scene.addItem(&m_center); 
     m_scene.addItem(&m_macroGraticule); 
     m_scene.addItem(&m_microGraticule); 
     m_scene.addItem(&m_target); 
     m_targetPen.setCosmetic(true); 
     addGraticules(); 
    } 

Мы можем контролировать графический вид для изменения размера; мы также раскрываем видимость микрогранулы.

void monitor(QGraphicsView *view) { view->installEventFilter(this); } 
    QGraphicsScene * scene() { return &m_scene; } 
    Q_SLOT void setMicroGraticuleVisible(bool vis) { m_microGraticule.setVisible(vis); } 
    bool microGraticuleVisible() const { return m_microGraticule.isVisible(); } 
    Q_SIGNAL void viewResized(); 

Цели могут быть случайным образом сгенерированы. Цель имеет фиксированный размер в координатах вида. Его позиция, однако, подвержена любым преобразованиям с точки зрения зрения.

Ручки для целей и сетки - это косметические ручки: их ширина указывается в единицах устройства просмотра (в пикселях), а не в единицах сцены.

void newTargets(int count = 200) { 
     qDeleteAll(m_target.childItems()); 
     for (int i = 0; i < count; ++i) { 
      auto target = new QGraphicsEllipseItem(-1.5, -1.5, 3., 3., &m_target); 
      target->setPos(randomPosition()); 
      target->setPen(m_targetPen); 
      target->setBrush(m_targetPen.color()); 
      target->setFlags(QGraphicsItem::ItemIgnoresTransformations); 
     } 
    } 

В КАРТОГРАФИЧЕСКИХ СЕТКАХ представляют собой концентрические окружности с центром в начале координат (точках отсчета диапазона) и кросс в начале координат. Крест происхождения имеет фиксированный размер в единицах просмотра - это обозначается флагом ItemIgnoresTransformations.

void addGraticules() { 
     QPen pen{Qt::white, 1}; 
     pen.setCosmetic(true); 
     auto center = {QLineF{-5.,0.,5.,0.}, QLineF{0.,-5.,0.,5.}}; 
     for (auto l : center) { 
      auto c = new QGraphicsLineItem{l, &m_center}; 
      c->setFlags(QGraphicsItem::ItemIgnoresTransformations); 
      c->setPen(pen); 
     } 
     for (auto range = 10.; range < 101.; range += 10.) { 
      auto circle = new QGraphicsEllipseItem(0.-range, 0.-range, 2.*range, 2.*range, &m_macroGraticule); 
      circle->setPen(pen); 
     } 
     pen = QPen{Qt::white, 1, Qt::DashLine}; 
     pen.setCosmetic(true); 
     for (auto range = 2.5; range < 9.9; range += 2.5) { 
      auto circle = new QGraphicsEllipseItem(0.-range, 0.-range, 2.*range, 2.*range, &m_microGraticule); 
      circle->setPen(pen); 
     } 
    } 
}; 

Отображение между блоками сцены и представления сохраняется следующим образом:

  1. Каждый раз, когда диапазон зрения изменяется (от, например, в поле со списком), метод QGraphicsView::fitInView вызывается с прямоугольник в единицах сцены (морских миль). Это позаботится о все масштабирование, центрирование и т. Д.. Например. для выбора диапазона 10 Нм, мы бы назвали view.fitInView(QRect{-10.,-10.,20.,20.), Qt::KeepAspectRatio)

  2. Грануляр (ы) можно отключить/включить, если необходимо, для заданного диапазона, чтобы развязать представление.

    int main(int argc, char ** argv) { 
        QApplication app{argc, argv}; 
        SceneManager mgr; 
        mgr.newTargets(); 
    
        QWidget w; 
        QGridLayout layout{&w}; 
        QGraphicsView view; 
        QComboBox combo; 
        QPushButton newTargets{"New Targets"}; 
        layout.addWidget(&view, 0, 0, 1, 2); 
        layout.addWidget(&combo, 1, 0); 
        layout.addWidget(&newTargets, 1, 1); 
    
        view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
        view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
        view.setBackgroundBrush(Qt::black); 
        view.setScene(mgr.scene()); 
        view.setRenderHint(QPainter::Antialiasing); 
        mgr.monitor(&view); 
    
        combo.addItems({"10", "25", "50", "100"}); 
        auto const recenterView = [&]{ 
         auto range = combo.currentText().toDouble(); 
         view.fitInView(-range, -range, 2.*range, 2.*range, Qt::KeepAspectRatio); 
         mgr.setMicroGraticuleVisible(range <= 20.); 
        }; 
        QObject::connect(&combo, &QComboBox::currentTextChanged, recenterView); 
        QObject::connect(&mgr, &SceneManager::viewResized, recenterView); 
        QObject::connect(&newTargets, &QPushButton::clicked, [&]{ mgr.newTargets(); }); 
        w.show(); 
        return app.exec(); 
    } 
    
    #include "main.moc" 
    
+0

Вау, это ответ Хелувы Куба, спасибо вам, сэр. – bauervision

1

Так как Куба предположил, я немного смутил это. С его помощью это привело к тому, что я получил результат, в котором я нуждался. На 100% не уверен, но на данный момент он работает так, как мне нужно.

view = ui->GraphicsView; 
scene = new QGraphicsScene(this); 
int baseSize = 1000; // MAGIC value that works, anything other than this, not so much 
view->setSceneRect(0,0,baseSize,baseSize); 
baseViewSize = view->sceneRect().width(); 
view->setScene(scene); 

Мой метод drawPoint работает нормально, никаких изменений не требовалось.

Наконец, вот мой слайдер

void MainWindow:: on_PlotSlider_sliderMoved(int position){ 
    const qreal factor = 1.01; 
    viewScaleValue = qPow(factor, -position);//-position to invert the scale 
    QMatrix matrix; 
    // below is the update, again 6 is a MAGIC number, no clue why 6 works... 
    matrix.scale((baseViewSize/6/position, baseViewSize/6/position); 
    view->setMatrix(matrix); 

}

Хотя моя проблема решена, я хотел бы какое-то объяснение, как мои 2 номера MAGIC.

Почему все это работает только в baseSize 1000?

Почему он правильно масштабируется, если я разделяю BaseViewSize на 6?

+1

Трудно сказать, как это работает, потому что мы понятия не имеем, что такое позиция, какие единицы вы используете в своей сцене и т. Д. Другие фрагменты вашего кода отсутствуют. Нет смысла настраивать прямоугольник сцены - сцена делает это уже и делает это правильно. Скажите, что вы хотите установить диапазон 10NM вокруг начала координат сцены - это область 20 Нм в ширину/высоту и начинается с -10NM, -10NM. Все, что вам нужно сделать, это вызвать 'view-> fitInView (-10., - 10., 20., 20.)'. Вот и все. Представление позаботится о том, какое масштабирование нужно установить. Вы должны вызывать это каждый раз, когда вид изменяет размер, конечно. –