stdware / qwindowkit

Cross-platform frameless window framework for Qt. Support Windows, macOS, Linux.
Apache License 2.0
490 stars 78 forks source link

以使用QWK的窗口为父窗口弹出另一个使用QWK的子窗口,父窗口丢失QWK特性 #51

Closed Ikok9c6s closed 6 months ago

Ikok9c6s commented 6 months ago

如下图所示,在example代码中创建MainWindow w1,并创建以w1为父窗口的w2。 两个窗口显示后,即使w2使用的是show不是exec,w1仍不能移动,右键无法触发系统菜单,也丢失了SnapLayout的特性。关掉w2后也无法恢复正常。而当我设置MainWindow属性为Qt::WA_DeleteOnClose,关闭w2,发生了崩溃,这是不合理的。 在我的项目中,我打算完全引入QWK作为无边框窗口的解决方案,所以软件窗口打开的情况下,会频繁地弹出子对话框,这个问题是致命的,希望作者能关注下。 image

SineStriker commented 6 months ago
  1. "而当我设置MainWindow属性为Qt::WA_DeleteOnClose,关闭w2,发生了崩溃,这是不合理的。"这是你自己的问题,因为你把w2创建在了栈上,程序退出会double free。
  2. 参考 #50 ,当使用了QWK的窗口使用了具有Qt::WA_NativeWindow属性的widget时,顶级窗口将会收不到WM_NCHITTEST消息,这个我研究了很久暂时没有找到解决办法。针对你的需求,我建议最外面套一层使用了QWK的widget作为顶级窗口。
Ikok9c6s commented 6 months ago

1.发生崩溃并不是在程序退出时,在关闭w2时就会发生,所以我并不认为是双重析构的问题 2.你的意思是QMainWindow具有NativeWindow的属性才导致的这个后果吗?按你说的,是要在QWidget里套一个QMainWindow?

SineStriker commented 6 months ago

1.发生崩溃并不是在程序退出时,在关闭w2时就会发生,所以我并不认为是双重析构的问题 2.你的意思是QMainWindow具有NativeWindow的属性才导致的这个后果吗?按你说的,是要在QWidget里套一个QMainWindow?

我说错了,只要删除在栈上的东西就会引发异常,而不是double free的问题,不信你可以随便写个程序在栈上创建一个int然后delete它试试,开调试看看异常断在什么地方。第二点对的,在QWidget里套一个QMainWindow,确保要移动进无边框窗口的那个QWidget永远不是native window,因为顶级窗口永远是native window。

Ikok9c6s commented 6 months ago

我刚尝试了一下,和 #43 类似,通过设置QApplication的Qt::AA_DontCreateNativeWidgetSiblings可以解决这个问题,暂时不清楚原因

Ikok9c6s commented 6 months ago

这个属性我记得在FramelessHelper的初始化函数中被设置了,QWK出于什么原因取消了这个属性的设置呢

SineStriker commented 6 months ago

我刚尝试了一下,和 #43 类似,通过设置QApplication的Qt::AA_DontCreateNativeWidgetSiblings可以解决这个问题,暂时不清楚原因

我试了一下似乎只能让标题栏特性回来,如果不启用系统边框的话下面的边角是resize不了的,另外画面会有严重错位,所以我还是建议套一层。这个设置可能是忘了迁移,但是现在貌似设了也没啥用...

Ikok9c6s commented 6 months ago

关于2还是不太清楚究竟该怎么做,我的项目中软件界面使用的是QMainWindow,子对话框使用的是QDialog,我该将哪个窗口套上一层QWidget呢?套上之后,widgetWindowAgent->setup的是this(QWidget),还是被包含的QMainWindow,QDialog?

SineStriker commented 6 months ago

关于2还是不太清楚究竟该怎么做,我的项目中软件界面使用的是QMainWindow,子对话框使用的是QDialog,我该将哪个窗口套上一层QWidget呢?套上之后,widgetWindowAgent->setup的是this(QWidget),还是被包含的QMainWindow,QDialog?

我没搞懂你想做什么,你就是想弹一个对话框然后对话框也是无边框吗?

Ikok9c6s commented 6 months ago

是的,主界面和对话框都是用的QWK的无边框

SineStriker commented 6 months ago

我用以下代码试了,发现没有出现任何问题。

class FramelessDialog : public QDialog {
public:
    FramelessDialog(QWidget *parent = nullptr) : QDialog(parent) {
        auto agent = new QWK::WidgetWindowAgent(this);
        agent->setup(this);

        auto btn = new QPushButton("ok");
        connect(btn, &QPushButton::clicked, this, &QDialog::accept);

        auto layout = new QHBoxLayout();
        layout->addWidget(btn);
        setLayout(layout);
    }
};

// MainWindow function
FramelessDialog dlg(this);
dlg.exec();

注意不能是show。在dialog存在的时候主窗口仍然存在一个是本地窗口的child,因为exec的时候主窗口被阻塞就可以忽略主窗口特性丢失的问题,show的时候就不行。

SineStriker commented 6 months ago

开启QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings)似乎可以解决这个问题,但是还是请注意不要把具有native句柄的widget作为内嵌在父窗口中的子控件,作为dialog倒确实看起来没什么问题。

Ikok9c6s commented 6 months ago

我用以下代码试了,发现没有出现任何问题。

class FramelessDialog : public QDialog {
public:
    FramelessDialog(QWidget *parent = nullptr) : QDialog(parent) {
        auto agent = new QWK::WidgetWindowAgent(this);
        agent->setup(this);

        auto btn = new QPushButton("ok");
        connect(btn, &QPushButton::clicked, this, &QDialog::accept);

        auto layout = new QHBoxLayout();
        layout->addWidget(btn);
        setLayout(layout);
    }
};

// MainWindow function
FramelessDialog dlg(this);
dlg.exec();

注意不能是show。在dialog存在的时候主窗口仍然存在一个是本地窗口的child,因为exec的时候主窗口被阻塞就可以忽略主窗口特性丢失的问题,show的时候就不行。

使用如下代码测试还是丢失了QWK特性。 image 不指定w2的parent是w1就是正常的

Ikok9c6s commented 6 months ago

开启QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings)似乎可以解决这个问题,但是还是请注意不要把具有native句柄的widget作为内嵌在父窗口中的子控件,作为dialog倒确实看起来没什么问题。

我是否可以理解为现在推荐的做法是: 1.设置QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings) 2.软件界面使用QMainWindow+QWK,对话框使用QDialog+QWK 3.布局中的控件不能有Qt::WA_NativeWindow的属性

SineStriker commented 6 months ago

开启QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings)似乎可以解决这个问题,但是还是请注意不要把具有native句柄的widget作为内嵌在父窗口中的子控件,作为dialog倒确实看起来没什么问题。

我是否可以理解为现在推荐的做法是: 1.设置QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings) 2.软件界面使用QMainWindow+QWK,对话框使用QDialog+QWK 3.布局中的控件不能有Qt::WA_NativeWindow的属性

是的,你总结得非常正确,我觉得我要更新一下readme了