PDA

View Full Version : Creating a Pixmap for drag and drop



Cruz
19th January 2009, 09:38
Hello,

I would like to drag and drop QFrames around in my own application and I'm wondering if there is a generic way to construct the Pixmaps of the QFrames that are shown when the frame is being dragged. From what I saw so far the Pixmaps are always constructed by hand. Can't I do something like using the QFrame's already existing paintEvent() method to have it paint on a QImage instead and then convert the QImage to a QPixmap? Because then I figure the Pixmap would look just like the frame appears on the screen and that's exactly what I need.

Thanks
Cruz

spirit
19th January 2009, 09:47
what do you need a screenshot of QFrame? if yes, then take a look at QPixmap QPixmap::grabWidget ( QWidget * widget, int x = 0, int y = 0, int width = -1, int height = -1 )

Cruz
19th January 2009, 09:54
It's not really a screenshot what I want. When you drag something over the screen a pixmap is displayed that moves along with the cursor. When I drag a frame, I want this pixmap to show the very same frame. Just like in the puzzle and the fridgemagnets example. Or when you drag an image in Firefox, you see the very same image just a bit blurred and transparent. It's a really cool effect. And now instead of constructing a pixmap for every kind of draggable widget I have, I would like to find a generic way to construct it. So I guess your suggestion is good! Even if you call it a screenshot. :) I will look into it shortly.

spirit
19th January 2009, 09:56
you need to reimplement paintEvent and draw that pixmap.

Cruz
19th January 2009, 11:45
I tried QPixmap::grabWidget and it only grabs the background. What I draw on the frame in paintEvent is lost. :(

Is there really no other way? If I reimplement paintEvent then I have to write the painting code for every different kind of widget twice. Plus the style sheet is not applied, so I have to figure out a way to transfer the style from the widget to the QImage. It's ugly and cumbersome. There must be a generic way.

spirit
19th January 2009, 12:00
take a look at this example QTDIR/examples/draganddrop/draggableicons

Cruz
19th January 2009, 12:12
In that example the pixmaps are loaded from images.

spirit
19th January 2009, 12:14
yes, but you can create needed image and then use it like in that example.

aamer4yu
19th January 2009, 12:31
Have a look at QWidget::render

You can render on to a pixmap which is a paintdevice :)

Cruz
19th January 2009, 12:35
When creating the images I'm facing the same problem: how to create them in a generic way.

I'm trying this approach:



/* A paint function, that takes a painter as an argument.
* It's supposed to paint the contents of the frame either
* on the frame itself or on a pixmap. */
void KeyFrame::myPaint(QPainter* painter)
{
painter->setPen(Qt::black);
painter->drawText(0, 0, "hello Qt");
}

/* The frame is drawing itself on the gui. */
void KeyFrame::paintEvent(QPaintEvent *event)
{
QFrame::paintEvent(event);
QPainter painter(this); // Construct a painter that draws on this frame.
myPaint(&painter); // Pass it to my painter.
}

/* Drag action starting. */
void KeyFrame::mousePressEvent(QMouseEvent *event)
{
/* Prepare a pixmap for the drag that will be moved along with the mouse. */

// Grab the widget so you get a styled background.
QPixmap pixmap = QPixmap::grabWidget(this);

// Initialize a painter that draws on the pixmap....
QPainter painter;
painter.begin(&pixmap); // ...and pass it to my painter.
myPaint(&painter);
painter.end();

// Create the drag object.
QDrag *drag = new QDrag(this);
drag->setPixmap(pixmap);
}


But it doesn't work. What I paint on the pixmap is not shown. Do you see an error?

Cruz
19th January 2009, 12:47
Have a look at QWidget::render

You can render on to a pixmap which is a paintdevice :)

That would be perfect! But again, it doesn't work.



void KeyFrame::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
// Prepare the mime data for the drag.
QMimeData *mimeData = new QMimeData;
mimeData->setText("hello");

QPixmap pixmap(this->size());
this->render(&pixmap);

// Create the drag object.
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(pixmap);

Qt::DropAction dropAction = drag->exec();
}
}


The drag starts and all I see is a blank rectangle of the right size, but with the window background color. Not even the frame background color actually. Neither the style is applied, nor are the contents of the frame rendered. Am I not doing something right?

spirit
19th January 2009, 13:10
try this (modificated draggableicons example)


void Test::mousePressEvent(QMouseEvent *event)
{
QPixmap pixmap = QPixmap::grabWidget(this);

QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << pixmap << QPoint(event->pos());

QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-dnditemdata", itemData);

QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(pixmap);
drag->setHotSpot(event->pos());

QPainter painter(&pixmap);
painter.drawText(pixmap.rect().center().x(), pixmap.rect().center().y(), "hello Qt");

drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
}

NOTE: you must set setAcceptDrops(true) in Test::ctor.
PS. read more about Drag&Drop (http://doc.trolltech.com/4.4/dnd.html)

Cruz
19th January 2009, 13:40
I tried it by copy&paste. It also doesn't work. All I see is a rectangle of the right size and the wrong background color and nothing written on it. Also tried not grabbing the pixmap, but just initializing a blank one and then drawing on it: nothing. Not working. Here is the simplest example I can image:



void KeyFrame::mousePressEvent(QMouseEvent *event)
{
QPixmap pixmap = QPixmap(this->size());
QPainter painter(&pixmap);
painter.drawText(pixmap.rect().center().x(), pixmap.rect().center().y(), "hello Qt");

QMimeData *mimeData = new QMimeData;
mimeData->setText("hello");

QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(pixmap);
drag->setHotSpot(event->pos());

drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
}


Not working. I tried amoving the pixmap to the heap, but that doesn't help either. I'm out of ideas.

spirit
19th January 2009, 13:53
did you set 'setAcceptDrops(true)'?

Cruz
19th January 2009, 14:07
I can drag and drop the frame into a QLineEdit and it will show whatever I set as text into the MimeData. So I assume this line edit has its accept drops set to true. The drag itself works fine. The problem is only related to what the drag looks like. In particular I don't seem to be able to draw on the pixmap. I don't thing the accept drops has anyhting to do with it.

spirit
19th January 2009, 14:28
this example works fine for me, but it is not complite, just demo for creating and moving grabbed image.


#include <QtGui>
#include "test.h"

Test::Test(QWidget *parent)
: QMainWindow(parent)
{
QWidget *widget = new QWidget;
QGridLayout *grid = new QGridLayout(widget);

QPushButton *pb = new QPushButton(tr("Button"));
pb->installEventFilter(this);
QComboBox *cb = new QComboBox;
cb->installEventFilter(this);
QCheckBox *chb = new QCheckBox(tr("Check box"));
chb->installEventFilter(this);
QRadioButton *rb = new QRadioButton(tr("Radio button"));
rb->installEventFilter(this);
QSpinBox *sb = new QSpinBox;
sb->installEventFilter(this);
QDoubleSpinBox *dsp = new QDoubleSpinBox;
dsp->installEventFilter(this);

grid->addWidget(pb, 0, 0);
grid->addWidget(cb, 0, 1);
grid->addWidget(chb, 0, 2);
grid->addWidget(rb, 1, 0);
grid->addWidget(sb, 1, 1);
grid->addWidget(dsp, 1, 2);

setCentralWidget(widget);

setAcceptDrops(true);
}

void Test::mousePressEvent(QMouseEvent *event)
{
processDrag(this, event);
}

void Test::processDrag(QWidget *widget, QMouseEvent *event)
{
QPixmap pixmap = QPixmap::grabWidget(widget);
QPainter painter(&pixmap);
painter.setPen(Qt::red);
painter.drawText(0, pixmap.rect().center().y(), "hello Qt");

QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << pixmap << QPoint(event->pos());

QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-dnditemdata", itemData);

QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(pixmap);
drag->setHotSpot(event->pos());

drag->exec();
}

bool Test::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent *me = static_cast<QMouseEvent *>(e);
QWidget *widget = qobject_cast<QWidget *>(o);
if (widget && me->button() == Qt::LeftButton) {
processDrag(widget, me);
return true;
}
}

return QMainWindow::eventFilter(o, e);
}

Cruz
19th January 2009, 15:07
Wow! Thanks a lot for all the effort you are putting into this. I built a project from your example and it works for me too. Now I need to scrutinize the differences and see which bit is responsible.

Cruz
19th January 2009, 15:34
I found the bugger! It's the style sheet.



QWidget {
background-color: beige;
}


With this I set the bg color of the application. If I take this out, the drag looks like it should. If this is back in, the drag is only a beige rect.

Why is the style sheet influencing the drag this way? The drag itself is not a widget is it?

I tried taking this out of my style sheet and explicitly setting the background of my main window (which is a QWidget and not a QMainWindow in my case) in Designer. It has the same bad effect on the drag.

So the last thing to figure out is how to set a background color for the application AND have nice looking drags.

spirit
19th January 2009, 15:38
try to use QPalette insted of using style sheets.

Cruz
19th January 2009, 16:11
Yes, the palette works. At the end using style sheets brought me more trouble than good. It's nice how you can change the appearance easily by editing a singe style sheet and not even have to recompile for it. But it doesn't look the same on Windows and on Linux and there is troubles like with the drag. There is still nothing like pixel exact coding. :)

spirit
19th January 2009, 16:14
try to update your Qt version, maybe that behavior which you described is a bug. what version do you use?

Cruz
19th January 2009, 16:22
4.4 I think it's the latest. I just installed it 5 days ago.

onamatic
20th January 2009, 14:51
Thanks for this interesting thread chaps; I just wanted to say that I had a similar experience with dragging pixmaps being messed up by stylesheets (I gave up on the fancy stuff in the end and just settled for the standard little drag box). If I remember correctly, I also had problems displaying tooltips for widgets which had stylesheets. There was a note somewhere in the documentation warning against mixing palette and stylesheet effects which is fair enough I suppose but stylesheets on their own cause unexpected results sometimes. I try to avoid them now.