当先锋百科网

首页 1 2 3 4 5 6 7

Draggable Icons Example例子展示了在同一个应用程序的窗体部件之间如何拖放图像数据。

(关于一些拖放的基本知识在我上一篇博客有介绍。)

例子程序功能是这样的,当我们在一个自定义widget(图中两个相同大小的框框就是自定义的widget)内拖动一个图片,会将图片位置改变。当我们将一个自顶一个widget中的图片拖动放置到另外一个中时,将会复制图到另外一边。


首先看main.cpp:

#include <QApplication>

#include <QHBoxLayout>

#include "dragwidget.h"

int main(int argc, char *argv[])

{

   Q_INIT_RESOURCE(draggableicons);

   QApplication app(argc, argv);

   QWidget mainWidget;

   QHBoxLayout *horizontalLayout = new QHBoxLayout;

   horizontalLayout->addWidget(new DragWidget);

   horizontalLayout->addWidget(new DragWidget);

   mainWidget.setLayout(horizontalLayout);

   mainWidget.setWindowTitle(QObject::tr("Draggable Icons"));

#ifdef Q_OS_SYMBIAN

   mainWidget.showMaximized();

#else

   mainWidget.show();

#endif

   return app.exec();

}

Q_INIT_RESOURCE是一个初始化资源的函数,在一些平台,我们的资源文件.qrc需要存储在静态库,使用它将自动为我们导入资源文件,这就确保资源能够在启动时被初始化。

然后就是主程序类对象,然后实例化一个QWidget作为主窗口,再实例化两个自定义widget,用QHBoxLayout排版与呈现。#ifdef Q_OS_SYMBIAN ...可以不用管也可以去掉,这是用在塞班系统里的显示。

下面进入自定义的这个部件代码,看看它怎么样写的。

#include <QFrame>

QT_BEGIN_NAMESPACE

class QDragEnterEvent;

class QDropEvent;

QT_END_NAMESPACE

class DragWidget : public QFrame

{

public:

   DragWidget(QWidget *parent=0);

protected:

   void dragEnterEvent(QDragEnterEvent *event);

   void dragMoveEvent(QDragMoveEvent *event);

   void dropEvent(QDropEvent *event);

   void mousePressEvent(QMouseEvent *event);

};

DragWidget是一个QFrame 的子类(QFrameQWidget的子类,QFrame主要是添加了一个框)。DragWidget就是普通的重实现了拖放需要的事件响应函数。


DragWidget::DragWidget(QWidget *parent)

   QFrame(parent)

{

#if defined(Q_OS_SYMBIAN) || defined(Q_WS_MAEMO_5) || defined(Q_WS_SIMULATOR)

#else

   setMinimumSize(200200);

#endif

   setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);

   setAcceptDrops(true);

   QLabel *boatIcon = new QLabel(this);

   boatIcon->setPixmap(QPixmap(":/images/boat.png"));

   boatIcon->move(1010);

   boatIcon->show();

   boatIcon->setAttribute(Qt::WA_DeleteOnClose);

   QLabel *carIcon = new QLabel(this);

   carIcon->setPixmap(QPixmap(":/images/car.png"));

   carIcon->move(10010);

   carIcon->show();

   carIcon->setAttribute(Qt::WA_DeleteOnClose);

   QLabel *houseIcon = new QLabel(this);

   houseIcon->setPixmap(QPixmap(":/images/house.png"));

   houseIcon->move(1080);

   houseIcon->show();

   houseIcon->setAttribute(Qt::WA_DeleteOnClose);

}

SunKenQFrame::Shadow的一种,是下沉的3D投影效果。StyledPanel是QFrame::Shape的一种,定义框框的形状,即画一个矩形的框框,它可以呈现突出(Raised)效果和下沉(Sunken)效果。

为了使放置可行,setAcceptDrops(true)是必须的。

然后就是添加了三个label这里我们可以看到,那些图片就是用的QLabel实例化的。

其中setAttribute(Qt::WA_DeleteOnClose);也是很常见的,它是让我们关闭程序时自动释放内存。

void DragWidget::mousePressEvent(QMouseEvent *event)

{

   QLabel *child = static_cast<QLabel*>(childAt(event->pos()));

   if (!child)

       return;

   QPixmap pixmap = *child->pixmap();

   QByteArray itemData;

   QDataStream dataStream(&itemData, QIODevice::WriteOnly);

   dataStream << pixmap << QPoint(event->pos() - child->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() - child->pos());    

   QPixmap tempPixmap = pixmap;

   QPainter painter;

   painter.begin(&tempPixmap);

   painter.fillRect(pixmap.rect(), QColor(127127127127));

   painter.end();

   child->setPixmap(tempPixmap);

   if (drag->exec(Qt::CopyAction | Qt::MoveActionQt::CopyAction) == Qt::MoveAction)

       child->close();

   else {

       child->show();

       child->setPixmap(pixmap);

   }

}

鼠标按下事件,获取是否是点击到了图片(QLabel),没有则返回。

然后就是设置一个QDarg对象。用QDataStream形式将图片对象存储到QByteArray。再将这个数据存到QMimeData以供QDarg使用。

setHotSpot()设置热点,一般热点是左上角,这里点击下鼠标,这里不使用默认,而是设置在设置到我们鼠标点到的图片中的位置。

tempPixmap是干什么的?我们在获取的QLabel图像的位置覆盖画了一张原图片加灰的图片,这样来表示拖动时原图片的阴影效果。

在接下来就是开始执行拖动了。drag->exec() 如果接收到的返回值是Qt::MoveAction(dragEnterEvent, dragMoveEven, dropEvent的同一个对象内的拖动)视为移动,那么就关闭掉原先位置的图片。否则在新位置也显示一个。

void DragWidget::dragEnterEvent(QDragEnterEvent *event)

{

   if (event->mimeData()->hasFormat("application/x-dnditemdata")) {

       if (event->source() == this) {

           event->setDropAction(Qt::MoveAction);

           event->accept();

       else {

           event->acceptProposedAction();

       }

   else {

       event->ignore();

   }

}

当拖动对象进入DragWidget时就会调用dragEnterEvent()。在这里面我们通常都是设置响应何种放置对象事件,对于这里的格式,"application/x-dnditemdata"是后面QMimeData::setData自定义设置的。必须与后面设置相同。

这里event->source() == this是判定是否在同一个对象,如果是同一个对象就是接收Qt::MoveAction,否则接收目的活动。

void DragWidget::dragMoveEvent(QDragMoveEvent *event)

{

   if (event->mimeData()->hasFormat("application/x-dnditemdata")) {

       if (event->source() == this) {

           event->setDropAction(Qt::MoveAction);

           event->accept();

       else {

           event->acceptProposedAction();

       }

   else {

       event->ignore();

   }

}


对拖动移动事件也和上面响应相同。

void DragWidget::dropEvent(QDropEvent *event)

{

   if (event->mimeData()->hasFormat("application/x-dnditemdata")) {

       QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");

       QDataStream dataStream(&itemData, QIODevice::ReadOnly);

       

       QPixmap pixmap;

       QPoint offset;

       dataStream >> pixmap >> offset;

       QLabel *newIcon = new QLabel(this);

       newIcon->setPixmap(pixmap);

       newIcon->move(event->pos() - offset);

       newIcon->show();

       newIcon->setAttribute(Qt::WA_DeleteOnClose);

       if (event->source() == this) {

           event->setDropAction(Qt::MoveAction);

           event->accept();

       else {

           event->acceptProposedAction();

       }

   else {

       event->ignore();

   }

}

放置事件中,使用自定义放置对象。将QMimeData信息获取出来,相当于之前写入的逆过程。将创建放置的对象图片。