2016-11-18 112 views
2

我对理解QGraphicsScene/View的缩放值感到迷茫。QGraphicsScene/View Scale了解

下面是我如何将我的目标放置在场景中。

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个NMS只应可见一旦滑块> = 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来自我的UI中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方法被调用用矩形的场景单位(海里)。这照顾了所有的缩放,居中等。例如。为了选择10NM的范围,我们打电话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

哇,这是一个helluva答案库巴,谢谢你好先生。 – 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张幻数。

为什么这一切只工作是baseSize是1000?

为什么只有在将BaseViewSize除以6时才能正确缩放?

+1

很难讲它是如何工作的,因为我们不知道什么位置,你在你的场景,等等。用什么单位代码的其他位失踪。设置场景矩形没有意义 - 场景已经完成并且正确地执行。假设你想在场景坐标中围绕原点的10NM范围进行调整 - 这是一个20NM宽/高的区域,从-10NM,-10NM开始。所有你需要做的就是调用'view-> fitInView(-10。, - 10.,20.,20。)'。而已。该视图负责确定要设置的缩放比例。当然,每当视图改变大小时,您都需要调用它。 –