如果鼠标在小部件窗口外单击,关闭窗口。

10
这有点像鸡生蛋问题。我希望我的小部件窗口在鼠标点击外部时关闭。据我所知,如果点击发生在小部件之外,我的小部件将不会有任何鼠标事件。有一个 SetFocus 插槽,但它的对应项或焦点丢失在哪里呢?"focusOutEvent" 并未被调用到我的类中。
我的小部件窗口是主窗口上始终显示的小部件的子窗口,它是一个 "Qt::ToolTip",因此我认为可能会有一些问题。有什么方法可以解决这个问题吗?
我的目标是:我有一个自定义工具栏小部件,其中的按钮可能有“下拉”小部件。这些下拉小部件没有标准的窗口框架。我不想让它们“窃取”主窗口的标题焦点,并且希望它们在用户在屏幕区域之外的任何地方单击后立即消失。我在寻找一个不需要妥协 Qt 的策略来完成这个任务,但遇到了严重困难。
我有什么遗漏的吗?(我敢打赌我有)。
5个回答

20

我使用了:

setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);

这在OSX和Windows上似乎运行良好。 我的窗口正确显示,不会从我的主窗口标题中夺取焦点,并且在我单击其外部时正确调用焦点丢失事件。


这正是弹出窗口的用途。据我所知,组合框所显示的小部件是作为弹出窗口显示的小部件,这似乎就是您想要做的。 - Caleb Huitt - cjhuitt
2
在Qt 4.8中,这种技术不再起作用。但在4.7.4版本中效果很好。 - JasonGenX
使用Qt 5.9.2,我可以在主窗口失去焦点(单击或切换)时隐藏它。这比使用StrongFocus策略的focusOutEvent效果更好,后者会产生非常不一致的结果。 - Ad N
很遗憾,我可以确认在GNU/Linux上的Qt 5.14.1中,Qt::Popup确实会窃取焦点。 - Tenders McChiken

8
如果您的小部件可以获得焦点,并“窃取”其他小部件的标题焦点,那么这将更容易。类似这样的代码可以实现:
class ToolBarWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ToolBarWidget(QWidget * parent = 0)
    {
        setFocusPolicy(Qt::ClickFocus);
    }

protected:
    void focusOutEvent(QFocusEvent * event)
    {
        close();
    }
}

当您创建任何小部件时,您需要执行以下操作:

ToolBarWidget * pWidget = new ToolBarWidget(this);
pWidget->show();
pWidget->setFocus();

完成了!好吧,我想并不完全。首先,您不希望ToolBarWidget在第一次获得焦点。其次,您希望用户能够单击任何位置并隐藏ToolBarWidget。 因此,您可以跟踪创建的每个ToolBarWidget。例如,在“ QList ttWidgets”成员变量中。然后,每当您创建一个新的ToolBarWidget时,您会这样做:

ToolBarWidget * pWidget = new ToolBarWidget(this);
pWidget->installEventFilter(this);
pWidget->show();

在您的主部件类中,实现eventFilter()函数。代码示例如下:
bool MainWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::FocusOut ||
        event->type() == QEvent::KeyPress ||
        event->type() == QEvent::MouseButtonPress)
    {
        while (!ttWidgets.isEmpty()) {
            ToolBarWidget * p = ttWidgets->takeFirst();
            p->close();
            p->deleteLater();
        }
    }
    return MainWidget::eventFilter(obj, event);
}

这样做是可行的。因为即使您的ToolTabWidgets没有获得焦点,主控件中的其他小部件也会获得焦点。一旦焦点发生变化(无论是用户点击窗口外部,还是点击其中的其他控件,或者在此情况下按下键盘或鼠标按钮),该控件将到达eventFilter()函数并关闭所有选项卡小部件。

顺便说一句,为了从其他小部件中捕获MouseButtonPress、KeyPress等事件,您需要在它们上面安装eventFilter,或者在主控件中重新实现QWidget::event(QEvent * event)函数,并在那里查找这些事件。


如果您的小部件中有一个LineEdit控件,并且当您点击LineEdit时,整个小部件似乎会消失。 - Yarco
1
显然,eventFilter示例将无限递归。 - Flint

2
您可以使用QDesktopWidget.h来实现此功能,方法如下:
void MainWindow::on_actionAbout_triggered()
{
    AboutDialog aboutDialog;
    //Set location of player in center of display
    aboutDialog.move(QApplication::desktop()->screen()->rect().center() -aboutDialog.rect().center());
    // Adding popup flags so that dialog closes when it losses focus
    aboutDialog.setWindowFlags(Qt::Popup);
    //finally opening dialog
    aboutDialog.exec();

}

0

对于 Qt 版本低于 4.8 的情况,OP 的回答非常好,但正如他们在回答中提到的那样,它不适用于高于该版本的版本。当鼠标在小部件外单击时,Qt::Popup 小部件不会消失,并且它将吞噬所有通常会关闭它的输入。

经过进一步调查,这只是非对话框小部件的问题。使用 Qt::Popup 的 QDialog 在用户单击其外部时将正确关闭,但任何其他 QWidget,例如 QFrame,都不会。因此,为了解决 Qt 4.8 中的这种行为变化,所需的全部就是将小部件包装在 QDialog 中。


0
这是我为了不从主应用程序中夺取焦点所做的工作:
.h
bool eventFilter(QObject *obj, QEvent *event) override;

.cpp

bool Notification::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::MouseButtonPress)
        deleteLater();

    return QObject::eventFilter(obj, event);
}
...
// somewhere else (i.e. constructor, main window,...)
qApp->installEventFilter(this);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接