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

OMG*_*OMG 6 linux qt qcombobox qtoolbar qt5

在浮动工具栏中恢复具有QCombobox的QMainWindow状态时,我看到一个问题。恢复浮动工具栏后,我的QCombobox无法获得焦点,直到我单击工具栏手柄并将其移动。以下是显示问题的gif,使用QT 5.13。 在此处输入图片说明

文件float_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
Run Code Online (Sandbox Code Playgroud)

档案:main.cpp

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

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

    return a.exec();
}
Run Code Online (Sandbox Code Playgroud)

文件: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
Run Code Online (Sandbox Code Playgroud)

档案: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);
}
Run Code Online (Sandbox Code Playgroud)

Max*_*rno 2

好的,解决方法是在工具栏首次显示且浮动时重置工具栏上的窗口标志。我通过观察工具栏被拖动(但未插入主窗口)后被放下后会发生什么来追踪这一点。(它调用setWindowState(),在这种情况下所做的就是隐藏工具栏,调用updateWindowFlags(),然后再次显示它)。

这可以从QMainWindow::showEvent()eventFilter安装到 上进行处理QToolBar。我认为它比前者更简单。

更新:只要工具栏首次显示,即使不是在应用程序启动时,例如应用程序启动后用户从切换视图菜单中显示,实际上就会发生此问题。我也更新了下面的代码来解决该问题。请参阅下面有关最小化主窗口的另一个问题的注释。

我将其添加到MainWindowMCVE 的课程中​​:

  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.
Run Code Online (Sandbox Code Playgroud)

我还注意到最初浮动的工具栏存在另一个问题。当主窗口最小化时,工具栏不会隐藏,而是保留在屏幕上的位置。无论工具栏中有什么(例如,没有组合框,只有 QActions)。此解决方法也可以解决该问题(请参阅代码注释),但仅限第二次窗口最小化。第一次最小化需要更好的解决方法。

其他人可以证实这一点吗?这可能是一个比可编辑组合更大的问题,如果之前没有人注意到,我会感到惊讶。

我想无论怎样这都应该被归档为 Qt bug。

UPDATE2:此版本还修复了最小化问题。我猜想之后会发生一些事情,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;
};
Run Code Online (Sandbox Code Playgroud)

添加:一个QToolBar子类,它自己应用解决方法,在QMainWindow. 最小化修复仍然仅在adjustToolbar()函数排队restoreState()在之后调用时才起作用show()(请参阅代码注释)。

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
};
Run Code Online (Sandbox Code Playgroud)

UPDATE3 :QDockWidget 如果QMainWindow在显示之前恢复状态,则浮动也存在最小化问题。事实上,对于“较旧”的 Qt 版本,浮动小部件根本不会显示(对于 <= 5.9.5 不显示,但对于 >= 5.12.4 则显示,中间没有任何东西可以尝试 ATM)。所以正确的做法是show()先到主窗口,然后再到主窗口 restoreState()。不幸的是,这似乎不适用于QToolBar.

更新 4:归档为QTBUG-78293