我在理解QGraphicsScene / View的比例值时迷失了方向。
以下是我将目标放在场景中的方法。
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;
}
所以每个目标都有一个方位和范围。只要我不缩放或缩放场景,这些值就足够了。我的问题是我需要实现缩放。
这里出了问题:
我在Bearing 270,Range 10有一个目标。
当应用程序运行时,我的垂直滑块值为零时,我可以在视图中看到此目标。我不应该。我需要让这个目标只在滑块达到10时进入视图。只需认为滑块上的每个位置值等于1海里。因此,如果目标是10个NM,则只有在滑块> = 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 :(得分:2)
QGraphicsView::fitInView
是您选择显示范围并使视图居中所需的一切。
这是你怎么做的。这是一个完整的例子。
// 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"
答案 1 :(得分: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才能正确缩放?