如何确保小部件上的两个标签之一并排获得focusInEvent?

Posted

技术标签:

【中文标题】如何确保小部件上的两个标签之一并排获得focusInEvent?【英文标题】:How to ensure one of two labels side-by-side on a widget getting focusInEvent? 【发布时间】:2021-02-17 22:54:42 【问题描述】:

这里的代码创建了两个在 MDI 区域中以选项卡方式查看的小部件。每个小部件都包含两个标签。用户可以通过 Ctrl+Tab 在这两个小部件之间切换。当小部件是 Ctrl+tab 时,如何确保左标签获得 focusInEvent()?

我已经在代码中尝试了 activateWindow() 和 raise()。它们似乎没有区别。

我试图在这些小部件中只使用一个标签 - focusInEvent() 发生没有任何问题。当这些小部件中有两个或多个标签时(如代码中所示),Ctrl+tab 有时(但不总是)会触发左侧标签上的 focusInEvent,这反过来又会触发 MouseMoveEvent 以在鼠标位于左标签 - 如果您将鼠标放在左侧并多次按 Ctrl+tab 可以观察到这一点:有时工具提示不显示。

如果第二个标签是由QLabel *lbl2 = new QLabel; 创建的,则focusInEvent() 也会发生在第一个标签上,没有问题。

#include <QtCore>
#include <QtWidgets>
#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
#include <QEvent>
#include <QGridLayout>

class MyLabel : public QLabel

public:
    MyLabel(QWidget*parent = nullptr) : QLabel(parent)
    
        setMouseTracking(true);
        setFocusPolicy(Qt::FocusPolicy::StrongFocus);
        setWindowFlag(Qt::ToolTip);
    

protected:
    virtual void focusInEvent(QFocusEvent *ev) override
    
        static int count;
        qDebug() << __PRETTY_FUNCTION__ << " count: " << ++count;
        QPoint posGlabal = QCursor::pos();
        QPoint pos = this->mapFromGlobal(posGlabal);
        if(this->rect().contains(pos)) 
            // I want the label show a tooltip by mouseMoveEvent, when it is Ctrl+Tab on, without moving mouse
            QMouseEvent* evt = new QMouseEvent(QEvent::MouseMove, QPointF(pos), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
            QCoreApplication::postEvent(this,evt);
        

        QLabel::focusInEvent(ev);
    

    virtual void mouseMoveEvent(QMouseEvent *ev) override
    
        QString msg(QString::number(ev->pos().x()) +", " +QString::number(ev->pos().y()));
        QToolTip::showText(ev->globalPos(), msg);

        QLabel::mouseMoveEvent(ev);
    
;

class MyWidget : public QWidget

public:
    MyWidget(QWidget *parent=nullptr) : QWidget(parent) 
        QGridLayout *layout = new QGridLayout;

        // First label on left... I want it to show tooptip when Ctrl+Tab on if mouse on it (without moving mouse)
        MyLabel *lbl1 = new MyLabel;
        lbl1->setStyleSheet("QLabel  background-color : green;");
        layout->addWidget(lbl1, 0, 0);

        // second label
        MyLabel *lbl2 = new MyLabel;
        //QLabel *lbl2 = new QLabel; // if I use this instead, no problem getting the tooltip on the left label.
        lbl2->setStyleSheet("QLabel  background-color : blue;");
        layout->addWidget(lbl2, 0, 1);

        // I want the left label to get focusInEvent()
        // So I added these two lines in.
        // But they do NOT make differences
        lbl1->activateWindow();
        lbl1->raise();

        setLayout(layout);
    

;

class MainWindow : public QMainWindow

public:
    MainWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    
        QMdiArea *mdiArea = new QMdiArea;
        this->setCentralWidget(mdiArea);
        mdiArea->setViewMode(QMdiArea::TabbedView);

        MyWidget *w1 = new MyWidget; w1->setWindowTitle("w1");
        mdiArea->addSubWindow(w1);
        MyWidget *w2 = new MyWidget; w2->setWindowTitle("w2");
        mdiArea->addSubWindow(w2);

    
;

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

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();

【问题讨论】:

【参考方案1】:

我只进行了一些更改以帮助我调试您的问题。 我将 QMdiArea::subWindowActivated() 连接到 MyWidget 中的一个新插槽,该插槽将焦点设置到左侧标签。 我觉得这有点像 hack,但你应该能够改进我的想法。

#include <QtCore>
#include <QtWidgets>
#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
#include <QEvent>
#include <QGridLayout>

class MyLabel : public QLabel

public:
    MyLabel(const QString &name, QWidget*parent = nullptr)
        : QLabel(parent)
        , m_name(name)
    
        setMouseTracking(true);
        setFocusPolicy(Qt::FocusPolicy::StrongFocus);
        setWindowFlag(Qt::ToolTip);
    

    QString name() const  return m_name; 

protected:
    virtual void focusInEvent(QFocusEvent *ev) override
    
        static int count;
        qDebug() << __PRETTY_FUNCTION__ << m_name << " count: " << ++count;

        QPoint posGlabal = QCursor::pos();
        QPoint pos = this->mapFromGlobal(posGlabal);

        if(this->rect().contains(pos)) 
            // I want the label show a tooltip by mouseMoveEvent, when it is Ctrl+Tab on, without moving mouse
            QMouseEvent* evt = new QMouseEvent(QEvent::MouseMove, QPointF(pos), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
            QCoreApplication::postEvent(this,evt);
        

        QLabel::focusInEvent(ev);
    

    virtual void mouseMoveEvent(QMouseEvent *ev) override
    
        QString msg(QString::number(ev->pos().x()) +", " +QString::number(ev->pos().y()));

        QToolTip::showText(ev->globalPos(), msg);

        QLabel::mouseMoveEvent(ev);
    

private:
    QString m_name;
;

class MyWidget : public QWidget

public:
    MyWidget(const QString &nameLeft, const QString &colorLeft, const QString &nameRight, const QString &colorRight, QWidget *parent=nullptr)
        : QWidget(parent)
        , lbl1(nameLeft)
        , lbl2(nameRight)
    
        QGridLayout *layout = new QGridLayout;

        // First label on left... I want it to show tooptip when Ctrl+Tab on if mouse on it (without moving mouse)
        lbl1.setStyleSheet(QString("QLabel  background-color : %1;").arg(colorLeft));
        layout->addWidget(&lbl1, 0, 0);

        // second label
        //QLabel *lbl2 = new QLabel; // if I use this instead, no problem getting the tooltip on the left label.
        lbl2.setStyleSheet(QString("QLabel  background-color : %1;").arg(colorRight));
        layout->addWidget(&lbl2, 0, 1);

        setLayout(layout);
    
// ----------------------------------------------------------------
public slots:
    // The new slot that looks for the left label and sets the focus on it
    void onSubWindowActivated(QMdiSubWindow *window)
    
        auto lbl = window->findChild<MyLabel *>(lbl1.objectName());

        // The events must be filtered, otherwise they'll trigger each other to death
        if (lbl == nullptr)
            return;

        lbl->setFocus();
    
// ----------------------------------------------------------------
private:
    MyLabel lbl1;
    MyLabel lbl2;
;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)

    QMdiArea *mdiArea = new QMdiArea;
    this->setCentralWidget(mdiArea);
    mdiArea->setViewMode(QMdiArea::TabbedView);

    MyWidget *w1 = new MyWidget("w1_L", "green", "w1_R", "blue");
    w1->setWindowTitle("w1");
    mdiArea->addSubWindow(w1);
// ----------------------------------------------------------------
    connect(mdiArea, &QMdiArea::subWindowActivated,
            w1, &MyWidget::onSubWindowActivated);
// ----------------------------------------------------------------

    MyWidget *w2 = new MyWidget("w2_L", "yellow", "w2_R", "red");
    w2->setWindowTitle("w2");
    mdiArea->addSubWindow(w2);
// ----------------------------------------------------------------
    connect(mdiArea, &QMdiArea::subWindowActivated,
            w2, &MyWidget::onSubWindowActivated);
// ----------------------------------------------------------------

这里是QMdiArea::subWindowActivated()的官方文档https://doc.qt.io/qt-5/qmdiarea.html#subWindowActivated

【讨论】:

以上是关于如何确保小部件上的两个标签之一并排获得focusInEvent?的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的 Twitter 克隆中并排设置侧边栏、Feed 和小部件?

Qt 设计器和 Dock 小部件

如何并排执行两个 QGraphicsViews 的 50/50 布局?

Flutter – 连续两个文本,左边一个优雅地溢出

无论小部件结构如何,如何确保按钮关闭应用程序?

颤动:从其兄弟中截取小部件的屏幕截图