恢复QMainWindow浮动工具栏时出现问题

6

在恢复具有QCombobox的浮动工具栏中的QMainWindow状态时,我遇到了一个问题。 在恢复浮动工具栏后,我的QCombobox无法获得焦点,直到我单击工具栏手柄并移动它。 以下是显示问题的GIF,使用QT 5.13。 enter image description here

文件floating_toolbar.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = floating_toolbar
TEMPLATE = app


DEFINES += QT_DEPRECATED_WARNINGS

 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        mainwindow.cpp

HEADERS += \
        mainwindow.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

文件:main.cpp

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

文件:mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    void closeEvent(QCloseEvent *event);
    void readSettings();
    bool eventFilter(QObject* xObj, QEvent* xEvent);
    ~MainWindow();

    public slots:
    void mCheck();
};

#endif // MAINWINDOW_H

文件:mainwindow.cpp

#include "mainwindow.h"
#include <QToolBar>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLayout>
#include <QSettings>
#include <QEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QApplication>



MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QToolBar* lToolbar = new QToolBar(this);
    QComboBox* lComobox = new QComboBox(this);
    lComobox->setEditable(true);

    lToolbar->setWindowTitle("MyToolBar");
    lToolbar->setObjectName("NiceBaby");
    lToolbar->addWidget(lComobox);
    //lToolbar->addAction("check", lComobox, SLOT(clearEditText()));


    addToolBar(lToolbar);
    lToolbar->installEventFilter(this);
    readSettings();


}

void MainWindow::mCheck()
{

}
void MainWindow::closeEvent(QCloseEvent *event)
{
    QSettings settings("MyCompany", "MyApp");
    settings.setValue("windowState", saveState());
    QMainWindow::closeEvent(event);
}
void MainWindow::readSettings()
{
    QSettings settings("MyCompany", "MyApp");
    restoreState(settings.value("windowState").toByteArray());
}

MainWindow::~MainWindow()
{

}

bool MainWindow::eventFilter(QObject* xObj, QEvent* xEvent)
{
    //qDebug()<<xEvent->type();

    return QMainWindow::eventFilter(xObj, xEvent);
}

你使用的是哪个发行版?Gnome 还是 KDE? - KcFnMi
1
它是在RHEL6.5上的KDE。 - SaurabhS
你可以发布你的MCVE代码吗?看起来可能有一个bug... 在工具栏中使用QWidgets有时会表现得非常奇怪,这取决于平台。由于与QMainWindow和平台集成需求的交互作用,QToolBar代码有点复杂。看起来好像有东西在窃取焦点,直到通过点击句柄"激活"工具栏(或其他操作)。 - Maxim Paperno
在Linux Mint 19.2(我相信是基于Ubuntu 18.4)中也已确认。Linux-RHEL_7_4-X86_64。测试了Qt 5.13.1、5.12.4和5.7.1。在Windows中没有问题,无论是使用MSVC还是MinGW。似乎很明显是一个焦点问题——例如,上下文菜单仍然可以工作,您可以将文本粘贴到框中,甚至选择文本。但是,如果您尝试输入,则焦点在其他地方。我将QLineEdit放入主窗口中并保持焦点……您在工具栏组合框中突出显示文本并开始键入,输入将进入主窗口内的QLineEdit - Maxim Paperno
在 Arch Linux 上使用 KDE(kwin 5.16.4)、QtCreator 4.9.2、Qt 5.13.0 进行确认,出现了相同的行为。 - matteo martelli
显示剩余6条评论
2个回答

2

好的,一种解决方法是在工具栏浮动时首次显示时重置窗口标志。我通过查看工具栏拖动后放置发生了什么来跟踪这个问题(但未插入主窗口)。 (它调用setWindowState(),在这种情况下,这样做的全部作用就是隐藏工具栏,调用updateWindowFlags()并再次显示它)。

可以从QMainWindow::showEvent()或安装在QToolBar上的eventFilter中处理此问题。 我认为前者更简单。

更新:实际上,只要首次显示工具栏,哪怕不是在应用程序启动时,例如用户在应用程序启动后从切换视图菜单中显示,该问题就会发生。我更新了下面的代码以修复该问题。请参见有关最小化主窗口的其他问题的注意事项。

我将以下内容添加到MCVE中的MainWindow类:

  protected:
    void showEvent(QShowEvent *e) override {
      QMainWindow::showEvent(e);
#ifdef Q_OS_LINUX
      if (lToolbar->isFloating() 
          // remove the next condition and the toolsbar will get hidden the 2nd time main window is minimized.
          && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint)  
          ) {
        const bool vis = !lToolbar->isHidden();
        qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
        lToolbar->hide();
        lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
        if (vis)
          lToolbar->show();
#endif
    }

    QToolBar* lToolbar;  // Use this in MainWindow constructor to save the instance pointer.

我还注意到初始浮动工具栏存在另一个问题。当主窗口最小化时,工具栏不会被隐藏,而是停留在屏幕上的原位置。无论工具栏中有什么(例如没有组合框,只有QActions)。这个解决方法也可以解决这个问题(见代码注释),但只能在第二次最小化窗口时生效。第一次最小化需要更好的解决方案。
其他人能确认这个问题吗?这可能比可编辑组合框更严重,如果没有人注意到我会很惊讶。
我猜这应该作为Qt bug进行报告。
更新2:这个版本也修复了最小化问题。我猜在QMainWindow::showEvent()之后发生了一些改变,导致工具栏的行为发生变化。这就解释了为什么上面的解决方法只在第一次最小化后有效。因此,将工具栏“修复”安排到以后就可以解决这个问题。
class MainWindow : public QMainWindow
{
...
#ifdef Q_OS_LINUX
  protected:
    void showEvent(QShowEvent *e) override
    {
      QMainWindow::showEvent(e);
      if (lToolbar->isFloating() && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
        //  QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
        QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
        // If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
        //adjustToolbar();
      }
    }

  private slots:
    void adjustToolbar() const
    {
      const bool vis = !lToolbar->isHidden();
      qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
      lToolbar->hide();
      lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
      if (vis)
        lToolbar->show();
    }
#endif

  private:
    QToolBar* lToolbar;
};

新增: 一个QToolBar子类可以自己应用解决方法,QMainWindow中不需要特殊操作。最小化修复仍然只在adjustToolbar()函数排队时生效 或者 在show()之后调用restoreState()(参见代码注释)。

class ToolBar : public QToolBar
{
    Q_OBJECT
  public:
    using QToolBar::QToolBar;

#ifdef Q_OS_LINUX
  protected:
    void showEvent(QShowEvent *e) override
    {
      QToolBar::showEvent(e);
      if (isFloating() && windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
        //  QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
        QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
        // If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
        //adjustToolbar();
      }
    }

  private slots:
    void adjustToolbar()
    {
      const bool vis = !isHidden();
      hide();
      setWindowFlag(Qt::X11BypassWindowManagerHint, false);
      if (vis)
        show();
    }
#endif
};
更新3: 如果在显示浮动QDockWidget之前恢复QMainWindow状态,则最小化问题也存在。实际上,使用“较旧”的Qt版本时,浮动小部件根本不会显示出来(在<= 5.9.5中不会,在>= 5.12.4中会,目前没有其他版本可供尝试)。因此,正确的方法是先show()主窗口,然后再restoreState()。不幸的是,这似乎对QToolBar无效。 更新4:已提交为QTBUG-78293

0

在 macOS 上似乎正常工作:

enter image description here


3
我认为OP环境是Linux。 - eyllanesc
2
是的,问题出在 Linux 上。 - SaurabhS

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