QGraphicsView::fitInView
是您需要選擇顯示的範圍和居中視圖的所有內容。
以下是您可能會這樣做的方法。這是一個完整的例子。
![screenshot of the example](https://i.stack.imgur.com/eDFIs.png)
// 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);
}
}
};
現場單元和視圖之間的映射被保持如下:
每次視圖範圍內改變(例如從組合框中),則QGraphicsView::fitInView
方法被調用用矩形的場景單位(海里)。這照顧了所有的縮放,居中等。。例如。爲了選擇10NM的範圍,我們打電話view.fitInView(QRect{-10.,-10.,20.,20.), Qt::KeepAspectRatio)
對於給定的範圍來說,可以禁用/啓用格線以清理視圖。
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"
我認爲,「基地」場景大小的想法是不必要的。使用任何有意義的單位,例如海里作爲您的場景單位,並假設您的船隻位於座標系的原點。然後將目標放置在QPoint(範圍* cos(方位),範圍* sin(方位)'的座標上。縮放選擇場景和視圖之間的映射,以便給定數量的英里適合視圖。現場將負責其餘部分。 –
基本場景大小是爲了確保在對距離和方位轉換進行偏移之前將對象放置在視圖的中心。此外,這是一個測試元素,用於嘗試查看會改變場景的單位測量的內容。 我怎麼「使用任何單位,我的場景單位」? – bauervision
爲了澄清,355 baseSize來自我的UI中QGraphicsView的實際尺寸。 如果我不 的centerX = baseSceneSize/2; 則項都創建基於斷視圖,不是中心的左上角。 – bauervision