前两天在测试的时候,发现QListWidget 类 调用 listWidget->itemWidget(pItem) 函数时返回了Q_NULLPTR,这不是一个平常会遇到的问题,探查了一下资料,找到了问题的原因,记录一下这个问题。
首先,我们创建一个简单的QListWidget,并且自定义其item:
namespace Ui {
class Item;
}
class Item : public QWidget
{
Q_OBJECT
public:
explicit Item(const QString& strName, QWidget *parent = Q_NULLPTR);
~Item();
QString getPoseName();
private slots:
void on_pushButton_clicked(); // 按钮点击的时候发送信号 signal_delete
signals:
void signal_delete();
private:
Ui::Item* ui;
};
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked(); //增加item,不进行初始化添加的目的是为了方便的看问题
void slot_delete();
private:
Ui::MainWindow *ui;
};
接下来是实现部分:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ui_item.h"
#include <QDebug>
#define SAFEDELETE(p) if(Q_NULLPTR != (p)) { delete (p);(p) = Q_NULLPTR;}
Item::Item(const QString& strName, QWidget *parent) : QWidget(parent), ui(new Ui::Item)
{
ui->setupUi(this);
ui->label->setText(strName);
}
Item::~Item()
{
delete ui;
}
QString Item::getPoseName()
{
return ui->label->text();
}
void Item::on_pushButton_clicked()
{
emit signal_delete();
}
上面的这部分是Item类的实现。
主界面的按钮槽,点击一次会新增一个item,并且打印相关的信息。item能够新增成功并且打印信息正常。
void MainWindow::on_pushButton_clicked()
{
static int nIndex = 1;
Item* pTest = new Item(QString("test_%1").arg(nIndex++));
QListWidgetItem* pItem = new QListWidgetItem(ui->listWidget);
qDebug() << "create QWidget" << (pTest != Q_NULLPTR ? pTest->getPoseName() : "pTest == Q_NULL ") << pTest;
qDebug() << "create listItem" << pItem;
ui->listWidget->addItem(pItem);
ui->listWidget->setItemWidget(pItem, pTest);
connect(pTest, &Item::signal_delete, this, &MainWindow::slot_delete);
}
下面是删除一行的操作:
void MainWindow::slot_delete()
{
QListWidgetItem* item = ui->listWidget->takeItem(0); //这边写为0是为了方便测试
auto pTest = dynamic_cast<Item*>(ui->listWidget->itemWidget(item));
qDebug() << "delete listItem" << item;
qDebug() << "delete QWidget" << (pTest != Q_NULLPTR ? pTest->getPoseName() : "pTest == Q_NULL ") << pTest;
SAFEDELETE(item);
SAFEDELETE(pTest);
}
运行工程时发现:
刚开始是没有取打印信息的,是在调试的发现 点击删除按钮后 Item 的数量和 QListWidgetItem 的数量不相等,觉得奇怪,于是加了断点和打印信息看下是不是缺了什么。通过打印信息发现:
新增一行时信息正常,但是在删除一行的时候出现了 pTest 为 Q_NULLPTR;咦!这就奇怪了,我们在新增行的时候明明已经将Item通过setItemWidget函数添加到QListWidgetItem上了,为什么后面取的时候,反而取不到呢?
这个时候第一反应是看下 itemWidget 这个接口是不是会有返回失败的情况,于是打开了帮助文档瞄了一眼:
QWidget *QListWidget::itemWidget(QListWidgetItem *item) const
Returns the widget displayed in the given item.
This function was introduced in Qt 4.1.
See also setItemWidget() and removeItemWidget().
看到是不会返回失败,为了确认,还专门去看了setItemWidget()、removeItemWidget()这俩接口,同样没发现问题。
这个时候就百思不得其解,然后返回继续撸了这整个的流程,看看是不是在某个时候不小心释放了QWidget这个指针,撸完的结果是整个流程并没有问题,也没有在其他任何地方释放Item的指针。
静下来想一想,决定还是再看一看帮助文档,再次翻开帮助文档并认真的读了解释,这一次当看到 displayed 的时候瞬间就明白犯了什么错误了。
大家看一看这两行代码:
QListWidgetItem* item = ui->listWidget->takeItem(0);
auto pTest = dynamic_cast<Item*>(ui->listWidget->itemWidget(item));
首先我们使用了takeItem接口删除并取得一个QListWidgetItem,注意是先从QListWidget上删除一个QListWidgetItem,然后返回删除的这个QListWidgetItem,因此当下面调用itemWidget接口时,这个QListWidgetItem是不可见的。所以该接口返回了Q_NULLPTR。
换成下面:
QListWidgetItem* item = ui->listWidget->item(0);
auto pTest = dynamic_cast<Item*>(ui->listWidget->itemWidget(item));
ui->listWidget->takeItem(0);
我们首先通过item接口获取到QListWidgetItem,然后通过该QListWidgetItem获取QWidget,最后从QListWidget上将该QListWidgetItem删除,程序正常。
单对这个问题来说,这个问题是很简单的,本应该第一次在查看帮助文档的时候就能够发现问题,但是我只是瞄了一眼,并没有仔细的理解,也就是这些着急造成了时间成本的增加。后续在处理问题时,
应该脚踏实地的弄清楚每一步的操作和其中的涵义,不要急着解决问题,可能会节约很多时间。