更改对话框父级禁用拖放
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了更改对话框父级禁用拖放相关的知识,希望对你有一定的参考价值。
我有一个主窗口和一个对话框,通过单击按钮从该窗口打开。出于性能原因,有一个对话框缓存,它保留对话框的实例,并且只在打开对话框而不是创建新实例时显示它。在对话框中,有一个带有一些项目的QListWidget
,可以通过拖放来改变顺序。这在我第一次打开对话框时有效,但当我关闭它并再次打开它时,我无法丢弃这些项目,我得到了一个Qt::ForbiddenCursor
。
这个问题似乎是在关闭对话框时调用setParent(nullptr)
引起的(或者可能只是通过更改父对象)。如果我删除此行,拖放工作。但是我需要这个来防止父对象被父对象删除,对话框可以在不同的上下文中有不同的父对象(这在我的简化示例中并不明显)。知道这种方法有什么问题吗?我的Qt版本是5.9.3。这可能是Qt错误吗?
MainWindow.h:
#include "ui_mainwindow.h"
#include "dialog.h"
#include <QPushButton>
#include <QMainWindow>
#include <memory>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
dialog.reset(new Dialog(this));
dialog->setAttribute(Qt::WA_DeleteOnClose, false);
connect(ui->button, &QPushButton::pressed, [&]
{
dialog->setParent(this, dialog->windowFlags());
dialog->open();
});
}
~MainWindow()
{
delete ui;
}
private:
Ui::MainWindow* ui;
std::unique_ptr<Dialog> dialog;
};
Dialog.h:
#include "ui_dialog.h"
#include <QDialog>
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget* parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->listWidget->addItem("first");
ui->listWidget->addItem("second");
ui->listWidget->addItem("third");
}
~Dialog()
{
delete ui;
}
public slots:
virtual void reject() override
{
setParent(nullptr);
QDialog::reject();
}
private:
Ui::Dialog* ui;
};
Dialog.ui - 使用QListWidget和拒绝按钮的简单对话框
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>548</width>
<height>397</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="listWidget">
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
MainWindow.ui - 带一个按钮的默认主窗口
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>316</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QPushButton" name="button">
<property name="geometry">
<rect>
<x>40</x>
<y>30</y>
<width>80</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>PushButton</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
main.cpp中
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
下面重现了这个问题。这确实是一个Qt bug。 OP报告了这个错误:https://bugreports.qt.io/browse/QTBUG-70240
问题是因为QWidget
在Qt::Window
标志关闭的同时重新创建了drop网站,调用了QWindowsWindow::updateDropSite
,它执行了错误的操作并调用了setDropSiteEnabled(false)
。
两个相同的解决方法是:
dialog->setParent(newParent)
被替换为:auto flags = dialog->windowFlags(); dialog->setParent(newParent, {}); dialog->setWindowFlags(flags);
dialog->setParent(nullptr)
被替换为:dialog->setParent(nullptr, dialog->windowFlags());
第一个解决方法撤消了小部件的损坏状态。第二种解决方法不是,即需要始终使用,否则必须调用第一个解决方法一次以恢复可用的放置目标状态。
// https://github.com/KubaO/stackoverflown/tree/master/questions/dialog-parent-dnd-52061919
#include <QtWidgets>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget ui;
QVBoxLayout layout{&ui};
QPushButton button{"Toggle List"};
QCheckBox workaround1{"Workaround 1"};
QCheckBox workaround2{"Workaround 2"};
for (auto w : QWidgetList{&button, &workaround1, &workaround2}) layout.addWidget(w);
workaround2.setChecked(true);
QListWidget listWidget;
Q_ASSERT(!listWidget.testAttribute(Qt::WA_DeleteOnClose));
listWidget.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
listWidget.setDragDropMode(QAbstractItemView::DragDrop);
listWidget.setDefaultDropAction(Qt::MoveAction);
for (auto s : QStringList{"first", "second", "third"}) listWidget.addItem(s);
QObject::connect(&button, &QPushButton::pressed, [&] {
if (!listWidget.parent()) {
if (!workaround1.isChecked())
listWidget.setParent(&button, listWidget.windowFlags());
else {
auto flags = listWidget.windowFlags();
listWidget.setParent(&button, {});
listWidget.setWindowFlags(flags);
}
listWidget.show();
} else {
if (!workaround2.isChecked())
listWidget.setParent(nullptr);
else
listWidget.setParent(nullptr, listWidget.windowFlags());
listWidget.close();
}
});
ui.setMinimumSize(320, 200);
ui.show();
return app.exec();
}
以上是关于更改对话框父级禁用拖放的主要内容,如果未能解决你的问题,请参考以下文章