我在下面发布的代码是从一个纸牌游戏项目中简化的
您可以在Qt编辑器中尝试、复制和粘贴它;
我将简要描述情况以及问题:您可以看到三个标签:Label1、Label2、Label3(我称它们为L1、L2、L3);每个对象(classlabel)从QLabel继承并重新实现一些受保护的方法(QMouseEvent处理程序);跌落动作;L1和L2都可以被拖动,但它们不接受放下动作;相反,L3不能拖动,但它接受水滴;如果Y接受掉落,则将标签X掉落在标签Y上将导致X在Y的顶部移动(否则X将返回到其起始位置);当鼠标光标进入标签时,输入事件会发送到标签,而当鼠标光标离开标签时,离开事件会发送给标签。请注意,当可以拖动时,光标的形状将更改为Qt::OpenHandCursor(标准光标为Qt:;ArrowCursor,设置在mouseReleaseEvent和LeaveEvents之后);问题是:将L1拖到L2的顶部(显然L1会移回原来的位置),然后在不移动光标的情况下释放;现在光标在L2上,但我们是第一次输入L2,因此Qt应该向L2传递enterEvent,向L1传递leaveEvent非您移动鼠标),因此光标保持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和leaveEventntrEvent()两次(我不明白Qt为什么不立即发送它们,而不是等待鼠标移动)。现在你可以建议:尝试使用cursor().setPos(…)来伪造鼠标移动。这似乎并不总是有效(我不知道为什么,但我调试了很多,最终发现了它)。
伙计们我请求你们的帮助;你有没有建议更好的方法?提前谢谢。我使用的是Qt 5.6.,操作系统:Ubuntu 16.04 LTS
这是我的代码:
主窗口.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(){}
标签.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 dropnLabel3: can't drag, can drop", &w);
lab4.resize(250, 100);
lab4.move(20, 250);
w.show();
return a.exec();
}
我不久前撞到了这堵墙。它与哪些类型的小部件提供悬停事件和启用悬停事件有关。
// in your widget's constructor (probably)
this->setAttribute(WA_HOVER, true);
https://stackoverflow.com/a/26392025/999943
希望能有所帮助。
UPDATE:在鼠标释放结束时强制附加事件的示例:
针对代码/注释:https://gist.github.com/anonymous/f2472dcffaf703614e956380a6fd1ca7#file-标签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);
}
这看起来是最干净的方法,可以让它在不做太多工作或不会变得太乱的情况下完成降落。
备选方案包括:
用QApplication::widgetAt()
或类似于当前鼠标位置的东西手动计算出您正在处理的小部件。
然后使用qApp->postEvent()
发布所需事件,必要时再使用qApp->processEvents()
。在发布结束时移动鼠标可以解决这个问题,而不会带来太多麻烦或引入新的错误。