I would like to draw a notification that always appears in the upper right corner of my QGraphicsView
. However, QGraphicsItems
positions are specified in scene coordinates, so if the user panned/zoomed to view a different part of the scene, this notification would move off screen.
I have figured out that I could simulate this behavior by moving and scaling the notification any time the current view changes. But this seems terribly ineffective and not at all elegant.
It seems that QGraphicsView
should support this kind of behavior. The docs mention a flag ItemIsPanel
that sounds hopeful, but mentions nothing about static placement in the view. ItemIgnoresTransformations
also will help with scaling/zooming, but not panning.
Is there any built-in Qt functionality that supports this behavior?
QGraphicsScene also provides functionality that lets you efficiently determine both the location of items, and for determining what items are visible within an arbitrary area on the scene. With the QGraphicsView widget, you can either visualize the whole scene, or zoom in and view only parts of the scene.
If unset, or if set to a null QRectF, sceneRect () will return the largest bounding rect of all items on the scene since the scene was created (i.e., a rectangle that grows when items are added to or moved in the scene, but never shrinks). See also width (), height (), and QGraphicsView::sceneRect.
The class serves as a container for QGraphicsItems. It is used together with QGraphicsView for visualizing graphical items, such as lines, rectangles, text, or even custom items, on a 2D surface.
The selection changes whenever an item is selected or unselected, a selection area is set, cleared or otherwise changed, if a preselected item is added to the scene, or if a selected item is removed from the scene. QGraphicsScene emits this signal only once for group selection operations.
The naive solution of having the notification be a part of the original scene is bad - it breaks the model-view separation. You can have multiple views, all showing one scene, but generally on only one of them can the notification appear as desired.
Another simple way to do it would be to overlay a QWidget notification on top of your view. The problem is that on some architectures, overlaying regular QWidgets on top of accelerated QGLWidgets will make the former disappear. Do note that a QGraphicsView's viewport may be a QGLWidget!
Thus, the only portable solution is to explicitly do the painting on top of everything else in QGraphicsSceneView's viewport()
.
Below is a complete example.
// main.cpp
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsItem>
qreal rnd() { return qrand() / (float)RAND_MAX; }
class OverlaidGraphicsView : public QGraphicsView
{
Q_OBJECT
QGraphicsScene * m_overlayScene;
public:
explicit OverlaidGraphicsView(QWidget* parent = 0) :
QGraphicsView(parent), m_overlayScene(NULL) {}
explicit OverlaidGraphicsView(QGraphicsScene * scene = 0, QWidget * parent = 0) :
QGraphicsView(scene, parent), m_overlayScene(NULL) {}
void setOverlayScene(QGraphicsScene * scene) {
if (scene == m_overlayScene) return;
m_overlayScene = scene;
connect(scene, SIGNAL(changed(QList<QRectF>)), SLOT(overlayChanged()));
update();
}
QGraphicsScene * overlayScene() const { return m_overlayScene; }
void paintEvent(QPaintEvent *ev) {
QGraphicsView::paintEvent(ev);
if (m_overlayScene) paintOverlay();
}
virtual void paintOverlay() {
QPainter p(viewport());
p.setRenderHints(renderHints());
m_overlayScene->render(&p, viewport()->rect());
}
Q_SLOT void overlayChanged() { update(); }
};
class Window : public QWidget
{
QGraphicsScene scene, notification;
OverlaidGraphicsView * view;
QGraphicsSimpleTextItem * item;
int timerId;
int time;
public:
Window() :
view(new OverlaidGraphicsView(&scene, this)),
timerId(-1), time(0)
{
for (int i = 0; i < 20; ++ i) {
qreal w = rnd()*0.3, h = rnd()*0.3;
scene.addEllipse(rnd()*(1-w), rnd()*(1-h), w, h, QPen(Qt::red), QBrush(Qt::lightGray));
}
view->fitInView(0, 0, 1, 1);
view->setResizeAnchor(QGraphicsView::AnchorViewCenter);
view->setRenderHint(QPainter::Antialiasing);
view->setOverlayScene(¬ification);
item = new QGraphicsSimpleTextItem();
item->setPen(QPen(Qt::blue));
item->setBrush(Qt::NoBrush);
item->setPos(95, 0);
notification.addItem(item);
notification.addRect(0, 0, 100, 0, Qt::NoPen, Qt::NoBrush); // strut
timerId = startTimer(1000);
QTimerEvent ev(timerId);
timerEvent(&ev);
}
void resizeEvent(QResizeEvent * ev) {
view->resize(size());
view->fitInView(0, 0, 1, 1, Qt::KeepAspectRatio);
QWidget::resizeEvent(ev);
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != timerId) return;
item->setText(QString::number(time++));
}
};
int main(int argc, char ** argv)
{
QApplication a(argc, argv);
Window window;
window.show();
a.exec();
}
#include "main.moc"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With