【发布时间】: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 会自动发送 enterEvent 和 leaveEvent,因此您最终会调用 enterEvent()两次(我不明白为什么 Qt 不立即发送它们而不是等到鼠标移动)。 现在您可以建议:尝试使用 cursor().setPos(...) 来伪造鼠标移动。这似乎并不总是有效(我不知道为什么,但我调试了很多,最后发现了它)。
伙计们我请求你们的帮助;你有没有建议更好的方法来做到这一点?提前致谢。我正在使用 Qt 5.6.1,操作系统:Ubuntu 16.04 LTS
这是我的代码:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};
#endif // MAINWINDOW_H
mainwindow.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;
}
main.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