Qt实例学习--事件处理和简单拖拽实现原理

来源:互联网 发布:德尔菲神谕 知乎 编辑:程序博客网 时间:2024/05/21 07:55
这是Qt的关于drag&paste主题的Demo的一个实例,我做了注解。效果图如下下面的部分是主要的实现的部分,从下面的例子可以看出,实现drag&drop功能,主要要做的事情就是重写几个event handler以及设计event的MIME数据类型。
#include #include "draglabel.h"#include "dragwidget.h"DragWidget::DragWidget(QWidget *parent)    : QWidget(parent){    QFile dictionaryFile(":/dictionary/words.txt");                       //此文件中存放了需要创建的所有的label的名字    dictionaryFile.open(QIODevice::ReadOnly);                             //打开文件    QTextStream inputStream(&dictionaryFile);    int x = 5;    int y = 5;    while (!inputStream.atEnd()) {                               //对于打开文件中的每一个单词,都创建一个text label        QString word;        inputStream >> word;        if (!word.isEmpty()) {            DragLabel *wordLabel = new DragLabel(word, this);    //在这里创建label            wordLabel->move(x, y);                               //放置label到适当的位置            wordLabel->show();            wordLabel->setAttribute(Qt::WA_DeleteOnClose);       //设置属性,当此widget被设置为非可见模式时,就删除它            x += wordLabel->width() + 2;            if (x >= 195) {                x = 5;                y += wordLabel->height() + 2;            }        }    }    QPalette newPalette = palette();    newPalette.setColor(QPalette::Window, Qt::white);    setPalette(newPalette);    setAcceptDrops(true);                                         //此处的设置很关键,允许释放在本widget上    setMinimumSize(400, qMax(200, y));    setWindowTitle(tr("Draggable Text"));}void DragWidget::dragEnterEvent(QDragEnterEvent *event)      //这个handler是在拖拽进入到本widget的时候,是针对从程序外面拖拽到应用程序内的情况,在应用程序中拖动的情况,不适用这个handler{    if (event->mimeData()->hasText()) {        if (children().contains(event->source())) {         //判断拖动源是否是本widget的children widget.如果是的话,就执行移动操作            event->setDropAction(Qt::MoveAction);           //此处的setDropAction()函数是否只是相当于设置了一个flag.此flag做为返回值被返回给调用的函数,用于判断event执行的是何种操作??            event->accept();        } else {                                            //这种情况是当拖拽是从本应用程序以外的地方拖进来的话            event->acceptProposedAction();        }    } else {        event->ignore();    }}void DragWidget::dropEvent(QDropEvent *event)               //当拖动的widget放下的时候,此事件被触发,此handler被调用{    if (event->mimeData()->hasText()) {                      //此条件过滤掉了一些比如拖拽图标啊,图片啊进来的情况,只允许拖拽文字进来        const QMimeData *mime = event->mimeData();                     //取出伴随着event的MIME数据        QStringList pieces = mime->text().split(QRegExp("//s+"),                             QString::SkipEmptyParts);                //有可能很多个词(比如一句话)被拖拽,那么根据空格来将多个词变为多个label        QPoint position = event->pos();                               //这个point很明显是widget被放下时的point.因为这是在dropEvent中        QPoint hotSpot;                                               //hotSpot是拖拽开始时的point.注意,hotSpot的位置记录的是拖拽开始点在被拖拽widget中的相对位置        QList hotSpotPos = mime->data("application/x-hotspot").split(' ');    //类型application/x-hotspot是在下面的mousePressEvent事件处理器中定义的。        if (hotSpotPos.size() == 2) {            hotSpot.setX(hotSpotPos.first().toInt());            hotSpot.setY(hotSpotPos.last().toInt());        }        foreach (QString piece, pieces) {                         //如上所述,有可能拖动了好多label进来,所以需要foreach循环            DragLabel *newLabel = new DragLabel(piece, this);            newLabel->move(position - hotSpot);                  //放置新的label.为什么要减去hotSpot的坐标,参考上面对hotSpot的解释。            newLabel->show();            newLabel->setAttribute(Qt::WA_DeleteOnClose);            position += QPoint(newLabel->width(), 0);        }        if (event->source() == this) {                                    //如果拖动的源widget是本widget的时,那么此时的拖动操作被设为Move action.            event->setDropAction(Qt::MoveAction);                         //设置为moveaction.我理解为设置返回值,其他不做什么具体的事情            event->accept();        } else {            event->acceptProposedAction();        }    } else {        event->ignore();    }    foreach (QObject *child, children()) {                                 //此处有必要么?        if (child->inherits("QWidget")) {            QWidget *widget = static_cast(child);            if (!widget->isVisible())                widget->deleteLater();                                     //此函数表示,当跳出此事件处理函数,进入事件循环的时候,就删除这些widget.        }    }}void DragWidget::mousePressEvent(QMouseEvent *event)                //当按键被按下的时候,进入此event handler{    QLabel *child = static_cast(childAt(event->pos()));    //返回在此widget中pos位置的widget    if (!child)        return;    QPoint hotSpot = event->pos() - child->pos();               //相对于child的相对位置,从此处可见dropEvent中的hotSpot的适用,返回鼠标点击点在widget中的相对位置    QMimeData *mimeData = new QMimeData;                        //因为需要自定义此事件携带的数据,所以建立一个QMimeData对象    mimeData->setText(child->text());     //插入文字    mimeData->setData("application/x-hotspot",                      QByteArray::number(hotSpot.x())                      + " " + QByteArray::number(hotSpot.y()));          //注意,此处实际上定义了一个key类型为application/x-hotspot,此key的value为hotSpot的坐标,以空格分开    QPixmap pixmap(child->size());    child->render(&pixmap);    QDrag *drag = new QDrag(this);    drag->setMimeData(mimeData);    drag->setPixmap(pixmap);    drag->setHotSpot(hotSpot);    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);   //此函数启动拖拽的操作,直到操作结束,是一个阻塞型的操作,在windows上    if (dropAction == Qt::MoveAction)                //我草,原来设为MoveAction只是置了一个flag啊        child->close();                              //将widget设置为不显示}
原创粉丝点击