Solved take a widget from a QTableWidget
-
Hi my friends.
I have populated a QTableWidget with many QWidget's. I do this with setCellWidget.
I want to move widgets from one row to another, i want to swap rows.
It is possible to "take" a widget like "takeItem" and "set" it with "setItem" (i would use setCellWidget) ?
If i simple copy a pointer with "cellWidget()", then in a moment i use "setCellWidget" it will delete this widget.
Or i simply need to make a copy ?
Thanks for any answer.
Daniel -
Hi
When you use setCellWidget the QTableWidget will own the widget.
Reading the doc, there doesn't seems to be a way to take back ownership as it seems
cellWidget() only allows access to it not to take it back so to speak.So a way is to create function that can create the widgets as you want and
simply recreate the widgets when you want to "swap" them as
you cannot really make a copy as such.So you could make a function that takes widget and copy some data
from it and create new one with those data
or create a new with default value if widget is not supplied. -
Thanks for you answer.
I already read all docs , and i know that its not possible to take back ownership, ...
but you idea of recreate the widgets is very good, and its possible to do.
At this time, i am already trying another approach :m_tagsTable->verticalHeader()->moveSection(row,rowsel);
i also catch this moving section action with :
connect(m_tagsTable->verticalHeader(),SIGNAL(sectionMoved(int,int,int)),SLOT(tagsTableVerticalSectionMoved(int,int,int)));
And on place in which i catch this ... i change just this verticalHeader()... :
QTableWidgetItem* item1 = new QTableWidgetItem(QString::number(newVisualIndex+1)+"j"); m_tagsTable->setVerticalHeaderItem(newVisualIndex,item1);
but what now is very strange its, that newVisualIndex its not true. I mean he always give a number which is the first number which has at beginning, but not the actual row. My slot is :
void TagExtSearchDlg::tagsTableVerticalSectionMoved(int, int oldVisualIndex, int newVisualIndex)
and its same as in
http://doc.qt.io/qt-4.8/qheaderview.html#sectionMoved
so, this third argument is "newVisualIndex", but it give me like a not changing logicalIndex
So for example if at begin, it write in all rows of vertical header in succession : 1,2,3,4
After doing a drag drop from first row to second row, all is moving right but, it write at vertical header : 2, 1j, 3, 4
And result must be : 2, 2j, 3, 4
(of course i would write also in first row)
I put a "j" for a signal to know which header have changed my code, after a drag and drop.
Thanks for any more help.
Daniel -
Hi
Good to hear that recreate is possible if section move fails.
Also good idea as that is truly a column swap.I agree that I would expect new visual index be 2 also
when moved.
Could you check with
int QHeaderView::visualIndex(int logicalIndex) const
to see if its the SectionMoved that reports unexpected value or
if it really mean it. -
Hi
Its a row swap.
But i slowly encounter which problems arise using a moving section.
Problem its i react inside "cellEntered", and from this slot i do a "moveSection".
Problem its, that it makes recursive (until a 10 times may be), because moving a section also cause a new "cellEntered" activation.
At next i will try you approach, which i am sure not will be a problem.
Ok, not it will be easy at all, because i need to adjust may conexions of subbuttons from swapped rows.
Now i need to sleep, but i can give you all code of a part which actualize all rows :void TagExtSearchDlg::actualizeTagsTable(bool useAtagsList) { if (!useAtagsList) actualizeAtagsList();//useAtagsList mean we not actualize AtagsList setDefaultRelationTypes(); //m_tagsTable->clear(); m_tagsTable->setRowCount(m_atagsList.size()); //m_tagManager.actualizeAtagToIndex();//deprecated int* defaults[4] = { &typeDefaultSubset,&typeDefaultSSubset,&typeDefaultEquiv,&typeDefaultOther}; int col[4] = { 1,2,3,4}; const TagExtManager& tagManager = m_tagManager; for(int row=0;row<m_atagsList.size();row++) { int atag = m_atagsList.at(row); const TagExt& tag = tagManager.getTagQ(atag); QTableWidgetItem* item = new QTableWidgetItem(tag.m_name); item->setData(Qt::UserRole,QVariant(atag)); m_tagsTable->setItem(row,0,item); // QTableWidgetItem* hitem = new QTableWidgetItem(QString::number(row+1)); // hitem->setData(Qt::UserRole,QVariant(atag)); // m_tagsTable->setVerticalHeaderItem(row,hitem); } m_buttonHash.clear(); QSignalMapper* signalMapper1 = new QSignalMapper(this); QSignalMapper* signalMapper2 = new QSignalMapper(this); QSignalMapper* signalMapper3 = new QSignalMapper(this); typedef RelationClass RC; RelationClass classes[4] = { RC::partialOrder(), RC::strictPartialOrder(), RC::equivalenceRelation(), RC::directedGraph() }; m_tagManager.actualizeRelationsInv(); for(int t=0;t<4;t++) { if ((*defaults[t])==0) continue; m_tagManager.actualizeSpecRelations(*defaults[t]); m_tagManager.actualizeSpecRelationsInv(*defaults[t]); QList<int> arels; if (t<3) arels = m_relManager.getRelationTypeArels(classes[t]); else arels = m_relManager.getRelationTypeArels(); QSet<int> arelsS; for(int t=0;t<arels.size();t++) arelsS.insert(arels[t]); if (t<3) arelsS.remove(*defaults[t]); else { for(int tt=0;tt<3;tt++) arelsS.remove(*defaults[t]); } QString relName = m_relManager.getRelationTypeName(*defaults[t]); for(int row=0;row<m_atagsList.size();row++) { int atag = m_atagsList.at(row); const TagExt& tag = tagManager.getTagQ(atag); QPair<int,int> rowCol(row,t+1); //QTableWidgetItem* item2 = new QTableWidgetItem() int sizeUp = tag.m_specRelations.size(); int sizeDown = tag.m_specRelationsInv.size(); bool haveUp = sizeUp>0; bool haveMore = false; QSet<int> tagArelsS; for(int r=0;r<tag.m_relations.size();r++) tagArelsS.insert(tag.m_relations.at(r).arel); for(int r=0;r<tag.m_relationsInv.size();r++) tagArelsS.insert(tag.m_relationsInv.at(r).arel); for(int arel : tagArelsS) if (arelsS.contains(arel)) { haveMore=true; break;} bool haveDown = sizeDown>0; QWidget* w=0; if (haveUp || haveMore || haveDown) { //will have same botton QGroupBox* box = new QGroupBox(); QHBoxLayout* lay = new QHBoxLayout; if (haveUp) { QPushButton* but1 = new QPushButton("\u25b2"); but1->setToolTip(relName); lay->addWidget(but1); m_buttonHash.insert(but1,rowCol); connect(but1,SIGNAL(clicked()),signalMapper1,SLOT(map())); signalMapper1->setMapping(but1, qobject_cast<QWidget*>(but1)); } if (haveMore) { QPushButton* but2 = new QPushButton("\u2026"); lay->addWidget(but2); m_buttonHash.insert(but2,rowCol); connect(but2,SIGNAL(clicked()),signalMapper2,SLOT(map())); signalMapper2->setMapping(but2, qobject_cast<QWidget*>(but2)); } if (haveDown) { QPushButton* but3 = new QPushButton("\u25bc"); but3->setToolTip(relName); lay->addWidget(but3); m_buttonHash.insert(but3,rowCol); connect(but3,SIGNAL(clicked()),signalMapper3,SLOT(map())); signalMapper3->setMapping(but3, qobject_cast<QWidget*>(but3)); } box->setLayout(lay); //box->setFlat(true); box->setStyleSheet("border:0;"); w = box; } m_tagsTable->setCellWidget(row,col[t],w); } } connect(signalMapper1, SIGNAL(mapped(QWidget*)),this, SLOT(butUpClicked(QWidget*))); connect(signalMapper2, SIGNAL(mapped(QWidget*)),this, SLOT(butMoreClicked(QWidget*))); connect(signalMapper3, SIGNAL(mapped(QWidget*)),this, SLOT(butDownClicked(QWidget*))); }
-
Hi,
i already have managed it, to actualize (rewrite) 2 rows. And it works as expected.
In order to reuse code, last sended function reduce to less as 50 lines, and then is better, however use 2 subrutines.
Thanks for your help.
In case somebody also want to know how drag and drop rows from a QTableWidget with QWidgets, i send here my code :
I use cellClicked - signal for know a begginning place for a drag-and-drop :void TagExtSearchDlg::tagsTableCellPressed(int row, int) { m_clickedRow = row; }
In order to know a destination row of a drag and drop action i use signal "cellEntered". My slot is :
void TagExtSearchDlg::tagsTableCellEntered(int row, int) { int row2; if(m_clickedRow<row) row2=row-1; //down else if(m_clickedRow>row) row2=row+1; //up else return; if (m_orderStyleCombo->currentData().toInt()!=byCustom) return; //m_tagsTable->verticalHeader()->moveSection(rowsel,row); m_clickedRow = row; //swap rows inside m_atagsList if (m_atagsList.size()<=std::max(row2,row)) return; int tempAtag = m_atagsList.at(row); m_atagsList[row] = m_atagsList[row2]; m_atagsList[row2] = tempAtag; actualizeTagsTableRows(QList<int>()<<row2<<row); }
I also send all code used for actualize rows :
void TagExtSearchDlg::actualizeTagsTableRows(QList<int> rows) { int* defaults[4] = { &typeDefaultSubset,&typeDefaultSSubset,&typeDefaultEquiv,&typeDefaultOther}; const TagExtManager& tagManager = m_tagManager; for(int row : rows) { int atag = m_atagsList.at(row); const TagExt& tag = tagManager.getTagQ(atag); QTableWidgetItem* item = new QTableWidgetItem(tag.m_name); item->setData(Qt::UserRole,QVariant(atag)); m_tagsTable->setItem(row,0,item); } typedef RelationClass RC; RelationClass classes[4] = { RC::partialOrder(), RC::strictPartialOrder(), RC::equivalenceRelation(), RC::directedGraph() }; m_tagManager.actualizeRelationsInv(); for(int t=0;t<4;t++) { int defaultType = (*defaults[t]); if (defaultType==0) continue; m_tagManager.actualizeSpecRelations(defaultType); m_tagManager.actualizeSpecRelationsInv(defaultType); QSet<int> arelsS; actualizeTagsTableGetArelsS(arelsS,t==3,defaultType,classes[t]); QString relName = m_relManager.getRelationTypeName(defaultType); for(int row : rows) { int atag = m_atagsList.at(row); const TagExt& tag = tagManager.getTagQ(atag); actualizeTagsTableSubbuttons(tag,row,t+1,arelsS,relName); } } } void TagExtSearchDlg::actualizeTagsTableGetArelsS(QSet<int> &arelsS, bool isMoreCol, int defaultType, const RelationClass &cl) { QList<int> arels; if (!isMoreCol) arels = m_relManager.getRelationTypeArels(cl); else arels = m_relManager.getRelationTypeArels(); arelsS.clear(); for(int t=0;t<arels.size();t++) arelsS.insert(arels[t]); if (!isMoreCol) arelsS.remove(defaultType); else { for(int tt=0;tt<3;tt++) arelsS.remove(defaultType); } } void TagExtSearchDlg::actualizeTagsTableSubbuttons(const TagExt &tag, int row, int col, const QSet<int> &arelsS, QString relName) { QPair<int,int> rowCol(row,col); //QTableWidgetItem* item2 = new QTableWidgetItem() int sizeUp = tag.m_specRelations.size(); int sizeDown = tag.m_specRelationsInv.size(); bool haveUp = sizeUp>0; bool haveMore = false; QSet<int> tagArelsS; for(int r=0;r<tag.m_relations.size();r++) tagArelsS.insert(tag.m_relations.at(r).arel); for(int r=0;r<tag.m_relationsInv.size();r++) tagArelsS.insert(tag.m_relationsInv.at(r).arel); for(int arel : tagArelsS) if (arelsS.contains(arel)) { haveMore=true; break;} bool haveDown = sizeDown>0; QWidget* w=0; if (haveUp || haveMore || haveDown) { //will have same botton QGroupBox* box = new QGroupBox(); QHBoxLayout* lay = new QHBoxLayout; if (haveUp) { QPushButton* but1 = new QPushButton("\u25b2"); but1->setToolTip(relName); lay->addWidget(but1); m_buttonHash.insert(but1,rowCol); connect(but1,SIGNAL(clicked()),m_signalMapper1,SLOT(map())); m_signalMapper1->setMapping(but1, qobject_cast<QWidget*>(but1)); } if (haveMore) { QPushButton* but2 = new QPushButton("\u2026"); lay->addWidget(but2); m_buttonHash.insert(but2,rowCol); connect(but2,SIGNAL(clicked()),m_signalMapper2,SLOT(map())); m_signalMapper2->setMapping(but2, qobject_cast<QWidget*>(but2)); } if (haveDown) { QPushButton* but3 = new QPushButton("\u25bc"); but3->setToolTip(relName); lay->addWidget(but3); m_buttonHash.insert(but3,rowCol); connect(but3,SIGNAL(clicked()),m_signalMapper3,SLOT(map())); m_signalMapper3->setMapping(but3, qobject_cast<QWidget*>(but3)); } box->setLayout(lay); box->setStyleSheet("border:0;"); w = box; } m_tagsTable->setCellWidget(row,col,w); }
-
Super.
I tried yesterday if it was possible to steal the widget back using set Parent but
looking at the source code , made me realize that no matter what, the QTableWidget
would call deletelater on the cell widget so we would crash.So Its good to see you made recreate work and thank you for sharing your solution.
-
In case, also want to know how look this QTableWidget, i also send a picture :
https://goo.gl/photos/2dRkAcwTDz4kgXyY9
QTableWidget which i actualize are at upper left corner.
greatings, Daniel