为 QMainWindow 恢复浮动工具栏时出现问题
Problem in restoring floating toolbar for QMainWindow
我在恢复浮动工具栏中具有 QCombobox 的 QMainWindow 状态时遇到问题。恢复浮动工具栏后,我的 QCombobox 无法获得焦点,直到我单击工具栏句柄并移动它。
以下是显示问题的 gif,使用 QT 5.13。
文件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);
}
在 macOS 上似乎可以正常工作:
好的,解决方法是在工具栏首次显示并浮动时重置工具栏上的 window 标志。我通过查看一旦工具栏 dropped after being dragged(但未插入主 window)会发生什么来追踪这一点。 (它调用 setWindowState()
,在这种情况下所做的只是隐藏工具栏,调用 updateWindowFlags()
,然后再次显示它)。
这可以通过 QMainWindow::showEvent()
或安装在 QToolBar
上的 eventFilter
来处理。我认为它比前者更简单。
更新:这个问题实际上发生在工具栏首次显示时,即使不是在应用程序启动时,例如一旦应用程序启动,用户从切换视图菜单中。我也更新了下面的代码来解决这个问题。请参阅下面关于最小化主 window.
的另一个问题的注释
我从 MCVE 添加到 MainWindow
class:
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.
我还注意到 另一个问题 最初是浮动的工具栏。当主 window 最小化时,工具栏不会隐藏,而是保留在屏幕上的位置。无论工具栏中有什么(例如,没有组合框,只有 QActions)。此解决方法也可以解决该问题(请参阅代码注释),但仅在第二次最小化 window 时。第一次最小化需要更好的解决方法。
其他人可以证实这一点吗?这可能是一个比可编辑组合更大的问题,如果之前没有人注意到我会感到惊讶。
我想无论哪种方式,这都应该作为 Qt 错误提交。
UPDATE2:此版本还修复了最小化问题。我猜想在 QMainWindow::showEvent()
之后发生了一些事情,改变了工具栏的行为方式。这解释了为什么上述解决方法仅在第一次最小化后才有效。因此,为以后安排工具栏 "fix" 也可以解决这个问题。
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;
};
ADDED:一个 QToolBar
subclass,它自己应用解决方法,在 QMainWindow
中没有什么特别需要的。如果 restoreState()
仅在 show()
之后调用(请参阅代码注释),则最小化修复仍然仅在 adjustToolbar()
函数排队 或 时有效。
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
};
UPDATE3:最小化问题也存在于浮动 QDockWidget
if 之前恢复 QMainWindow
状态它被显示。事实上,对于 "older" Qt 版本,浮动小部件根本不显示(<= 5.9.5 不显示,但 >= 5.12.4,中间没有任何东西可以尝试 ATM) .所以正确的做法是先show()
主window然后然后restoreState()
。不幸的是,这似乎不适用于 QToolBar
.
UPDATE4:归档为 QTBUG-78293
我在恢复浮动工具栏中具有 QCombobox 的 QMainWindow 状态时遇到问题。恢复浮动工具栏后,我的 QCombobox 无法获得焦点,直到我单击工具栏句柄并移动它。
以下是显示问题的 gif,使用 QT 5.13。
文件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);
}
在 macOS 上似乎可以正常工作:
好的,解决方法是在工具栏首次显示并浮动时重置工具栏上的 window 标志。我通过查看一旦工具栏 dropped after being dragged(但未插入主 window)会发生什么来追踪这一点。 (它调用 setWindowState()
,在这种情况下所做的只是隐藏工具栏,调用 updateWindowFlags()
,然后再次显示它)。
这可以通过 QMainWindow::showEvent()
或安装在 QToolBar
上的 eventFilter
来处理。我认为它比前者更简单。
更新:这个问题实际上发生在工具栏首次显示时,即使不是在应用程序启动时,例如一旦应用程序启动,用户从切换视图菜单中。我也更新了下面的代码来解决这个问题。请参阅下面关于最小化主 window.
的另一个问题的注释我从 MCVE 添加到 MainWindow
class:
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.
我还注意到 另一个问题 最初是浮动的工具栏。当主 window 最小化时,工具栏不会隐藏,而是保留在屏幕上的位置。无论工具栏中有什么(例如,没有组合框,只有 QActions)。此解决方法也可以解决该问题(请参阅代码注释),但仅在第二次最小化 window 时。第一次最小化需要更好的解决方法。
其他人可以证实这一点吗?这可能是一个比可编辑组合更大的问题,如果之前没有人注意到我会感到惊讶。
我想无论哪种方式,这都应该作为 Qt 错误提交。
UPDATE2:此版本还修复了最小化问题。我猜想在 QMainWindow::showEvent()
之后发生了一些事情,改变了工具栏的行为方式。这解释了为什么上述解决方法仅在第一次最小化后才有效。因此,为以后安排工具栏 "fix" 也可以解决这个问题。
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;
};
ADDED:一个 QToolBar
subclass,它自己应用解决方法,在 QMainWindow
中没有什么特别需要的。如果 restoreState()
仅在 show()
之后调用(请参阅代码注释),则最小化修复仍然仅在 adjustToolbar()
函数排队 或 时有效。
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
};
UPDATE3:最小化问题也存在于浮动 QDockWidget
if 之前恢复 QMainWindow
状态它被显示。事实上,对于 "older" Qt 版本,浮动小部件根本不显示(<= 5.9.5 不显示,但 >= 5.12.4,中间没有任何东西可以尝试 ATM) .所以正确的做法是先show()
主window然后然后restoreState()
。不幸的是,这似乎不适用于 QToolBar
.
UPDATE4:归档为 QTBUG-78293