I'd like to dynamically resize a QGraphicsTextItem object and keep the text in top left corner.
I've tried to tackle this problem in many ways without any success so I included the version of code which is IMO the closest to the working version.
ltext.cpp
#include <QApplication>
#include <QRectF>
#include <QAbstractTextDocumentLayout>
#include <qmath.h>
#include "ltext.h"
LText::LText(QGraphicsTextItem *parent) : QGraphicsTextItem(parent)
{
setNeededFlags();
QString *text = new QString("Text");
setPlainText(*text);
setFont(QFont("Arial", 42));
m_rect = QRectF();
}
QRectF LText::boundingRect() const
{
if (QRectF(0, 0, 0, 0) == m_rect)
return QGraphicsTextItem::boundingRect();
return m_rect;
}
void LText::updateRect(const QRectF rect)
{
prepareGeometryChange();
setPos(mapToScene(rect.topLeft()));
m_rect.moveTo(0, 0);
m_rect.setSize(rect.size());
update(m_rect);
}
void LText::setNeededFlags()
{
setFlags(QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemSendsGeometryChanges |
QGraphicsItem::ItemIsFocusable);
}
ltext.h
#ifndef LTEXT_H
#define LTEXT_H
#include <QGraphicsTextItem>
#include <QTextDocument>
class LText : public QGraphicsTextItem
{
Q_OBJECT
public:
enum { Type = UserType + QGraphicsTextItem::Type };
LText(QGraphicsTextItem *parent = nullptr);
int type() const override { return Type; }
QRectF boundingRect() const override;
void setNeededFlags();
void updateRect(const QRectF rect);
signals:
void signalRectChanged();
private:
QRectF m_rect;
};
#endif // LTEXT_H
sizegripitem.cpp
/*
* SizeGripItem - A size grip QGraphicsItem for interactive resizing.
* Copyright (c) 2011 Cesar L. B. Silveira
*/
#include <QBrush>
#include <qmath.h>
#include "sizegripitem.h"
#include "ltext.h"
SizeGripItem::HandleItem::HandleItem(int positionFlags, SizeGripItem* parent)
: QGraphicsRectItem(-4, -4, 8, 8, parent),
positionFlags_(positionFlags),
parent_(parent)
{
setBrush(QBrush(Qt::lightGray));
setFlag(ItemIsMovable);
setFlag(ItemSendsGeometryChanges);
}
int SizeGripItem::HandleItem::positionFlags() const
{
return positionFlags_;
}
QVariant SizeGripItem::HandleItem::itemChange(GraphicsItemChange change,
const QVariant &value)
{
QVariant retVal = value;
if (change == ItemPositionChange)
{
retVal = restrictPosition(value.toPointF());
}
else if (change == ItemPositionHasChanged)
{
QPointF pos = value.toPointF();
switch (positionFlags_)
{
case TopLeft:
parent_->setTopLeft(pos);
break;
case Top:
parent_->setTop(pos.y());
break;
case TopRight:
parent_->setTopRight(pos);
break;
case Right:
parent_->setRight(pos.x());
break;
case BottomRight:
parent_->setBottomRight(pos);
break;
case Bottom:
parent_->setBottom(pos.y());
break;
case BottomLeft:
parent_->setBottomLeft(pos);
break;
case Left:
parent_->setLeft(pos.x());
break;
}
}
return retVal;
}
void SizeGripItem::HandleItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
parent_->parentItem()->setFlag(ItemIsMovable, false);
QGraphicsRectItem::mousePressEvent(mouseEvent);
}
void SizeGripItem::HandleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
parent_->parentItem()->setFlag(ItemIsMovable, true);
QGraphicsRectItem::mouseReleaseEvent(mouseEvent);
}
QPointF SizeGripItem::HandleItem::restrictPosition(const QPointF& newPos)
{
QPointF retVal = pos();
if (positionFlags_ & Top || positionFlags_ & Bottom)
retVal.setY(newPos.y());
if (positionFlags_ & Left || positionFlags_ & Right)
retVal.setX(newPos.x());
if (positionFlags_ & Top && retVal.y() > parent_->rect_.bottom())
retVal.setY(parent_->rect_.bottom());
else if (positionFlags_ & Bottom && retVal.y() < parent_->rect_.top())
retVal.setY(parent_->rect_.top());
if (positionFlags_ & Left && retVal.x() > parent_->rect_.right())
retVal.setX(parent_->rect_.right());
else if (positionFlags_ & Right && retVal.x() < parent_->rect_.left())
retVal.setX(parent_->rect_.left());
return retVal;
}
SizeGripItem::SizeGripItem(Resizer *resizer, QGraphicsItem* pItem, QObject* parent)
: QObject(parent),
resizer_(resizer)
{
setParentItem(pItem);
if (parentItem())
{
rect_ = parentItem()->boundingRect();
}
handleItems_.append(new HandleItem(TopLeft, this));
handleItems_.append(new HandleItem(Top, this));
handleItems_.append(new HandleItem(TopRight, this));
handleItems_.append(new HandleItem(Right, this));
handleItems_.append(new HandleItem(BottomRight, this));
handleItems_.append(new HandleItem(Bottom, this));
handleItems_.append(new HandleItem(BottomLeft, this));
handleItems_.append(new HandleItem(Left, this));
updateHandleItemPositions();
}
SizeGripItem::~SizeGripItem()
{
}
QRectF SizeGripItem::boundingRect() const
{
return rect_;
}
void SizeGripItem::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget)
{
Q_UNUSED(painter);
Q_UNUSED(option);
Q_UNUSED(widget);
}
#define IMPL_SET_FN(TYPE, POS) \
void SizeGripItem::set ## POS (TYPE v) \
{ \
rect_.set ## POS (v); \
doResize(); \
}
IMPL_SET_FN(qreal, Top)
IMPL_SET_FN(qreal, Right)
IMPL_SET_FN(qreal, Bottom)
IMPL_SET_FN(qreal, Left)
IMPL_SET_FN(const QPointF&, TopLeft)
IMPL_SET_FN(const QPointF&, TopRight)
IMPL_SET_FN(const QPointF&, BottomRight)
IMPL_SET_FN(const QPointF&, BottomLeft)
void SizeGripItem::doResize()
{
if (resizer_)
{
(*resizer_)(parentItem(), rect_);
updateHandleItemPositions();
}
}
void SizeGripItem::updateHandleItemPositions()
{
foreach (HandleItem* item, handleItems_)
{
item->setFlag(ItemSendsGeometryChanges, false);
switch (item->positionFlags())
{
case TopLeft:
item->setPos(rect_.topLeft());
break;
case Top:
item->setPos(rect_.left() + rect_.width() / 2 - 1,
rect_.top());
break;
case TopRight:
item->setPos(rect_.topRight());
break;
case Right:
item->setPos(rect_.right(),
rect_.top() + rect_.height() / 2 - 1);
break;
case BottomRight:
item->setPos(rect_.bottomRight());
break;
case Bottom:
item->setPos(rect_.left() + rect_.width() / 2 - 1,
rect_.bottom());
break;
case BottomLeft:
item->setPos(rect_.bottomLeft());
break;
case Left:
item->setPos(rect_.left(),
rect_.top() + rect_.height() / 2 - 1);
break;
}
item->setFlag(ItemSendsGeometryChanges, true);
}
}
sizegripitem.h
#ifndef SIZEGRIPITEM_H
#define SIZEGRIPITEM_H
#include <QGraphicsItem>
#include <QGraphicsRectItem>
#include <QScopedPointer>
#include <QObject>
class SizeGripItem : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private:
enum
{
Top = 0x1,
Bottom = 0x2,
Left = 0x4,
TopLeft = Top | Left,
BottomLeft = Bottom | Left,
Right = 0x8,
TopRight = Top | Right,
BottomRight = Bottom | Right
};
class HandleItem : public QGraphicsRectItem
{
public:
HandleItem(int positionFlags, SizeGripItem* parent);
int positionFlags() const;
protected:
virtual QVariant itemChange(GraphicsItemChange change,
const QVariant &value) override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
private:
QPointF restrictPosition(const QPointF& newPos);
int positionFlags_;
SizeGripItem* parent_;
};
public:
class Resizer
{
public:
virtual void operator()(QGraphicsItem* item,
const QRectF rect) = 0;
virtual ~Resizer() {}
};
SizeGripItem(Resizer* resizer = 0, QGraphicsItem* pItem = nullptr, QObject* parent = nullptr);
virtual ~SizeGripItem();
virtual QRectF boundingRect() const;
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = 0);
void setTopLeft(const QPointF& pos);
void setTop(qreal y);
void setTopRight(const QPointF& pos);
void setRight(qreal x);
void setBottomRight(const QPointF& pos);
void setBottom(qreal y);
void setBottomLeft(const QPointF& pos);
void setLeft(qreal x);
private:
void doResize();
void updateHandleItemPositions();
QList<HandleItem*> handleItems_;
QRectF rect_;
QScopedPointer<Resizer> resizer_;
};
#endif // SIZEGRIPITEM_H
resizer.cpp
class TextResizer : public SizeGripItem::Resizer
{
public:
virtual void operator()(QGraphicsItem* item, const QRectF rect)
{
LText* textItem =
qgraphicsitem_cast<LText*>(item);
if (textItem)
textItem->updateRect(rect);
}
};
I'm using Qt 5.15.2.
Do you have any suggestions how can I fix my code? Thanks!
Related
By default, QSlider move his thumbtrack by a value belonging to the pageStep() prop on mouse click. To make thumbtrack jump directly at the mouse click point, we need to create a new class inherited by QSlider.
.h
#ifndef QIMPROVEDSLIDER_H
#define QIMPROVEDSLIDER_H
#include <QSlider>
class QImprovedSlider : public QSlider
{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
public:
explicit QImprovedSlider(QWidget *parent = 0);
signals:
void clicked(int value) const;
};
#endif // QIMPROVEDSLIDER_H
.cpp
#include <QWidget>
#include <QMouseEvent>
#include <QStyle>
#include <QStyleOptionSlider>
#include <QProxyStyle>
#include "QImprovedSlider.h"
class QImprovedSliderStyle : public QProxyStyle
{
public:
using QProxyStyle::QProxyStyle;
int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0,
const QWidget* widget = 0, QStyleHintReturn* returnData = 0) const
{
if (hint == QStyle::SH_Slider_AbsoluteSetButtons)
return (Qt::LeftButton | Qt::MidButton | Qt::RightButton);
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
QImprovedSlider::QImprovedSlider(QWidget *parent) :
QSlider(parent)
{
setStyle(new QImprovedSliderStyle(this->style()));
}
void QImprovedSlider::mousePressEvent(QMouseEvent *event) {
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sr = style()->subControlRect(QStyle::CC_Slider,
&opt,
QStyle::SC_SliderHandle,
this);
qDebug() << sr.height() << sr.width();
if (!sr.contains(event->pos()) && event->button() == Qt::LeftButton) {
if (orientation() == Qt::Vertical)
setValue(minimum() + ((maximum()-minimum()) * (height()-event->y())) / height() ) ;
else
setValue(minimum() + ((maximum()-minimum()) * event->x()) / width() ) ;
}
QSlider::mousePressEvent(event);
}
void QImprovedSlider::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
emit clicked(value());
QSlider::mouseReleaseEvent(event);
}
}
QImprovedSliderStyle make the handle drag more fluid, but in this way, the event is fired even when the click falls inside the handle, while the condition
!sr.contains(event->pos())
should avoid this.
I want to make widgets increase in height with QPropertyAnimation, when widgets are arranged with QVboxLayout.
The problem is that when I open more than one widget, they start to move/shake during animation.
I have prepared minimum working example, here tar gz project
The problem appears when you press "open" button for first, second, then third widget, you can see then that they are shaking, moving slightly up and down during "open" animation.
Has someone idea what to do to avoid this ?
I can set setSizeConstraint(QLayout::SetFixedSize) on main layout and they dont shake, but then resizing and other doesn't work.
Best Regards
Marek
Some time ago I've wrote a layout which animates widget position it contains.
You should build your layout in such way that each widget which should be animated should be inside this layout (one AnimLayout per widget which should be animated):
#include <QLayout>
QT_FORWARD_DECLARE_CLASS(QPropertyAnimation)
class AnimLayout : public QLayout
{
Q_OBJECT
Q_PROPERTY(QPoint delta
READ delta
WRITE setDelta
NOTIFY deltaChanged)
Q_PROPERTY(QRect widgetRect
READ widgetRect
WRITE setWidgetRect
NOTIFY widgetRectChanged)
Q_PROPERTY(bool active
READ isDeltaActive
WRITE setDeltaActive
NOTIFY deltaActiveChanged)
public:
explicit AnimLayout(QWidget *parent = 0);
~AnimLayout();
QPoint delta() const;
void setDelta(const QPoint &value);
QSize sizeHint() const;
void setGeometry(const QRect &);
QSize minimumSize() const;
int count() const;
QSize deltaSize() const;
QRect widgetRect() const;
void setWidgetRect(const QRect &value);
bool isDeltaActive() const;
void setDeltaActive(bool active = true);
void updateItemPosition();
private:
void addItem(QLayoutItem *item);
QLayoutItem *itemAt(int index) const;
QLayoutItem *takeAt(int index);
signals:
void deltaChanged(const QPoint &value);
void widgetRectChanged(const QRect &value);
void deltaActiveChanged(bool active);
public slots:
void testIt();
private:
QLayoutItem *item;
QPropertyAnimation *animation;
QPoint mDelta;
bool mDeltaActive;
};
///////////////////////////////////////////////////////////
#include "animlayout.h"
#include <QPropertyAnimation>
AnimLayout::AnimLayout(QWidget *parent) :
QLayout(parent) ,
item(0)
{
animation = new QPropertyAnimation(this);
animation->setPropertyName("widgetRect");
animation->setDuration(400);
animation->setTargetObject(this);
mDeltaActive = false;
}
AnimLayout::~AnimLayout()
{
delete item;
}
QPoint AnimLayout::delta() const
{
return mDelta;
}
void AnimLayout::setDelta(const QPoint &value)
{
if (mDelta != value) {
mDelta = value;
emit deltaChanged(mDelta);
invalidate();
}
}
void AnimLayout::addItem(QLayoutItem *newItem)
{
Q_ASSERT(!item);
animation->stop();
item =newItem;
emit widgetRectChanged(item->geometry());
invalidate();
}
QSize AnimLayout::sizeHint() const
{
if (!item)
return QSize();
QSize result(item->sizeHint());
result += deltaSize();
int m = 2*margin();
result += QSize(m,m);
return result;
}
void AnimLayout::updateItemPosition()
{
QRect dest = contentsRect();
QPoint d = delta();
if (isDeltaActive()) {
d = -d;
}
if (d.x()!=0) {
if (d.x()>0) {
dest.setLeft(dest.left()+d.x());
} else {
dest.setRight(dest.right()+d.x());
}
}
if (d.y()) {
if (d.y()>0) {
dest.setTop(dest.top()+d.y());
} else {
dest.setBottom(dest.bottom()+d.y());
}
}
animation->setEndValue(dest);
if (widgetRect()!=dest) {
animation->start();
}
}
void AnimLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
updateItemPosition();
}
QLayoutItem *AnimLayout::itemAt(int i) const
{
return i==0?item:0;
}
QLayoutItem *AnimLayout::takeAt(int i)
{
Q_ASSERT(i==0);
QLayoutItem *r = item;
item = 0;
return r;
}
void AnimLayout::testIt()
{
setDeltaActive(!isDeltaActive());
}
QRect AnimLayout::widgetRect() const
{
if (item)
return item->geometry();
return QRect();
}
void AnimLayout::setWidgetRect(const QRect &value)
{
if (item && item->geometry()!=value) {
item->setGeometry(value);
emit widgetRectChanged(item->geometry());
}
}
bool AnimLayout::isDeltaActive() const
{
return mDeltaActive;
}
void AnimLayout::setDeltaActive(bool active)
{
if (active!=mDeltaActive) {
mDeltaActive = active;
animation->stop();
updateItemPosition();
emit deltaActiveChanged(active);
}
}
QSize AnimLayout::minimumSize() const
{
QSize result(deltaSize());
if (item) {
result += item->minimumSize();
}
int m = 2*margin();
result += QSize(m,m);
return result;
}
int AnimLayout::count() const
{
return item?1:0;
}
QSize AnimLayout::deltaSize() const
{
return QSize(qAbs(mDelta.x()), qAbs(mDelta.y()));
}
It has some extra functionality you don't need (mDelta).
Sorry it took me so long ;)
I have tested it and it works great.
However, when I was working with my previous code I have made it work without shaking. The change I made was to add QWidget into QScrollArea and then set QVBoxLayout on that widget.
Anyway many thanks for help.
Below there is example in one main.cpp and there is variable "animatedLayout" which turns on or off your AnimLayout.
#include
#include
class AnimLayout : public QLayout
{
Q_OBJECT
Q_PROPERTY(QRect widgetRect
READ widgetRect
WRITE setWidgetRect
NOTIFY widgetRectChanged)
public:
explicit AnimLayout(QWidget *parent = 0);
~AnimLayout();
QSize sizeHint() const;
void setGeometry(const QRect &);
QSize minimumSize() const;
int count() const;
QRect widgetRect() const;
void setWidgetRect(const QRect &value);
void updateItemPosition();
private:
void addItem(QLayoutItem *item);
QLayoutItem *itemAt(int index) const;
QLayoutItem *takeAt(int index);
signals:
void widgetRectChanged(const QRect &value);
public slots:
private:
QLayoutItem *item;
QPropertyAnimation *animation;
};
struct FrameDataStruct {
QFrame *mainFrame;
QFrame *upFrame;
QFrame *downFrame;
QPushButton *button;
QVBoxLayout *upFrameLayout;
QLabel *text;
QVBoxLayout *downFrameLayout;
QVBoxLayout *frameLayout;
QPropertyAnimation *animation;
int frame_id;
int basic_height;
bool expanded;
AnimLayout *animLayout;
};
class Proptest : public QMainWindow
{
Q_OBJECT
public:
explicit Proptest();
~Proptest();
private slots:
void setDataStruct();
void startAnimation(int frame_id);
void animFinished(int frame_id);
private:
QMap frameMap;
QSignalMapper *animStartMapper;
QSignalMapper *animFinishedMapper;
bool initialized;
QWidget *scrollWidget;
QVBoxLayout *main_layout;
QWidget *widget;
QScrollArea *scrollArea;
QVBoxLayout *central_layout;
bool layoutAnimated;
};
Proptest::Proptest()
: widget(new QWidget)
{
setCentralWidget(widget);
this->setGeometry(200,200,300,600);
central_layout=new QVBoxLayout(widget);
scrollArea=new QScrollArea(widget);
central_layout->addWidget(scrollArea);
animStartMapper=new QSignalMapper(this);
connect(animStartMapper,SIGNAL(mapped(int)),this,SLOT(startAnimation(int)));
animFinishedMapper=new QSignalMapper(this);
connect(animFinishedMapper,SIGNAL(mapped(int)),this,SLOT(animFinished(int)));
scrollWidget=new QWidget(widget);
scrollArea->setWidget(scrollWidget);
main_layout=new QVBoxLayout(scrollWidget);
main_layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
scrollArea->setWidgetResizable(true);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
layoutAnimated=true;
this->setDataStruct();
}
void Proptest::setDataStruct() {
for(int i=0;iexpanded=false;
r->frame_id=i;
r->mainFrame=new QFrame(scrollWidget);
r->upFrame=new QFrame(r->mainFrame);
r->upFrame->setMinimumHeight(40);
r->button=new QPushButton(QString("open"),r->upFrame);
r->upFrameLayout=new QVBoxLayout(r->upFrame);
r->upFrameLayout->addWidget(r->button);
r->downFrame=new QFrame(r->mainFrame);
r->text=new QLabel(QString("some text SOME TEXT some text"),r->downFrame);
r->downFrameLayout=new QVBoxLayout(r->downFrame);
r->downFrameLayout->addWidget(r->text);
r->frameLayout=new QVBoxLayout(r->mainFrame);
r->frameLayout->addWidget(r->upFrame);
r->frameLayout->addItem(new QSpacerItem(10,10));
r->frameLayout->addWidget(r->downFrame);
r->frameLayout->setStretch(0,0);
r->frameLayout->setStretch(1,1);
r->frameLayout->setStretch(2,0);
r->downFrame->setVisible(false);
r->animation=new QPropertyAnimation(r->mainFrame,"minimumHeight");
r->animation->setDuration(500);
connect(r->button,SIGNAL(clicked(bool)),animStartMapper,SLOT(map()));
animStartMapper->setMapping(r->button,r->frame_id);
connect(r->animation,SIGNAL(finished()),animFinishedMapper,SLOT(map()));
animFinishedMapper->setMapping(r->animation,r->frame_id);
if(layoutAnimated) {
r->animLayout=new AnimLayout();
r->animLayout->addWidget(r->mainFrame);
main_layout->addItem(r->animLayout);
}
else {
main_layout->addWidget(r->mainFrame);
}
frameMap.insert(r->frame_id,r);
}
main_layout->addItem(new QSpacerItem(10,10,QSizePolicy::Minimum,QSizePolicy::Expanding));
main_layout->setStretch(main_layout->count()-1,1);
}
void Proptest::startAnimation(int frame_id) {
FrameDataStruct *r=frameMap[frame_id];
if(r->expanded) {
r->expanded=false;
if(layoutAnimated) {
r->downFrame->hide();
}
else {
r->downFrame->setVisible(false);
r->animation->setStartValue(r->mainFrame->geometry().height());
r->animation->setEndValue(r->basic_height);
}
} else {
r->expanded=true;
if(layoutAnimated) {
r->downFrame->show();
}
else {
r->basic_height=r->mainFrame->geometry().height();
r->animation->setStartValue(r->basic_height);
r->animation->setEndValue(r->basic_height*2);
r->upFrame->setMinimumHeight(r->upFrame->height());
}
}
if(!layoutAnimated)
r->animation->start();
}
void Proptest::animFinished(int frame_id) {
FrameDataStruct *r=frameMap[frame_id];
if(r->expanded)
r->downFrame->setVisible(true);
}
Proptest::~Proptest() {
}
AnimLayout::AnimLayout(QWidget *parent) :
QLayout(parent) ,
item(0)
{
animation = new QPropertyAnimation(this);
animation->setPropertyName("widgetRect");
animation->setDuration(400);
animation->setTargetObject(this);
}
AnimLayout::~AnimLayout()
{
delete item;
}
void AnimLayout::addItem(QLayoutItem *newItem)
{
Q_ASSERT(!item);
animation->stop();
item =newItem;
emit widgetRectChanged(item->geometry());
invalidate();
}
QSize AnimLayout::sizeHint() const
{
if (!item)
return QSize();
QSize result(item->sizeHint());
int m = 2*margin();
result += QSize(m,m);
return result;
}
void AnimLayout::updateItemPosition()
{
QRect dest = contentsRect();
animation->setEndValue(dest);
if (widgetRect()!=dest) {
animation->start();
}
}
void AnimLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
updateItemPosition();
}
QLayoutItem *AnimLayout::itemAt(int i) const
{
return i==0?item:0;
}
QLayoutItem *AnimLayout::takeAt(int i)
{
Q_ASSERT(i==0);
QLayoutItem *r = item;
item = 0;
return r;
}
QRect AnimLayout::widgetRect() const
{
if (item)
return item->geometry();
return QRect();
}
void AnimLayout::setWidgetRect(const QRect &value)
{
if (item && item->geometry()!=value) {
item->setGeometry(value);
emit widgetRectChanged(item->geometry());
}
}
QSize AnimLayout::minimumSize() const
{
QSize result(item->minimumSize());
int m = 2*margin();
result += QSize(m,m);
return result;
}
int AnimLayout::count() const
{
return item?1:0;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Proptest w;
w.show();
return a.exec();
}
#include "main.moc"
Best Regards
Marek
My answer again, dont know why but previosu was deleted.
I have used Your AnimLayout and it works great.
Below there is example in main.cpp with "layoutAnimated" variable to switch on and off AnimLayout.
#include <QApplication>
#include <QtWidgets>
class AnimLayout : public QLayout
{
Q_OBJECT
Q_PROPERTY(QRect widgetRect
READ widgetRect
WRITE setWidgetRect
NOTIFY widgetRectChanged)
public:
explicit AnimLayout(QWidget *parent = 0);
~AnimLayout();
QSize sizeHint() const;
void setGeometry(const QRect &);
QSize minimumSize() const;
int count() const;
QRect widgetRect() const;
void setWidgetRect(const QRect &value);
void updateItemPosition();
private:
void addItem(QLayoutItem *item);
QLayoutItem *itemAt(int index) const;
QLayoutItem *takeAt(int index);
signals:
void widgetRectChanged(const QRect &value);
public slots:
private:
QLayoutItem *item;
QPropertyAnimation *animation;
};
struct FrameDataStruct {
QFrame *mainFrame;
QFrame *upFrame;
QFrame *downFrame;
QPushButton *button;
QVBoxLayout *upFrameLayout;
QLabel *text;
QVBoxLayout *downFrameLayout;
QVBoxLayout *frameLayout;
QPropertyAnimation *animation;
int frame_id;
int basic_height;
bool expanded;
AnimLayout *animLayout;
};
class Proptest : public QMainWindow
{
Q_OBJECT
public:
explicit Proptest();
~Proptest();
private slots:
void setDataStruct();
void startAnimation(int frame_id);
void animFinished(int frame_id);
private:
QMap<int,FrameDataStruct*> frameMap;
QSignalMapper *animStartMapper;
QSignalMapper *animFinishedMapper;
bool initialized;
QWidget *scrollWidget;
QVBoxLayout *main_layout;
QWidget *widget;
QScrollArea *scrollArea;
QVBoxLayout *central_layout;
bool layoutAnimated;
};
Proptest::Proptest()
: widget(new QWidget)
{
setCentralWidget(widget);
this->setGeometry(200,200,300,600);
central_layout=new QVBoxLayout(widget);
scrollArea=new QScrollArea(widget);
central_layout->addWidget(scrollArea);
animStartMapper=new QSignalMapper(this);
connect(animStartMapper,SIGNAL(mapped(int)),this,SLOT(startAnimation(int)));
animFinishedMapper=new QSignalMapper(this);
connect(animFinishedMapper,SIGNAL(mapped(int)),this,SLOT(animFinished(int)));
scrollWidget=new QWidget(widget);
scrollArea->setWidget(scrollWidget);
main_layout=new QVBoxLayout(scrollWidget);
main_layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
scrollArea->setWidgetResizable(true);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
layoutAnimated=true;
this->setDataStruct();
}
void Proptest::setDataStruct() {
for(int i=0;i<5;i++) {
FrameDataStruct *r=new FrameDataStruct;
r->expanded=false;
r->frame_id=i;
r->mainFrame=new QFrame(scrollWidget);
r->upFrame=new QFrame(r->mainFrame);
r->upFrame->setMinimumHeight(40);
r->button=new QPushButton(QString("open"),r->upFrame);
r->upFrameLayout=new QVBoxLayout(r->upFrame);
r->upFrameLayout->addWidget(r->button);
r->downFrame=new QFrame(r->mainFrame);
r->text=new QLabel(QString("some text SOME TEXT some text"),r->downFrame);
r->downFrameLayout=new QVBoxLayout(r->downFrame);
r->downFrameLayout->addWidget(r->text);
r->frameLayout=new QVBoxLayout(r->mainFrame);
r->frameLayout->addWidget(r->upFrame);
r->frameLayout->addItem(new QSpacerItem(10,10));
r->frameLayout->addWidget(r->downFrame);
r->frameLayout->setStretch(0,0);
r->frameLayout->setStretch(1,1);
r->frameLayout->setStretch(2,0);
r->downFrame->setVisible(false);
r->animation=new QPropertyAnimation(r->mainFrame,"minimumHeight");
r->animation->setDuration(500);
connect(r->button,SIGNAL(clicked(bool)),animStartMapper,SLOT(map()));
animStartMapper->setMapping(r->button,r->frame_id);
connect(r->animation,SIGNAL(finished()),animFinishedMapper,SLOT(map()));
animFinishedMapper->setMapping(r->animation,r->frame_id);
if(layoutAnimated) {
r->animLayout=new AnimLayout();
r->animLayout->addWidget(r->mainFrame);
main_layout->addItem(r->animLayout);
}
else {
main_layout->addWidget(r->mainFrame);
}
frameMap.insert(r->frame_id,r);
}
main_layout->addItem(new QSpacerItem(10,10,QSizePolicy::Minimum,QSizePolicy::Expanding));
main_layout->setStretch(main_layout->count()-1,1);
}
void Proptest::startAnimation(int frame_id) {
FrameDataStruct *r=frameMap[frame_id];
if(r->expanded) {
r->expanded=false;
if(layoutAnimated) {
r->downFrame->hide();
}
else {
r->downFrame->setVisible(false);
r->animation->setStartValue(r->mainFrame->geometry().height());
r->animation->setEndValue(r->basic_height);
}
} else {
r->expanded=true;
if(layoutAnimated) {
r->downFrame->show();
}
else {
r->basic_height=r->mainFrame->geometry().height();
r->animation->setStartValue(r->basic_height);
r->animation->setEndValue(r->basic_height*2);
r->upFrame->setMinimumHeight(r->upFrame->height());
}
}
if(!layoutAnimated)
r->animation->start();
}
void Proptest::animFinished(int frame_id) {
FrameDataStruct *r=frameMap[frame_id];
if(r->expanded)
r->downFrame->setVisible(true);
}
Proptest::~Proptest() {
}
AnimLayout::AnimLayout(QWidget *parent) :
QLayout(parent) ,
item(0)
{
animation = new QPropertyAnimation(this);
animation->setPropertyName("widgetRect");
animation->setDuration(400);
animation->setTargetObject(this);
}
AnimLayout::~AnimLayout()
{
delete item;
}
void AnimLayout::addItem(QLayoutItem *newItem)
{
Q_ASSERT(!item);
animation->stop();
item =newItem;
emit widgetRectChanged(item->geometry());
invalidate();
}
QSize AnimLayout::sizeHint() const
{
if (!item)
return QSize();
QSize result(item->sizeHint());
int m = 2*margin();
result += QSize(m,m);
return result;
}
void AnimLayout::updateItemPosition()
{
QRect dest = contentsRect();
animation->setEndValue(dest);
if (widgetRect()!=dest) {
animation->start();
}
}
void AnimLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
updateItemPosition();
}
QLayoutItem *AnimLayout::itemAt(int i) const
{
return i==0?item:0;
}
QLayoutItem *AnimLayout::takeAt(int i)
{
Q_ASSERT(i==0);
QLayoutItem *r = item;
item = 0;
return r;
}
QRect AnimLayout::widgetRect() const
{
if (item)
return item->geometry();
return QRect();
}
void AnimLayout::setWidgetRect(const QRect &value)
{
if (item && item->geometry()!=value) {
item->setGeometry(value);
emit widgetRectChanged(item->geometry());
}
}
QSize AnimLayout::minimumSize() const
{
QSize result(item->minimumSize());
int m = 2*margin();
result += QSize(m,m);
return result;
}
int AnimLayout::count() const
{
return item?1:0;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Proptest w;
w.show();
return a.exec();
}
#include "main.moc"
Best Regards
Marek
I have different subclasses for my entities. The entities are drawn in GraphicsView, How can apply the Clipboard operations for Cut, Copy and Paste.
gEntity.h
#ifndef GENTITY_H
#define GENTITY_H
class gEntity :public QObject
{
public:
gEntity(QObject* parent=0) : QObject(parent) {}
virtual ~gEntity() {}
virtual gEntity* my_clone() { return 0; }
};
#endif // GENTITY_H
circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
#include <QPainter>
#include <QGraphicsItem>
#include <gentity.h>
#include "qmath.h"
class Circle : public QObject, public QGraphicsItem, public gEntity
{
Q_OBJECT
public:
Circle(QObject* parent=0) : gEntity(parent){
}
Circle(int, QPointF, QPointF);
Circle(int, QPointF, qreal);
QRectF boundingRect() const;
virtual void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget);
enum { Type = UserType + 3 };
int type() const;
int id;
QPointF center_p, end_p, move_p;
qreal radius;
void setRadius((const qreal &radius);
private:
QVector<QPointF> stuff;
};
#endif // CIRCLE_H
circle.cpp
#include "circle.h"
#include "gentity.h"
Circle::Circle(int i, QPointF p1, QPointF p2)
{
// assigns id
id = i;
/* set values of center point, end point
and calculate radius of circle */
center_p = p1;
end_p = p2;
radius = qSqrt(qPow((end_p.x()-center_p.x()), 2)
+ qPow((end_p.y()-center_p.y()), 2));
}
Circle::Circle(int i, QPointF p1, qreal rad)
{
// assigns id
id = i;
/* set values of center point
and radius of circle */
center_p = p1;
radius = rad;
}
int Circle::type() const
{
// Enable the use of qgraphicsitem_cast with circle item.
return Type;
}
QRectF Circle::boundingRect() const
{
// bounding rectangle for circle
return QRectF((center_p.x()-radius), (center_p.y()-radius),
(2*radius), (2*radius));
}
void Circle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
// draws/paints the path of circle
QPen paintpen(Qt::black);
paintpen.setWidth(1);
painter->setRenderHint(QPainter::Antialiasing);
if (isSelected())
{
// sets brush for center point
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
painter->drawEllipse(center_p, 2, 2);
// sets pen for circumference
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setBrush(Qt::NoBrush);
painter->setPen(paintpen);
painter->drawEllipse(center_p, radius, radius);
}
else
{ painter->save();
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawEllipse(center_p, 2, 2);
painter->setBrush(Qt::NoBrush);
painter->drawEllipse(center_p, radius, radius);
painter->restore();
}
}
gEntity* my_clone(){
Circle *c = new Circle();
c->setRadius(radius);
return c;
}
Clipboard.h
#include<QStack>
#include<QClipboard>
class MyClipBoard
{
public:
static MyClipBoard* instance()
{
if(!inst)
inst = new MyClipBoard;
return inst;
}
void push(gEntity* g) {
clips.push(g);
}
gEntity* last()
{
if(clips.count() == 0) return 0;
return clips.last();
}
gEntity* pop()
{
if(clips.count() == 0) return 0;
return clips.pop();
}
bool isempty() const { return clips.empty(); }
private:
QStack<gEntity*> clips;
static MyClipBoard* inst;
};
CadgraphicsScene.cpp
MyClipBoard* MyClipBoard::inst = 0;
CadGraphicsScene::CadGraphicsScene(QObject *parent, QUndoStack *undoStack)
: QGraphicsScene(parent)
{
setFlags();
id = 0;
mUndoStack = undoStack;
m_context = new QMenu;
m_context->addAction("Insert Circle");
a_cut = m_context->addAction("cut");
a_copy = m_context->addAction("copy");
a_paste = m_context->addAction("paste");
context_item = 0;
connect(m_context, SIGNAL(triggered(QAction*)), this, SLOT(contmenu(QAction*)));
}
void CadGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event){
m_context->exec(event->screenPos());
}
void CadGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// mousePressEvent in the graphicsScene
if(mouseEvent->button() == Qt::LeftButton)
{
switch (entityMode)
{
case NoMode:
qDebug() << "No Mode";
break;
case PointMode:
pointItem = new Point(++id);
pointItem->setPos(mouseEvent->scenePos());
itemList.append(pointItem);
mUndoStack->push(new CadCommandAdd(this, pointItem));
break;
case LineMode:
if (mFirstClick)
{
start_p = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = true;
}
else if (!mFirstClick && mSecondClick)
{
end_p = mouseEvent->scenePos();
mPaintFlag = true;
mSecondClick = false;
}
if (mPaintFlag)
{
lineItem = new Line(++id, start_p, end_p);
lineItem->setLine(start_p.x(), start_p.y(), end_p.x(), end_p.y());
itemList.append(lineItem);
mUndoStack->push(new CadCommandAdd(this, lineItem));
setFlags();
}
break;
case CircleMode:
if (mFirstClick)
{
start_p = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = true;
}
else if (!mFirstClick && mSecondClick)
{
end_p = mouseEvent->scenePos();
mPaintFlag = true;
mSecondClick = false;
}
if (mPaintFlag)
{
circleItem = new Circle(++id, start_p, end_p);
itemList.append(circleItem);
mUndoStack->push(new CadCommandAdd(this, circleItem));
setFlags();
}
break;
case EllipseMode:
if (mFirstClick)
{
start_p = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = true;
}
else if (!mFirstClick && mSecondClick)
{
mid_p = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = false;
mThirdClick = true;
}
else if (!mSecondClick && mThirdClick)
{
end_p = mouseEvent->scenePos();
mThirdClick = false;
mPaintFlag = true;
}
if (mPaintFlag)
{
ellipseItem = new Ellipse(++id, start_p, mid_p, end_p);
itemList.append(ellipseItem);
mUndoStack->push(new CadCommandAdd(this, ellipseItem));
setFlags();
}
break;
case TextMode:
textItem = new mText(++id);
textItem->setPos(mouseEvent->scenePos());
itemList.append(textItem);
textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
mUndoStack->push(new CadCommandAdd(this, textItem));
connect(textItem, SIGNAL(lostFocus(mText*)),
this, SLOT(editorLostFocus(mText*)));
connect(textItem, SIGNAL(selectedChange(QGraphicsItem*)),
this, SIGNAL(itemSelected(QGraphicsItem*)));
setFlags();
default:
;
}
}else if(event->button() & Qt::RightButton)
{
context_item = itemAt(event->scenePos().toPoint(), QTransform());//base operand not a pointer
cpos = event->scenePos();//says invalid use of member function
if(!context_item)//Here it says all variables out of scope
{
a_cut->setEnabled(false);
a_copy->setEnabled(false);
if(MyClipBoard::instance()->isempty())
a_paste->setEnabled(false);
else a_paste->setEnabled(true);
}
else
{
a_cut->setEnabled(true);
a_copy->setEnabled(true);
a_paste->setEnabled(false);
}
}
QGraphicsScene::mousePressEvent(mouseEvent);
}
cadgraphicsscene.h
private:
QMenu* m_context;
QAction* a_cut;
QAction* a_copy;
QAction* a_paste;
QGraphicsItem* context_item;
QPointF cpos;
what are you mean of cutting a grahicsitem? (change its position or move to another view)
for copy and paste search for cloning a object in c++ it's not hard, and in paste just change position of new object and add that to your graphicsview or scene
update:
#ifndef MYSCENE_H
#define MYSCENE_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>
#include <QStack>
class gEntity : public QObject, public QGraphicsItem
{
public:
gEntity(QObject* parent=0) : QObject(parent) {}
virtual ~gEntity() {}
virtual gEntity* my_clone() { return 0; }
};
class Circle : public gEntity
{
public:
Circle(QObject* parent=0) : gEntity(parent) {
m_radius = 10;
}
qreal radius() const;
void setRadius(const qreal &radius);
gEntity* my_clone()
{
Circle* c = new Circle;
c->setRadius(m_radius);
return c;
}
QRectF boundingRect() const
{
return QRectF(-m_radius, -m_radius, 2 * m_radius, 2 * m_radius);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->save();
painter->setBrush(Qt::yellow);
painter->drawEllipse(QPointF(0,0), m_radius, m_radius);
painter->restore();
}
private:
qreal m_radius;
};
class MyClipBoard
{
public:
static MyClipBoard* instance()
{
if(!inst)
inst = new MyClipBoard;
return inst;
}
void push(gEntity* g) {
clips.push(g);
}
gEntity* last()
{
if(clips.count() == 0) return 0;
return clips.last();
}
gEntity* pop()
{
if(clips.count() == 0) return 0;
return clips.pop();
}
bool isempty() const { return clips.empty(); }
private:
QStack<gEntity*> clips;
static MyClipBoard* inst;
};
class MyScene : public QGraphicsScene
{
Q_OBJECT
public:
MyScene(QObject* parent=0);
virtual ~MyScene()
{
delete m_context;
}
protected:
void contextMenuEvent(QGraphicsSceneContextMenuEvent* event);
void mousePressEvent(QGraphicsSceneMouseEvent* event);
public slots:
void insertCircle(const QPointF& pos)
{
Circle* mcircle = new Circle;
addItem(mcircle);
mcircle->setPos(pos);
}
void cut(gEntity* obj)
{
removeItem(obj);
MyClipBoard::instance()->push(obj);
}
void copy(gEntity* obj)
{
MyClipBoard::instance()->push(obj->my_clone());
}
void paste(const QPointF& pos)
{
gEntity* last = MyClipBoard::instance()->pop();
if(last)
{
addItem(last);
last->setPos(pos);
}
}
void contmenu(QAction* a)
{
if(a->text() == "Insert Circle")
{
insertCircle(cpos);
}
else if(a == a_cut)
{
cut(static_cast<gEntity*>(context_item));
}
else if(a == a_copy)
{
copy(static_cast<gEntity*>(context_item));
}
else if(a == a_paste)
{
paste(cpos);
}
}
private:
QMenu* m_context;
QAction* a_cut;
QAction* a_copy;
QAction* a_paste;
QGraphicsItem* context_item;
QPointF cpos;
};
#endif // MYSCENE_H
scene.cpp
#include "myscene.h"
#include <QGraphicsSceneMouseEvent>
MyClipBoard* MyClipBoard::inst = 0;
qreal Circle::radius() const
{
return m_radius;
}
void Circle::setRadius(const qreal &radius)
{
m_radius = radius;
}
MyScene::MyScene(QObject *parent) : QGraphicsScene(parent)
{
m_context = new QMenu;
m_context->addAction("Insert Circle");
a_cut = m_context->addAction("cut");
a_copy = m_context->addAction("copy");
a_paste = m_context->addAction("paste");
context_item = 0;
connect(m_context, SIGNAL(triggered(QAction*)), this, SLOT(contmenu(QAction*)));
}
void MyScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event){
m_context->exec(event->screenPos());
}
void MyScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsScene::mousePressEvent(event);
if(event->button() & Qt::RightButton)
{
context_item = itemAt(event->scenePos().toPoint(), QTransform());
cpos = event->scenePos();
if(!context_item)
{
a_cut->setEnabled(false);
a_copy->setEnabled(false);
if(MyClipBoard::instance()->isempty())
a_paste->setEnabled(false);
else a_paste->setEnabled(true);
}
else
{
a_cut->setEnabled(true);
a_copy->setEnabled(true);
a_paste->setEnabled(false);
}
}
}
main.cpp
#include <QApplication>
#include "myscene.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView w(new MyScene);
w.setSceneRect(0,0,500,500);
w.show();
return a.exec();
}
I'm using QGraphicView to show game map that consist QGraphicsPixmapItems. I need to show tooltip on mouse hover at QGraphicsPixmapItem.
For saving QGraphicsPixmapItem position I using MazeItem:
#ifndef MAZEITEM_H
#define MAZEITEM_H
#include <QPoint>
#include <QGraphicsItem>
class MazeItem
{
private:
QPoint myPosition;
QString myToolTip;
public:
MazeItem();
QPoint position() const;
QString toolTip() const;
void setToolTip(const QString &toolTip);
void setPosition(const QPoint &position);
QPoint getPosition();
QGraphicsPixmapItem * pixmap;
};
#endif // MAZEITEM_H
I have widget class to display game map:
#include <QWidget>
#include <QtGui>
#include <QGraphicsView>
#include <QToolTip>
#include "mazeitem.h"
class MazeGUI : public QWidget
{
Q_OBJECT
private:
QGraphicsView * graphicsView;
QGraphicsScene * graphicsScene;
QString sceneString;
int imageSize;
QList<MazeItem> mazeItems;
void addItem(int x, int y, QPixmap picture);
bool event(QEvent *event);
int itemAt(const QPoint &pos);
public:
explicit MazeGUI(QWidget *parent = 0);
void setScene(QString sceneString);
signals:
public slots:
void redraw();
};
#endif // MAZEGUI_H
In constructor I set mouse tracking.
MazeGUI::MazeGUI(QWidget *parent) :
QWidget(parent)
{
setMouseTracking(true);
...
}
This is how I add new maze item.
void MazeGUI::addItem(int x, int y, QPixmap picture)
{
MazeItem mazeItem;
mazeItem.setPosition(QPoint(x, y));
mazeItem.setToolTip("text");
mazeItem.pixmap = this->graphicsScene->addPixmap(picture);
mazeItem.pixmap->setPos(y, x);
mazeItems.append(mazeItem);
}
And this I have from Qt tutorials,
bool MazeGUI::event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
// HERE - it never goes here!!
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
int index = itemAt(helpEvent->pos());
if (index != -1) {
QToolTip::showText(helpEvent->globalPos(), mazeItems[index].toolTip());
} else {
QToolTip::hideText();
event->ignore();
}
return true;
}
return QWidget::event(event);
}
int MazeGUI::itemAt(const QPoint &pos)
{
for (int i=0; i < mazeItems.size(); ++i)
{
if (mazeItems[i].getPosition() == pos)
return i;
}
return -1;
}
Was adding the tooltip on wrong object:
Instead of:
mazeItem.setToolTip("text");
It should be:
mazeItem.pixmap->setToolTip("text");
How exactly can I subclass QGraphicsLayoutItem?
I write a class that subclass QGraphicsLayoutItem and reimplement sizeHint and setGeometry but when I add my custom item to linear or grid layout. It does not shown?
What is missing?
Here is my trial.
//basicitem.h
#include <QGraphicsWidget>
#include <QtCore/QRectF>
#include <QtGui/QPainter>
#include <QtGui/QBrush>
#include <QtGui/QPen>
#include <QtCore/QSizeF>
class BasicItem : public QGraphicsWidget
{
public:
BasicItem(qreal x1,qreal y1,qreal x2,qreal y2);
~BasicItem();
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
private:
qreal x1,y1,x2,y2;
};
//basicitem.cpp
#include "basicitem.h"
#include <math.h>
BasicItem::BasicItem(qreal x1, qreal y1, qreal x2, qreal y2)
{
this->x1 = x1;
this->y1 = y1;
this->x2 = x2;
this->y2 = y2;
}
BasicItem::~BasicItem()
{
}
QRectF BasicItem::boundingRect() const
{
return QRectF(x1,y1,abs(x2-x1),5);
}
void BasicItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(QBrush(Qt::yellow),4,Qt::SolidLine,Qt::FlatCap,Qt::BevelJoin));
painter->drawLine(x1,y1,x2,y2);
}
QSizeF BasicItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
Q_UNUSED(which);
Q_UNUSED(constraint);
return QSizeF(abs(x2-x1),5);
}
//compositeitem.h
#include <QGraphicsItemGroup>
#include <QGraphicsLayoutItem>
#include <QSizeF>
#include <QList>
#include "basicitem.h"
class CompositeItem:public QGraphicsItemGroup,public QGraphicsLayoutItem
{
public: CompositeItem(QList<BasicItem *> children);
~CompositeItem();
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
void setGeometry(const QRectF &rect);
void updateGeometry();
};
//compositeitem.cpp
#include "compositeitem.h"
CompositeItem::CompositeItem(QList<BasicItem *> children)
{
BasicItem *child;
foreach(child,children)
{
addToGroup(child);
}
}
CompositeItem::~CompositeItem()
{
}
QSizeF CompositeItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
Q_UNUSED(which);
Q_UNUSED(constraint);
//Arbitrary values but big enough
return QSizeF(300,300);
}
void CompositeItem::setGeometry(const QRectF &rect)
{
Q_UNUSED(rect);
QGraphicsLayoutItem::setGeometry(QRectF(0,0,300,300));
}
void CompositeItem::updateGeometry()
{
QGraphicsLayoutItem::updateGeometry();
}
//mainwindow.cpp - important parts
...
QGraphicsScene *scene = new QGraphicsScene;
ui->graphicsView->setScene(scene);
BasicItem *basic1 = new BasicItem(10,400,110,400);
BasicItem *basic2 = new BasicItem(10,400,110,500);
QList<BasicItem *> basicItemList;
basicItemList.push_back(basic1);
basicItemList.push_back(basic2);
CompositeItem *ci = new CompositeItem(basicItemList);
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
layout->addItem(ci);
QGraphicsWidget *container = new QGraphicsWidget;
container->setLayout(layout);
scene->addItem(container);
...
May be it will be useful, just a small example from my working project:
GraphicsLayoutItem::GraphicsLayoutItem( QGraphicsItem *parent ) :
QGraphicsItemGroup(parent),
QGraphicsLayoutItem(),
{
addToGroup(item_);
addToGroup(text_item_);
setGraphicsItem(this);
}
GraphicsLayoutItem::~GraphicsLayoutItem()
{
removeFromGroup( item_ );
removeFromGroup( text_item_ );
delete item_;
delete text_item_;
setGraphicsItem(0);
}
void GraphicsLayoutItem::setGeometry( const QRectF &r )
{
prepareGeometryChange();
QGraphicsLayoutItem::setGeometry(r);
setPos(r.topLeft());
}
QRectF GraphicsLayoutItem::boundingRect() const
{
return QRectF(QPointF(0, 0), geometry().size());
}
QSizeF GraphicsLayoutItem::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(),
MyOptions::DefaultHeight);
default:
return this->boundingRect().size();
}
return constraint;
}