How do I use paintEvent to paint a QImage from video data? - c++

I construct a QImage with data I get from an API call. This QImage I will draw with the overridden paintEvent(QPaintEvent*) method into a widget. From the function that does the API call I emit a signal that sends the grabbed data to a slot that constructs the QImage. With this process there is no problem. The data is transfered and the construct of the image works.
My question is how can I use the constucted image in the overridden paintEvent? I can open a image from the disk and draw it into the widget, but this works from inside the paintEvent() method. With the API call I got the data from outside.
class GrabberClass : public QObject
{
Q_OBJECT
public:
GrabberClass(QObject* parent = nullptr);
virtual ~GrabberClass();
bool applyFrameCallback();
bool getFrameData(DtMxData* data);
bool startProcessing();
private:
RenderClass *m_renderClass;
DtMxProcess *m_process;
DTAPI_RESULT m_result;
DtFrame *m_Frame
signals:
void sendFrameData(uchar* pData, int width, int height, int bytesPerRow);
};
//GrabberClass.cpp
//constructor
GrabberClass::GrabberClass(QObject *parent) :
m_renderClass(new RenderClass)
{
const bool connected = QObject::connect(this, &GrabberClass::sendFrameData, m_renderClass, &RenderClass::receiveFrameData, Qt::DirectConnection);
Q_ASSERT(connected); //value is true.
}
bool GrabberClass::applyFrameCallback()
{
auto frameCallback = [](DataContainer* frameData, void* pContext)
{
((GrabberClass*)pContext)->getFrameData(frameData);
};
m_result = m_process->AddMatrixCbFunc(frameCallback, this);
if (m_result != DTAPI_OK) {
return false;
}
return true;
}
bool GrabberClass::grabFrame(DataContainer* frameData)
{
m_Frame = frameData->m_Rows[IN_ROW].m_CurFrame;
uchar* pData = m_Frame->m_Video->m_Planes->m_pBuf;
int bytesPerRow = m_Frame->m_Video->m_Planes->m_Stride;
int frameWidth = m_Frame->m_Video->m_Width;
int frameHeight = m_Frame->m_Video->m_Height;
emit sendFrameData(pData, frameWidth, frameHeight, bytesPerRow);
return true;
}
//Render Class.h
class RenderClass : public QOpenGLWidget {
Q_OBJECT
public:
RenderClass(QWidget* parent = nullptr);
virtual ~RenderClass();
private:
QImage m_srcImage;
protected:
void paintEvent(QPaintEvent*);
public slots:
void receiveFrameData(uchar* pData, int width, int height, int bytesPerRow);
};
//RenderClass.cpp
VideoMonitor::VideoMonitor(QWidget* parent) :
QOpenGLWidget(parent),
m_srcImage(nullptr)
{
}
void RenderClass::receiveFrameData(uchar* pData, int width, int height, int bytesPerRow)
{
m_srcImage = QImage(pData, width, height, bytesPerRow, QImage::Format_Indexed8);
qDebug() << "Slot is called." << m_srcImage; //QImage is constructed with valid data
QWidget::update();
}
void RenderClass::paintEvent(QPaintEvent*)
{
if (!m_srcImage.isNull()) { //m_srcImage is null
QPainter painter(this);
painter.drawImage(this->rect(), m_srcImage);
painter.end();
qDebug() << "Draw image.";
}
else {
qDebug() << "QImage is null";
}
}
I'am new to C++, so it would be nice if someone of you can give me an advice and explaination.

Related

Qt C++ Drag QHeaderView between tables

I want to copy the selected column of a QTableWidget to another one.
So I tried to make selected columns draggable by adding this code:
void makeDraggable(QTableWidget *table)
{
table->setDragEnabled(true);
table->setAcceptDrops(true);
table->setSelectionBehavior(QAbstractItemView::SelectColumns);
}
Result I got:
But I want to drag a whole column (horizontal and vertical headers) by clicking on headers only, not on cells, and copy its data to another table including the header text.
Dragging between different tables inside one application can be done with reimplementing custom QHeaderView and QTableWidget. In my example I generate text with indecies of table and column for drag event. Custom header:
#include <QHeaderView>
class ITableManager;
class DraggableHeaderView : public QHeaderView
{
Q_OBJECT
public:
explicit DraggableHeaderView(Qt::Orientation orientation, QWidget *parent = 0);
int tag() const;
void setTag(const int tag);
void setTableManager(ITableManager* manager);
protected:
void mouseMoveEvent(QMouseEvent *e);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
signals:
public slots:
private:
int m_tag; //internal index of table
ITableManager *m_tableManager; //manager will convert table index into pointer
};
Custom header cpp
#include <QMouseEvent>
#include <QDrag>
#include <QMimeData>
#include <QDebug>
#include <QTableWidget>
#include <ITableManager.h>
DraggableHeaderView::DraggableHeaderView(Qt::Orientation orientation, QWidget *parent) :
QHeaderView(orientation, parent)
{
m_tag = 0;
m_tableManager = 0;
setAcceptDrops(true);
}
void DraggableHeaderView::mouseMoveEvent(QMouseEvent *e)
{
if (e->buttons() & Qt::LeftButton)
{
int index = logicalIndexAt(e->pos());
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
//custom drag text with indecies inside
QString mimeTxt = "MoveHeader;Table:" + QString::number(m_tag) +
";Index:" + QString::number(index);
mimeData->setText(mimeTxt);
drag->setMimeData(mimeData);
Qt::DropAction dropAction = drag->exec();
}
}
int DraggableHeaderView::tag() const
{
return m_tag;
}
void DraggableHeaderView::setTag(const int tag)
{
m_tag = tag;
}
void DraggableHeaderView::dragEnterEvent(QDragEnterEvent *event)
{
if (!m_tableManager)
{
event->ignore();
return;
}
QString dragText = event->mimeData()->text();
int index = dragText.indexOf("MoveHeader;");
if (index == 0)
{
event->accept();
}
else
{
event->ignore();
}
}
void DraggableHeaderView::dropEvent(QDropEvent *event)
{
if (!m_tableManager)
{
event->ignore();
return;
}
QStringList dragText = event->mimeData()->text().split(';');
if (dragText.count() < 3 || dragText.at(0) != "MoveHeader")
{
event->ignore();
return;
}
int tableIndex = dragText.at(1).mid(6).toInt();//6 - length 'Table:'
QTableWidget* tableSrc = m_tableManager->getTableFromIndex(tableIndex);
if (!tableSrc)
{
event->ignore();
return;
}
//dst table as parent for header view
QTableWidget *tableDst = qobject_cast<QTableWidget*> (this->parentWidget());
if (!tableDst)
{
event->ignore();
return;
}
//move column: modify for your needs
//now moves only items text
int columnIndex = logicalIndexAt(event->pos());
int srcColumnIndex = dragText.at(2).mid(6).toInt(); //6 - length of 'Index:'
tableDst->insertColumn(columnIndex);
for (int iRow = 0; iRow < tableDst->rowCount() && iRow < tableSrc->rowCount(); ++iRow)
{
if (tableSrc->item(iRow, srcColumnIndex))
{
tableDst->setItem(iRow, columnIndex,
new QTableWidgetItem(tableSrc->item(iRow, srcColumnIndex)->text()));
}
else
{
tableDst->setItem(iRow, columnIndex, new QTableWidgetItem());
}
}
tableSrc->removeColumn(srcColumnIndex);
}
void DraggableHeaderView::setTableManager(ITableManager *manager)
{
m_tableManager = manager;
}
Now create custom QTableWidget with DraggableHeaderView inside
class CustomTableWidget : public QTableWidget
{
Q_OBJECT
public:
explicit CustomTableWidget(QWidget *parent = 0);
void setTag(const int tag);
void setTableManager(ITableManager* manager);
};
CustomTableWidget::CustomTableWidget(QWidget *parent) :
QTableWidget(parent)
{
DraggableHeaderView *headerView = new DraggableHeaderView(Qt::Horizontal, this);
setHorizontalHeader(headerView);
setAcceptDrops(true);
}
void CustomTableWidget::setTag(const int tag)
{
DraggableHeaderView *header = qobject_cast<DraggableHeaderView*> (horizontalHeader());
if (header)
{
header->setTag(tag);
}
}
void CustomTableWidget::setTableManager(ITableManager *manager)
{
DraggableHeaderView *header = qobject_cast<DraggableHeaderView*> (horizontalHeader());
if (header)
{
header->setTableManager(manager);
}
}
For converting table index to pointer I use ITableManager
class ITableManager
{
public:
virtual QTableWidget* getTableFromIndex(const int index) = 0;
};
And implement it in QMainWindow
class MainWindow : public QMainWindow, ITableManager
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QTableWidget* getTableFromIndex(const int index);
}
QTableWidget * MainWindow::getTableFromIndex(const int index)
{
switch (index)
{
case 1:
return ui->tableWidget;
case 2:
return ui->tableWidget_2;
default:
return nullptr;
}
}
Dont forget setup tags (indecies) and table manager for tables (in main window constructor)
ui->tableWidget->setTag(1);
ui->tableWidget_2->setTag(2);
ui->tableWidget->setTableManager(this);
ui->tableWidget_2->setTableManager(this);
EDIT: If you want change custom pixmap for dragging just set QDrag::setPixmap
void DraggableHeaderView::mouseMoveEvent(QMouseEvent *e)
{
if (e->buttons() & Qt::LeftButton)
{
int index = logicalIndexAt(e->pos());
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
QString mimeTxt = "MoveHeader;Table:" + QString::number(m_tag) +
";Index:" + QString::number(index);
mimeData->setText(mimeTxt);
drag->setMimeData(mimeData);
drag->setPixmap(pixmapForDrag(index));
Qt::DropAction dropAction = drag->exec();
}
}
And method for taking pixmap of column can be like this
QPixmap DraggableHeaderView::pixmapForDrag(const int columnIndex) const
{
QTableWidget *table = qobject_cast<QTableWidget*> (this->parentWidget());
if (!table)
{
return QPixmap();
}
//image for first 5 row
int height = table->horizontalHeader()->height();
for (int iRow = 0; iRow < 5 && iRow < table->rowCount(); ++iRow)
{
height += table->rowHeight(iRow);
}
//clip maximum size
if (height > 200)
{
height = 200;
}
QRect rect(table->columnViewportPosition(columnIndex) + table->verticalHeader()->width(),
table->rowViewportPosition(0),
table->columnWidth(columnIndex),
height);
QPixmap pixmap(rect.size());
table->render(&pixmap, QPoint(), QRegion(rect));
return pixmap;
}

Qt cannot save pixmap

Hello i have a strange problem to save pixmap.
My Widget Header
public:
QPixmap *base; //Base Poses
QPixmap *Hair; //Hair
QPixmap *Composited; //Final Composition
bool compisition = false;
void Composite();
My Widget Cpp
At paintEvent
base = &pic;
Hair = &hairs;
if(compisition)
{
QPixmap pix(128,192);
QPainter *p = new QPainter(&pix);
p->drawPixmap(0,0,128,192,*base);
p->drawPixmap(0,0,128,192,*Hair);
Composited = &pix;
compisition = false;
}
void AnimPreview::Composite()
{
compisition = true;
this->update();
}
At main form source
void MainWindow::on_commandLinkButton_clicked()
{
QString file = QFileDialog::getSaveFileName(this,
tr("Save Sprite file"),
"",tr("File PNG (*.png)"));
const QPixmap *pix = ui->SpriteFront->pixmap();
if(!file.isEmpty())
{
QFile files(file);
files.open(QIODevice::WriteOnly);
ui->SpriteFront->Composite();
ui->SpriteFront->Composited->save(&files,"PNG");
}
}
When i try to save a file, the process work but whit on error
An unhandled win32 exception
For more information
complete code here
https://pastebin.com/GtaVCXGf
I have avoided reviewing where the error can be generated since there are many possible sources of the problem, among them the following:
It is not necessary that you create QPixmap pointers since in the end you will have the job of eliminating it from memory.
The same happens with QPainter since it only needs to be a local variable, in addition to that the painting is not done immediately, to be sure that it is painted you must call its end() method.
paintEvent is a protected method, so by design we prefer it to remain so.
It is not necessary to use a QFile to save an image, you can directly pass the filename to it.
Considering all the above, we obtain the following:
*.h
#ifndef ANIMPREVIEW_H
#define ANIMPREVIEW_H
#include <QLabel>
#include <QPixmap>
class AnimPreview : public QLabel
{
public:
AnimPreview(QWidget *parent = 0);
void Rotate(int value);
void Composite();
void Create(int _sex, int _hair);
QPixmap Composited;
private:
int sex = 0;
int hair = 0;
bool draw = true;
int rotation = 0;
const int offsetX = 16;
const int offsetY = 32;
QPixmap base;
QPixmap Hair;
bool compisition = false;
protected:
void paintEvent(QPaintEvent *);
};
#endif // ANIMPREVIEW_H
*.cpp
#include "animpreview.h"
#include <QPainter>
AnimPreview::AnimPreview(QWidget *parent):QLabel(parent)
{
}
void AnimPreview::paintEvent(QPaintEvent *){
QPainter p(this);
QPixmap pic;
QPixmap hairs;
if(draw)
{
//Sesso
switch(sex)
{
case 0:
pic.load(":/Male/Base/Res/man_f.png");
break;
case 1:
pic.load(":/Male/Base/Res/woman_f.png");
break;
}
//capelli
if(sex == 1)
{
switch(hair)
{
case 1:
hairs.load(":/Female/Hair/Res/7_aqua.png");
break;
case 2:
hairs.load(":/Female/Hair/Res/5_gold.png");
break;
}
}
if(sex == 0)
{
switch (hair)
{
case 0:
break;
case 1:
hairs.load(":/Male/Hair/Res/1_aqua.png");
break;
case 2:
hairs.load(":/Male/Hair/Res/14_black.png");
break;
}
}
}
p.drawPixmap(width()/2 - offsetX,height()/2 - offsetY,pic,0,rotation,32,48);
p.drawPixmap(width()/2 - offsetX,height()/2 - offsetY,hairs,0,rotation,32,48);
p.drawRect(0,0, width()-1, height()-1);
base = pic;
Hair = hairs;
if(compisition)
{
QPixmap pix(128,192);
QPainter p(&pix);
p.drawPixmap(0,0,128,192, base);
p.drawPixmap(0,0,128,192, Hair);
p.end();
Composited = pix;
compisition = false;
}
}
void AnimPreview::Rotate(int value)
{
rotation = value;
update();
}
void AnimPreview::Create(int _sex, int _hair)
{
sex = _sex;
hair = _hair;
draw = true;
}
void AnimPreview::Composite()
{
compisition = true;
update();
}
void MainWindow::on_commandLinkButton_clicked()
{
QString file = QFileDialog::getSaveFileName(this,
tr("Save Sprite file"),
"",tr("File PNG (*.png)"));
if(!file.isEmpty())
{
ui->SpriteFront->Composite();
ui->SpriteFront->Composited.save(file,"PNG");
}
}

Qthread signal emit not connecting

I have two signals that are connecting and one that will not.
I have a mainwindow widget to display images processed by a Qthread which emits the original and processed images to the my mainwidow where they are displayed, this works. I would also like to emit a frames per second calculation from this thread, but it will not connect.
all code here:
https://github.com/ianzur/qtGui_imageProcessing
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Timer for UI responsivness
tmrTimer = new QTimer(this);
connect(tmrTimer,SIGNAL(timeout()),this,SLOT(UpdateGUI()));
tmrTimer->start(20);
// Set initial sliders postion (for selecting ROI)
ui->SldLower->setSliderPosition(0);
ui->SldUpper->setSliderPosition(480);
ui->SldLeft->setSliderPosition(0);
ui->SldRight->setSliderPosition(640);
//connections to video processor thread
const bool c = connect(&processor,SIGNAL(outFPS(float)),ui->FPS,SLOT(setNum(float)));
connect(&processor,SIGNAL(inDisplay(QPixmap)),ui->inVideo,SLOT(setPixmap(QPixmap)));
connect(&processor,SIGNAL(outDisplay(QPixmap)),ui->outVideo,SLOT(setPixmap(QPixmap)));
qDebug() << "connected" << c;
}
Thread declaration
class ProcessorThread : public QThread
{
Q_OBJECT
public:
explicit ProcessorThread(QObject *parent = 0);
void update(QRect r, float low, float high);
int CLOCK();
float avgfps();
signals:
void inDisplay(QPixmap pixmap);
void outDisplay(QPixmap pixmap);
void outFPS(float fps);
protected:
void run() override;
private:
cv::VideoCapture capture;
//Region of interest
cv::Rect myROI;
float lowHz;
float highHz;
//float fps;
int _fpsstart=0;
float _avgfps=0;
float _fps1sec=0;
};
Thread definition
ProcessorThread::ProcessorThread(QObject *parent) : QThread(parent)
{
}
int ProcessorThread::CLOCK()
{
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (t.tv_sec * 1000)+(t.tv_nsec*1e-6);
}
float ProcessorThread::avgfps()
{
if(CLOCK()-_fpsstart>1000)
{
_fpsstart=CLOCK();
_avgfps=0.9*_avgfps+0.1*_fps1sec;
_fps1sec=0;
}
_fps1sec++;
return _avgfps;
}
void ProcessorThread::run()
{
VideoCapture camera(0);
cv::Mat inFrame, outFrame, cropped, bit;
while(camera.isOpened() && !isInterruptionRequested())
{
camera >> inFrame;
if(inFrame.empty())
continue;
outFrame = inFrame.clone();
cropped = inFrame(myROI);
bitwise_not(cropped, bit);
bit.copyTo(outFrame(myROI));
//qDebug() << avgfps();
emit outFPS(avgfps());
emit inDisplay(QPixmap::fromImage(QImage(inFrame.data,inFrame.cols,inFrame.rows,inFrame.step,QImage::Format_RGB888).rgbSwapped()));
emit outDisplay(QPixmap::fromImage(QImage(outFrame.data,outFrame.cols,outFrame.rows,outFrame.step,QImage::Format_RGB888).rgbSwapped()));
}
}
void ProcessorThread::update(QRect r, float low, float high)
{
this->myROI = cv::Rect(r.x(),r.y(),r.width(),r.height());
this->lowHz = low;
this->highHz = high;
}
I cannot figure out why only the outFPS will not connect.
My first thoughts, looking at this line:
const bool c = connect(&processor,SIGNAL(outFPS(float)),ui->FPS, SLOT(setNum(float)));
In your UI, FPS looks like a QPlainTextEdit. I don't see a setNum() slot documented for that class. There is one for a QLabel (it takes an int or double), is that what you meant to use.

Qt QPropertyAnimation for widgets in layout - widgets shaking

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

Qt drag & drop button; drop not detecting

I'm creating a 2D game in QT and i'm trying to implement a drag & drop into my program.
For some reason the drop is not registered: qDebug should print a message on dropping but this doesn't happen.
#include "dialog.h"
#include "ui_dialog.h"
#include "world.h"
#include <vector>
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
MySquare *item;
QGraphicsRectItem *enemyItem;
World *myWorld = new World();
std::vector<Tile*> tiles = myWorld->createWorld(":/texture.jpg");
int count = 0;
foreach (Tile *tile, tiles){
count++;
item = new MySquare(tile->getXPos()*4,tile->getYPos()*4,4,4);
item->setBrush(QColor(tile->getValue()*255,tile->getValue()*255,tile->getValue()*255));
item->setAcceptDrops(true);
scene->addItem(item);
}
player = new MySquare(10,20,10,10);
player->setAcceptDrops(true);
scene->addItem(player);
//drag & drop part
QPushButton *pushButton = new QPushButton("Click Me",this);
connect(pushButton,SIGNAL(pressed()),this,SLOT(makeDrag()));
setAcceptDrops(true);
}
void Dialog::makeDrag()
{
QDrag *dr = new QDrag(this);
// The data to be transferred by the drag and drop operation is contained in a QMimeData object
QMimeData *data = new QMimeData;
data->setText("This is a test");
// Assign ownership of the QMimeData object to the QDrag object.
dr->setMimeData(data);
// Start the drag and drop operation
dr->start();
}
mysquare.cpp
#include "mysquare.h"
MySquare::MySquare(int _x,int _y, int _w, int _h)
{
isPlayer=false;
Pressed=false;
setFlag(ItemIsMovable);
setFlag(ItemIsFocusable);
setAcceptDrops(true);
color=Qt::red;
color_pressed = Qt::green;
x = _x;
y = _y;
w = _w;
h = _h;
}
QRectF MySquare::boundingRect() const
{
return QRectF(x,y,w,h);
}
void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rec = boundingRect();
QBrush brush(color);
if (Pressed){
brush.setColor(color);
} else {
brush.setColor(color_pressed);
}
painter->fillRect(rec,brush);
painter->drawRect(rec);
}
void MySquare::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=true;
update();
QGraphicsItem::mousePressEvent(event);
qDebug() << "mouse Pressed";
}
void MySquare::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=false;
update();
QGraphicsItem::mousePressEvent(event);
qDebug() << "mouse Released";
}
void MySquare::keyPressEvent(QKeyEvent *event){
int x = pos().x();
int y = pos().y();
//key handling
QGraphicsItem::keyPressEvent(event);
}
void MySquare::dropEvent(QDropEvent *event)
{
qDebug("dropEvent - square");
// Unpack dropped data and handle it the way you want
qDebug("Contents: %s", event->mimeData()->text().toLatin1().data());
}
void MySquare::dragMoveEvent(QDragMoveEvent *event){
qDebug("dragMoveEvent - square ");
event->accept();
}
void MySquare::dragEnterEvent(QDragEnterEvent *event){
event->setAccepted(true);
qDebug("dragEnterEvent - square");
event->acceptProposedAction();
}
void MySquare::setBrush(QColor _color){
color = _color;
color_pressed = _color;
update(); //repaint
}
edit; there is no problem with qDebug() i'm just using it to test them i'm inside the drag events..which i'm not
In your mouseReleaseEvent, you pass to QGraphicsItem::mousePressEvent instead of QGraphicsItem::mouseReleaseEvent
Edit: I don't know if this matters, but initialize the QGraphicsItem in your constructor
MySquare::MySquare(int _x,int _y, int _w, int _h) : QGraphicsItem()