[SOLVED]QGraphicsGridLayout & QGraphicsLayoutItem boundingRect struggles.
-
I have a traditional QWidget based UI with a QGraphicsView (well inside a QVBoxLayout of a QWidget) set as the QMainWindows central widget.
I subclassed QGraphicsVideoItem and QGraphicsLayoutItem so that I could build a NxN grid of videos. When I start the application it looks like this:
EDIT Removed: "Screenshot of app before Resizing"
For the most part it works, and once the first resize event triggers the videos correctly fill the available space.
EDIT Removed: "Screenshot of app after Resizing"
At this point when I dynamically resize the application, they grow and shrink (keeping aspect ratio) as they should.
I realize that until the show event, geometry sizes will be 0. That would explain why my boundingRect() of each custom GraphicsLayoutItem returns the equivalent of QRectF(0, 0, QSize(0, 0)). However, this fact is causing multiple problems for me and I feel like i'm spinning my wheels trying to fix it. If you look at the first screenshot, you will see that the video sizes are set to a 400x300 size because i was forced to set the minimum row/col sizes in the QGraphicsGridLayout. Without those values being set the video items will be drawn as if they have zero size (because my boundingRect is zero) appearing as a point with my borders painted around them). In addition to that, all mouse events are not being propagated to the individual GraphicsItems since you cant click on something registered as having a size of zero.
I have tried multiple approaches. I have tried setting the boundingRect of the custom GraphicsItems to return a default size of 400x300. This sort of fixes the mouse event propagation issue, but the videos no longer resize larger than that, and when I resize the mouse area no longer sits over the top and is offset. I've tried manually calling setGeometry() on all custom Graphics Items within a resizeEvent() function in the QGraphicsView's parent QWidget. That caused other problems as I was trying to use the parents new size to calculate what an individual grid cell would be, which resulted in odd behavior as the setPos() call in setGeometry(x, y, w, h) would move the custom QGraphicsItem out of the grid and paint it at the (X,Y) point passed in.
The one area I'm not exactly sure of is the behavior of QGraphicView's fitInView() function. I'm using that to keep the scene centered and taking up all available space in the view. However I'm not sure if thats just artificially zooming in and out (without changing the actual sizes of the QGraphicsItems) or vice versa.
Update
I realized after a while that I misunderstood the function of QGraphicsView::fitInView(). Once I revisited it and realized it was just scaling, i was able to go back and define boundingRects for my custom QGraphicsItems. This solved all my issues. -
Had to break up this into multiple posts because of the size:
viewwidget.h
@#ifndef VIDEOWIDGET_H
#define VIDEOWIDGET_H#include <QHash>
#include <QWidget>class QGraphicsView;
class QGraphicsScene;
class QGraphicsGridLayout;
class VideoPlayer;
class ImagePlayer;class VideoWidget : public QWidget
{
Q_OBJECT
public:
explicit VideoWidget(QWidget *parent = 0);
~VideoWidget();QSize sizeHint() const { return QSize(800, 600); } QGraphicsView* GetGraphicsView() { return _view; } QGraphicsScene* GetGraphicsScene() { return _scene; }
signals:
void Signal_VideoActive(QString name, bool active);protected:
void resizeEvent(QResizeEvent* event);private:
void SetupUI();
void Test();int _rows, _cols; QGraphicsView* _view; QGraphicsScene* _scene; QGraphicsGridLayout *_grid; QHash<QString, VideoPlayer*> _videos; QHash<QString, ImagePlayer*> _images;
};
#endif // VIDEOWIDGET_H
@ -
videowidget.cpp
@
#include <QtWidgets>
#include <QHash>
#include <QHashIterator>
#include <QGraphicsVideoItem>
#include <QGLWidget>
#include <QGraphicsView>
#include <QGraphicsWidget>#include "videowidget.h"
#include "videoplayer.h"
#include "imageplayer.h"VideoWidget::VideoWidget(QWidget *parent) :
QWidget(parent)
{
//Test Values
_rows = 2;
_cols = 2;SetupUI(); Test(); // Fit the view in the scene's bounding rect _view->fitInView(_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
}
VideoWidget::~VideoWidget()
{
_videos.clear();
_images.clear();
}void VideoWidget::SetupUI()
{
_view = new QGraphicsView(this);
_view->setAcceptDrops(true);
_view->viewport()->setMouseTracking(true);
_view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
_view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
_view->setResizeAnchor(QGraphicsView::AnchorViewCenter);_scene = new QGraphicsScene(this); _scene->setBackgroundBrush(QColor(39,39,39)); _view->setScene(_scene); _grid = new QGraphicsGridLayout; _grid->setContentsMargins(5, 5, 5, 5); QGraphicsWidget *container = new QGraphicsWidget; container->setAcceptHoverEvents(true); container->setAcceptTouchEvents(true); container->setLayout(_grid); container->setContentsMargins(0, 0, 0, 0); _scene->addItem(container); QBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_view); this->setLayout(layout);
}
void VideoWidget::Test()
{
//Fill the grid with VideoPlayers
VideoPlayer* videoPlayer;
QString video("/Users/chucks/test.mp4");
int count = 0;
for(int i=0; i<_rows; i++)
{
for(int j=0; j<_cols; j++)
{
QString name = "Video" + QString::number(++count);
videoPlayer = new VideoPlayer(name, video);
_scene->addItem(videoPlayer);
_videos.insert(name, videoPlayer);
_grid->addItem(videoPlayer, i, j);_grid->setColumnAlignment(j, Qt::AlignHCenter); _grid->setColumnMinimumWidth(j, 400); _grid->setColumnSpacing(j, 10); } _grid->setRowAlignment(i, Qt::AlignCenter); _grid->setRowMinimumHeight(i, 300); _grid->setRowSpacing(i, 10); }
}
void VideoWidget::resizeEvent(QResizeEvent* event)
{
_view->fitInView(_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
QWidget::resizeEvent(event);
}
@ -
videoplayer.h
@#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H#include <QMediaPlayer>
#include <QMovie>
#include <QGraphicsLayoutItem>
#include <QGraphicsVideoItem>
#include <QPainter>
#include <QPixmap>
#include <QGraphicsSceneMouseEvent>class VideoPlayer : public QGraphicsVideoItem, public QGraphicsLayoutItem
{
public:
VideoPlayer(QString name, QString fileName);QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QString name() { return _name; } QString file() { return _fileName; } bool isPlaying() { return _isPlaying; } void setGeometry(const QRectF & rect); void updateGeometry();
signals:
public slots:
void Slot_OpenFile();
void Slot_Play();
void Slot_Stop();
void Slot_Rewind();
void Slot_FastForward();protected:
QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF()) const;
void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent * event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);private slots:
void mediaStateChanged(QMediaPlayer::State state);
void positionChanged(qint64 position);
void durationChanged(qint64 duration);
void setPosition(int position);private:
QMediaPlayer* _mediaPlayer;
QPixmap *_pm;
QString _name;
QString _fileName;bool _isPlaying;
};
#endif // VIDEOPLAYER_H
@ -
videoplayer.cpp
@#include "videoplayer.h"#include <QtWidgets>
#include <QVideoSurfaceFormat>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOption>VideoPlayer::VideoPlayer(QString name, QString fileName) :
QGraphicsVideoItem(),
QGraphicsLayoutItem()
{
setFlag(QGraphicsItem::ItemIsSelectable, true);
setFlag(QGraphicsItem::ItemIsFocusable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); setAcceptHoverEvents(true); setAcceptDrops(true); setAspectRatioMode(Qt::KeepAspectRatio); _name = name; _fileName = fileName; _isPlaying = false; _mediaPlayer = new QMediaPlayer(0, QMediaPlayer::VideoSurface); _mediaPlayer->setVideoOutput(this); connect(_mediaPlayer, &QMediaPlayer::stateChanged, this, &VideoPlayer::mediaStateChanged); connect(_mediaPlayer, &QMediaPlayer::positionChanged, this, &VideoPlayer::positionChanged); connect(_mediaPlayer, &QMediaPlayer::durationChanged, this, &VideoPlayer::durationChanged); _pm = new QPixmap(QLatin1String(":/images/Resources/noise.png")); setGraphicsItem(this);
}
QRectF VideoPlayer::boundingRect() const
{
return QRectF(QPointF(0, 0), geometry().size());
}void VideoPlayer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
Q_UNUSED(option);//Not needed for forum post QGraphicsVideoItem::paint(painter, option, widget);
}
void VideoPlayer::Slot_OpenFile()
{
//Not needed for forum post
}void VideoPlayer::Slot_Play()
{
//Not needed for forum post
}void VideoPlayer::Slot_Stop()
{
//Not needed for forum post
}void VideoPlayer::Slot_Rewind()
{
//Not needed for forum post
}void VideoPlayer::Slot_FastForward()
{
//Not needed for forum post
}void VideoPlayer::mediaStateChanged(QMediaPlayer::State state)
{
//Not needed for forum post
}void VideoPlayer::positionChanged(qint64 position)
{
//Not needed for forum post
}void VideoPlayer::durationChanged(qint64 duration)
{
//Not needed for forum post
}void VideoPlayer::setPosition(int position)
{
//Not needed for forum post
}void VideoPlayer::setGeometry(const QRectF & rect)
{
prepareGeometryChange();
QGraphicsLayoutItem::setGeometry(rect);
setPos(rect.topLeft());
}void VideoPlayer::updateGeometry()
{
QGraphicsLayoutItem::updateGeometry();
}QSizeF VideoPlayer::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
{
switch ( which )
{
case Qt::MinimumSize:
case Qt::PreferredSize:
return this->boundingRect().size();
case Qt::MaximumSize:
return QSizeF(parentItem()->boundingRect().width(), parentItem()->boundingRect().height());
default:
return this->boundingRect().size();
}return constraint;
}
void VideoPlayer::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsVideoItem::mouseMoveEvent(event);
}void VideoPlayer::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsVideoItem::mousePressEvent(event);
}void VideoPlayer::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsVideoItem::mouseReleaseEvent(event);
}void VideoPlayer::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsVideoItem::mouseDoubleClickEvent(event);
}
@