2016-11-18 113 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。)'。而已。該視圖負責確定要設置的縮放比例。當然,每當視圖改變大小時,您都需要調用它。 –