有时我们需要 QListWidget 完成更复杂的操作,而不仅限于添加文本或者图标,那么就会使用到 setItemWidget 函数,但是这也会伴生一个问题,插入自定义组件后,QListWidget 对选项点击事件的获取会收到阻塞,因为点击会有概率落到我们的自定义组件上,这时候我们可以这样解决该问题:
方案1:
组件1 *1 = new 组件1(组件有点击信号)
组件2 *2 = new 组件2(组件有点击信号)
QListWidgetItem *temporary_item1 = new QListWidgetItem();
QListWidgetItem *temporary_item2 = new QListWidgetItem();
QListWidget->addItem(temporary_item);
QListWidget->setItemWidget(temporary_item1, 1);
QListWidget->addItem(temporary_item2);
QListWidget->setItemWidget(temporary_item2, 2);
connect(1, &组件1::点击信号, this, [&](){LF_favoriteList->item(1)->setSelected(true);});
connect(2, &组件2::点击信号, this, [&](){LF_favoriteList->item(2)->setSelected(true);});
这样我们就可以通过组件的点击信号来手动触发 QListWidget 的点击事件,选中选项,但是问题也很明显,如果选项非静态的话呢?静态的情况下麻烦一点一个一个写好放进去就ok了,动态的话行不通,这就使用到方案2了
方案2(推荐->适用更复杂的自定义组件):
void MediaLogicGO::favoraListLoad(QString text, QString logo_path){
Nwidget *temporary = new Nwidget();
temporary->setAttribute(Qt::WA_DeleteOnClose);
QHBoxLayout *temHbox = new QHBoxLayout(temporary);
NLabel * favora_logo = new NLabel(temporary);
NLineEdit *lineEdit = new NLineEdit(temporary);
NLabel *favora_rename = new NLabel(temporary);
NLabel *favora_remove = new NLabel(temporary);
favora_rename->setSyncEnable(true);
favora_remove->setSyncEnable(true);
favora_logo->setSyncEnable(true);
favora_logo->setFixedHeight(23);
favora_rename->setFixedHeight(23);
favora_remove->setFixedHeight(23);
favora_logo->setAdjustFixedWidth(true);
favora_rename->setAdjustFixedWidth(true);
favora_remove->setAdjustFixedWidth(true);
favora_rename->setdrawType(NT::DRAWTYPE_ROUND);
favora_remove->setdrawType(NT::DRAWTYPE_ROUND);
favora_rename->setSyncPixdrawType(NT::DRAWTYPE_RECT, 3);
favora_remove->setSyncPixdrawType(NT::DRAWTYPE_RECT, 3);
favora_rename->setSyncBackColorOption({{WIN_MOUSE_ATTRIBUTE.WIN_ENTER, QColor(255,255,255,230)}});
favora_remove->setSyncBackColorOption({{WIN_MOUSE_ATTRIBUTE.WIN_ENTER, QColor(255,255,255,230)}});
favora_logo->setPixmapC(logo_path);
favora_rename->setPixmapC(":/PIXMAP/bench/private-rename.png");
favora_remove->setPixmapC(":/PIXMAP/bench/private-remove.png");
lineEdit->setStyleSheet("QLineEdit{background:transparent; border:0px; color:rgba(89,89,89,200); font:12px;}");
lineEdit->setReadOnly(true);
lineEdit->setText(text);
temHbox->addWidget(favora_logo);
temHbox->addWidget(lineEdit);
temHbox->addWidget(favora_rename);
temHbox->addWidget(favora_remove);
temHbox->setAlignment(Qt::AlignVCenter);
temHbox->setContentsMargins(0,0,0,0);
temHbox->setSpacing(0);
QListWidgetItem *temporary_item = new QListWidgetItem();
LF_favoriteList->addItem(temporary_item);
LF_favoriteList->setItemWidget(temporary_item, temporary);
temporary->setObjectName(QString::number(RdoMap_Integer["Favora-item-addtion"]));
lineEdit->setObjectName(QString::number(RdoMap_Integer["Favora-item-addtion"]));
favora_logo->setObjectName(QString::number(RdoMap_Integer["Favora-item-addtion"]));
favora_rename->setObjectName(QString::number(RdoMap_Integer["Favora-item-addtion"]));
favora_remove->setObjectName(QString::number(RdoMap_Integer["Favora-item-addtion"]));
connect(temporary, &Nwidget::msnm_rls, this, &MediaLogicGO::favora_selection);
connect(lineEdit, &NLineEdit::msnm_rls, this, &MediaLogicGO::favora_selection);
connect(favora_logo, &NLabel::msnm_rls, this, &MediaLogicGO::favora_selection);
connect(favora_rename, &NLabel::msnm_rls, this, [&](QString name){favora_selection(name);favoraItemAction(1);});
RdoMap_Integer["Favora-item-addtion"] += 1;
}
void MediaLogicGO::favora_selection(QString name){
QListWidgetItem *currantItem = Favora_ListWidget->item(name.toInt());
if (currantItem != NULL) currantItem->setSelected(true);
}
如以上实例代码,我们添加了一个由logo图标,文本框,两个操作按钮的自定义组件,并给他们做了信号连接,正常情况下,QListWidget 选项会被自定义组件遮挡一部分,非遮挡部分正常点击选中
例如左图这个情况,大概率是全部遮挡了,那么为了解决这个问题,我们把自定义组件、以及组件中的文本框、logo、重命名和删除组件 的点击事件做一个信号连接,把对象名称传出来,对象名称我们就设置该组件的行数,以此手动触发选项的选中事件,其中重命名按钮和删除按钮触发选项的选中还触发该组件的点击,目的是为了避免已经操作了按钮但是选项并没有选中的尴尬情况
效果:
既然触发了选项的点击,那就好办了,使用 QListWidget 的 itemSelectionChanged 信号获取选中数据,这里我获取文本框的文本,也可以获取 QListWidgetItem 或者 选中的行,对应操作需要什么数据就获取什么
void MediaLogicCT::priLiswItemChange(/*QListWidgetItem *Item*/){
QList<QListWidgetItem*> items = LF_favoriteList->selectedItems();
QList<QString> texts;
foreach(QListWidgetItem *item, items)
{
int item_row = LF_favoriteList->row(item);
NLineEdit *edit = LF_favoriteList->itemWidget(item)->findChild<NLineEdit*>(QString::number(item_row));
QString text = edit->text();
texts.append(text);
}
qDebug() << "当前选项" << texts.last();
qDebug() << "所选选项" << texts;
RdoMap_String["list-text"] = texts.last();
}
下面是操作按钮对应的函数
void MediaLogicGO::favoraItemAction(int Type){
switch (Type) {
case 0:
qDebug() << RdoMap_String["list-text"] << "选项即将删除";
在这里编写删除的逻辑代码
break;
case 1:
qDebug() << RdoMap_String["list-text"] << "选项即将重命名";
在这里编写删除的逻辑代码
break;
}
}
完成,以上仅仅是个示例,按这个思路写就行了
好吧,这个情况与仅仅添加QListWidgetItem不同,当删除选项时必须得先删除选项绑定的 自定义组件,因为自定义组件是绑定在 Item 上的,不这么做会导致系统崩溃,下面补充下删除的思路
删除方案1: 全部清除再重新添加
优点: 比较安全稳定
缺点: 数据大的时候,如几百上千条的时候,就卡了,得放线程里跑
int favora_size = Favora_ListWidget->count(); //数据总行数
int row = 0; //行数
int expectrow = 0; //首行
while (row < favora_size) //小于总行数的情况下,行数累加
{
QListWidgetItem *current_item = Favora_ListWidget->item(expectrow); //获取第一个选项
QWidget *itemWidget = Favora_ListWidget->itemWidget(current_item); //获取对应自定义组件
if (itemWidget) //如果有自定义组件
{
Favora_ListWidget->removeItemWidget(current_item);
delete itemWidget; //删掉
}
if (current_item != NULL) //如果选项不为空(有效)
{
delete Favora_ListWidget->takeItem(expectrow); 删掉
}
row++;
}
//这是测试数据,我们对应把测试数据的需要删除的项也给删了
RdoMap_ListStr["ttt"].removeAt(RdoMap_Integer["list-row"]);
foreach(QString text, RdoMap_ListStr["ttt"]) //重新加载数据
{
favoraListLoad(text, ":/PIXMAP/bench/private-logo.png");
}
删除方案2: 单个删除
优点: 不用大动干戈的做删除添加
缺点: 删除掉某个数据之后,这个数据之后的选项,选中时点击的选项和选中不一致,因为索引往 前掉一位了,也就是说后面的这些选项,本来索引是 6的,前面的数据删了一个,索引应该改为5才能正常对应上,但是我们的索引在添加数据时写入作为对象名称了,你改的话还得后面的元素全部改,再者貌似选项没有选中时,还获取不到 Item里面的自定义组件。
*****所以,我们需要创建一个列表,用于维护索引改变的情况,已删除的项在列表里置为false状态,当选定 10 索引的选项时,判断下 10 索引之前的选项有几个是状态为false的,将索引减去为false的数据总和,即可得出正确的索引,选的到正确的索引,其他的也步入正轨了,对象名称仅仅是这个作用而已
如我们有 10个选项,已经删了3个,选中第8个,
QList<bool> stateList({true, true, true, true, true, true, true, true, true, true});
//1、3、5 已删除
stateList[1] = false;
stateList[3] = false;
stateList[5] = false;
//选中索引8的选项 ************************************
int count = 0;
foreach(bool state, stateList.mid(0, 8)) if (!state) count++;
qDebug() << "当前正确的选项" << 8 - count;
我们只用在 favora_selection 函数加上 星号之后的代码,星号之前的除了已定义好的列表,将选项置为False的操作在删除 Item时已经做了。删除的代码改为针对单个选项就即可, 后面再补充 stateList 对应 expectrow 的项置为false就行了
int expectrow = RdoMap_Integer["list-row"];
QListWidgetItem *current_item = Favora_ListWidget->item(expectrow);
QWidget *itemWidget = Favora_ListWidget->itemWidget(current_item);
if (itemWidget)
{
Favora_ListWidget->removeItemWidget(current_item);
delete itemWidget;
}
if (current_item != NULL)
{
delete Favora_ListWidget->takeItem(expectrow);
}