I am trying to design something like a timeline view for my video player. I decided to use QTableWidget
as the timeline since it suits my purpose. My widget looks like this:
I want the green line to run through the widget when i click on play. Here is my MVCE example:
//View.cpp
View::View(QWidget* parent) : QGraphicsView(parent)
{
QGraphicsScene* scene = new QGraphicsScene(this);
TableWidget* wgt = new TableWidget;
scene->addWidget(wgt);
QGraphicsLineItem* item = new QGraphicsLineItem(30, 12, 30, wgt->height() - 9);
item->setPen(QPen(QBrush(Qt::green), 3));
item->setFlags(QGraphicsItem::ItemIsMovable);
scene->addItem(item);
setScene(scene);
}
Here is TableWidget
TableWidget::TableWidget(QWidget* parent) : QTableWidget(parent)
{
setColumnCount(10);
setRowCount(10);
//Hides the numbers on the left side of the table
verticalHeader()->hide();
//Prevents top header from highlighting on selection
horizontalHeader()->setHighlightSections(false);
//Makes the cells un-editable
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionMode(QAbstractItemView::MultiSelection);
}
Problem:
Moving the line item reflects changes to the scene it has been added to i.e. when i drag the line using mouse, the line moves in the scene but not inside the TableWidget
.
What do i want
I want the green bar to act like a horizontal slider. It should go through the TableWidget
horizontally making the widget scroll along with it showing the current position of the frame indicated by the numbers shown on the header.
Something like as shown below (notice the Red line):
I know this might not be the best way to implement a timeline but i would appreciate any other ideas to implement.
A possible solution is to overwrite the itemChange method to restrict movement as shown below:
#include <QApplication>
#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QTableWidget>
#include <QHeaderView>
#include <QGraphicsProxyWidget>
class SeekBarItem: public QGraphicsRectItem{
public:
SeekBarItem(QRectF rect, QGraphicsItem *parent=nullptr)
: QGraphicsRectItem(rect, parent)
{
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setBrush(Qt::red);
}
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value){
if(change == QGraphicsItem::ItemPositionChange){
QPointF p = value.toPointF();
qreal max = parentItem()->boundingRect().bottom()- boundingRect().bottom();
qreal min = parentItem()->boundingRect().top()-boundingRect().top();
if(p.y() > max) p.setY(max);
else if (p.y() < min) p.setY(min);
p.setX(pos().x());
return p;
}
return QGraphicsRectItem::itemChange(change, value);
}
};
class TableWidget: public QTableWidget
{
public:
TableWidget(QWidget* parent=nullptr) : QTableWidget(10, 10, parent)
{
verticalHeader()->hide();
horizontalHeader()->setHighlightSections(false);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionMode(QAbstractItemView::MultiSelection);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView view;
QGraphicsScene *scene = new QGraphicsScene;
view.setScene(scene);
QGraphicsProxyWidget *proxy = scene->addWidget(new TableWidget);
QGraphicsRectItem *it = new QGraphicsRectItem(QRectF(0, 0, 10, proxy->boundingRect().height()), proxy);
it->setBrush(Qt::green);
SeekBarItem *seekBarItem = new SeekBarItem(QRectF(-5, 0, 20, 50));
seekBarItem->setParentItem(it);
view.resize(640, 480);
view.show();
return a.exec();
}
Update:
#include <QApplication>
#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QTableWidget>
#include <QHeaderView>
#include <QGraphicsProxyWidget>
#include <QScrollBar>
class TableWidget: public QTableWidget
{
public:
TableWidget(QWidget* parent=nullptr) : QTableWidget(10, 10, parent)
{
verticalHeader()->hide();
horizontalHeader()->setHighlightSections(false);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionMode(QAbstractItemView::MultiSelection);
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
}
};
class SeekBarItem: public QGraphicsRectItem{
public:
SeekBarItem(int width, QAbstractItemView *view, QGraphicsScene *scene)
: QGraphicsRectItem(nullptr),
proxy(new QGraphicsProxyWidget()),
m_view(view)
{
proxy->setWidget(m_view);
scene->addItem(proxy);
setParentItem(proxy);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setBrush(Qt::red);
setRect(0, 0, width, m_view->height());
scrollbar = m_view->horizontalScrollBar();
}
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value){
if(change == QGraphicsItem::ItemPositionChange){
QPointF p = value.toPointF();
qreal max = parentItem()->boundingRect().right()- boundingRect().right();
qreal min = parentItem()->boundingRect().left()-boundingRect().left();
if(p.x() > max) p.setX(max);
else if (p.x() < min) p.setX(min);
p.setY(pos().y());
float percentage = (p.x()-min)*1.0/(max-min);
int value = scrollbar->minimum() + percentage*(scrollbar->maximum() - scrollbar->minimum());
scrollbar->setValue(value);
return p;
}
return QGraphicsRectItem::itemChange(change, value);
}
private:
QGraphicsProxyWidget *proxy;
QAbstractItemView *m_view;
QScrollBar *scrollbar;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView view;
QGraphicsScene *scene = new QGraphicsScene;
view.setScene(scene);
TableWidget *table = new TableWidget;
SeekBarItem *seekBarItem = new SeekBarItem(15, table, scene);
view.resize(640, 480);
view.show();
return a.exec();
}