前兩天在測試的時候,發現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刪除,程序正常。
單對這個問題來說,這個問題是很簡單的,本應該第一次在查看幫助文檔的時候就能夠發現問題,但是我只是瞄了一眼,並沒有仔細的理解,也就是這些着急造成了時間成本的增加。後續在處理問題時,
應該腳踏實地的弄清楚每一步的操作和其中的涵義,不要急着解決問題,可能會節約很多時間。