【问题标题】:Qt, enterEvents not sent to QLabel, why?Qt,enterEvents没有发送到QLabel,为什么?
【发布时间】:2017-01-12 16:45:16
【问题描述】:

我在下面发布的代码是对单人纸牌游戏项目的简化;
你可以试试,复制粘贴到Qt编辑器中;


我将简要描述情况以及问题: 可以看到三个标签:Label1、Label2、Label3(我称它们为L1、L2、L3);每个对象(label 类)都继承自 QLabel 并重新实现了一些启用拖放操作的受保护方法(QMouseEvent 处理程序); L1 和 L2 都可以拖动,但它们不接受放置动作; L3,反之,不能拖动,但接受drop;如果 Y 接受丢弃,将标签 X 放到标签 Y 上将导致 X 移动到 Y 之上(否则 X 将返回其起始位置); 当鼠标光标进入时向标签发送一个输入事件,而当鼠标光标离开时向标签发送一个离开事件。 注意,当可以拖动时,光标变为Qt::OpenHandCursor(标准光标为Qt::ArrowCursor,设置在mouseReleaseEvents之后> 和 离开事件); 问题来了:将 L1 拖到 L2 上(显然 L1 会移回原来的位置),然后松开而不移动光标;现在光标在 L2 上,但我们是第一次进入 L2,因此 Qt 应该将 enterEvent 传递给 L2,将 leaveEvent 传递给 L1;然而,这不会发生(除非你移动鼠标),所以光标保持在Qt::ArrowCursor;尽管如此,我们还是可以拖动 L2;为什么 Qt 没有向 L2 发送输入事件,光标离开 L1 并进入 L2?我怎样才能解决这个问题? 将 L1(或 L2)放在 L3 之上后会出现类似的问题。现在光标在 L1 上,但我们已经输入了它,所以 Qt 不会传递输入事件,因此光标仍然是 Qt::ArrowCursor。我希望光标改变形状为 Qt::OpenHandCursor,因为我们实际上可以拖动 L1;


这是我尝试的方法:您可以使用 QApplication::sendEvent(...) 发送 enterEvent;所以检查光标下的标签就足够了(我为 labelAtPos() 编写了代码,因为 QApplication::widgetAt() 返回顶级小部件,即标签当前被拖动,而我需要它下面的那个)并向其发送一个输入事件。但这很糟糕,因为在您移动鼠标后,Qt 会自动发送 enterEventleaveEvent,因此您最终会调用 enterEvent()两次(我不明白为什么 Qt 不立即发送它们而不是等到鼠标移动)。 现在您可以建议:尝试使用 cursor().setPos(...) 来伪造鼠标移动。这似乎并不总是有效(我不知道为什么,但我调试了很多,最后发现了它)。


伙计们我请求你们的帮助;你有没有建议更好的方法来做到这一点?提前致谢。我正在使用 Qt 5.6.1,操作系统:Ubuntu 16.04 LTS


这是我的代码

ma​​inwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
  Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
};
#endif // MAINWINDOW_H

ma​​inwindow.cpp

#include "mainwindow.h"
#include "label.h"

#include <QApplication>
#include <QDesktopWidget>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(300, 350);
    move(QApplication::desktop()->screen()->rect().center() - rect().center());
}

MainWindow::~MainWindow(){}

label.h

#ifndef LABEL_H
#define LABEL_H

#include <QLabel>
#include <QEvent>
#include <QMouseEvent>

class label : public QLabel
{
    Q_OBJECT
public:
    explicit label(QString name, QString color, QPoint pos, bool canDrag, bool canDrop, QWidget *parent = 0);
    bool isDraggable() const;
    bool isOkToDrop() const;
    static label * labelAtPos(QPoint p, QWidget * parent, const label * w);

private:
    QString name;
    QString color;
    QPoint startDrag;
    QPoint originalPosition;
    bool canDrag;
    bool canDrop;

protected:
    void mousePressEvent(QMouseEvent*);
    void mouseMoveEvent(QMouseEvent*);
    void mouseReleaseEvent(QMouseEvent *);
    void enterEvent(QEvent*);
    void leaveEvent(QEvent*);

};

#endif // LABEL_H

label.cpp

#include "label.h"
#include <QApplication>

bool dragging = false;
#include "label.h"
#include <QApplication>

bool dragging = false;

label::label(QString n, QString c, QPoint p, bool drag, bool drop, QWidget *parent) : QLabel(parent)
{
    name = n;
    color = c;
    resize(80, 100);
    setAlignment(Qt::AlignCenter);
    setText(name);
    setStyleSheet(QString("background-color: %1").arg(c));
    originalPosition = p;
    canDrag = drag;
    canDrop = drop;
    move(p);
}

bool label::isDraggable() const
{
    return canDrag;
}

bool label::isOkToDrop() const
{
    return canDrop;
}

void label::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton && isDraggable()){
        raise();
        startDrag = event->pos();
        setCursor(Qt::ClosedHandCursor);
        dragging = true;
    }
    QLabel::mousePressEvent(event);
}

void label::mouseMoveEvent(QMouseEvent *event)
{
    if(dragging){
        QPoint p = mapToParent(event->pos());
        move(p - startDrag);
    }
    QLabel::mouseMoveEvent(event);
}

void label::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton && dragging){
        setCursor(Qt::ArrowCursor);
        QPoint p = mapToParent(event->pos());
        label * lab = labelAtPos(p, parentWidget(), this);
        if(lab && lab->isOkToDrop()){
            move(lab->pos());
        }
        else
            move(originalPosition);
        dragging = false;
    }
    QLabel::mouseReleaseEvent(event);
}

void label::enterEvent(QEvent *event)
{
    if(isDraggable())
        setCursor(Qt::OpenHandCursor);
    QLabel::enterEvent(event);
}

void label::leaveEvent(QEvent *event)
{
    setCursor(Qt::ArrowCursor);
    QLabel::leaveEvent(event);
}

label * label::labelAtPos(QPoint p, QWidget * parent, const label * w)
{
    // it returns the first label under w, at point p, according to reverse parent's Z-order
    // if w is null, it returns the first label at point p according to reverse parent's Z-order

    label * result = 0;
    bool underw = (w == 0);
    if (parent) {
        for (int j = parent->children().count()-1; j >= 0; j--) {
            label * t = qobject_cast<label *>(parent->children().at(j));
            if(t == w) underw = true;
            if (t && t != w && underw) {
                QPoint topleft = t->mapToParent(t->rect().topLeft());
                QPoint bottomRight = t->mapToParent(t->rect().bottomRight());
                QRect rect(topleft, bottomRight);
                if(rect.contains(p)){
                    result = t;
                    break;
                };
            };
        };
    };
    return result;
}
label::label(QString n, QString c, QPoint p, bool drag, bool drop, QWidget *parent) : QLabel(parent)
{
    name = n;
    color = c;
    resize(80, 100);
    setAlignment(Qt::AlignCenter);
    setText(name);
    setStyleSheet(QString("background-color: %1").arg(c));
    originalPosition = p;
    canDrag = drag;
    canDrop = drop;
    move(p);
}

bool label::isDraggable() const
{
    return canDrag;
}

bool label::isOkToDrop() const
{
    return canDrop;
}

void label::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton && isDraggable()){
        raise();
        startDrag = event->pos();
        setCursor(Qt::ClosedHandCursor);
        dragging = true;
    }
    QLabel::mousePressEvent(event);
}

void label::mouseMoveEvent(QMouseEvent *event)
{
    if(dragging){
        QPoint p = mapToParent(event->pos());
        move(p - startDrag);
    }
    QLabel::mouseMoveEvent(event);
}

void label::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton && dragging){
        setCursor(Qt::ArrowCursor);
        QPoint p = mapToParent(event->pos());
        label * lab = labelAtPos(p, parentWidget(), this);
        if(lab && lab->isOkToDrop()){
            move(lab->pos());
        }
        else
            move(originalPosition);
        dragging = false;
    }
    QLabel::mouseReleaseEvent(event);
}

void label::enterEvent(QEvent *event)
{
    if(isDraggable())
        setCursor(Qt::OpenHandCursor);
    QLabel::enterEvent(event);
}

void label::leaveEvent(QEvent *event)
{
    setCursor(Qt::ArrowCursor);
    QLabel::leaveEvent(event);
}

label * label::labelAtPos(QPoint p, QWidget * parent, const label * w)
{
    // it returns the first label under w, at point p, according to reverse parent's Z-order
    // if w is null, it returns the first label at point p according to reverse parent's Z-order

    label * result = 0;
    bool underw = (w == 0);
    if (parent) {
        for (int j = parent->children().count()-1; j >= 0; j--) {
            label * t = qobject_cast<label *>(parent->children().at(j));
            if(t == w) underw = true;
            if (t && t != w && underw) {
                QPoint topleft = t->mapToParent(t->rect().topLeft());
                QPoint bottomRight = t->mapToParent(t->rect().bottomRight());
                QRect rect(topleft, bottomRight);
                if(rect.contains(p)){
                    result = t;
                    break;
                };
            };
        };
    };
    return result;
}

ma​​in.cpp

#include "mainwindow.h"
#include "label.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    label lab1("Label 1", "#ff9966", QPoint(20, 20), true, false, &w);
    label lab2("Label 2", "#ccccff", QPoint(150, 20), true, false, &w);
    label lab3("Label 3", "#00cc66", QPoint(20, 150), false, true, &w);
    QLabel lab4("Label1, Label2: can drag, can't drop\nLabel3: can't drag, can drop", &w);
    lab4.resize(250, 100);
    lab4.move(20, 250);
    w.show();

    return a.exec();
}

【问题讨论】:

标签: c++ qt drag-and-drop event-handling label


【解决方案1】:

我不久前撞到了这堵墙。它与什么样的小部件提供悬停事件和启用悬停事件有关。

// in your widget's constructor (probably)
this->setAttribute(WA_HOVER, true);

https://stackoverflow.com/a/26392025/999943

希望对您有所帮助。


更新:在鼠标释放结束时强制附加事件的示例:

回复代码/评论:https://gist.github.com/anonymous/f2472dcffaf703614e956380a6fd1ca7#file-label-cpp

尝试拖动标签,然后从任何小部件中释放出来。很不幸的是,不行 hoverLeave 事件被发送给它。 ——

void label::mouseReleaseEvent(QMouseEvent *event)
{
    qDebug() << Q_FUNC_INFO << event->type();

    if(event->button() == Qt::LeftButton && dragging){
        //setCursor(Qt::ArrowCursor);
        unsetCursor();// unsetting the cursor is typically better than setting back to arrow
        QPoint p = mapToParent(event->pos());
        label * lab = labelAtPos(p, parentWidget(), this);
        if(lab && lab->isOkToDrop()){
            move(lab->pos());
        }
        else
        {
            move(originalPosition);
            // Force the mouse to move, too
            QCursor::setPos(QCursor::pos()+QPoint(1,1));
            // could move back with an opposite operation like -QPoint(1,1)
        }
        dragging = false;
    }
    QLabel::mouseReleaseEvent(event);
}

这看起来是让它在没有太多工作或不会变得太乱的情况下完成 drop 结束的最干净的方法。

替代方案包括:

使用QApplication::widgetAt() 或与当前鼠标位置类似的东西手动确定您在哪个小部件上。

然后使用qApp-&gt;postEvent() 发布所需的事件,然后在必要时使用qApp-&gt;processEvents()。在发布结束时稍微移动鼠标即可解决问题,而不会带来太多麻烦或引入新错误。

【讨论】:

  • QEvent::HoverEvent 是否传递给 enterEvent() ?
  • 使用event-&gt;type() 找出它是什么,然后使用该信息进行静态转换。我认为是mouseMoveEvent...
  • 我没有收到任何 HoverEvent :(。为什么?
  • if(event->type() == QEvent::HoverEnter) qDebug()
  • 我还将鼠标跟踪设置为 true;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
  • 2021-11-16
  • 2019-07-03
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
  • 2013-10-27
相关资源
最近更新 更多